refactor: 大规模代码整理与功能优化

1. 重构后端API路由、CRUD与模块结构,整合日志管理,移除废弃demo代码
2. 优化前端组件类型定义、样式与路由配置,修复权限判断逻辑
3. 调整默认排序规则、滚动条样式与工具类函数,更新依赖与配置文件
4. 修复多处类型不匹配与默认值问题,完善表单与菜单验证逻辑
This commit is contained in:
zhangtao
2026-06-17 01:56:31 +08:00
parent 17b3cd0a4c
commit 73f2823692
500 changed files with 40763 additions and 26616 deletions
+239 -27
View File
@@ -39,7 +39,7 @@ class TestModels:
def test_tenant_model(self) -> None:
from app.api.v1.module_platform.tenant.model import TenantModel
assert TenantModel.__tablename__ == "sys_tenant"
assert TenantModel.__tablename__ == "platform_tenant"
assert hasattr(TenantModel, "name")
assert hasattr(TenantModel, "code")
assert hasattr(TenantModel, "status")
@@ -49,27 +49,30 @@ class TestModels:
"""测试租户模型中的配额字段(单一大表设计)"""
from app.api.v1.module_platform.tenant.model import TenantModel
assert hasattr(TenantModel, "max_users")
assert hasattr(TenantModel, "max_roles")
assert hasattr(TenantModel, "max_storage_mb")
assert hasattr(TenantModel, "max_depts")
assert hasattr(TenantModel, "package_id")
assert hasattr(TenantModel, "start_time")
def test_tenant_config_model(self) -> None:
from app.api.v1.module_platform.tenant.model import TenantConfigModel
"""TenantModel 包含配置字段(单一大表设计)"""
from app.api.v1.module_platform.tenant.model import TenantModel
assert TenantConfigModel.__tablename__ == "sys_tenant_config"
assert hasattr(TenantConfigModel, "config_key")
# 配置字段已合并到 TenantModel
assert hasattr(TenantModel, "name")
assert hasattr(TenantModel, "code")
assert hasattr(TenantModel, "status")
def test_tenant_menu_model(self) -> None:
from app.api.v1.module_platform.tenant.model import TenantMenuModel
def test_package_model_quota(self) -> None:
from app.api.v1.module_platform.package.model import PackageModel
assert TenantMenuModel.__tablename__ == "sys_tenant_menu"
assert hasattr(TenantMenuModel, "menu_id")
assert hasattr(PackageModel, "max_users")
assert hasattr(PackageModel, "max_roles")
assert hasattr(PackageModel, "max_depts")
assert hasattr(PackageModel, "max_storage_mb")
def test_tenant_user_model(self) -> None:
from app.api.v1.module_platform.tenant.model import TenantUserModel
assert TenantUserModel.__tablename__ == "sys_user_tenant"
assert TenantUserModel.__tablename__ == "platform_user_tenant"
assert hasattr(TenantUserModel, "role")
assert hasattr(TenantUserModel, "is_default")
@@ -86,7 +89,7 @@ class TestModels:
assert hasattr(RoleModel, "menus")
def test_menu_model_has_type(self) -> None:
from app.api.v1.module_system.menu.model import MenuModel
from app.api.v1.module_platform.menu.model import MenuModel
assert hasattr(MenuModel, "type")
assert hasattr(MenuModel, "name")
@@ -102,16 +105,16 @@ class TestModels:
def test_plugin_model(self) -> None:
from app.api.v1.module_platform.plugin.model import PluginModel
assert PluginModel.__tablename__ == "sys_plugin"
assert PluginModel.__tablename__ == "platform_plugin"
assert hasattr(PluginModel, "code")
def test_tenant_plugin_model(self) -> None:
from app.api.v1.module_platform.plugin.model import TenantPluginModel
assert TenantPluginModel.__tablename__ == "sys_tenant_plugin"
assert TenantPluginModel.__tablename__ == "platform_tenant_plugin"
def test_ticket_model(self) -> None:
from app.api.v1.module_platform.ticket.model import TicketModel
from app.api.v1.module_system.ticket.model import TicketModel
assert TicketModel.__tablename__ == "sys_ticket"
assert hasattr(TicketModel, "ticket_type")
@@ -127,7 +130,7 @@ class TestSchemas:
def test_tenant_create_schema(self) -> None:
from app.api.v1.module_platform.tenant.schema import TenantCreateSchema
s = TenantCreateSchema(name="test", code="test001", status="0")
s = TenantCreateSchema(name="test", code="test001", status=0)
assert s.name == "test"
assert s.code == "test001"
@@ -138,18 +141,19 @@ class TestSchemas:
assert s.name == "renamed"
assert s.code is None # 未传则为 None
def test_tenant_quota_schema_validation(self) -> None:
from app.api.v1.module_platform.tenant.schema import TenantQuotaUpdateSchema
def test_package_schema_quota(self) -> None:
from app.api.v1.module_platform.package.schema import PackageCreateSchema
s = TenantQuotaUpdateSchema(max_users=100, max_roles=30)
s = PackageCreateSchema(name="test", code="test", max_users=100, max_roles=30)
assert s.max_users == 100
assert s.max_roles == 30
# 负数应被拒绝
with pytest.raises(Exception):
TenantQuotaUpdateSchema(max_users=-1)
with pytest.raises(ValueError):
PackageCreateSchema(name="test", code="test", max_users=-1)
def test_ticket_schema(self) -> None:
from app.api.v1.module_platform.ticket.schema import TicketCreateSchema
from app.api.v1.module_system.ticket.schema import TicketCreateSchema
s = TicketCreateSchema(title="test", ticket_type="suggestion")
assert s.title == "test"
@@ -157,7 +161,7 @@ class TestSchemas:
def test_menu_type_enum(self) -> None:
"""菜单类型: 1=目录 2=菜单 3=按钮 4=链接"""
from app.api.v1.module_system.menu.model import MenuModel
from app.api.v1.module_platform.menu.model import MenuModel
assert hasattr(MenuModel, "type")
# type 是 int 字段
@@ -203,9 +207,217 @@ class TestEnums:
def test_menu_type_values(self) -> None:
"""type: 1=目录 2=菜单 3=按钮 4=链接"""
from app.api.v1.module_system.menu.model import MenuModel
from app.api.v1.module_platform.menu.model import MenuModel
assert MenuModel.__tablename__ == "sys_menu"
assert MenuModel.__tablename__ == "platform_menu"
# type 字段是 int 类型
col = next((c for c in MenuModel.__table__.columns if c.name == "type"), None)
assert col is not None
# ==================== 06. 租户自助注册(PRD §4.5====================
class TestTenantRegister:
"""TenantRegisterSchema / TenantRegisterOutSchema 校验。"""
def test_register_schema_valid(self) -> None:
from app.api.v1.module_system.auth.schema import TenantRegisterSchema
s = TenantRegisterSchema(
username="testowner",
password="TestPass123",
email="test@example.com",
tenant_name="测试企业",
)
assert s.username == "testowner"
assert s.email == "test@example.com"
assert s.tenant_name == "测试企业"
def test_register_schema_minimal(self) -> None:
"""仅必填字段。"""
from app.api.v1.module_system.auth.schema import TenantRegisterSchema
s = TenantRegisterSchema(
username="owner2",
password="secret123",
email="owner2@example.com",
)
assert s.tenant_name is None
def test_register_schema_short_password(self) -> None:
"""密码少于 6 位应拒绝。"""
from pydantic import ValidationError
from app.api.v1.module_system.auth.schema import TenantRegisterSchema
with pytest.raises(ValidationError):
TenantRegisterSchema(
username="x",
password="123",
email="a@b.com",
)
def test_register_schema_bad_email(self) -> None:
"""非法邮箱应拒绝。"""
from pydantic import ValidationError
from app.api.v1.module_system.auth.schema import TenantRegisterSchema
with pytest.raises(ValidationError):
TenantRegisterSchema(
username="x",
password="123456",
email="not-an-email",
)
def test_register_out_schema_fields(self) -> None:
"""注册响应 Schema 包含所有必要字段。"""
from app.api.v1.module_system.auth.schema import TenantRegisterOutSchema
fields = set(TenantRegisterOutSchema.model_fields.keys())
for f in ("user_id", "tenant_id", "tenant_name", "tenant_code", "package", "trial_end", "message"):
assert f in fields, f"{f} 未在 TenantRegisterOutSchema 中找到"
# ==================== 07. 支付回调套餐激活 ====================
class TestActivateTenantPackage:
"""PaymentService._activate_tenant_package 日期计算逻辑。"""
def test_new_calculation(self) -> None:
"""order_type=new:设置 start_time=now, end_time=now+period。"""
from datetime import datetime, timedelta
now = datetime(2025, 1, 1, 12, 0, 0)
period_months = 1
duration = timedelta(days=30 * period_months)
# 模拟 _activate_tenant_package 中 new 分支的日期计算
fake = {"package_id": None, "start_time": None, "end_time": None, "status": 1}
fake["package_id"] = 1
fake["start_time"] = now
fake["end_time"] = now + duration
fake["status"] = 0
assert fake["package_id"] == 1
assert fake["start_time"] == now
assert fake["end_time"] == now + timedelta(days=30)
assert fake["status"] == 0
def test_renew_extends_end_time(self) -> None:
"""order_type=renew:在原有 end_time 基础上顺延。"""
from datetime import datetime, timedelta
now = datetime(2025, 1, 1)
original_end = now + timedelta(days=30)
fake = {"end_time": original_end, "status": 1}
# 模拟 renew 分支:base = max(end_time, now) + duration
base = max(fake["end_time"], now)
fake["end_time"] = base + timedelta(days=30)
fake["status"] = 0
assert fake["end_time"] == original_end + timedelta(days=30)
assert fake["status"] == 0
def test_upgrade_changes_package(self) -> None:
"""order_type=upgrade:更换 package_id,状态置为 active。"""
fake = {"package_id": 1, "status": 1}
fake["package_id"] = 2
fake["status"] = 0
assert fake["package_id"] == 2
assert fake["status"] == 0
# ==================== 08. 插件热重载 ====================
class TestPluginReload:
"""discover.reload_dynamic_router 可正常导入且签名正确。"""
def test_reload_function_exists(self) -> None:
from app.core.discover import reload_dynamic_router
assert callable(reload_dynamic_router)
def test_set_app_ref_exists(self) -> None:
from app.core.discover import set_app_ref
assert callable(set_app_ref)
def test_reload_service_exists(self) -> None:
"""PluginService.reload_service 方法存在且为类方法。"""
import inspect
from app.api.v1.module_platform.plugin.service import PluginService
assert hasattr(PluginService, "reload_service")
assert isinstance(
inspect.getattr_static(PluginService, "reload_service"),
(classmethod, classmethod),
)
# ==================== 18. status 字段类型一致性 ====================
class TestStatusFieldType:
"""验证 status 字段从 String(10) 迁移为 SmallInteger 后的一致性"""
def test_model_mixin_status_is_integer(self) -> None:
"""验证 ModelMixin 子类的 status 字段已转为 SmallInteger"""
from app.api.v1.module_system.user.model import UserModel
col = UserModel.__table__.c["status"]
assert "SMALLINT" in str(col.type).upper(), (
f"UserModel.status 应为 SmallInteger,当前为 {col.type}"
)
def test_tenant_status_is_integer(self) -> None:
from app.api.v1.module_platform.tenant.model import TenantModel
col = TenantModel.__table__.c["status"]
assert "SMALLINT" in str(col.type).upper(), (
f"TenantModel.status 应为 SmallInteger,当前为 {col.type}"
)
def test_user_status_is_integer(self) -> None:
from app.api.v1.module_system.user.model import UserModel
col = UserModel.__table__.c["status"]
assert "SMALLINT" in str(col.type).upper()
def test_role_status_is_integer(self) -> None:
from app.api.v1.module_system.role.model import RoleModel
col = RoleModel.__table__.c["status"]
assert "SMALLINT" in str(col.type).upper()
def test_dept_status_is_integer(self) -> None:
from app.api.v1.module_system.dept.model import DeptModel
col = DeptModel.__table__.c["status"]
assert "SMALLINT" in str(col.type).upper()
def test_menu_status_is_integer(self) -> None:
from app.api.v1.module_platform.menu.model import MenuModel
col = MenuModel.__table__.c["status"]
assert "SMALLINT" in str(col.type).upper()
def test_package_status_is_integer(self) -> None:
from app.api.v1.module_platform.package.model import PackageModel
col = PackageModel.__table__.c["status"]
assert "SMALLINT" in str(col.type).upper(), (
f"PackageModel.status 应为 SmallInteger,当前为 {col.type}"
)
def test_ticket_status_is_integer(self) -> None:
from app.api.v1.module_system.ticket.model import TicketModel
col = TicketModel.__table__.c["status"]
assert "SMALLINT" in str(col.type).upper()