feat(phase4): 全部业务模块迁移到 modules/(结构迁移)

- 21 个模块目录(auth/system/dictionary/configuration/cms/member/region/link/notice/job/log/upload/index)
- 每个模块 router.py(合并模块导出多个 router)/schemas.py/service.py/repository.py/models.py
- routers/v1.py 与 routers/__init__.py 改为直接引用 modules,前缀与权限串原样保留
- core/middleware.py 懒加载指向 modules.system.menu / modules.log;core/redis_client.py 指向 modules.job.scheduler
- apps/views|services|forms 全部改为 re-export 垫片
- 迁移后 grep 守门通过:modules/core 无 apps/extensions 残留引用
- 实测登录、27+ 模块接口、写操作 commit、操作日志落库均正常

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude
2026-08-07 10:13:30 +08:00
co-authored by Claude
parent b2c7b93786
commit 64a5504396
156 changed files with 7977 additions and 7533 deletions
+2 -44
View File
@@ -1,44 +1,2 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
# 广告表单验证
class AdForm(BaseModel):
# 广告ID
id: Optional[int] = Field(None, description="广告ID")
# 广告标题
title: str = Field(..., min_length=1, max_length=255, description="广告标题")
# 广告位ID
sort_id: int = Field(..., ge=0, description="广告位ID")
# 广告类型:1-图片 2-文字 3-视频 4-推荐
type: int = Field(..., ge=1, le=4, description="广告类型")
# 广告封面
cover: Optional[str] = Field(None, max_length=255, description="广告封面")
# 广告URL
url: str = Field(..., min_length=1, max_length=255, description="广告URL")
# 广告宽度
width: int = Field(..., ge=0, description="广告宽度")
# 广告高度
height: int = Field(..., ge=0, description="广告高度")
# 开始时间
start_time: datetime = Field(..., description="开始时间")
# 结束时间
end_time: datetime = Field(..., description="结束时间")
# 广告状态
status: int = Field(..., ge=1, le=2, description="广告状态")
# 广告排序
sort: int = Field(..., ge=0, le=99999, description="广告排序")
# 广告备注
note: Optional[str] = Field(None, max_length=255, description="广告备注")
# 广告内容
content: Optional[str] = Field(None, description="广告内容")
# 设置状态表单
class AdStatusForm(BaseModel):
# 广告ID
id: int = Field(..., gt=0, description="广告ID")
# 广告状态
status: int = Field(..., ge=1, le=2, description="广告状态")
# 兼容垫片:由 modules.cms.ad.schemas 迁移
from modules.cms.ad.schemas import * # noqa: F401,F403
+2 -23
View File
@@ -1,23 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 广告位表单验证
class AdSortForm(BaseModel):
# 广告位ID
id: Optional[int] = Field(None, description="广告位ID")
# 广告位名称
name: str = Field(..., min_length=1, max_length=255, description="广告位名称")
# 站点ID
item_id: Optional[int] = Field(None, description="站点ID")
# 栏目ID
cate_id: Optional[int] = Field(None, description="栏目ID")
# 广告位置
loc_id: int = Field(..., ge=0, description="广告位置")
# 投放平台:1-PC站 2-WAP站 3-微信小程序 4-APP应用
platform: int = Field(..., ge=1, le=4, description="投放平台")
# 广告位描述
description: str = Field(..., min_length=1, max_length=255, description="广告位描述")
# 广告位排序
sort: int = Field(..., ge=0, le=99999, description="广告位排序")
# 兼容垫片:由 modules.cms.ad_sort.schemas 迁移
from modules.cms.ad_sort.schemas import * # noqa: F401,F403
+2 -18
View File
@@ -1,18 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 城市表单验证
class CityForm(BaseModel):
id: Optional[int] = Field(None, description="城市ID")
name: str = Field(..., min_length=1, max_length=150, description="城市名称")
short_name: str = Field(..., min_length=1, max_length=150, description="城市简称")
pinyin: str = Field(..., min_length=1, max_length=150, description="城市拼音")
city_code: str = Field(..., min_length=1, max_length=6, description="城市区号")
area_code: str = Field(..., min_length=1, max_length=20, description="行政编码")
zip_code: str = Field(..., min_length=1, max_length=6, description="城市邮编")
level: int = Field(..., ge=1, le=4, description="城市级别")
lng: str = Field(..., min_length=1, max_length=150, description="城市经度")
lat: str = Field(..., min_length=1, max_length=150, description="城市纬度")
pid: Optional[int] = Field(None, description="上级ID")
# 兼容垫片:由 modules.region.schemas 迁移
from modules.region.schemas import * # noqa: F401,F403
+2 -11
View File
@@ -1,11 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 配置表单验证
class ConfigForm(BaseModel):
id: Optional[int] = Field(None, description="配置ID")
name: str = Field(..., min_length=1, max_length=150, description="配置名称")
sort: int = Field(..., ge=0, le=99999, description="配置排序")
note: Optional[str] = Field(None, max_length=255, description="配置备注")
# 兼容垫片:由 modules.configuration.schemas 迁移
from modules.configuration.schemas import * # noqa: F401,F403
+2 -22
View File
@@ -1,22 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 配置项表单验证
class ConfigItemForm(BaseModel):
id: Optional[int] = Field(None, description="配置项ID")
title: str = Field(..., min_length=1, max_length=150, description="配置项标题")
code: str = Field(..., min_length=1, max_length=150, description="配置项编码")
value: str = Field(..., min_length=1, max_length=1000, description="配置项值")
options: Optional[str] = Field(None, max_length=1000, description="配置选项")
config_id: int = Field(..., ge=0, description="配置ID")
type: str = Field(..., min_length=1, max_length=150, description="配置类型")
sort: int = Field(..., ge=0, le=99999, description="配置项排序")
note: Optional[str] = Field(None, max_length=255, description="配置项备注")
# 设置状态表单
class ConfigItemStatusForm(BaseModel):
id: int = Field(..., gt=0, description="配置项ID")
status: int = Field(..., ge=1, le=2, description="配置项状态")
# 兼容垫片:由 modules.configuration.schemas 迁移
from modules.configuration.schemas import * # noqa: F401,F403
+2 -14
View File
@@ -1,14 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 部门表单验证
class DeptForm(BaseModel):
id: Optional[int] = Field(None, description="部门ID")
name: str = Field(..., min_length=1, max_length=150, description="部门名称")
code: str = Field(..., min_length=1, max_length=150, description="部门编码")
type: int = Field(..., ge=1, le=2, description="部门类型")
pid: Optional[int] = Field(None, description="上级ID")
sort: int = Field(..., ge=0, le=99999, description="部门排序")
note: Optional[str] = Field(None, max_length=255, description="部门备注")
# 兼容垫片:由 modules.system.dept.schemas 迁移
from modules.system.dept.schemas import * # noqa: F401,F403
+2 -12
View File
@@ -1,12 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 字典表单验证
class DictForm(BaseModel):
id: Optional[int] = Field(None, description="字典ID")
name: str = Field(..., min_length=1, max_length=150, description="字典名称")
code: str = Field(..., min_length=1, max_length=150, description="字典编码")
sort: int = Field(..., ge=0, le=99999, description="字典排序")
note: Optional[str] = Field(None, max_length=255, description="字典备注")
# 兼容垫片:由 modules.dictionary.schemas 迁移
from modules.dictionary.schemas import * # noqa: F401,F403
+2 -17
View File
@@ -1,17 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 字典项表单验证
class DictItemForm(BaseModel):
id: Optional[int] = Field(None, description="字典项ID")
name: str = Field(..., min_length=1, max_length=150, description="字典项名称")
value: str = Field(..., min_length=1, max_length=150, description="字典项值")
dict_id: int = Field(..., ge=0, description="字典ID")
note: Optional[str] = Field(None, max_length=255, description="字典项备注")
sort: int = Field(..., ge=0, le=99999, description="字典项排序")
# 设置状态表单
class DictItemStatusForm(BaseModel):
id: int = Field(..., gt=0, description="字典项ID")
status: int = Field(..., ge=1, le=2, description="字典项状态")
# 兼容垫片:由 modules.dictionary.schemas 迁移
from modules.dictionary.schemas import * # noqa: F401,F403
+2 -15
View File
@@ -1,15 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 站点表单验证
class ItemForm(BaseModel):
id: Optional[int] = Field(None, description="站点ID")
name: str = Field(..., min_length=1, max_length=150, description="站点名称")
type: int = Field(..., ge=1, le=2, description="站点类型")
url: str = Field(..., min_length=1, max_length=255, description="站点URL")
image: str = Field(..., min_length=1, max_length=255, description="站点图片")
status: int = Field(..., ge=1, le=2, description="站点状态")
sort: int = Field(..., ge=0, le=99999, description="站点排序")
note: Optional[str] = Field(None, max_length=255, description="站点备注")
# 兼容垫片:由 modules.cms.item.schemas 迁移
from modules.cms.item.schemas import * # noqa: F401,F403
+2 -18
View File
@@ -1,18 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 栏目表单验证
class ItemCateForm(BaseModel):
id: Optional[int] = Field(None, description="栏目ID")
name: str = Field(..., min_length=1, max_length=150, description="栏目名称")
pid: Optional[int] = Field(None, description="上级ID")
item_id: int = Field(..., ge=0, description="站点ID")
pinyin: str = Field(..., min_length=1, max_length=150, description="拼音全称")
code: str = Field(..., min_length=1, max_length=150, description="拼音简称")
is_cover: int = Field(..., ge=1, le=2, description="是否有封面")
cover: Optional[str] = Field(None, max_length=255, description="栏目封面")
status: int = Field(..., ge=1, le=2, description="栏目状态")
sort: int = Field(..., ge=0, le=99999, description="栏目排序")
note: Optional[str] = Field(None, max_length=255, description="栏目备注")
# 兼容垫片:由 modules.cms.item_cate.schemas 迁移
from modules.cms.item_cate.schemas import * # noqa: F401,F403
+2 -32
View File
@@ -1,32 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field, field_validator
# 定时任务表单验证
class JobForm(BaseModel):
id: Optional[int] = Field(None, description="任务ID")
job_name: str = Field(..., min_length=1, max_length=255, description="任务名称")
job_alias: Optional[str] = Field(None, max_length=255, description="任务别名")
job_group: Optional[str] = Field(None, max_length=255, description="任务分组")
job_trigger: Optional[str] = Field(None, max_length=255, description="任务触发器")
status: int = Field(..., ge=0, le=3, description="任务状态")
cron_expression: Optional[str] = Field(None, max_length=255, description="Cron表达式")
execute_policy: int = Field(..., ge=1, le=3, description="执行策略")
is_sync: int = Field(..., ge=0, le=1, description="是否同步任务")
url: Optional[str] = Field(None, max_length=255, description="任务URL")
params: Optional[str] = Field(None, max_length=2000, description="任务参数")
note: Optional[str] = Field(None, max_length=255, description="任务备注")
@field_validator('cron_expression')
@classmethod
def validate_cron(cls, v):
if v:
v = v.strip()
return v
# 任务状态表单
class JobStatusForm(BaseModel):
id: int = Field(..., gt=0, description="任务ID")
status: int = Field(..., ge=0, le=3, description="任务状态")
# 兼容垫片:由 modules.job.schemas 迁移
from modules.job.schemas import * # noqa: F401,F403
+2 -15
View File
@@ -1,15 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 职级表单验证
class LevelForm(BaseModel):
id: Optional[int] = Field(None, description="职级ID")
name: str = Field(..., min_length=1, max_length=150, description="职级名称")
status: int = Field(..., ge=1, le=2, description="职级状态")
sort: int = Field(..., ge=0, le=99999, description="职级排序")
# 设置状态表单
class LevelStatusForm(BaseModel):
id: int = Field(..., gt=0, description="职级ID")
status: int = Field(..., ge=1, le=2, description="职级状态")
# 兼容垫片:由 modules.system.level.schemas 迁移
from modules.system.level.schemas import * # noqa: F401,F403
+2 -29
View File
@@ -1,29 +1,2 @@
from typing import Optional
from pydantic import BaseModel, ConfigDict, Field, field_validator
from apps.models.link import Link
# 友链表单验证
class LinkForm(BaseModel):
model_config = ConfigDict(populate_by_name=True)
id: Optional[int] = Field(None, description="友链ID")
name: str = Field(..., min_length=1, max_length=255, description="友链名称")
type: int = Field(..., ge=1, le=2, description="友链类型")
url: str = Field(..., min_length=1, max_length=255, description="友链URL")
item_id: Optional[int] = Field(None, description="站点ID")
cate_id: Optional[int] = Field(None, description="栏目ID")
platform: int = Field(..., ge=1, le=4, description="投放平台")
link_form: int = Field(..., ge=1, le=2, alias='form', description="友链形式")
image: str = Field(..., min_length=1, max_length=255, description="友链图片")
status: int = Field(..., ge=1, le=2, description="友链状态")
sort: int = Field(..., ge=0, le=99999, description="友链排序")
note: Optional[str] = Field(None, max_length=255, description="友链备注")
# 设置状态表单
class LinkStatusForm(BaseModel):
id: int = Field(..., gt=0, description="友链ID")
status: int = Field(..., ge=1, le=2, description="友链状态")
# 兼容垫片:由 modules.link.schemas 迁移
from modules.link.schemas import * # noqa: F401,F403
+2 -11
View File
@@ -1,11 +1,2 @@
from pydantic import BaseModel, Field
from config import CAPTCHA_LENGTH
# 登录表单验证
class LoginForm(BaseModel):
username: str = Field(..., min_length=1, max_length=20, description="登录账号")
password: str = Field(..., min_length=6, max_length=128, description="登录密码")
captcha: str = Field(..., min_length=CAPTCHA_LENGTH, max_length=CAPTCHA_LENGTH, description="验证码")
idKey: str = Field(..., min_length=1, description="KEY值")
# 兼容垫片:由 modules.auth.schemas 迁移
from modules.auth.schemas import * # noqa: F401,F403
+2 -31
View File
@@ -1,31 +1,2 @@
from datetime import date
from typing import Any, Optional
from pydantic import BaseModel, Field
# 会员表单验证
class MemberForm(BaseModel):
id: Optional[int] = Field(None, description="会员ID")
realname: str = Field(..., min_length=1, max_length=150, description="会员姓名")
nickname: str = Field(..., min_length=1, max_length=150, description="会员昵称")
gender: int = Field(..., ge=1, le=3, description="性别")
avatar: str = Field(..., min_length=1, max_length=255, description="头像")
birthday: date = Field(..., description="出生日期")
email: str = Field(..., min_length=1, max_length=30, description="邮箱")
city: Any = Field(..., description="行政区划")
province_code: Optional[str] = Field(None, max_length=30, description="省份编码")
city_code: Optional[str] = Field(None, max_length=30, description="城市编码")
district_code: Optional[str] = Field(None, max_length=30, description="县区编码")
address: str = Field(..., min_length=1, max_length=255, description="详细地址")
username: str = Field(..., min_length=1, max_length=30, description="登录账号")
password: Optional[str] = Field(None, max_length=255, description="登录密码")
member_level: int = Field(..., ge=0, description="会员等级")
intro: Optional[str] = Field(None, max_length=255, description="个人简介")
signature: Optional[str] = Field(None, max_length=255, description="个人签名")
source: int = Field(..., ge=1, le=5, description="注册来源")
status: int = Field(..., ge=1, le=2, description="会员状态")
# 设置状态表单
class MemberStatusForm(BaseModel):
id: int = Field(..., gt=0, description="会员ID")
status: int = Field(..., ge=1, le=2, description="会员状态")
# 兼容垫片:由 modules.member.schemas 迁移
from modules.member.schemas import * # noqa: F401,F403
+2 -10
View File
@@ -1,10 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 会员等级表单验证
class MemberLevelForm(BaseModel):
id: Optional[int] = Field(None, description="会员等级ID")
name: str = Field(..., min_length=1, max_length=255, description="会员等级名称")
sort: int = Field(..., ge=0, le=99999, description="会员等级排序")
# 兼容垫片:由 modules.member.schemas 迁移
from modules.member.schemas import * # noqa: F401,F403
+2 -21
View File
@@ -1,21 +1,2 @@
from typing import Any, List, Optional
from pydantic import BaseModel, Field
# 菜单表单验证
class MenuForm(BaseModel):
id: Optional[int] = Field(None, description="菜单ID")
title: str = Field(..., min_length=1, max_length=150, description="菜单标题")
icon: Optional[str] = Field(None, max_length=50, description="菜单图标")
path: Optional[str] = Field(None, max_length=255, description="路由地址")
component: Optional[str] = Field(None, max_length=255, description="组件路径")
parent_id: int = Field(..., ge=0, description="上级ID")
type: Optional[int] = Field(None, ge=0, le=1, description="菜单类型")
target: Optional[str] = Field(None, max_length=30, description="打开方式")
permission: Optional[str] = Field(None, max_length=150, description="权限节点")
hide: Optional[int] = Field(None, ge=0, le=1, description="是否可见")
status: int = Field(..., ge=1, le=2, description="菜单状态")
sort: int = Field(..., ge=0, le=99999, description="菜单排序")
note: Optional[str] = Field(None, max_length=255, description="菜单备注")
checked_list: Optional[List[int]] = Field(default=[], description="权限节点")
# 兼容垫片:由 modules.system.menu.schemas 迁移
from modules.system.menu.schemas import * # noqa: F401,F403
+2 -15
View File
@@ -1,15 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 通知表单验证
class NoticeForm(BaseModel):
id: Optional[int] = Field(None, description="通知ID")
title: str = Field(..., min_length=1, max_length=255, description="通知标题")
source: int = Field(..., ge=1, le=4, description="通知来源")
url: str = Field(..., min_length=1, max_length=255, description="通知地址")
status: int = Field(..., ge=1, le=2, description="通知状态")
is_top: int = Field(..., ge=1, le=2, description="是否置顶")
click: int = Field(..., ge=0, description="点击率")
content: Optional[str] = Field(None, description="通知内容")
# 兼容垫片:由 modules.notice.schemas 迁移
from modules.notice.schemas import * # noqa: F401,F403
+2 -15
View File
@@ -1,15 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 岗位表单验证
class PositionForm(BaseModel):
id: Optional[int] = Field(None, description="岗位ID")
name: str = Field(..., min_length=1, max_length=150, description="岗位名称")
status: int = Field(..., ge=1, le=2, description="岗位状态")
sort: int = Field(..., ge=0, le=99999, description="岗位排序")
# 设置状态表单
class PositionStatusForm(BaseModel):
id: int = Field(..., gt=0, description="岗位ID")
status: int = Field(..., ge=1, le=2, description="岗位状态")
# 兼容垫片:由 modules.system.position.schemas 迁移
from modules.system.position.schemas import * # noqa: F401,F403
+2 -17
View File
@@ -1,17 +1,2 @@
from typing import Optional
from pydantic import BaseModel, Field
# 角色表单验证
class RoleForm(BaseModel):
id: Optional[int] = Field(None, description="角色ID")
name: str = Field(..., min_length=1, max_length=150, description="角色名称")
code: str = Field(..., min_length=1, max_length=30, description="角色编码")
status: int = Field(..., ge=1, le=2, description="角色状态")
sort: int = Field(..., ge=0, le=99999, description="角色排序")
note: Optional[str] = Field(None, max_length=255, description="角色备注")
# 设置状态表单
class RoleStatusForm(BaseModel):
id: int = Field(..., gt=0, description="角色ID")
status: int = Field(..., ge=1, le=2, description="角色状态")
# 兼容垫片:由 modules.system.role.schemas 迁移
from modules.system.role.schemas import * # noqa: F401,F403
+2 -9
View File
@@ -1,9 +1,2 @@
from typing import List, Optional
from pydantic import BaseModel, Field
# 角色菜单表单
class RoleMenuForm(BaseModel):
role_id: int = Field(..., gt=0, description="角色ID")
menu_id: Optional[List[int]] = Field(default=[], description="菜单ID列表")
# 兼容垫片:由 modules.system.role.schemas 迁移
from modules.system.role.schemas import * # noqa: F401,F403
+2 -61
View File
@@ -1,61 +1,2 @@
from datetime import date
from typing import Any, Optional
from pydantic import BaseModel, Field, model_validator
# 用户表单验证
class UserForm(BaseModel):
id: Optional[int] = Field(None, description="用户ID")
realname: str = Field(..., min_length=1, max_length=150, description="用户姓名")
nickname: str = Field(..., min_length=1, max_length=150, description="用户昵称")
gender: int = Field(..., ge=1, le=3, description="性别")
avatar: str = Field(..., min_length=1, max_length=255, description="头像")
mobile: str = Field(..., min_length=1, max_length=30, description="手机号")
email: str = Field(..., min_length=1, max_length=30, description="邮箱")
birthday: date
dept_id: int = Field(..., ge=0, description="部门ID")
level_id: int = Field(..., ge=0, description="职级ID")
position_id: int = Field(..., ge=0, description="岗位ID")
city: Any = Field(..., description="行政区划")
province_code: Optional[str] = Field(None, max_length=30, description="省份编码")
city_code: Optional[str] = Field(None, max_length=30, description="城市编码")
district_code: Optional[str] = Field(None, max_length=30, description="县区编码")
address: str = Field(..., min_length=1, max_length=255, description="详细地址")
username: str = Field(..., min_length=1, max_length=30, description="登录账号")
password: Optional[str] = Field(None, min_length=6, max_length=128, description="登录密码")
intro: Optional[str] = Field(None, max_length=255, description="个人简介")
status: int = Field(..., ge=1, le=2, description="用户状态")
note: Optional[str] = Field(None, max_length=255, description="用户备注")
sort: int = Field(..., ge=0, le=99999, description="用户排序")
roles: Optional[Any] = Field(default=[], description="用户角色")
# 设置状态表单
class UserStatusForm(BaseModel):
id: int = Field(..., gt=0, description="用户ID")
status: int = Field(..., ge=1, le=2, description="用户状态")
# 修改个人信息表单验证
class UserInfoForm(BaseModel):
realname: str = Field(..., min_length=1, max_length=150, description="用户姓名")
nickname: str = Field(..., min_length=1, max_length=150, description="用户昵称")
gender: int = Field(..., ge=1, le=3, description="性别")
mobile: str = Field(..., min_length=1, max_length=30, description="手机号")
email: str = Field(..., min_length=1, max_length=30, description="邮箱")
address: str = Field(..., min_length=1, max_length=255, description="详细地址")
intro: Optional[str] = Field(None, max_length=255, description="个人简介")
# 修改密码表单验证
class UpdatePwdForm(BaseModel):
oldPassword: str = Field(..., min_length=6, max_length=128, description="原始密码")
newPassword: str = Field(..., min_length=6, max_length=128, description="新密码")
rePassword: str = Field(..., min_length=6, max_length=128, description="确认密码")
@model_validator(mode='after')
def check_passwords_match(self):
if self.newPassword != self.rePassword:
raise ValueError('rePassword: 两次输入的密码不一致')
return self
# 重置密码表单
class ResetPwdForm(BaseModel):
id: int = Field(..., gt=0, description="用户ID")
# 兼容垫片:由 modules.system.user.schemas 迁移
from modules.system.user.schemas import * # noqa: F401,F403