Files
dpb/CHANGELOG.md
T
34047007@qq.com b95053c52c init: 初始化 dpb 桃育种系统代码库
前后端 + 后端 FastAPI 全量源码、部署脚本与文档。
2026-08-06 00:17:49 +08:00

3133 lines
143 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SaaS 多租户平台需求文档
> 版本:v3.6.0
> 最后更新:2026-06-03
---
# Part 1:平台架构与基础设施
---
## 1. 概述
### 1.1 背景
FastapiAdmin 是一个基于 FastAPI + SQLAlchemy 的管理后台框架,需要支持 SaaS 多租户模式。平台提供完善的多租户隔离和授权体系,包含平台管理端、套餐体系、租户独立授权、插件系统、工单系统等能力。
### 1.2 核心目标
1. **数据隔离**:不同租户间的业务数据严格隔离,通过 `tenant_id` 行级过滤实现
2. **权限分层**:平台层(菜单/套餐/插件)→ 租户层(可见菜单/配额/配置)→ 用户层(角色/数据权限)
3. **灵活授权**:通过套餐体系预设权限 + 自定义授权相结合,简化租户开通流程
4. **资源管控**:租户配额管理(用户数/角色数/存储空间等)防止资源滥用
### 1.3 角色定义
| 角色 | 说明 | 租户范围 |
|------|------|---------|
| **超级管理员 (Super Admin)** | 平台拥有者,管理所有租户和套餐,不受租户过滤 | 平台 |
| **租户管理员 (Tenant Admin)** | 被指定为租户的 owner/admin,管理租户内部资源 | 单个租户 |
| **租户用户 (Tenant User)** | 普通业务用户,使用租户内的功能 | 单个租户 |
### 1.4 模块总览
| 模块 | 类型 | 租户隔离 | 核心用途 |
|------|------|---------|---------|
| Auth | 系统 | 无 | 登录认证、OAuth、JWT |
| User | 系统 | ✅ TenantMixin | 用户管理、角色/岗位分配 |
| Role | 系统 | ✅ TenantMixin | 角色定义、菜单/部门权限分配 |
| Dept | 系统 | ✅ TenantMixin | 树形部门管理 |
| Position | 系统 | ✅ TenantMixin | 岗位管理 |
| Menu | 系统 | **无** | 平台菜单树(纯平台资源) |
| Dict | 系统 | ✅ TenantMixin(平台共享) | 字典类型+数据 |
| Notice | 系统 | ✅ TenantMixin | 通知公告 |
| Params | 系统 | ✅ TenantMixin | 系统参数配置 |
| LoginLog | 平台 | **无** | 登录日志(平台级) |
| OperationLog | 系统 | ✅ TenantMixin | 操作日志(租户级) |
| Tenant | 平台 | **无**(自身为租户定义) | 租户管理 |
| Package | 平台 | **无** | 套餐管理 |
| Ticket | 系统 | ✅ TenantMixin | 工单反馈 |
| Plugin | 平台 | **无**(平台资源) | 插件注册表 |
| Cronjob | 插件 | ✅ TenantMixin | 定时任务 |
| Workflow | 插件 | ✅ TenantMixin | 工作流引擎 |
| AI Chat | 插件 | ✅ TenantMixin | AI 对话 |
| CodeGen | 插件 | ✅ TenantMixin | 代码生成器 |
| Invoice | 平台 | **无**(关联 order) | 发票管理(普票/专票) |
| AuditLog | 平台 | **无** | 审计日志(不可篡改) |
| Dashboard | 平台 | **无** | 运营数据大盘 |
---
## 2. 平台架构
### 2.1 整体架构
```
┌─────────────────────────────────────────────────┐
│ Controller 层 │
│ 路由定义 / 参数校验 / 响应封装 / 操作日志 │
├─────────────────────────────────────────────────┤
│ Service 层 │
│ 业务逻辑编排 / 数据校验 / 权限检查 │
├─────────────────────────────────────────────────┤
│ CRUD 层 │
│ CRUDBase 通用增删改查 / 租户过滤 / 权限过滤 │
├─────────────────────────────────────────────────┤
│ Model 层 │
│ SQLAlchemy ORM / Mixin 体系 / 关系定义 │
├─────────────────────────────────────────────────┤
│ DB (MySQL/PgSQL/SQLite) │ Redis 缓存 │
└─────────────────────────────────────────────────┘
```
### 2.2 请求链路
```
请求 → Middleware链 → 租户中间件(解析token,设置ContextVar) →
路由匹配 → 依赖注入(DI) → Controller → Service → CRUD →
ORM(自动注入tenant_id) → DB → 反向响应 → ContextVar清理
```
### 2.3 模块目录结构
每个业务模块遵循统一结构:
```
module_xxx/
├── __init__.py
├── controller.py # API 路由定义
├── service.py # 业务逻辑
├── crud.py # 数据操作(继承 CRUDBase
├── model.py # SQLAlchemy 模型
└── schema.py # Pydantic 请求/响应模型
```
---
## 3. 数据隔离模型
### 3.1 核心设计原则
```
平台资源(无 tenant_id
├── platform_menu ← 菜单定义,纯平台资源
├── platform_package ← 套餐定义
├── platform_plugin ← 插件注册表
└── platform_tenant ← 租户定义
租户资源(含 tenant_idORM 自动过滤)
├── sys_user ← 用户
├── sys_role ← 角色
├── sys_dept ← 部门
├── sys_position ← 岗位
├── sys_notice ← 通知公告
├── sys_param ← 系统参数
├── sys_log ← 日志
├── platform_ticket ← 工单
└── 插件业务表
平台共享资源(tenant_id=1 的平台数据对所有租户可读)
├── sys_dict_type ← 字典类型
└── sys_dict_data ← 字典数据
```
### 3.2 三层隔离机制
| 层级 | 实现文件 | 机制说明 |
|------|---------|---------|
| **ORM 事件层** | `tenant_filter.py` | SQLAlchemy `do_orm_execute` 事件自动注入 `WHERE tenant_id = ?` |
| **CRUD 层** | `base_crud.py` | `__build_conditions` / `__tenant_condition` 二次确认 |
| **权限策略层** | `permission.py` | 基于角色 `data_scope` 字段精细化控制 |
#### ORM 事件层行为
| 操作 | 超管 | 普通用户 |
|------|------|---------|
| SELECT | 不过滤 | 自动追加 `WHERE tenant_id = ?``__platform_data_shared__` 模型跳过此层过滤,由 CRUD 层处理) |
| INSERT | 不自动设置 | 自动设置 `tenant_id = 当前租户` |
| UPDATE/DELETE | 不过滤 | 自动追加 `WHERE tenant_id = ?` |
| 系统表(platform_tenant) | 不过滤 | 不过滤 |
> **⚠️ 特别注意**:标记了 `__platform_data_shared__ = True` 的模型(DictType/DictData),ORM 事件层**跳过**自动 tenant_id 过滤,由 CRUD 层的 `__tenant_condition(read_mode=True)` 统一处理 `WHERE tenant_id = current OR tenant_id = 1` 逻辑。防止 ORM 事件层覆盖了"平台共享"读取策略。
#### 权限策略层
| 策略 | 枚举值 | 说明 | 适用模型 |
|------|--------|------|---------|
| `ROLE_BASED` | 1 | 仅显示用户角色授权的数据 | Menu |
| `DEPT_BASED` | 2 | 基于部门范围过滤 | Dept |
| `USER_ROLE` | 3 | 仅显示用户绑定的角色 | Role |
| `SELF_ONLY` | 4 | 仅本人数据 | 预留 |
| `DATA_SCOPE` | 5 | 基于 data_scope 字段 | Tenant(通用) |
#### data_scope 数据范围
| 值 | 说明 |
|----|------|
| 1 | 仅本人数据 |
| 2 | 本部门数据 |
| 3 | 本部门及以下数据 |
| 4 | 全部数据 |
| 5 | 自定义数据(通过 sys_role_depts 指定可见部门) |
### 3.3 Mixin 体系
```
MappedBase (声明式基类)
├── ModelMixin (id, uuid, status, description, 时间戳, 软删除)
├── TenantMixin (tenant_id FK → platform_tenant.id, NOT NULL, default=1, ON DELETE RESTRICT)
└── UserMixin (created_id, updated_id, deleted_id FK → sys_user)
```
#### ModelMixin 通用字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Integer PK AI | 主键 |
| `uuid` | String(64) UNIQUE | UUID 全局唯一标识 |
| `status` | Integer | default=0 | 状态(0:启动 1:停用) |
| `description` | Text nullable | 备注/描述 |
| `created_time` | DateTime | 创建时间 |
| `updated_time` | DateTime | 更新时间(onupdate |
| `is_deleted` | Boolean default=False | 软删除标记 |
| `deleted_time` | DateTime nullable | 删除时间 |
#### __platform_data_shared__ 机制
标记了 `__platform_data_shared__ = True` 的模型(DictType/DictData),在 CRUD 层查询时:
- 超管:不过滤,可查看/修改所有租户的数据
- 普通用户:`WHERE tenant_id = current_tenant_id OR tenant_id = 1`
---
# Part 2:核心业务模块需求
---
## 4. Auth 认证模块
### 4.1 业务描述
提供用户认证、授权、会话管理功能,支持多种登录方式(密码登录、OAuth2 第三方登录),支持验证码安全校验。
### 4.2 业务流程
```
登录请求 → 验证码校验(启用时) → 用户认证(用户名+密码) →
检查用户状态 → 更新最后登录时间 → 查询用户关联租户列表 →
判断租户数量:
├── 单租户: 直接生成 JWT(含 tenant_id) → 返回 token
└── 多租户: 生成临时 JWT(不含 tenant_id, 仅限调用 /auth/select-tenant) →
返回 租户选择 token + 租户列表 →
用户选择 → /auth/select-tenant/{id} → 生成含 tenant_id 的正式 token
记录在线会话 → 返回正式 token
```
> **多租户登录说明**
> - **临时 token**(不含 tenant_id):仅能调用 `POST /auth/select-tenant/{id}`,其他任何接口均返回 403
> - **正式 token**(含 tenant_id):正常访问所有已授权的 API
> - 超管用户跳过租户选择,直接生成含 `is_super_admin=True` 的正式 token(不绑定任何 tenant_id
### 4.3 核心规则
| 规则 | 说明 |
|------|------|
| **验证码** | 配置控制是否启用,API 文档请求(docs/redoc)跳过验证码 |
| **密码校验** | Bcrypt 哈希比对 |
| **状态检查** | 用户 status="1"(禁用)时拒绝登录 |
| **JWT 载荷** | 包含 session_id, user_id, tenant_id, is_super_admin, 登录信息 |
| **Token 刷新** | refresh_token 专用,不可用 access_token 刷新 |
| **多租户登录** | 登录后判断:单租户用户直接签发含 tenant_id 的正式 token;多租户用户签**临时 token**(不含 tenant_id,仅可访问 `POST /auth/select-tenant/{id}`),选择租户后签正式 token |
| **在线记录** | 登录成功后 Redis 记录在线会话,含 IP/OS/浏览器/登录位置 |
| **日志记录** | 操作日志路由类自动记录登录日志 |
### 4.4 数据模型
无独立数据表,使用 Redis 存储会话和验证码。
### 4.5 用户自助注册
```
POST /auth/register
├── 接收:username, password, email, tenant_name(可选)
├── 校验:用户名/邮箱唯一性
├── 创建租户记录(platform_tenant
│ ├── name = tenant_name 或 "{username}的租户"(默认名)
│ ├── code = 自动生成(基于 name 拼音首字母 + 4位随机数)
│ ├── package_id → 取全局默认套餐(platform_package.is_default=true 的第一条,若无则 id=1
│ ├── end_time = now + trial_days(取套餐的 trial_days,默认 7 天)
│ ├── max_users/max_roles/max_depts → 取套餐配额默认值(见 §16.2)
│ └── status = 0(active)
├── 创建用户记录(sys_usertenant_id=新租户ID
├── 创建 owner 角色(sys_rolecode="owner"
├── 将用户绑定到 owner 角色
├── 将租户可用菜单全量分配给 owner 角色
└── 返回注册成功(用户需邮箱验证后激活)
```
> **默认套餐获取优先级**:套餐 `is_default=true` > id=1 > 无套餐(仅自定义菜单)。若平台未配置任何套餐,新租户仅有自定义菜单体系,需超管后续手动配置。
### 4.6 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/auth/login` | 登录(多租户用户返回临时 token + 租户列表) |
| POST | `/auth/token/refresh` | 刷新 token |
| POST | `/auth/logout` | 退出登录(清除 Redis 会话) |
| GET | `/auth/captcha` | 获取验证码(Base64 图片) |
| POST | `/auth/register` | 用户注册(创建用户 → 自动创建默认租户并设为 owner) |
| POST | `/auth/select-tenant/{id}` | 选择/切换租户(生成含 tenant_id 的正式 token |
| POST | `/auth/forgot-password` | 忘记密码(发送重置邮件) |
| GET | `/auth/auto-login/{token}` | 免登录(用于邮件/消息免登链接) |
| GET | `/auth/oauth/{provider}/login` | OAuth2 授权跳转 |
| GET | `/auth/oauth/{provider}/callback` | OAuth2 回调处理(需用户预绑定第三方账号;首次 OAuth 登录不绑定租户,需选择/创建租户) |
---
## 5. User 用户模块
### 5.1 业务描述
管理平台和租户下的用户账号,支持角色分配、岗位分配、部门归属、密码管理、Excel 导入导出。用户数据按租户严格隔离。
### 5.2 数据模型
**表名**`sys_user`TenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `username` | String(64) | NOT NULL, UNIQUE(tenant_id) | 用户名/登录账号 |
| `password` | String(255) | NOT NULL | Bcrypt 密码哈希 |
| `name` | String(32) | NOT NULL | 昵称/姓名 |
| `mobile` | String(11) | nullable | 手机号 |
| `email` | String(64) | nullable | 邮箱 |
| `gender` | String(1) | default="2" | 性别(0:男 1:女 2:未知) |
| `avatar` | String(255) | nullable | 头像 URL |
| `is_superuser` | Boolean | default=False | 是否超级管理员 |
| `last_login` | DateTime | nullable | 最后登录时间 |
| `dept_id` | FK→sys_dept.id | nullable, ON DELETE SET NULL | 所属部门 |
| `gitee_login` | String(32) | nullable | Gitee 第三方登录 |
| `github_login` | String(32) | nullable | Github 第三方登录 |
| `wx_login` | String(32) | nullable | 微信第三方登录 |
| `qq_login` | String(32) | nullable | QQ 第三方登录 |
**关联关系**
| 关联表 | 关系类型 | 说明 |
|--------|---------|------|
| `sys_user_roles` | 多对多 | 用户 ↔ 角色 |
| `sys_user_positions` | 多对多 | 用户 ↔ 岗位 |
| `platform_user_tenant` | 多对多 | 用户 ↔ 租户(跨租户支持) |
### 5.3 业务规则
| 类别 | 规则 |
|------|------|
| **创建** | username 字母开头、3~32位;不允许创建超管;username/mobile/email 唯一 |
| **修改** | 不可修改超管;username/mobile/email 唯一性检查;部门必须存在且可用 |
| **删除** | 仅已禁用(status=1)用户可删除;不可删除超管;不可删除当前登录用户 |
| **密码** | Bcrypt 加密存储;修改需验证原密码;重置不可操作超管 |
| **导入导出** | 支持 Excel 导入导出;导入时密码字段处理策略:密码列为空 → 系统自动生成12位随机密码并通过邮件发送给用户;密码列有值 → Bcrypt 加密后存储,首次登录强制修改密码 |
| **状态** | 批量启用/禁用;不可操作超管 |
### 5.4 当前用户菜单权限
```
get_current_user_info_service:
├── 超管 → 返回全部 PC 端菜单(type=1/2/4, client=pc
└── 普通用户:
├── 收集角色菜单 ID(角色→菜单,去重)
├── 与租户可用菜单取交集
└── 构建菜单树返回
```
### 5.5 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/user/detail/{id}` | 用户详情 |
| GET | `/user/list` | 用户列表 |
| POST | `/user/create` | 创建用户 |
| PUT | `/user/update/{id}` | 更新用户 |
| DELETE | `/user/delete` | 删除用户(批量) |
| PATCH | `/user/status/batch` | 批量设置用户状态 |
| GET | `/user/current/info` | 获取当前用户信息(含菜单树) |
| PUT | `/user/current/update` | 更新当前用户信息 |
| PUT | `/user/current/password/change` | 修改密码(本人操作,需验证原密码) |
| PUT | `/user/password/reset` | 重置密码(管理员操作,跳过原密码) |
| POST | `/user/import` | 导入用户(Excel |
| POST | `/user/export` | 导出用户(Excel |
---
## 6. Role 角色模块
### 6.1 业务描述
角色是权限分配的核心载体,每个角色可绑定多个菜单(功能权限)和多个部门(数据权限)。角色数据按租户严格隔离。
### 6.2 数据模型
**表名**`sys_role`TenantMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(64) | NOT NULL | 角色名称 |
| `code` | String(64) | NOT NULL, UNIQUE(tenant_id) | 角色编码 |
| `order` | Integer | default=999 | 显示排序 |
| `data_scope` | Integer | default=1 | 数据权限范围(1~5 |
**关联关系**
| 关联表 | 关系类型 | 说明 |
|--------|---------|------|
| `sys_role_menus` | 多对多 | 角色 ↔ 菜单 |
| `sys_role_depts` | 多对多 | 角色 ↔ 部门(仅 data_scope=5 时使用) |
| `sys_user_roles` | 多对多 | 用户 ↔ 角色 |
### 6.3 业务规则
| 规则 | 说明 |
|------|------|
| **编码规则** | 字母开头,仅含字母/数字/下划线 |
| **租户唯一** | (tenant_id, code) 唯一约束 |
| **权限策略** | `USER_ROLE` — 非超管用户只能看到自己绑定的角色 |
| **菜单约束** | 非超管只能为角色分配租户可用菜单内的菜单,越权时抛出异常含菜单名称 |
| **数据范围** | 1=仅本人 2=本部门 3=本部门及以下 4=全部 5=自定义(绑定部门) |
| **默认 owner 角色** | 创建租户时自动创建 code="owner" 的角色,不可删除、不可禁用。自动分配租户全部可用菜单 |
### 6.4 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/role/detail/{id}` | 角色详情 |
| GET | `/role/list` | 角色列表 |
| POST | `/role/create` | 创建角色 |
| PUT | `/role/update/{id}` | 更新角色 |
| DELETE | `/role/delete` | 删除角色(批量) |
| PATCH | `/role/status/batch` | 批量设置角色状态 |
| PUT | `/role/menus` | 设置角色菜单 |
| PUT | `/role/permission` | 设置角色权限(含数据范围+部门) |
---
## 7. Dept 部门模块
### 7.1 业务描述
部门是组织架构的核心,采用树形结构支持无限层级。部门数据按租户严格隔离。
### 7.2 数据模型
**表名**`sys_dept`TenantMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(64) | NOT NULL | 部门名称 |
| `code` | String(64) | NOT NULL, UNIQUE(tenant_id, code) | 部门编码 |
| `parent_id` | Integer FK | nullable | 父级部门 |
| `order` | Integer | default=999 | 显示排序 |
| `leader` | String(32) | nullable | 负责人 |
| `phone` | String(20) | nullable | 联系电话 |
| `email` | String(128) | nullable | 邮箱 |
### 7.3 业务规则
| 规则 | 说明 |
|------|------|
| **树形结构** | parent_id 自引用,支持无限层级。创建/更新 parent_id 时需检测循环引用 |
| **编码规则** | 字母开头,仅含字母/数字/下划线 |
| **租户唯一** | (tenant_id, code) 唯一约束 |
| **权限策略** | `DEPT_BASED` — 基于部门范围过滤 |
| **删除约束** | 有子部门的父部门不可删除 |
| **状态级联** | 父部门禁用时子部门同步禁用(业务层实现) |
### 7.4 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/dept/detail/{id}` | 部门详情 |
| GET | `/dept/list` | 部门列表(树形) |
| POST | `/dept/create` | 创建部门 |
| PUT | `/dept/update/{id}` | 更新部门 |
| DELETE | `/dept/delete` | 删除部门(批量) |
| PATCH | `/dept/status/batch` | 批量设置部门状态 |
---
## 8. Position 岗位模块
### 8.1 业务描述
岗位用于定义用户在组织内的职务角色,一个用户可绑定多个岗位。
### 8.2 数据模型
**表名**`sys_position`TenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(64) | NOT NULL | 岗位名称 |
| `order` | Integer | default=1 | 显示排序 |
### 8.3 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/position/detail/{id}` | 岗位详情 |
| GET | `/position/list` | 岗位列表 |
| POST | `/position/create` | 创建岗位 |
| PUT | `/position/update/{id}` | 更新岗位 |
| DELETE | `/position/delete` | 删除岗位(批量) |
| PATCH | `/position/status/batch` | 批量设置岗位状态 |
---
## 9. Menu 菜单模块
### 9.1 业务描述
菜单是系统功能权限的基础定义单元,属于**平台级资源**(无 tenant_id),由超级管理员统一管理。菜单以树形结构组织,支撑前端动态路由和后端权限控制。
### 9.2 数据模型
**表名**`platform_menu`ModelMixin**无 TenantMixin**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(50) | NOT NULL | 菜单名称 |
| `type` | Integer | NOT NULL, default=2 | 类型(1:目录 2:菜单 3:按钮 4:外链) |
| `order` | Integer | NOT NULL, default=999 | 显示排序 |
| `permission` | String(100) | nullable | 权限标识(如 `system:user:query` |
| `icon` | String(50) | nullable | 菜单图标 |
| `route_name` | String(100) | nullable | 路由名称 |
| `route_path` | String(200) | nullable | 路由路径(以 `/` 开头) |
| `component_path` | String(200) | nullable | 组件路径(不能以 `/` 开头) |
| `redirect` | String(200) | nullable | 重定向地址 |
| `hidden` | Boolean | default=False | 是否隐藏 |
| `keep_alive` | Boolean | default=True | 是否缓存 |
| `always_show` | Boolean | default=False | 是否始终显示 |
| `title` | String(50) | nullable | 菜单标题 |
| `params` | JSON | nullable | 路由参数 |
| `affix` | Boolean | default=False | 是否固定标签页 |
| `client` | String(20) | NOT NULL, default="pc" | 终端(pc/app |
| `parent_id` | FK→platform_menu.id | nullable, ON DELETE SET NULL | 父菜单 |
### 9.3 菜单类型
| 类型 | 说明 | 路由 | 前端行为 |
|------|------|------|---------|
| 1 | 目录 | 无 | 展开项,不可点击 |
| 2 | 菜单 | 有 | 可点击进入页面 |
| 3 | 按钮/权限 | 无 | 页面内操作权限标识 |
| 4 | 外部链接 | 有 | 跳转外部 URL |
### 9.4 业务规则
| 规则 | 说明 |
|------|------|
| **平台资源** | 无 tenant_id,所有租户共享菜单池 |
| **路由规则** | `route_path``/` 开头,`component_path` 不能以 `/` 开头 |
| **类型校验** | ge=1, le=4 |
| **client 过滤** | 前端菜单渲染仅取 `client="pc"` 的菜单 |
| **权限策略** | `ROLE_BASED` — 非超管用户按角色菜单过滤 |
| **树形结构** | parent_id 自引用,children 按 order 排序。创建/更新 parent_id 时需检测循环引用(如 A→B→C→A),禁止导致循环的操作 |
### 9.5 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/menu/detail/{id}` | 菜单详情 |
| GET | `/menu/list` | 菜单列表(树形) |
| POST | `/menu/create` | 创建菜单 |
| PUT | `/menu/update/{id}` | 更新菜单 |
| DELETE | `/menu/delete` | 删除菜单(批量) |
| PATCH | `/menu/status/batch` | 批量设置菜单状态 |
---
## 10. Dict 字典模块
### 10.1 业务描述
字典模块提供统一的类型-数据管理,用于维护系统中固定的下拉选项和枚举值。字典数据支持**平台共享**(tenant_id=1 的平台字典对所有租户可读)。
### 10.2 数据模型
#### DictTypesys_dict_type
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `dict_name` | String(64) | NOT NULL | 字典名称 |
| `dict_type` | String(255) | NOT NULL, UNIQUE(tenant_id) | 字典类型编码 |
**平台共享**`__platform_data_shared__ = True`
#### DictDatasys_dict_data
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `dict_sort` | Integer | default=0 | 排序 |
| `dict_label` | String(255) | NOT NULL | 字典标签 |
| `dict_value` | String(255) | NOT NULL | 字典键值 |
| `css_class` | String(255) | nullable | 样式属性 |
| `list_class` | String(255) | nullable | 表格回显样式 |
| `is_default` | Boolean | default=False | 是否默认 |
| `dict_type` | String(255) | NOT NULL | 字典类型(冗余字段) |
| `dict_type_id` | FK→sys_dict_type.id | NOT NULL, ON DELETE CASCADE | 字典类型 ID |
### 10.3 业务规则
| 规则 | 说明 |
|------|------|
| **平台共享** | tenant_id=1 的字典对所有租户可读。**写保护**:修改/删除 tenant_id=1 的平台字典数据时,仅允许超管操作,普通租户管理员不可修改平台字典 |
| **编码规则** | dict_type 以小写字母开头,仅含小写字母/数字/下划线 |
| **级联删除** | 删除 DictType 时,关联的 DictData 自动级联删除 |
| **双关联** | DictData 同时保留 dict_type(字符串冗余)和 dict_type_id(FK)双重关联。`dict_type` 为冗余字段,用于避免频繁 JOIN DictType 表获取类型编码。两者应保持一致,业务层插入时自动填充 dict_type 并与 dict_type_id 对应 |
| **唯一约束** | DictData 表:`UNIQUE(tenant_id, dict_type_id, dict_value)`,同一字典类型下不可有重复的 dict_value |
### 10.4 API 端点
#### 字典类型
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/dict/type/detail/{id}` | 字典类型详情 |
| GET | `/dict/type/list` | 字典类型列表 |
| POST | `/dict/type/create` | 创建字典类型 |
| PUT | `/dict/type/update/{id}` | 更新字典类型 |
| DELETE | `/dict/type/delete` | 删除字典类型(批量) |
| PATCH | `/dict/type/status/batch` | 批量设置字典类型状态 |
#### 字典数据
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/dict/data/detail/{id}` | 字典数据详情 |
| GET | `/dict/data/list` | 字典数据列表 |
| POST | `/dict/data/create` | 创建字典数据 |
| PUT | `/dict/data/update/{id}` | 更新字典数据 |
| DELETE | `/dict/data/delete` | 删除字典数据(批量) |
| PATCH | `/dict/data/status/batch` | 批量设置字典数据状态 |
---
## 11. Notice 通知公告模块
### 11.1 业务描述
管理租户内部的通知和公告发布。通知数据按租户严格隔离。
### 11.2 数据模型
**表名**`sys_notice`TenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `notice_title` | String(64) | NOT NULL | 公告标题 |
| `notice_type` | String(1) | NOT NULL | 类型(1:通知 2:公告) |
| `notice_content` | Text | nullable | 公告内容(富文本,XSS 过滤) |
**已读状态表**`sys_notice_read`(按租户隔离)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `user_id` | FK→sys_user.id | PK, ON DELETE CASCADE | 用户ID |
| `notice_id` | FK→sys_notice.id | PK, ON DELETE CASCADE | 通知ID |
| `read_time` | DateTime | NOT NULL, default=now | 已读时间 |
> 唯一约束:`UNIQUE(user_id, notice_id)`。未建立记录即代表未读。
### 11.3 业务规则
| 规则 | 说明 |
|------|------|
| **类型校验** | 仅支持 "1"(通知) 和 "2"(公告) |
| **XSS 防护** | notice_content 经过 `sanitize_html` 清洗 |
| **已读追踪** | 使用后端 `sys_notice_read` 表记录已读状态(多设备同步)。未读数量通过 LEFT JOIN 统计;通知列表返回未读数量标记 |
### 11.4 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/notice/detail/{id}` | 公告详情 |
| GET | `/notice/list` | 公告列表 |
| POST | `/notice/create` | 创建公告 |
| PUT | `/notice/update/{id}` | 更新公告 |
| DELETE | `/notice/delete` | 删除公告(批量) |
| PATCH | `/notice/status/batch` | 批量设置公告状态 |
| POST | `/notice/read/{id}` | 标记已读(写入 sys_notice_read |
| POST | `/notice/read-all` | 全部标记已读 |
| GET | `/notice/unread-count` | 获取当前用户未读通知数量 |
---
## 12. Params 系统参数模块
### 12.1 业务描述
管理系统级别的配置参数,支持区分系统内置参数(不可删除)和自定义参数。参数数据按租户隔离。
### 12.2 数据模型
**表名**`sys_param`TenantMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `config_name` | String(64) | NOT NULL | 参数名称 |
| `config_key` | String(500) | NOT NULL | 参数键名 |
| `config_value` | String(500) | nullable | 参数键值 |
| `config_type` | Boolean | default=False | 是否系统内置 |
### 12.3 业务规则
| 规则 | 说明 |
|------|------|
| **键名规则** | 小写字母开头,仅含小写字母/数字/_.- |
| **系统内置** | config_type=True 的参数不允许删除(业务层实现) |
### 12.4 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/params/detail/{id}` | 参数详情 |
| GET | `/params/list` | 参数列表 |
| POST | `/params/create` | 创建参数 |
| PUT | `/params/update/{id}` | 更新参数 |
| DELETE | `/params/delete` | 删除参数(批量) |
---
## 13. LoginLog 登录日志模块
### 13.1 业务描述
记录用户登录行为,用于安全审计和登录统计。登录日志为平台级资源,不受租户隔离限制,平台管理员可查看所有租户的登录记录。
### 13.2 数据模型
**表名**`platform_login_log`ModelMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `status` | Integer | NOT NULL, default=1 | 登录状态(1:成功 2:失败) |
| `login_ip` | String(50) | nullable | 登录 IP |
| `login_location` | String(255) | nullable | 登录位置 |
| `request_os` | String(64) | nullable | 操作系统 |
| `request_browser` | String(64) | nullable | 浏览器 |
| `msg` | String(255) | nullable | 提示消息 |
### 13.3 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/platform/loginlog/detail/{id}` | 登录日志详情 | 平台管理员 |
| GET | `/platform/loginlog/list` | 登录日志列表 | 平台管理员 |
| DELETE | `/platform/loginlog/delete` | 删除登录日志(批量) | 平台管理员 |
---
## 14. OperationLog 操作日志模块
### 14.1 业务描述
记录系统的操作日志,用于审计和问题追踪。通过 `OperationLogRoute` 路由类自动记录操作日志。日志数据按租户隔离,租户管理员仅能查看本租户的操作日志。
### 14.2 数据模型
**表名**`sys_operation_log`ModelMixin, TenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `request_path` | String(255) | NOT NULL | 请求路径 |
| `request_method` | String(10) | NOT NULL | 请求方法 |
| `request_payload` | LONGTEXT/TEXT | nullable | 请求体 |
| `request_ip` | String(50) | nullable | 请求 IP |
| `request_os` | String(64) | nullable | 操作系统 |
| `request_browser` | String(64) | nullable | 浏览器 |
| `response_code` | Integer | NOT NULL | 响应状态码 |
| `response_json` | LONGTEXT/TEXT | nullable | 响应体 |
| `process_time` | String(20) | nullable | 处理耗时 |
### 14.3 存储适配
| 数据库 | 大字段类型 |
|--------|-----------|
| MySQL | LONGTEXT |
| PostgreSQL | TEXT |
| SQLite | Text |
### 14.4 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/system/operationlog/detail/{id}` | 操作日志详情 | 租户管理员 |
| GET | `/system/operationlog/list` | 操作日志列表 | 租户管理员 |
| DELETE | `/system/operationlog/delete` | 删除操作日志(批量) | 租户管理员 |
### 14.5 日志保留策略
操作日志表数据量大(生产环境可能每天数十万条),需配置自动清理机制:
| 配置项 | 说明 | 默认值 |
|--------|------|--------|
| `operation_log_retention_days` | 日志保留天数 | 90 天 |
| `operation_log_cleanup_enabled` | 是否启用自动清理 | true |
| `operation_log_cleanup_cron` | 定时清理 cron 表达式 | 每天凌晨 3:00 |
- 定时任务 `cleanup_operation_log` 删除 `create_time < now - retention_days` 的记录
- 清理前可选归档到外部存储(OSS/本地文件),由 `operation_log_archive_enabled` 控制
- 登录日志(`platform_login_log`)同样受此策略管理
---
## 15. Tenant 租户管理模块
### 15.1 业务描述
租户是 SaaS 平台的核心概念,代表一个独立的组织。租户管理包含:租户定义、配额管理、配置管理、用户关联、自定义菜单授权。
### 15.2 数据模型
#### 核心表:platform_tenantModelMixin,无 TenantMixin- **单一大表设计**
将配额和配置字段直接集成到主表,简化结构便于管理。
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(100) | NOT NULL, UNIQUE | 租户名称 |
| `code` | String(100) | NOT NULL, UNIQUE | 租户编码(字母数字) |
| `contact_name` | String(64) | nullable | 联系人 |
| `contact_phone` | String(20) | nullable | 联系电话 |
| `contact_email` | String(128) | nullable | 联系邮箱 |
| `address` | String(255) | nullable | 地址 |
| `domain` | String(255) | nullable | 自定义域名 |
| `logo_url` | String(500) | nullable | Logo URL |
| `description` | Text | nullable | 租户描述 |
| `version` | String(20) | nullable | 版本号 |
| `sort` | Integer | default=0 | 排序 |
| `status` | Integer | NOT NULL, default=0 | 生命周期:0=active(正常) 1=grace(宽限期) 2=suspended(暂停) 3=frozen(冻结) 4=expired(过期) 5=archived(归档) |
| `package_id` | FK→platform_package.id | nullable, ON DELETE SET NULL | 关联套餐 |
| `start_time` | DateTime | nullable | 开始时间 |
| `end_time` | DateTime | nullable | 结束时间 |
| `grace_period_days` | Integer | default=7, ge=0 | 宽限期天数(到期后延迟禁用天数) |
| `grace_start_time` | DateTime | nullable | 宽限期开始时间(自动写入) |
| `max_users` | Integer | default=50, ge=1 | 最大用户数 |
| `max_roles` | Integer | default=20, ge=1 | 最大角色数 |
| `max_storage_mb` | Integer | default=500, ge=1 | 最大存储(MB) |
| `max_depts` | Integer | default=50, ge=1 | 最大部门数 |
| `favicon` | String(500) | nullable | 网站图标 |
| `login_bg` | String(500) | nullable | 登录背景图 |
| `copyright` | String(255) | nullable | 版权信息 |
| `help_doc` | String(500) | nullable | 帮助文档地址 |
| `privacy` | String(500) | nullable | 隐私政策地址 |
| `clause` | String(500) | nullable | 服务条款地址 |
| `keep_record` | String(100) | nullable | ICP 备案号 |
| `git_code` | String(500) | nullable | 源码地址 |
#### 关联表(必要的多对多关系)
| 表名 | 说明 | 关键字段 |
|------|------|---------|
| `platform_user_tenant` | 用户-租户关联 | user_id, tenant_id, role(owner/admin/member), is_default |
| `platform_tenant_menu` | 租户自定义菜单 | tenant_id, menu_id, UNIQUE(tenant_id, menu_id) |
### 15.3 核心业务流程
#### 创建租户
```
POST /tenant/create
├── 创建租户记录(platform_tenant
├── 生成初始管理员:{code}_admin + 随机12位密码(含特殊字符)
├── 密码不返回、不记录日志;生成一次性密码重置链接(含时效 Token,有效期24小时)
├── 向 contact_email 发送重置链接邮件(若未填 contact_email 则跳过,超管需手动处理)
├── 创建默认 owner 角色(sys_role),编码固定为 "owner"
├── 将初始管理员绑定到 owner 角色(sys_user_roles
├── 将租户可用菜单(套餐菜单 + 自定义授权菜单)全量分配给 owner 角色(sys_role_menus
├── 初始化租户配额(默认值写入 platform_tenant 主表)
└── 返回租户信息(不含密码)
```
> **安全说明**:初始管理员密码仅通过邮件中的一次性链接设置,不通过日志、API 响应等任何渠道明文传递。首次登录强制修改密码。
#### 删除租户
```
DELETE /tenant/delete
├── 系统租户(id=1)不可删除
├── 仅支持删除 archived 状态的租户
├── 有关联数据时拒绝删除,提示需先清理
└── 通过则物理删除(不可恢复)
```
#### 租户生命周期状态机
```
┌── 冻结 ──┐
↓ │
创建 → active(0) ──┤ ├→ archived(5) → deleted(已删除)
│ │ ↑
└→ grace(1) → suspended(2) → expired(4) ┘
↑ │
└── 续期 ←──┘
```
**状态说明**
| 状态 | 编码 | 触发方式 | 说明 |
|------|------|---------|------|
| `active` | 0 | 创建/续期/恢复 | 正常访问,读写开放 |
| `grace` | 1 | 到期后自动 | 宽限期:可登录但提示续费,功能正常 |
| `suspended` | 2 | 宽限期结束后自动 | 暂停:禁止写操作,仅可查看数据 |
| `expired` | 4 | 暂停超过保留期后自动 | 过期:禁止登录,数据保留待归档 |
| `frozen` | 3 | 超管手动冻结 | 冻结:立即禁止访问(不经过宽限期),可恢复为 active。保留全部数据 |
| `archived` | 5 | 冻结/过期后定时归档 | 归档:禁止访问,数据保留。唯一可被物理删除的状态 |
| `deleted` | — | 物理删除 | 已移除记录,不可逆 |
**冻结/归档/删除操作流**
```
冻结(PATCH /tenant/status/batch → status=3)
├── 仅超管可操作
├── 系统租户(id=1)不可冻结
├── 仅 active(0) 状态可冻结
└── 租户内所有用户 session 失效(Redis token 缓存清除)
归档(定时任务自动或手动)
├── 扫描 status=3 且冻结超过 archive_after_days(默认30天) 的租户
├── 扫描 status=4 且过期超过 archive_after_days 的租户
└── 自动将 status 设置为 5(archived)
物理删除(DELETE /tenant/delete)
├── 仅 archived(status=5)状态的租户可删除
├── 系统租户(id=1)不可删除
├── 检查关联数据:用户/部门/角色/岗位
├── 有关联数据时拒绝删除,提示需先清理
└── 无关联数据 → 物理删除
```
#### 套餐变更影响预览
套餐变更前,系统返回影响预览,超管确认后再执行:
```
PUT /tenant/update/{id} (package_id 变更)
├── 仅超管可操作
├── 调用预检接口 GET /tenant/{id}/package-change-preview?new_package_id=xxx
│ 返回:
│ - 受影响的角色列表(名称、用户数)
│ - 将被移除的菜单清单(名称、路径)
│ - 配额变化对比(max_users/max_roles/max_depts 当前值 → 新值)
│ - 受影响用户数总计
├── 前端展示影响明细,超管确认
├── 更新 tenant.package_id
├── 获取新可用菜单 ID(套餐菜单 ∪ 自定义菜单)
├── 清理角色中不在可用菜单内的 RoleMenus 记录
├── 更新租户配额(max_users/max_roles/max_depts 同步为新套餐限制值)
│ ├── 升级:配额只增不减(新值 > 旧值时才更新)
│ └── 降级:当前使用量 > 新配额时降级操作可执行但不缩减已有数据,仅限制后续新增
├── 发送通知给租户管理员(站内信,列出被回收的菜单和配额变化)
└── 完成
```
> 预检接口:`GET /platform/tenant/{id}/package-change-preview?new_package_id={id}`
> 通知内容:本次套餐变更收回了 X 个菜单权限,涉及 Y 个角色,请知悉。
### 15.4 业务规则
| 类别 | 规则 |
|------|------|
| **系统租户** | id=1 不可删除、禁用(冻结/归档)、修改编码 |
| **编码** | 仅含字母和数字,用于生成初始管理员用户名 |
| **初始管理员** | 自动创建,用户名 `{code}_admin`,密码 12 位随机(含特殊字符)。同时自动创建 owner 角色,将初始管理员绑定为 owner,并将租户当前可用菜单(套餐菜单 ∪ 自定义菜单)全量分配给该角色。初始管理员登录后即可看到完整的租户菜单,无需超管手动介入 |
| **配额** | 创建租户时,配额默认值从所选套餐的 max_users/max_roles/max_depts 复制到 platform_tenant 主表。无套餐时使用硬编码默认值(users=10, roles=5, depts=10)。超管可在租户管理页手动调整 |
| **owner 保护** | 每个租户至少保留一个 owner。从租户移除用户时,检查该用户是否为该租户的唯一 owner,是则拒绝移除。修改用户租户角色时,禁止将最后一个 owner 降级为 member。默认 owner 角色(code="owner")不可删除、不可禁用,确保租户始终有可用角色来管理 |
| **配额执行** | 租户配额在创建资源时执行检查。UserCRUD.create 检查 max_usersRoleCRUD.create 检查 max_rolesDeptCRUD.create 检查 max_depts。达到上限时拒绝创建并提示 |
| **默认租户** | 用户首次加入的租户自动设为默认,设置新默认时清除旧默认 |
| **多租户** | 一个用户可关联多个租户 |
| **生命周期** | 租户状态流转:active(0)→grace(1)→suspended(2)→expired(4)→archived(5)→物理删除。超管可人工冻结 active→frozen(3)→archived(5)。frozen 可恢复为 active。仅 archived 状态可物理删除。expired/frozen 超过 archive_after_days(默认30天) 后由定时任务自动归档为 archived(5) |
| **冻结后失效** | 租户冻结后,Redis 中该租户所有用户的 token 缓存立即清除,用户下次请求时因 token 无效被拒绝访问 |
| **续期** | active/grace/suspended 状态的租户可通过 `PUT /tenant/renew/{id}` 续期,传入 `end_time` 延长有效期并恢复为 active(0)expired/frozen/archived 状态不可续期 |
### 15.5 配置缓存策略
| 机制 | 说明 |
|------|------|
| **缓存 key** | `tenant_config:{tenant_id}:{config_key}` |
| **读取策略** | 优先读 Redis,未命中回源 DB 并写回缓存 |
| **更新策略** | 更新后自动同步刷新 Redis |
| **预热** | 应用启动时 `init_tenant_config_cache` 预加载所有配置 |
### 15.6 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/tenant/detail/{id}` | 租户详情 |
| GET | `/tenant/list` | 租户列表 |
| POST | `/tenant/create` | 创建租户(自动配管理员) |
| PUT | `/tenant/update/{id}` | 修改租户 |
| DELETE | `/tenant/delete` | 删除租户(仅 archived 状态可删) |
| PATCH | `/tenant/status/batch` | 批量修改状态(含冻结/恢复) |
| PUT | `/tenant/status/{id}` | 启/禁用(冻结/恢复) |
| PUT | `/tenant/renew/{id}` | 续期(延长 end_time |
| GET | `/tenant/{id}/users` | 获取租户用户列表 |
| POST | `/tenant/{id}/users` | 向租户添加用户 |
| DELETE | `/tenant/{id}/users/{uid}` | 从租户移除用户 |
| GET | `/tenant/{id}/quota` | 获取租户配额 |
| PUT | `/tenant/{id}/quota` | 修改租户配额 |
| GET | `/tenant/{id}/config` | 获取租户配置 |
| GET | `/tenant/{id}/config/info` | 获取租户配置(公开,缓存) |
| PUT | `/tenant/{id}/config` | 批量更新配置 |
| GET | `/tenant/{id}/menus` | 获取租户自定义菜单 |
| PUT | `/tenant/{id}/menus` | 设置租户自定义菜单 |
---
## 16. Package 套餐模块(module_package
### 16.1 业务描述
套餐模块是独立的功能模块,用于管理租户的功能套餐配置。套餐是预定义的功能菜单集合,用于标准化租户授权流程。通过套餐体系可以减少逐个分配菜单的工作量,实现基础版/专业版/企业版等分级授权。
### 16.2 数据模型
#### platform_package
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(100) | NOT NULL, UNIQUE | 套餐名称 |
| `code` | String(100) | NOT NULL, UNIQUE | 套餐编码 |
| `status` | Integer | default=0 | 状态(0:启动 1:停用) |
| `is_default` | Boolean | default=False | 是否为默认套餐(自助注册时自动选用) |
| `price` | Integer | default=0 | 价格(分),0=免费 |
| `period` | String(10) | nullable | 计费周期:month/year/once |
| `trial_days` | Integer | default=0 | 试用天数,0=无试用 |
| `max_users` | Integer | default=10 | 套餐用户数上限 |
| `max_roles` | Integer | default=5 | 套餐角色数上限 |
| `max_depts` | Integer | default=10 | 套餐部门数上限 |
| `max_tenants` | Integer | nullable | 该套餐最大租户数限制(平台运营管控),null=不限制 |
| `sort` | Integer | default=0 | 排序 |
#### platform_package_menu
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `package_id` | FK→platform_package.id | PK, ON DELETE CASCADE | 套餐 ID |
| `menu_id` | FK→platform_menu.id | PK, ON DELETE CASCADE | 菜单 ID |
唯一约束:`(package_id, menu_id)`
### 16.3 核心流程
#### 租户可用菜单合并逻辑
```
get_tenant_available_menu_ids(tenant_id):
可用菜单 = set()
# 1. 如果租户关联了套餐,取出套餐的所有菜单
if tenant.package_id:
可用菜单.add(套餐菜单...)
# 2. 取出租户自定义授权菜单(platform_tenant_menu
可用菜单.add(自定义菜单...)
return list(可用菜单) # 并集
```
#### 套餐变更后清理
```
套餐变更 → 取新可用菜单 ID →
查询该租户所有角色 → 删除角色中不在可用菜单内的 RoleMenus 记录 → 完成
```
### 16.4 业务规则
| 规则 | 说明 |
|------|------|
| **套餐变更** | 仅超管可操作 |
| **删除约束** | 删除前检查是否有租户使用,有则拒绝 |
| **级联策略** | 套餐删除时,租户 package_id SET NULL |
| **菜单设置** | 套餐菜单全量替换(先删后插) |
| **套餐禁用** | 套餐 status=1 时,已关联该套餐的租户在 `get_tenant_available_menu_ids` 中**不再计入**套餐菜单,仅保留租户自定义菜单(platform_tenant_menu)。恢复 status=0 后套餐菜单自动恢复 |
| **套餐已删** | 套餐被删除(package_id SET NULL)后,租户降级为仅有自定义菜单,需及时为受影响租户迁移或补配权限 |
### 16.5 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/platform/package/detail/{id}` | 套餐详情 |
| GET | `/platform/package/list` | 套餐列表 |
| POST | `/platform/package/create` | 创建套餐 |
| PUT | `/platform/package/update/{id}` | 修改套餐 |
| DELETE | `/platform/package/delete` | 删除套餐 |
| GET | `/platform/package/{id}/menus` | 获取套餐菜单 |
| PUT | `/platform/package/{id}/menus` | 设置套餐菜单(全量替换) |
---
## 17. Ticket 工单模块
### 17.1 业务描述
工单系统用于用户提交反馈、建议和缺陷报告,支持指派处理人进行跟踪处理。工单数据按租户隔离。
### 17.2 数据模型
**表名**`platform_ticket`TenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `title` | String(200) | NOT NULL | 工单标题 |
| `ticket_content` | Text | nullable | 工单内容(富文本) |
| `summary` | Text | nullable | 工单内容(纯文本摘要) |
| `ticket_type` | String(20) | NOT NULL, default="suggestion" | 类型(suggestion/bug/optimize/other |
| `status` | Integer | NOT NULL, default=0 | 状态(0:待处理 1:处理中 2:已完成 3:已关闭) |
| `images` | Text | nullable | 图片 URL 列表(JSON 数组) |
| `reply` | Text | nullable | 回复内容 |
| `assigned_id` | FK→sys_user.id | nullable, ON DELETE SET NULL | 处理人 |
### 17.3 状态流转
```
待处理(0) → 处理中(1) → 已完成(2)
↑ │
└──── 已关闭(3)
```
#### 状态转换规则
| 源状态 | 目标状态 | 允许角色 | 说明 |
|-------|---------|---------|------|
| 待处理(0) | 处理中(1) | 创建人/处理人/超管 | 确认受理 |
| 待处理(0) | 已关闭(3) | 创建人/超管 | 取消提交 |
| 处理中(1) | 已完成(2) | 处理人/超管 | 处理完成 |
| 处理中(1) | 已关闭(3) | 创建人/处理人/超管 | 强行关闭(需填写原因) |
| 已完成(2) | 已关闭(3) | 创建人/超管 | 确认关闭 |
| 已关闭(3) | 待处理(0) | 超管 | 仅超管可重新打开 |
> 非法转换(如已完成→处理中)应在 Service 层校验并拒绝 |
### 17.4 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/ticket/detail/{id}` | 工单详情 |
| GET | `/ticket/list` | 工单列表 |
| POST | `/ticket/create` | 创建工单 |
| PUT | `/ticket/update/{id}` | 更新工单 |
| DELETE | `/ticket/delete` | 删除工单(批量) |
| PUT | `/ticket/batch/status` | 批量更新工单状态 |
---
## 18. Plugin 插件模块
### 18.1 业务描述
插件系统是平台的扩展机制。`platform_plugin` 作为插件注册表(平台级资源),记录所有可用插件的元数据。租户通过 `platform_tenant_plugin` 关联表安装插件。
### 18.2 数据模型
#### platform_plugin(平台资源,无 TenantMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(100) | NOT NULL, UNIQUE | 插件名称 |
| `code` | String(50) | NOT NULL, UNIQUE | 插件编码(module_xxx |
| `description` | Text | nullable | 插件描述 |
| `version` | String(20) | NOT NULL, default="1.0.0" | 版本号 |
| `author` | String(100) | nullable | 作者 |
| `icon` | String(500) | nullable | 图标 URL |
| `category` | String(20) | NOT NULL, default="tool" | 分类(tool/ai/monitor/business |
| `price` | Integer | NOT NULL, default=0 | 价格(分,0=免费) |
| `menu_path` | String(200) | nullable | 菜单路径(安装后显示) |
| `permission_prefix` | String(100) | nullable | 权限前缀 |
| `dependencies` | Text | nullable | 依赖插件编码(JSON 数组) |
| `sort` | Integer | NOT NULL, default=0 | 排序 |
#### platform_tenant_plugin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `tenant_id` | FK→platform_tenant.id | NOT NULL, ON DELETE CASCADE | 租户 ID |
| `plugin_id` | FK→platform_plugin.id | NOT NULL, ON DELETE CASCADE | 插件 ID |
| `enabled` | String(1) | NOT NULL, default="1" | 启用(1:启用 0:禁用) |
| `installed_time` | DateTime | NOT NULL | 安装时间 |
唯一约束:`(tenant_id, plugin_id)`
### 18.3 插件目录结构
```
plugin/module_xxx/
├── __init__.py
├── plugin.toml # 插件元数据(名称、版本、路由前缀等)
├── controller.py
├── service.py
├── crud.py
├── model.py
└── schema.py
```
已内置插件:
- `module_ai/chat` — AI 对话
- `module_example/demo` — 示例
- `module_generator/gencode` — 代码生成器
- `module_task/cronjob` — 定时任务
- `module_task/workflow` — 工作流引擎
### 18.4 API 端点
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/plugin/detail/{id}` | 插件详情 |
| GET | `/plugin/list` | 插件列表(含当前租户安装状态) |
| POST | `/plugin/create` | 创建插件 |
| PUT | `/plugin/update/{id}` | 更新插件 |
| DELETE | `/plugin/delete` | 删除插件(批量) |
| POST | `/plugin/install` | 租户安装插件 |
| POST | `/plugin/uninstall` | 租户卸载插件 |
---
## 19. 到期处理
### 19.1 到期阶段定义
租户到期后分三个阶段处理,避免粗暴直接禁用(编码与 §15.2 生命周期统一):
| 阶段 | 编码 | 说明 |
|------|------|------|
| `active` | 0 | 正常,在有效期内 |
| `grace` | 1 | 宽限期:到期后可登录但每次登录提示续费,功能正常 |
| `suspended` | 2 | 暂停:宽限期结束后禁用写操作,仅可查看数据 |
| `expired` | 4 | 过期:暂停超过保留期后,禁止登录 |
**阶段流转**(与生命周期统一):
```
active(0) → grace(1) → suspended(2) → expired(4) → archived(5)
↑ │ │
└── 续期 ←──┘────────────┘
```
### 19.2 自动处理逻辑
定时任务 `check_tenant_expiry` 定期扫描所有正常状态的租户:
1. 遍历 status=0(active) 或 status=1(grace) 或 status=2(suspended) 的租户
2. **未到达生效时间**`start_time` 存在且 `start_time > now` → 暂不处理,登录时提示"租户尚未生效"
3. **进入宽限期**status=0 且 `end_time` 存在且 `now > end_time` → 设 `status=1`,记录 `grace_start_time`
4. **进入暂停**status=1(grace) 且 `now > grace_start_time + grace_period_days`(默认7天)→ 设 `status=2`(suspended)
5. **进入过期**status=2(suspended) 且暂停超过 `expire_after_days`(默认30天)→ 设 `status=4`(expired)
6. **宽限期内续期**:若 status=1/2 时发现 `end_time` 已续期至未来 → 恢复 `status=0`(active),清除 `grace_start_time`
7. **即将到期提醒**`end_time` 在 30天/7天/1天 内 → 触发到期提醒
### 19.3 各阶段行为
| 阶段 | 登录 | 读操作 | 写操作 | 提示 |
|------|------|--------|--------|------|
| active(0) | ✅ | ✅ | ✅ | 无 |
| grace(1) | ✅ | ✅ | ✅ | 每次登录弹窗提示"您的租户已到期,请尽快续费" |
| suspended(2) | ✅ | ✅ | ❌ 拒绝写入 | 提示"租户已暂停,请联系管理员续费" |
| expired(4) | ❌ | — | — | 提示"租户已过期" |
### 19.4 到期配置参数
| 字段 | 位置 | 说明 |
|------|------|------|
| `grace_period_days` | `platform_tenant` 表,Integerdefault=7 | 宽限期天数 |
| `expire_after_days` | 全局配置,Integerdefault=30 | 暂停→过期天数(suspended 超过此天数后自动标记为 expired) |
| `archive_after_days` | 全局配置,Integerdefault=30 | frozen/expired 超过此天数后自动归档为 archived(5) |
### 19.5 提醒方式
| 触点 | 触发时机 | 渠道 |
|------|---------|------|
| 30天前 | `end_time - 30d <= now` | 站内信(sys_notice |
| 7天前 | `end_time - 7d <= now` | 站内信 + 邮件(contact_email |
| 1天前 | `end_time - 1d <= now` | 站内信 + 邮件 + 短信(contact_phone,可选) |
| 已到期(grace) | 每次登录 | 弹窗提示 |
当前邮件/短信为预留扩展点,未配置渠道时降级为站内信。
---
# Part 3:附录
---
## 20. API 接口汇总
### 20.1 认证模块
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/auth/login` | 登录 |
| POST | `/auth/token/refresh` | 刷新 token |
| POST | `/auth/logout` | 退出登录 |
| GET | `/auth/captcha` | 验证码 |
| POST | `/auth/register` | 用户注册(自动创建默认租户) |
| POST | `/auth/select-tenant/{id}` | 选择租户(生成正式 token) |
| GET | `/auth/tenants` | 获取可选租户列表 |
| POST | `/auth/forgot-password` | 忘记密码 |
| GET | `/auth/auto-login/users` | 获取免登录用户列表 |
| POST | `/auth/auto-login/token` | 获取免登录Token |
| POST | `/auth/auto-login` | 免登录 |
| GET | `/auth/oauth/{provider}/login` | OAuth2 授权跳转 |
| GET | `/auth/oauth/{provider}/callback` | OAuth2 回调处理 |
### 20.2 用户模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/user/detail/{id}` | user:query | 详情 |
| GET | `/user/list` | user:query | 列表 |
| POST | `/user/create` | user:create | 创建 |
| PUT | `/user/update/{id}` | user:update | 更新 |
| DELETE | `/user/delete` | user:delete | 删除 |
| PATCH | `/user/status/batch` | user:patch | 批量设状态 |
| GET | `/user/current/info` | - | 当前用户信息 |
| PUT | `/user/current/info/update` | - | 更新当前用户 |
| PUT | `/user/password/change` | - | 改密码(本人操作) |
| PUT | `/user/password/reset/{id}` | user:update | 重置密码(管理员操作) |
| GET | `/user/import/template` | user:download | 导入模板 |
| POST | `/user/import/data` | user:import | 导入 |
| POST | `/user/export` | user:query | 导出 |
### 20.3 角色模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/role/detail/{id}` | role:query | 详情 |
| GET | `/role/list` | role:query | 列表 |
| POST | `/role/create` | role:create | 创建 |
| PUT | `/role/update/{id}` | role:update | 更新 |
| DELETE | `/role/delete` | role:delete | 删除 |
| PATCH | `/role/status/batch` | role:patch | 批量设状态 |
| PUT | `/role/menus` | role:update | 设置菜单 |
| PUT | `/role/permission` | role:update | 设置权限 |
### 20.4 部门模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/dept/detail/{id}` | dept:query | 详情 |
| GET | `/dept/list` | dept:query | 列表 |
| POST | `/dept/create` | dept:create | 创建 |
| PUT | `/dept/update/{id}` | dept:update | 更新 |
| DELETE | `/dept/delete` | dept:delete | 删除 |
| PATCH | `/dept/status/batch` | dept:patch | 批量设状态 |
### 20.5 岗位模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/position/detail/{id}` | position:query | 详情 |
| GET | `/position/list` | position:query | 列表 |
| POST | `/position/create` | position:create | 创建 |
| PUT | `/position/update/{id}` | position:update | 更新 |
| DELETE | `/position/delete` | position:delete | 删除 |
| PATCH | `/position/status/batch` | position:patch | 批量设状态 |
### 20.6 菜单模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/menu/detail/{id}` | menu:query | 详情 |
| GET | `/menu/list` | menu:query | 列表 |
| POST | `/menu/create` | menu:create | 创建 |
| PUT | `/menu/update/{id}` | menu:update | 更新 |
| DELETE | `/menu/delete` | menu:delete | 删除 |
| PATCH | `/menu/status/batch` | menu:patch | 批量设状态 |
### 20.7 字典模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/dict/type/detail/{id}` | dict:query | 字典类型详情 |
| GET | `/dict/type/list` | dict:query | 字典类型列表 |
| POST | `/dict/type/create` | dict:create | 创建字典类型 |
| PUT | `/dict/type/update/{id}` | dict:update | 更新字典类型 |
| DELETE | `/dict/type/delete` | dict:delete | 删除字典类型 |
| PATCH | `/dict/type/status/batch` | dict:patch | 批量设状态 |
| GET | `/dict/data/detail/{id}` | dict:query | 字典数据详情 |
| GET | `/dict/data/list` | dict:query | 字典数据列表 |
| POST | `/dict/data/create` | dict:create | 创建字典数据 |
| PUT | `/dict/data/update/{id}` | dict:update | 更新字典数据 |
| DELETE | `/dict/data/delete` | dict:delete | 删除字典数据 |
### 20.8 通知公告
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/notice/detail/{id}` | notice:query | 详情 |
| GET | `/notice/list` | notice:query | 列表 |
| POST | `/notice/create` | notice:create | 创建 |
| PUT | `/notice/update/{id}` | notice:update | 更新 |
| DELETE | `/notice/delete` | notice:delete | 删除 |
| PATCH | `/notice/status/batch` | notice:patch | 批量设状态 |
### 20.9 系统参数
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/param/detail/{id}` | params:query | 详情 |
| GET | `/param/list` | params:query | 列表 |
| POST | `/param/create` | params:create | 创建 |
| PUT | `/param/update/{id}` | params:update | 更新 |
| DELETE | `/param/delete` | params:delete | 删除 |
| PATCH | `/param/status/batch` | params:patch | 批量设置状态 |
### 20.10 登录日志模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/platform/loginlog/detail/{id}` | module_platform:loginlog:query | 详情 |
| GET | `/platform/loginlog/list` | module_platform:loginlog:query | 列表 |
| DELETE | `/platform/loginlog/delete` | module_platform:loginlog:delete | 删除(批量) |
### 20.11 操作日志模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/system/operationlog/detail/{id}` | module_system:operationlog:query | 详情 |
| GET | `/system/operationlog/list` | module_system:operationlog:query | 列表 |
| DELETE | `/system/operationlog/delete` | module_system:operationlog:delete | 删除(批量) |
### 20.12 工单模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/ticket/detail/{id}` | ticket:query | 详情 |
| GET | `/ticket/list` | ticket:query | 列表 |
| POST | `/ticket/create` | ticket:create | 创建 |
| PUT | `/ticket/update/{id}` | ticket:update | 更新 |
| DELETE | `/ticket/delete` | ticket:delete | 删除 |
| PATCH | `/ticket/status/batch` | ticket:patch | 批量设置状态 |
### 20.13 插件模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/plugin/detail/{id}` | plugin:query | 详情 |
| GET | `/plugin/list` | plugin:query | 列表 |
| POST | `/plugin/create` | plugin:create | 创建 |
| PUT | `/plugin/update/{id}` | plugin:update | 更新 |
| DELETE | `/plugin/delete` | plugin:delete | 删除 |
| PATCH | `/plugin/status/batch` | plugin:patch | 批量设置状态 |
| POST | `/plugin/install` | - | 安装插件 |
| POST | `/plugin/uninstall` | - | 卸载插件 |
### 20.14 租户模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/tenant/detail/{id}` | tenant:query | 详情 |
| GET | `/tenant/list` | tenant:query | 列表 |
| POST | `/tenant/create` | tenant:create | 创建 |
| PUT | `/tenant/update/{id}` | tenant:update | 更新 |
| DELETE | `/tenant/delete` | tenant:delete | 删除 |
| PATCH | `/tenant/status/batch` | tenant:patch | 批量设置状态 |
| PUT | `/tenant/status/{id}` | tenant:update | 启/禁用 |
| GET | `/tenant/{id}/users` | tenant:query | 用户列表 |
| POST | `/tenant/{id}/users` | tenant:update | 添加用户 |
| DELETE | `/tenant/{id}/users/{uid}` | tenant:update | 移除用户 |
| GET | `/tenant/{id}/quota` | tenant:query | 获取配额 |
| PUT | `/tenant/{id}/quota` | tenant:update | 修改配额 |
| GET | `/tenant/{id}/config` | tenant:query | 获取配置 |
| PUT | `/tenant/{id}/config` | tenant:update | 更新配置 |
### 20.15 套餐模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/platform/package/detail/{id}` | package:query | 详情 |
| GET | `/platform/package/list` | package:query | 列表 |
| POST | `/platform/package/create` | package:create | 创建 |
| PUT | `/platform/package/update/{id}` | package:update | 更新 |
| DELETE | `/platform/package/delete` | package:delete | 删除 |
| GET | `/platform/package/{id}/menus` | package:query | 获取菜单 |
| PUT | `/platform/package/{id}/menus` | package:update | 设置菜单 |
### 20.16 监控模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/monitor/online/list` | module_monitor:online:query | 在线用户列表 |
| DELETE | `/monitor/online/delete` | module_monitor:online:delete | 强制下线 |
| DELETE | `/monitor/online/clear` | module_monitor:online:delete | 清空所有在线用户 |
| GET | `/monitor/cache/info` | module_monitor:cache:query | 获取缓存监控统计 |
| GET | `/monitor/cache/get/names` | module_monitor:cache:query | 获取缓存名称列表 |
| GET | `/monitor/cache/get/keys/{cache_name}` | module_monitor:cache:query | 获取缓存键名列表 |
| GET | `/monitor/cache/get/value/{cache_name}/{cache_key}` | module_monitor:cache:query | 获取缓存值 |
| DELETE | `/monitor/cache/delete/name/{cache_name}` | module_monitor:cache:delete | 清除指定缓存名称 |
| DELETE | `/monitor/cache/delete/key/{cache_key}` | module_monitor:cache:delete | 清除指定缓存键 |
| DELETE | `/monitor/cache/clear` | module_monitor:cache:delete | 清除所有缓存 |
| GET | `/monitor/resource/list` | module_monitor:resource:query | 目录列表(分页) |
| POST | `/monitor/resource/upload` | module_monitor:resource:upload | 上传文件 |
| GET | `/monitor/resource/download` | module_monitor:resource:download | 下载文件 |
| DELETE | `/monitor/resource/delete` | module_monitor:resource:delete | 删除文件 |
| POST | `/monitor/resource/move` | module_monitor:resource:move | 移动文件 |
| POST | `/monitor/resource/copy` | module_monitor:resource:copy | 复制文件 |
| POST | `/monitor/resource/rename` | module_monitor:resource:rename | 重命名文件 |
| POST | `/monitor/resource/mkdir` | module_monitor:resource:mkdir | 创建目录 |
| POST | `/monitor/resource/export` | module_monitor:resource:export | 导出资源列表 |
| GET | `/monitor/server/info` | module_monitor:server:query | 服务器监控信息 |
### 20.17 公共模块
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| POST | `/common/file/upload` | module_common:file:upload | 上传文件 |
| POST | `/common/file/download` | module_common:file:download | 下载文件 |
| GET | `/health` | — | 基础健康检查 |
| GET | `/health/live` | — | 存活探针 |
| GET | `/health/ready` | — | 就绪探针 |
| GET | `/metrics` | — | Prometheus 指标端点 |
### 20.18 邮件服务
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/platform/email/config` | platform:email:query | 获取 SMTP 配置 |
| PUT | `/platform/email/config` | platform:email:update | 更新 SMTP 配置 |
| POST | `/platform/email/test` | platform:email:update | 发送测试邮件 |
| GET | `/platform/email/template/list` | platform:email:query | 模板列表 |
| POST | `/platform/email/template/create` | platform:email:create | 创建模板 |
| PUT | `/platform/email/template/update/{id}` | platform:email:update | 更新模板 |
| GET | `/platform/email/log/list` | platform:email:query | 发送日志列表 |
### 20.19 订单与支付
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| POST | `/platform/order/create` | platform:order:create | 创建订单 |
| GET | `/platform/order/detail/{id}` | platform:order:query | 订单详情 |
| GET | `/platform/order/list` | platform:order:query | 订单列表 |
| POST | `/platform/order/cancel/{id}` | platform:order:update | 取消订单 |
| POST | `/platform/payment/callback/alipay` | — | 支付宝回调 |
| POST | `/platform/payment/callback/wxpay` | — | 微信支付回调 |
| GET | `/platform/payment/record/list` | platform:payment:query | 支付记录列表 |
### 20.20 租户自助服务
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/tenant/package/available` | tenant:package:query | 可选套餐列表 |
| GET | `/tenant/package/preview` | tenant:package:query | 套餐变更影响预览 |
| POST | `/tenant/order/create` | tenant:order:create | 创建自助订单 |
| GET | `/tenant/order/list` | tenant:order:query | 我的订单列表 |
| GET | `/tenant/order/detail/{id}` | tenant:order:query | 订单详情 |
### 20.21 用量统计
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/platform/api-usage/daily` | platform:api_usage:query | 按天用量统计 |
| GET | `/platform/api-usage/tenant/{id}` | platform:api_usage:query | 指定租户用量 |
| GET | `/platform/api-usage/rank` | platform:api_usage:query | 租户用量排行 |
| GET | `/platform/api-usage/anomalies` | platform:api_usage:query | 异常调用记录 |
### 20.22 用户邀请
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| POST | `/tenant/invite/send` | tenant:invite:create | 发送邀请 |
| GET | `/tenant/invite/list` | tenant:invite:query | 邀请列表 |
| DELETE | `/tenant/invite/cancel/{id}` | tenant:invite:delete | 取消邀请 |
| GET | `/invite/validate/{code}` | — | 校验邀请码(公开) |
| POST | `/invite/accept/{code}` | — | 接受邀请(需登录) |
### 20.23 发票管理
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| POST | `/tenant/invoice/apply` | tenant:invoice:create | 申请开票 |
| GET | `/tenant/invoice/list` | tenant:invoice:query | 我的发票列表 |
| GET | `/tenant/invoice/{id}/download` | tenant:invoice:download | 下载发票 PDF |
| GET | `/platform/invoice/list` | platform:invoice:query | 全部发票列表 |
| PUT | `/platform/invoice/issue/{id}` | platform:invoice:update | 开具发票 |
| PUT | `/platform/invoice/void/{id}` | platform:invoice:update | 作废发票 |
### 20.24 审计日志
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/platform/audit/list` | platform:audit:query | 审计日志列表 |
| GET | `/platform/audit/detail/{id}` | platform:audit:query | 审计日志详情 |
| GET | `/platform/audit/export` | platform:audit:export | 导出审计日志 |
### 20.25 运营大盘
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/platform/dashboard/overview` | platform:dashboard:query | 运营概览 |
| GET | `/platform/dashboard/revenue` | platform:dashboard:query | 收入趋势 |
| GET | `/platform/dashboard/tenants` | platform:dashboard:query | 租户统计 |
| GET | `/platform/dashboard/api-usage` | platform:dashboard:query | API 用量趋势 |
---
## 21. 数据库表结构
### 21.1 平台资源表(无 tenant_id
| 表名 | 说明 | 关键索引 |
|------|------|---------|
| `platform_tenant` | 租户(单一大表,含配额+配置字段) | UNIQUE(name), UNIQUE(code) |
| `platform_package` | 套餐 | UNIQUE(name), UNIQUE(code) |
| `platform_package_menu` | 套餐-菜单关联 | UNIQUE(package_id, menu_id) |
| `platform_menu` | 菜单 | - |
| `platform_plugin` | 插件注册表 | UNIQUE(name), UNIQUE(code) |
| `platform_email_config` | 邮件 SMTP 配置 | 单例表 |
| `platform_email_template` | 邮件模板 | UNIQUE(code) |
| `platform_email_log` | 邮件发送日志 | - |
| `platform_order` | 订单 | UNIQUE(order_no) |
| `platform_payment_record` | 支付记录 | UNIQUE(transaction_id) |
### 21.2 租户关联表(FK→tenant,无独立 tenant_id 列)
| 表名 | 说明 | 关键索引 |
|------|------|---------|
| `platform_tenant_menu` | 租户自定义菜单 | UNIQUE(tenant_id, menu_id) |
| `platform_tenant_plugin` | 租户安装插件 | UNIQUE(tenant_id, plugin_id) |
| `platform_user_tenant` | 用户-租户关联 | UNIQUE(user_id, tenant_id) |
### 21.3 平台级租户关联业务表(含 tenant_id)
| 表名 | 说明 | 关键索引 |
|------|------|---------|
| `platform_invite_record` | 用户邀请记录 | UNIQUE(invite_code) |
| `platform_api_usage_daily` | API 用量日统计 | UNIQUE(tenant_id, date, api_path) |
### 21.4 租户隔离业务表(含 tenant_id)
| 表名 | 说明 | 关键索引 |
|------|------|---------|
| `sys_user` | 用户 | UNIQUE(tenant_id, username) |
| `sys_role` | 角色 | UNIQUE(tenant_id, code) |
| `sys_dept` | 部门 | UNIQUE(tenant_id, code) |
| `sys_position` | 岗位 | - |
| `sys_notice` | 通知公告 | - |
| `sys_param` | 系统参数 | - |
| `sys_operation_log` | 操作日志(租户隔离) | - |
| `platform_login_log` | 登录日志(平台级,无 tenant_id | - |
| `platform_ticket` | 工单 | - |
### 21.4 平台共享业务表(含 tenant_id__platform_data_shared__
| 表名 | 说明 | 关键索引 |
|------|------|---------|
| `sys_dict_type` | 字典类型 | UNIQUE(tenant_id, dict_type) |
| `sys_dict_data` | 字典数据 | UNIQUE(tenant_id, dict_type_id, dict_value) |
### 21.5 关联表
| 表名 | 说明 | 约束 |
|------|------|------|
| `sys_user_roles` | 用户-角色关联 | PK(user_id, role_id)ON DELETE CASCADE |
| `sys_user_positions` | 用户-岗位关联 | PK(user_id, position_id)ON DELETE CASCADE |
| `sys_role_menus` | 角色-菜单关联 | PK(role_id, menu_id)ON DELETE CASCADE |
| `sys_role_depts` | 角色-部门关联 | PK(role_id, dept_id)ON DELETE CASCADE |
| `sys_notice_read` | 通知已读记录 | PK(user_id, notice_id)ON DELETE CASCADE |
### 21.6 插件表
| 表名 | 模块 | 说明 | 前缀规则 |
|------|------|------|---------|
| `task_workflow` | module_task/workflow | 工作流定义 | `task_` = module_task |
| `task_workflow_node_type` | module_task/workflow | 工作流节点类型 | `task_` = module_task |
| `task_node` | module_task/cronjob | 定时任务节点类型 | `task_` = module_task |
| `task_job` | module_task/cronjob | 任务执行日志 | `task_` = module_task |
| `gen_table` | module_generator/gencode | 代码生成表 | `gen_` = module_generator |
| `gen_table_column` | module_generator/gencode | 代码生成字段 | `gen_` = module_generator |
| `example_demo` | module_example/demo | 示例表 | `example_` = module_example |
| `example_demo01` | module_example/demo01 | 示例表01 | `example_` = module_example |
> **命名规范**`platform_` = 平台模块,`sys_` = 系统模块,`task_` = 任务插件,`gen_` = 生成器插件,`example_` = 示例模块
### 21.7 商业运营表
| 表名 | 模块 | 说明 |
|------|------|------|
| `platform_invoice` | Invoice | 发票记录 |
| `platform_refund` | Order | 退款记录 |
| `platform_audit_log` | AuditLog | 审计日志 |
---
## 22. 安全性要求
1. **JWT 租户上下文**:从 Token 中提取 `tenant_id``is_super_admin`,通过 ContextVar 在整个请求周期传递
2. **白名单路径**:登录、验证码、健康检查等公开接口不设置租户上下文
3. **系统租户保护**
- id=1 不可删除
- id=1 不可禁用
- id=1 的编码不可修改
4. **数据删除保护**:删除租户前检查关联数据,防止孤立记录
5. **租户 owner 保护**:每个租户至少保留一个 owner
6. **菜单越权防护**:非超管用户只能在租户可用菜单范围内分配角色菜单
7. **ContextVar 清理**:请求结束后清理 ContextVar,防止跨请求泄漏
8. **密码安全**:Bcrypt 哈希存储,不存储明文;普通用户密码最低 8 位(含字母+数字);初始管理员密码 12 位随机(含大小写字母+数字+特殊字符)
9. **XSS 防护**:通知公告内容经过 `sanitize_html` 清洗
10. **登录限流**:同一 IP/账号连续登录失败 5 次后锁定 15 分钟(Redis 计数 + TTL),防止暴力破解
11. **级联策略**:所有 FK 均有 ON DELETE/ON UPDATE 级联策略,保证数据完整性
12. **路径越权防护**:文件资源管理禁止路径遍历(`..`),防止越权访问
13. **CORS 配置**:通过白名单配置允许的来源域名,拒绝未授权的跨域请求
---
## 23. Email 邮件服务模块
### 23.1 业务描述
邮件服务是 SaaS 平台的通信基础设施,为密码重置、邀请通知、到期提醒、工单通知等业务提供统一的邮件发送能力。支持 SMTP 配置、模板管理、发送日志追踪。
### 23.2 数据模型
#### platform_email_config
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `smtp_host` | String(255) | NOT NULL | SMTP 服务器地址 |
| `smtp_port` | Integer | NOT NULL, default=465 | SMTP 端口 |
| `smtp_username` | String(255) | NOT NULL | SMTP 用户名 |
| `smtp_password` | String(255) | NOT NULL, 加密存储 | SMTP 密码 |
| `sender_name` | String(100) | NOT NULL | 发件人名称 |
| `sender_email` | String(255) | NOT NULL | 发件人邮箱 |
| `use_tls` | Boolean | default=True | 是否使用 TLS |
| `status` | Integer | default=0 | 状态(0:启用 1:禁用) |
> 单例表:仅一条记录。超管在平台配置中管理。
#### platform_email_template
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `code` | String(50) | NOT NULL, UNIQUE | 模板编码(如 `password_reset``tenant_invite``expiry_reminder``ticket_notify` |
| `name` | String(100) | NOT NULL | 模板名称 |
| `subject` | String(255) | NOT NULL | 邮件主题(支持 `{变量}` 占位符) |
| `body` | Text | NOT NULL | 邮件正文(HTML,支持 `{变量}` 占位符) |
| `variables` | Text | nullable | 可用变量说明(JSON 数组,如 `["{username}", "{reset_link}"]` |
| `status` | Integer | default=0 | 状态(0:启用 1:禁用) |
#### platform_email_log
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `template_code` | String(50) | nullable | 使用的模板编码 |
| `to_email` | String(255) | NOT NULL | 收件人邮箱 |
| `to_user_id` | FK→sys_user.id | nullable | 收件人用户 ID |
| `subject` | String(255) | NOT NULL | 实际发送的主题 |
| `body` | Text | NOT NULL | 实际发送的正文(渲染后) |
| `status` | Integer | NOT NULL, default=0 | 发送状态(0:待发送 1:成功 2:失败) |
| `error_msg` | Text | nullable | 失败原因 |
| `sent_time` | DateTime | nullable | 实际发送时间 |
| `retry_count` | Integer | default=0 | 重试次数 |
### 23.3 业务规则
| 规则 | 说明 |
|------|------|
| **发送模式** | 支持同步发送和异步队列两种模式。默认异步(Redis 队列),避免阻塞主请求 |
| **重试策略** | 发送失败自动重试,最多 3 次,间隔 5 分钟。3 次仍失败则标记失败状态 |
| **频率限制** | 同一收件人同一模板 1 小时内最多发送 5 封,防止滥用 |
| **模板渲染** | 调用 `send_email(template_code, to, variables)` 时,自动从模板渲染 `subject``body` |
| **链路追踪** | 每次发送记录 `platform_email_log`,关联 `template_code``to_user_id` |
### 23.4 业务集成点
| 场景 | 模板编码 | 触发时机 | 变量 |
|------|---------|---------|------|
| **密码重置** | `password_reset` | 创建租户初始管理员 / 用户忘记密码 | `{username}`, `{reset_link}` |
| **租户邀请** | `tenant_invite` | 管理员邀请用户加入租户 | `{inviter}`, `{tenant_name}`, `{invite_link}` |
| **到期提醒(30/7/1天)** | `expiry_reminder` | 定时任务检测到期 | `{tenant_name}`, `{expire_date}`, `{days_left}` |
| **工单通知** | `ticket_notify` | 工单创建/分配/关闭 | `{ticket_title}`, `{status}`, `{assignee}` |
| **套餐变更通知** | `package_change` | 租户套餐被超管变更 | `{old_package}`, `{new_package}`, `{removed_menus}` |
### 23.5 降级策略
当邮件服务不可用时(SMTP 故障、配置缺失),自动降级为**站内信**(`sys_notice`),确保关键信息不丢失。
### 23.6 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/platform/email/config` | 获取 SMTP 配置 | 超管 |
| PUT | `/platform/email/config` | 更新 SMTP 配置 | 超管 |
| POST | `/platform/email/test` | 发送测试邮件 | 超管 |
| GET | `/platform/email/template/list` | 模板列表 | 超管 |
| POST | `/platform/email/template/create` | 创建模板 | 超管 |
| PUT | `/platform/email/template/update/{id}` | 更新模板 | 超管 |
| GET | `/platform/email/log/list` | 发送日志列表 | 超管 |
---
## 24. Order 订单与支付模块
### 24.1 业务描述
订单与支付模块是 SaaS 平台的商业化基础,覆盖订单创建、支付回调、开通激活、续费/升级的完整交易闭环。对接支付宝和微信支付,支持套餐购买、续费和升级三种业务场景。
### 24.2 业务流程
```
用户/超管选择套餐 → 生成订单 → 跳转支付 → 支付回调 → 激活/变更套餐
↓ 超时(15分钟)
订单自动取消
```
### 24.3 数据模型
#### platform_order
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `order_no` | String(32) | NOT NULL, UNIQUE | 订单号(年月日+6位随机数) |
| `tenant_id` | FK→platform_tenant.id | NOT NULL | 购买租户 |
| `package_id` | FK→platform_package.id | NOT NULL | 购买套餐 |
| `order_type` | String(20) | NOT NULL | 类型:`new`(新购) `renew`(续费) `upgrade`(升级) `downgrade`(降级) |
| `amount` | Integer | NOT NULL | 金额(分,≥00=免费套餐) |
| `period_count` | Integer | NOT NULL, default=1 | 购买周期数(1个月=1 |
| `status` | Integer | NOT NULL, default=0 | 状态:0=待支付 1=已支付 2=已取消 3=已退款 |
| `pay_method` | String(20) | nullable | 支付方式:`alipay`(支付宝) / `wxpay`(微信支付) |
| `pay_time` | DateTime | nullable | 支付时间 |
| `expire_time` | DateTime | NOT NULL | 订单过期时间(创建后+15分钟),超时未支付自动取消 |
#### platform_payment_record
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `order_id` | FK→platform_order.id | NOT NULL | 关联订单 |
| `transaction_id` | String(64) | nullable | 第三方交易号 |
| `pay_method` | String(20) | NOT NULL | 支付方式 |
| `amount` | Integer | NOT NULL | 支付金额(分) |
| `status` | Integer | NOT NULL | 支付结果:0=处理中 1=成功 2=失败 |
| `raw_response` | Text | nullable | 支付平台原始回调数据(JSON) |
| `pay_time` | DateTime | nullable | 支付完成时间 |
### 24.4 支付回调处理流程
```
POST /platform/payment/callback/{method}
├── IP 白名单校验(仅允许支付宝/微信支付官方 IP 段)
├── 验证签名(支付宝 RSA / 微信支付 APIv3 签名)
├── 分布式锁(Redis SETNXkey=callback_lock:{transaction_id}TTL=30s
├── 校验金额一致性(回调金额 == 订单金额)
├── 校验订单状态(仅 status=0(待支付) 可处理,防止重复激活)
├── 更新 platform_order.status=1、pay_time=now
├── 写入 platform_payment_recordtransaction_id UNIQUE 约束,第二次写入自动失败)
├── 根据 order_type 执行激活逻辑:
│ ├── new/renew → 更新 tenant.end_time、恢复 status=0(active)
│ ├── upgrade → 更新 tenant.package_id、执行套餐变更影响预览逻辑(菜单+配额同步)
│ └── downgrade → 更新 tenant.package_id、清理超出的菜单关联、更新配额
├── 发送通知给租户管理员(邮件 + 站内信)
├── 释放分布式锁
└── 返回 success 给支付平台(防止重复回调)
```
> **安全要点**
> - IP 白名单:仅允许支付宝/微信支付的官方回调 IP,在 Nginx/LB 层配置
> - 分布式锁:解决支付平台可能同时回调多条相同交易的并发问题
> - 状态校验:status≠0 的订单拒绝处理,防止恶意/重复回调
> - 金额校验:回调金额与订单金额不一致时,标记异常并人工介入
### 24.5 业务规则
| 规则 | 说明 |
|------|------|
| **订单号生成** | `{YYYYMMDD}{6位随机数字}`,创建时检查唯一性 |
| **过期取消** | 定时任务 `cancel_expired_orders` 每分钟扫描 status=0 且 `expire_time < now` 的订单,设为 status=2(已取消) |
| **幂等性** | 同一 `transaction_id` 的回调只处理一次(transaction_id UNIQUE 约束 + 分布式锁双重保障) |
| **金额校验** | 回调金额必须与订单金额一致,不一致则拒绝并告警 |
| **IP 白名单** | 回调接口仅允许支付宝/微信支付官方 IP 调用,在 Nginx/负载均衡层配置 |
| **免费套餐** | amount=0 时不跳转支付,直接走激活流程 |
| **退款** | 退款为预留扩展,当前仅支持手动标记 status=3 |
### 24.7 退款流程
```
租户管理员申请退款
├── POST /tenant/order/refund/apply/{order_id}
│ 条件:订单 status=1(已支付) 且支付时间在 7 天内
│ body: {reason: "误购/重复支付/服务不满意"}
├── 更新 platform_order.refund_status=1(申请中)
├── 创建 platform_refund 记录
├── 通知超管审核(站内信)
└── 返回申请结果
超管审核退款
├── GET /platform/refund/list(待审核列表)
├── PUT /platform/refund/approve/{id}
│ 触发原路退款(调用支付宝/微信退款 API)
│ 更新 platform_refund.status=2(已退款)
│ 更新 platform_order.status=3(已退款)
│ 更新租户套餐(撤销本次购买升级效果,恢复至购买前套餐/到期时间)
└── PUT /platform/refund/reject/{id}
更新 platform_refund.status=3(已驳回)
记录驳回原因
```
#### platform_refund
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `order_id` | FK→platform_order.id | NOT NULL, UNIQUE | 关联订单 |
| `refund_no` | String(32) | NOT NULL, UNIQUE | 退款单号 |
| `amount` | Integer | NOT NULL | 退款金额(分) |
| `reason` | Text | NOT NULL | 退款原因 |
| `status` | Integer | NOT NULL, default=1 | 1=申请中 2=已退款 3=已驳回 4=已取消 |
| `refund_transaction_id` | String(64) | nullable | 退款交易号(第三方返回) |
| `reviewer_id` | FK→sys_user.id | nullable | 审核人 |
| `review_time` | DateTime | nullable | 审核时间 |
| `reject_reason` | Text | nullable | 驳回原因 |
| **退款规则** | 说明 |
|------|------|
| **可退款条件** | 支付后 7 天内,订单 status=1(已支付) |
| **退款金额** | 全额退款(暂不支持部分退款) |
| **退款方式** | 原路退回(支付宝→支付宝,微信→微信) |
| **套餐回退** | 退款后恢复至购买前的套餐和到期时间(若为升级/降级订单) |
| **降级补偿** | 若退款订单为降级类型,退款后套餐回升至降级前套餐 |
| **免费套餐** | amount=0 的免费套餐订单不支持退款 |
### 24.8 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| POST | `/platform/order/create` | 创建订单 | 超管 |
| GET | `/platform/order/detail/{id}` | 订单详情 | 超管 |
| GET | `/platform/order/list` | 订单列表 | 超管 |
| POST | `/platform/order/cancel/{id}` | 取消订单 | 超管 |
| POST | `/platform/payment/callback/alipay` | 支付宝回调(外网可访问,签名验证) | 公开 |
| POST | `/platform/payment/callback/wxpay` | 微信支付回调(外网可访问,签名验证) | 公开 |
| GET | `/platform/payment/record/list` | 支付记录列表 | 超管 |
| POST | `/tenant/order/create` | 租户端创建订单(自助购买/续费/升级) | 租户管理员 |
| POST | `/tenant/order/refund/apply/{id}` | 申请退款 | 租户管理员 |
| GET | `/platform/refund/list` | 退款审核列表 | 超管 |
| PUT | `/platform/refund/approve/{id}` | 批准退款(触发原路退回) | 超管 |
| PUT | `/platform/refund/reject/{id}` | 驳回退款 | 超管 |
---
## 25. TenantSelfService 租户自助服务(代码位于 module_platform/self_service
### 25.1 业务描述
租户管理员可在租户管理后台自助选择套餐、购买、续费或升级,无需超管介入。是 SaaS 产品商业化的核心用户侧功能。
### 25.2 自助套餐选择流程
```
GET /tenant/package/available
├── 返回所有启用的套餐列表
├── 标注当前套餐(is_current=true
├── 展示价格/周期/试用天数/功能对比
├── 标注可执行的操作:[购买][续费][升级][降级]
└── 限制:同一套餐已是当前套餐时不展示"升级"按钮
用户选择操作 → 创建订单 → 支付 → 自动激活
```
### 25.3 套餐变更影响预览(自助版)
```json
// GET /tenant/package/preview?target_package_id=xxx 返回
{
"current_package": "basic",
"target_package": "pro",
"action": "upgrade",
"amount": 29900,
"period": "month",
"gained_menus": [
{"name": "数据报表", "path": "/report/dashboard"},
{"name": "API 管理", "path": "/api/manage"}
],
"lost_menus": [],
"affected_roles": [],
"affected_users": 0
}
```
### 25.4 自助升级/降级流程
```
POST /tenant/order/create (body: {package_id, order_type: "upgrade"})
├── 校验权限:租户管理员及以上
├── 校验租户状态:仅 active(0)/grace(1)/suspended(2) 可操作
├── 校验目标套餐:状态启用且不等于当前套餐
├── amount > 0 → 跳转支付 → 支付回调激活
├── amount = 0 → 直接激活(免费套餐切换)
└── 激活时执行套餐变更影响预览逻辑(同超管操作 §15.3)
```
### 25.5 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/tenant/package/available` | 获取可选套餐列表(含当前套餐标记和可执行操作) | 租户管理员 |
| GET | `/tenant/package/preview` | 套餐变更影响预览 | 租户管理员 |
| POST | `/tenant/order/create` | 创建自助订单(购买/续费/升级/降级) | 租户管理员 |
| GET | `/tenant/order/list` | 我的订单列表 | 租户管理员 |
| GET | `/tenant/order/detail/{id}` | 我的订单详情 | 租户管理员 |
---
## 26. APIUsage 用量统计模块
### 26.1 业务描述
租户级别 API 调用量/频率统计,按天/月聚合,支持计费挂钩、安全异常检测和运营分析。
### 26.2 数据模型
#### platform_api_usage_daily
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `tenant_id` | FK→platform_tenant.id | NOT NULL | 租户 |
| `date` | Date | NOT NULL | 统计日期 |
| `api_path` | String(255) | NOT NULL | API 路径 |
| `request_count` | Integer | NOT NULL, default=0 | 请求次数 |
| `total_duration_ms` | BigInt | NOT NULL, default=0 | 总耗时(毫秒) |
| `error_count` | Integer | NOT NULL, default=0 | 错误次数(4xx/5xx |
> 唯一约束:`UNIQUE(tenant_id, date, api_path)`
### 26.3 统计机制
```
请求中间件(每个 API 调用)
├── 提取 tenant_id、api_path、status_code、响应时间
├── Redis 计数器原子递增:api_usage:{tenant_id}:{date}:{api_path}:count
├── Redis 计数器:api_usage:{tenant_id}:{date}:{api_path}:duration
├── 错误计数(status_code >= 400):api_usage:{tenant_id}:{date}:{api_path}:errors
└── 定时任务(每小时):读取 Redis → UPSERT 到 platform_api_usage_daily → 清理旧 Redis key
```
### 26.4 异常检测规则
| 规则 | 条件 | 动作 |
|------|------|------|
| **频率突变** | 同一 API 调用量超过过去 7 天均值的 5 倍 | 站内信告警 |
| **错误率过高** | 错误率 > 20% 且请求数 > 100 | 站内信告警 |
| **高频调用** | 单租户单 API 超过 1000 次/分钟 | 临时限流(返回 429) |
> 限流配置:`rate_limit_enabled`(全局开关),`rate_limit_threshold`(阈值),均可在平台配置中调整。
### 26.5 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/platform/api-usage/daily` | 按天用量统计(支持租户/日期范围筛选) | 超管 |
| GET | `/platform/api-usage/tenant/{id}` | 指定租户用量详情 | 超管 |
| GET | `/platform/api-usage/rank` | 租户用量排行 | 超管 |
| GET | `/platform/api-usage/anomalies` | 异常调用记录 | 超管 |
---
## 27. UserInvite 用户邀请流程
### 27.1 业务描述
租户管理员可通过邀请链接或邀请码邀请新用户加入租户。被邀请人通过邮箱接收邀请,点击链接完成注册并自动关联到指定租户和角色。
### 27.2 数据模型
#### platform_invite_record
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `invite_code` | String(32) | NOT NULL, UNIQUE | 邀请码(UUID,一次性链接参数) |
| `tenant_id` | FK→platform_tenant.id | NOT NULL | 目标租户 |
| `target_role_id` | FK→sys_role.id | NOT NULL | 预设角色 |
| `inviter_id` | FK→sys_user.id | NOT NULL | 邀请人 |
| `invitee_email` | String(255) | NOT NULL | 被邀请人邮箱 |
| `status` | Integer | NOT NULL, default=0 | 状态:0=待接受 1=已接受 2=已过期 3=已取消 |
| `expire_time` | DateTime | NOT NULL | 过期时间(创建后+7天) |
| `accepted_user_id` | FK→sys_user.id | nullable | 接受邀请后创建的用户 ID |
| `accepted_time` | DateTime | nullable | 接受时间 |
### 27.3 邀请流程
```
租户管理员发起邀请
├── POST /tenant/invite/send
│ body: {emails: [...], role_id, message?}
├── 批量创建 platform_invite_record 行,生成唯一 invite_code
├── 发送邮件(模板 `tenant_invite`,含邀请链接)
├── 邮件内容:{inviter} 邀请你加入 {tenant_name},点击链接注册
└── 链接格式:{domain}/invite/{invite_code},有效期 7 天
被邀请人接受邀请
├── 访问 /invite/{code} 页面
├── 校验邀请码:存在、未过期(expire_time > now)、未使用(status=0)
├── 如果用户已注册:
│ ├── 直接关联到租户(sys_user_tenant 插入记录)
│ ├── 分配预设角色(sys_user_role 插入记录)
│ └── 更新 invite_record.status=1
├── 如果用户未注册:
│ ├── 跳转到注册页面(邮箱已预填,不可修改)
│ ├── 用户完成注册
│ ├── 自动关联到租户,分配预设角色
│ └── 更新 invite_record.status=1, accepted_user_id
└── 通知邀请人"XXX 已接受您的邀请"
```
### 27.4 业务规则
| 规则 | 说明 |
|------|------|
| **邀请码唯一性** | 每次生成全局唯一的 UUID,即使同一邮箱被重复邀请也不同 |
| **有效期** | 默认 7 天,到期后自动标记 status=2(过期),不再可用 |
| **重复邀请** | 同一租户内,同一邮箱有"待接受"的邀请时,提示"该邮箱已有待接受的邀请",不重复发送 |
| **角色预分配** | 被邀请人加入租户时自动获得 `target_role_id` 指定的角色 |
| **权限** | 仅租户管理员(owner/admin)可发送邀请 |
| **邀请人可见** | 可查看自己发出的邀请列表及状态 |
| **降级** | 若邮件服务不可用,邀请码可通过站内消息手动复制链接 |
### 27.5 过期清理
定时任务 `cleanup_expired_invites` 每天扫描 `status=0``expire_time < now` 的记录,标记为 status=2。
### 27.6 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| POST | `/tenant/invite/send` | 发送邀请 | 租户管理员 |
| GET | `/tenant/invite/list` | 邀请列表(含状态) | 租户管理员 |
| DELETE | `/tenant/invite/cancel/{id}` | 取消邀请 | 租户管理员 |
| GET | `/invite/validate/{code}` | 校验邀请码(公开接口,返回租户名/邀请人) | 公开 |
| POST | `/invite/accept/{code}` | 接受邀请(需登录) | 登录用户 |
---
## 28. 未来扩展建议
### 28.1 已规划(短期)
1. **知识库/帮助中心**:租户自助查阅帮助文档、常见问题。富文本编辑器管理,支持多语言
2. **数据导出/备份**:租户自助数据导出(CSV/JSON),满足 GDPR/个保法合规要求。平台级全量备份还原(数据库级)
3. **租户自定义域名**:支持通过 `domain` 字段实现租户专属域名,需配合 Nginx 反向代理配置
### 28.2 中期扩展
| 能力 | 说明 | 优先级 |
|------|------|--------|
| **单点登录(SSO** | 支持 SAML/OIDC 协议,企业客户可使用自有 IdP(如 Okta/Azure AD/自有 LDAP)登录 | 低 |
| **多语言支持** | 租户级 i18n 配置,支持不同租户使用不同语言 | 低 |
| **Webhook 通知** | 关键事件(支付成功/到期提醒/套餐变更)的 webhook 回调,支持第三方集成 | 中 |
### 28.3 API 路径规范
| 问题 | 当前 | 建议 |
|------|------|------|
| 参数模块名不一致 | `/param/`(单数)vs 模块名 `params`(复数) | 统一为 `/param/`(与代码一致) |
| 日志层级问题 | 操作日志 `/system/operationlog/` 与其他模块不统一 | 建议保持现状。登录日志为平台级、操作日志为租户级,层级分开是合理的 |
### 28.4 非功能性需求(NFR
| 指标 | 要求 |
|------|------|
| **API 响应时间** | P95 ≤ 500ms(查询),P95 ≤ 2s(写入/批操作) |
| **并发用户** | 单实例支持 500+ 并发租户用户(需压测验证) |
| **可用性** | 99.5%(不包含计划运维窗口) |
| **数据安全** | 传输层 TLS 1.3,存储层 Bcrypt/AES-256,日志脱敏(手机号/邮箱部分掩码) |
| **兼容性** | 支持 MySQL 8.0+ / PostgreSQL 14+ / SQLite(开发环境);Python ≥ 3.12Node.js ≥ 20 |
| **数据库备份** | 每日全量备份(保留 30 天),每小时增量备份(保留 7 天)。备份异地存储,定期恢复演练(每季度 1 次) |
| **容灾恢复** | RTO ≤ 4 小时,RPO ≤ 1 小时。主库故障时自动切换只读副本,30 分钟内完成主从切换 |
| **版本升级策略** | 数据库迁移采用 Alembic 管理,所有 schema 变更通过 migration 脚本执行。升级前自动备份,升级失败可回滚至上一个备份点。主版本升级需提前通知租户(7 天),次版本/补丁版可灰度发布 |
| **i18n 基础** | 后端 API 错误消息统一使用 i18n key(如 `errors.user.not_found`),前端使用 vue-i18n。初始版本仅提供中文,保留英文翻译文件占位。租户级语言首选项存储在 `platform_tenant.lang` 字段(预留) |
| **缓存键命名规范** | 格式:`{namespace}:{sub_namespace}:{identifier}`。示例:`tenant:config:123``api:usage:456:2026-06-03``auth:session:abc123`。所有 key 设置 TTL,禁止无过期时间的 key |
---
## 29. 术语表(Glossary
| 术语 | 英文 | 说明 |
|------|------|------|
| **租户** | Tenant | SaaS 平台上的一个独立组织/客户 |
| **平台资源** | Platform Resource | 无 tenant_id 的资源,所有租户共享(菜单/套餐/插件) |
| **租户资源** | Tenant Resource | 含 tenant_id 的资源,按租户隔离(用户/角色/部门等) |
| **平台共享数据** | Shared Platform Data | tenant_id=1 的字典数据,所有租户可读 |
| **超管** | Super Admin | 平台级管理员,`is_super_admin=true`,可管理所有租户 |
| **租户管理员** | Tenant Admin | 租户内的管理员,角色为 owner/admin |
| **数据权限范围** | Data Scope | 角色绑定的数据可见范围(全部/本部门及子部门/仅本部门等) |
| **RBAC** | Role-Based Access Control | 基于角色的访问控制 |
| **Mixin** | Mixin | SQLAlchemy 混入模式,用于给模型添加通用字段(如 TenantMixin 自动添加 tenant_id |
| **处理人** | Assignee | 工单中指派的处理人员 |
| **生命周期** | Lifecycle | 租户的完整状态流转路径:active(0)→grace(1)→suspended(2)→expired(4)→archived(5)→deleted。人工冻结路径:active(0)→frozen(3)→archived(5) |
| **宽限期** | Grace Period | 租户到期后的缓冲期(默认7天),允许正常使用但提示续费 |
| **订单** | Order | 套餐购买/续费/升级的交易凭证,关联支付回调激活套餐 |
| **邀请码** | Invite Code | 租户管理员邀请用户加入的一次性链接参数(UUID,7天有效) |
| **用量统计** | API Usage | 租户级别 API 调用量按天聚合统计,用于计费和安全分析 |
| **邮件模板** | Email Template | 预定义的邮件格式(密码重置/邀请/到期提醒),支持变量占位符渲染 |
| **支付回调** | Payment Callback | 支付宝/微信支付完成后异步通知平台更新订单状态的机制 |
| **默认套餐** | Default Package | 标记 is_default=true 的套餐,自助注册时自动选用 |
| **分布式锁** | Distributed Lock | Redis SETNX 实现的并发互斥机制,防止支付回调等场景的重复处理 |
| **数据库迁移** | Database Migration | 使用 Alembic 管理的版本化 schema 变更脚本,支持升级和回滚 |
| **发票** | Invoice | 订单支付后开具的电子发票(增值税普票/专票),一单一票 |
| **审计日志** | Audit Log | 不可篡改的合规操作记录,仅超管可查阅,保留 3 年 |
| **运营大盘** | Dashboard | 平台运营数据的可视化看板,聚合租户/收入/API 用量等核心指标 |
| **原路退回** | Refund | 支付退款按原支付路径返回(支付宝→支付宝,微信→微信) |
---
## 30. 变更记录
| 版本 | 日期 | 变更内容 |
|------|------|---------|
| v3.1.0 | 2026-06-01 | 初始版本,完整的模块化需求文档 |
| v3.2.0 | 2026-06-02 | 新增 Plugin 子模块需求:AI Chat/Cronjob/Workflow/CodeGen/Demo;新增 Monitor 监控模块:Online/Cache/Resource/Server;新增 Common 公共模块:File/Health/Metrics |
| v3.2.1 | 2026-06-03 | **需求审查修复**:修复全部章节子标题编号错位;统一表名为 `platform_*` 前缀(与代码一致);移除已废弃的 `sys_tenant_quota`/`sys_tenant_config` 独立表引用;拆分 `sys_log``sys_operation_log` + `platform_login_log`;统一 Auth 端点路径;明确多租户登录临时/正式 token 机制;补充用户注册租户自动创建流程;添加 `start_time` 未生效校验;补充通知公告已读机制;补全 Tenant 模型遗漏字段(description/version/privacy 等);新增安全性要求(登录限流/密码复杂度/CORS);更新未来扩展建议 |
| v3.2.2 | 2026-06-03 | **命名规范统一**:修复文档中 `sys_login_log``platform_login_log``sys_ticket``platform_ticket` 两处表名错误;在 §21 新增 §21.6 插件表汇总(8 张 task_/gen_ 表);明确全模块命名规范:`platform_`=平台模块,`sys_`=系统模块,`task_`=任务插件,`gen_`=生成器插件 |
| v3.3.0 | 2026-06-03 | **SaaS 产品需求审查修订**:P0-1 租户生命周期状态机(active→frozen→archived→deleted);P0-2 定时任务/工作流代码执行安全性(任意代码→预定义处理器白名单);P0-3 套餐模型补充定价字段(price/period/trial_days/max_tenants);P0-4 初始管理员密码交付改为邮件一次性重置链接;P1-1 通知已读机制改为后端 sys_notice_read 表;P1-2 工单增加 close_reason/closed_time/closed_by 字段;P1-3 租户到期增加宽限期和阶段性处理(grace→suspended→expired);P1-4 用户导入密码处理策略;P1-5 套餐变更增加影响预览和确认流程;P2 操作日志保留策略、API 路径规范、缺失 SaaS 能力规划(用户邀请/数据导出/API用量/审计日志/SSO);新增 NFR 非功能性需求、术语表 |
| v3.4.0 | 2026-06-03 | **业务架构闭环补全**Fix-1 统一 status 编码(String(1)→Integer,合并生命周期与到期阶段编码:0=active/1=grace/2=suspended/3=frozen/4=expired/5=archived);Fix-2 新增 §23 Email 邮件服务模块(SMTP 配置/模板管理/发送日志/5大业务集成点/站内信降级);Fix-3 新增 §24 Order 订单与支付模块(订单表/支付记录表/支付宝&微信支付回调/开通续费升级流程);Fix-4 新增 §25 TenantSelfService 租户自助服务(套餐选择/影响预览/自助升级降级);Fix-5 新增 §26 APIUsage 用量统计模块(按天聚合/Redis计数器/异常检测/限流);Fix-6 新增 §27 UserInvite 用户邀请流程(邀请码/邮件邀请/角色预分配/过期清理);Fix-7 清理残留与全面重编号(移除 §19.4 残留 TODO、§20 新增 §20.18-§20.22 API端点、§21 新增7张新表、§28-§30 重编号、§31-§37 Plugin模块重编号、§29 术语表扩充8个词条、§30 变更记录更新) |
| v3.5.0 | 2026-06-03 | **PRD 正式评审修复**P0-1 修正 Part 4 Plugin 子模块编号(§31-§37 子节编号 25.x-31.x→31.x-37.x,共 34 处);P0-2 修复初始管理员体验断层(创建租户时自动创建 owner 角色并分配全量菜单,§6.3/§15.3/§15.4 同步更新);P0-3 统一 status 字段类型(package/ticket/order/payment/invite 全部从 String→Integer,保持与 tenant.status 一致);P1-1 标题版本号修正(v3.2.2→v3.5.0);P1-2 自助注册新增默认套餐/配额/到期时间来源(§4.5 注册流程、§16.2 is_default 字段);P1-3 套餐配额体系补充(§16.2 新增 max_users/max_roles/max_depts 配额字段);P1-4 套餐变更同步更新配额(§15.3 增加配额对比预览和升级/降级处理逻辑);P1-5 支付回调安全细节增强(§24.4/§24.5 新增 IP 白名单、分布式锁、状态校验);P2-1 API 路径规范(§16.5 套餐路径统一为 /platform/package/);P2-2 新增数据备份与容灾策略(§28.4 NFR);P2-3 新增版本升级/迁移策略(§28.4 NFR);P2-4 新增 i18n 基础设计(§28.4 NFR);P2-5 新增 Redis 缓存键命名规范(§28.4 NFR);术语表扩充 4 个词条(默认套餐/分布式锁/数据库迁移/冗余恢复) |
| v3.6.0 | 2026-06-03 | **PRD 100% 完整度达标**Fix-1 修正 §26.2/§27.2 子节编号错误(32.2→26.2、33.2→27.2);Fix-2 修正 §28 节编号排序(28.5→28.3、删除重复 28.5);Fix-3 §24.7 新增退款流程(platform_refund 表、申请→审核→原路退回、套餐回退逻辑);Fix-4 新增 §38 Invoice 发票管理模块(普票/专票、百望云等第三方对接、一单一票、30天开票时限);Fix-5 新增 §39 AuditLog 审计日志模块(不可篡改、JSON 变更对比、13 种审计事件、3年保留策略);Fix-6 新增 §40 Dashboard 运营大盘模块(MRR/退款率/API用量/套餐分布/收入趋势 9 项指标);Fix-7 更新 §1.4 模块总览(新增 3 个模块)、§20 新增 §20.23-§20.25 API端点、§21 新增 §21.7 商业运营表、§28 移除已实现项、§29 术语表扩充 4 个词条 |
---
# Part 4Plugin 子模块需求
---
## 31. AI Chat 聊天模块(module_ai/chat
### 31.1 业务描述
AI 对话模块,提供用户与大模型进行对话的能力。支持多会话管理、WebSocket 流式对话、非流式对话。ChatSession 数据按租户隔离。
### 31.2 数据模型
聊天会话数据存储在 ChatService 后端(支持内存存储/Redis/数据库三种模式,由配置决定)。Schema 层定义如下:
#### ChatSessionCreateSchema
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `title` | str | NOT NULL, min_length=1, max_length=200 | 会话标题 |
#### ChatSessionUpdateSchema
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `title` | str | NOT NULL, min_length=1, max_length=200 | 会话标题 |
#### AiChatRequestSchema
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `message` | str | NOT NULL, min_length=1 | 用户消息内容 |
| `session_id` | str | nullable | 会话ID,不传则创建新会话 |
#### AiChatResponseSchema
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `response` | str | NOT NULL | AI 回复内容 |
| `session_id` | str | NOT NULL | 会话ID |
| `function_calls` | list[dict] | nullable | 函数调用信息 |
| `action` | dict | nullable | 建议执行的操作 |
### 31.3 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/chat/detail/{session_id}` | module_ai:chat:detail | 会话详情 |
| GET | `/chat/list` | module_ai:chat:query | 会话列表 |
| POST | `/chat/create` | module_ai:chat:create | 创建会话 |
| PUT | `/chat/update/{session_id}` | module_ai:chat:update | 更新会话 |
| DELETE | `/chat/delete` | module_ai:chat:delete | 删除会话 |
| POST | `/chat/ai-chat` | module_ai:chat:query | AI 对话(非流式) |
| WS | `/chat/ws` | — | WebSocket 流式对话 |
---
## 32. Cronjob 定时任务模块(module_task/cronjob
### 32.1 业务描述
定时任务模块提供动态节点定义(NodeModel)和任务执行日志记录(JobModel)。节点定义执行代码块、触发器和参数,通过 APScheduler 调度执行。
### 32.2 数据模型
#### NodeModeltask_nodeTenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(64) | NOT NULL | 节点名称 |
| `code` | String(32) | NOT NULL, UNIQUE(tenant_id, code) | 节点编码 |
| `jobstore` | String(64) | nullable, default="default" | 存储器 |
| `executor` | String(64) | nullable, default="default" | 执行器 |
| `trigger` | String(64) | nullable | 触发器 |
| `trigger_args` | Text | nullable | 触发器参数 |
| `func` | Text | NOT NULL | 预定义处理器标识符(如 `handlers.send_email`)。禁止租户提交任意代码 |
| `args` | Text | nullable | 位置参数 |
| `kwargs` | Text | nullable | 关键字参数 |
| `coalesce` | Boolean | nullable, default=False | 是否合并运行 |
| `max_instances` | Integer | nullable, default=1 | 最大并发实例数 |
| `start_date` | String(64) | nullable | 开始时间 |
| `end_date` | String(64) | nullable | 结束时间 |
#### JobModeltask_jobTenantMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `job_id` | String(64) | NOT NULL, index | 任务ID |
| `job_name` | String(128) | nullable | 任务名称 |
| `trigger_type` | String(32) | nullable | 触发方式:cron/interval/date/manual |
| `status` | String(16) | NOT NULL, default="pending" | 执行状态:pending/running/success/failed/timeout/cancelled |
| `next_run_time` | String(64) | nullable | 下次执行时间 |
| `job_state` | Text | nullable | 任务状态信息 |
| `result` | Text | nullable | 执行结果 |
| `error` | Text | nullable | 错误信息 |
### 32.3 业务规则
| 规则 | 说明 |
|------|------|
| **Node 编码** | 字母开头,仅含字母/数字/下划线 |
| **触发器类型** | 仅支持 now/cron/interval/date |
| **非立即执行** | trigger != "now" 时必须提供 trigger_args |
| **时间校验** | end_date 不能早于 start_date |
| **func 必填** | Node 创建时 func 不能为空,须为已注册的处理器标识符 |
| **处理器白名单** | func 字段只能填写平台预注册的处理器(如 `handlers.send_email``handlers.call_api`),禁止填写任意代码。超管可在 `platform_handler_registry` 中注册新处理器 |
| **Job 状态** | 仅支持 pending/running/success/failed/timeout/cancelled |
| **trigger_type** | 仅支持 cron/interval/date/manual |
### 32.4 API 端点
#### Node(节点)
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/cronjob/node/detail/{id}` | module_task:cronjob:query | 节点详情 |
| GET | `/cronjob/node/list` | module_task:cronjob:query | 节点列表 |
| POST | `/cronjob/node/create` | module_task:cronjob:create | 创建节点 |
| PUT | `/cronjob/node/update/{id}` | module_task:cronjob:update | 更新节点 |
| DELETE | `/cronjob/node/delete` | module_task:cronjob:delete | 删除节点 |
| PATCH | `/cronjob/node/status/batch` | module_task:cronjob:patch | 批量设置状态 |
| POST | `/cronjob/node/execute/{id}` | module_task:cronjob:update | 执行节点 |
#### Job(执行日志)
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/cronjob/job/detail/{id}` | module_task:cronjob:query | 日志详情 |
| GET | `/cronjob/job/list` | module_task:cronjob:query | 日志列表 |
| DELETE | `/cronjob/job/delete` | module_task:cronjob:delete | 删除日志 |
---
## 33. Workflow 工作流模块(module_task/workflow
### 33.1 业务描述
工作流模块提供可视化流程编排和执行能力。基于 Vue Flow 画布定义流程节点和连线,通过 Prefect 引擎执行。数据按租户隔离。
### 33.2 数据模型
#### WorkflowModeltask_workflowTenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(128) | NOT NULL | 流程名称 |
| `code` | String(64) | NOT NULL, UNIQUE(tenant_id, code) | 流程编码 |
| `workflow_status` | String(32) | NOT NULL, default="draft" | 状态:draft/published/archived |
| `nodes` | JSON | nullable | Vue Flow nodes JSON |
| `edges` | JSON | nullable | Vue Flow edges JSON |
#### WorkflowNodeTypeModeltask_workflow_node_typeTenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(128) | NOT NULL | 显示名称 |
| `code` | String(64) | NOT NULL, UNIQUE(tenant_id, code) | 节点编码,对应画布 node.type |
| `category` | String(32) | NOT NULL, default="action" | 分类:trigger/action/condition/control |
| `func` | Text | NOT NULL | 预定义处理器标识符(如 `handlers.approve``handlers.send_http`)。禁止租户提交任意代码 |
| `args` | Text | nullable | 默认位置参数,逗号分隔 |
| `kwargs` | Text | nullable | 默认关键字参数 JSON |
| `sort_order` | Integer | NOT NULL, default=0 | 排序 |
| `is_active` | Boolean | NOT NULL, default=True | 是否启用 |
### 33.3 业务规则
| 规则 | 说明 |
|------|------|
| **Workflow 编码** | 字母开头,仅含字母/数字/下划线 |
| **Workflow 状态** | 仅支持 draft(草稿)、published(已发布)、archived(已归档) |
| **NodeType 分类** | 仅支持 trigger(触发器)、action(动作)、condition(条件)、control(控制) |
| **发布流程** | 发布时可选备注(remark),由 draft → published |
| **执行流程** | 需传入 workflow_id 和可选的 variables/business_key/job_id |
| **执行结果** | 返回 completed/failed 状态及各节点执行结果 |
### 33.4 API 端点
#### Workflow(流程定义)
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/workflow/flow/detail/{id}` | module_task:workflow:query | 流程详情 |
| GET | `/workflow/flow/list` | module_task:workflow:query | 流程列表 |
| POST | `/workflow/flow/create` | module_task:workflow:create | 创建流程 |
| PUT | `/workflow/flow/update/{id}` | module_task:workflow:update | 更新流程 |
| DELETE | `/workflow/flow/delete` | module_task:workflow:delete | 删除流程 |
| POST | `/workflow/flow/publish/{id}` | module_task:workflow:update | 发布流程 |
| POST | `/workflow/flow/execute/{id}` | module_task:workflow:update | 执行流程 |
#### NodeType(节点类型)
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/workflow/nodes/detail/{id}` | module_task:workflow:query | 节点详情 |
| GET | `/workflow/nodes/list` | module_task:workflow:query | 节点列表 |
| POST | `/workflow/nodes/create` | module_task:workflow:create | 创建节点 |
| PUT | `/workflow/node-type/update/{id}` | module_task:workflow:update | 更新节点 |
| DELETE | `/workflow/node-type/delete` | module_task:workflow:delete | 删除节点 |
---
## 34. CodeGen 代码生成器模块(module_generator/gencode
### 34.1 业务描述
代码生成器模块,通过读取数据库表结构自动生成 CRUD 代码(Python 后端 + Vue 前端 + TypeScript API 层)。支持主子表结构。数据按租户隔离。
### 34.2 数据模型
#### GenTableModelgen_tableTenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `table_name` | String(200) | NOT NULL | 数据库表名 |
| `table_comment` | String(500) | nullable | 表描述 |
| `class_name` | String(100) | NOT NULL | 实体类名称 |
| `package_name` | String(100) | nullable | 生成包路径(module_xxx |
| `module_name` | String(30) | nullable | 生成模块名 |
| `business_name` | String(30) | nullable | 功能子目录/路由段 |
| `function_name` | String(100) | nullable | 生成功能名 |
| `sub_table_name` | String(64) | nullable | 关联子表的表名 |
| `sub_table_fk_name` | String(64) | nullable | 子表关联的外键名 |
| `parent_menu_id` | Integer | nullable | 父菜单ID |
#### GenTableColumnModelgen_table_columnTenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `table_id` | FK→gen_table.id | NOT NULL, ON DELETE CASCADE | 归属表ID |
| `column_name` | String(200) | NOT NULL | 列名称 |
| `column_comment` | String(500) | nullable | 列描述 |
| `column_type` | String(100) | NOT NULL | 列类型 |
| `column_length` | String(50) | nullable | 列长度 |
| `column_default` | String(200) | nullable | 列默认值 |
| `is_pk` | Boolean | NOT NULL, default=False | 是否主键 |
| `is_increment` | Boolean | NOT NULL, default=False | 是否自增 |
| `is_nullable` | Boolean | NOT NULL, default=True | 是否允许为空 |
| `is_unique` | Boolean | NOT NULL, default=False | 是否唯一 |
| `python_type` | String(100) | nullable | Python 类型 |
| `python_field` | String(200) | nullable | Python 字段名 |
| `is_insert` | Boolean | NOT NULL, default=True | 是否为新增字段 |
| `is_edit` | Boolean | NOT NULL, default=True | 是否编辑字段 |
| `is_list` | Boolean | NOT NULL, default=True | 是否列表字段 |
| `is_query` | Boolean | NOT NULL, default=False | 是否查询字段 |
| `query_type` | String(50) | nullable | 查询方式 |
| `html_type` | String(100) | nullable, default="input" | 显示类型 |
| `dict_type` | String(200) | nullable, default="" | 字典类型 |
| `sort` | Integer | NOT NULL, default=0 | 排序 |
### 34.3 业务规则
| 规则 | 说明 |
|------|------|
| **表名校验** | table_name/class_name 非空去空白 |
| **包名规范** | package_name 必须以 module_ 开头 |
| **业务名规范** | business_name 支持斜杠多段(如 demo/demo01 |
| **同步预览** | 支持 DB→Gen 差异预览(新增/删除/变更字段) |
| **建表SQL** | 支持从 CREATE TABLE SQL 导入表结构 |
| **模板生成** | 支持 Python/TS/Vue 三端代码模板(Jinja2 |
| **主子表** | 通过 sub_table_name/sub_table_fk_name 配置主子表关联 |
### 34.4 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/gencode/detail/{id}` | module_generator:gencode:query | 业务表详情 |
| GET | `/gencode/list` | module_generator:gencode:query | 业务表列表 |
| POST | `/gencode/create` | module_generator:gencode:create | 创建业务表 |
| PUT | `/gencode/update/{id}` | module_generator:gencode:update | 更新业务表 |
| DELETE | `/gencode/delete` | module_generator:gencode:delete | 删除业务表 |
| PATCH | `/gencode/status/batch` | module_generator:gencode:patch | 批量设置状态 |
| GET | `/gencode/db/list` | module_generator:gencode:query | 数据库表列表 |
| POST | `/gencode/import` | module_generator:gencode:create | 导入表结构 |
| POST | `/gencode/sync/preview/{id}` | module_generator:gencode:query | 同步预览 |
| POST | `/gencode/sync/{id}` | module_generator:gencode:update | 同步表结构 |
| POST | `/gencode/create/table` | module_generator:gencode:create | 从SQL建表 |
| POST | `/gencode/preview/{id}` | module_generator:gencode:query | 预览代码 |
| POST | `/gencode/zip/{id}` | module_generator:gencode:query | 下载代码ZIP |
| POST | `/gencode/gen/{id}` | module_generator:gencode:update | 生成代码到本地 |
| POST | `/gencode/current/select` | module_generator:gencode:query | 切换当前业务表 |
---
## 35. Demo 示例模块(module_example/demo
### 35.1 业务描述
示例模块,演示 CRUD 标准开发模式和多种数据类型的用法。数据按租户隔离。
### 35.2 数据模型
#### DemoModelexample_demoTenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(64) | NOT NULL | 名称 |
| `a` | Integer | nullable | 整数 |
| `b` | BIGINT | nullable | 大整数 |
| `c` | Float | nullable | 浮点数 |
| `d` | Boolean | NOT NULL, default=True | 布尔型 |
| `e` | Date | nullable | 日期 |
| `f` | Time | nullable | 时间 |
| `g` | DateTime | nullable | 日期时间 |
| `h` | Text | nullable | 长文本 |
| `i` | JSON | nullable | 元数据 JSON |
#### Demo01Modelexample_demo01TenantMixin, UserMixin
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | String(64) | NOT NULL | 名称 |
### 35.3 业务规则
| 规则 | 说明 |
|------|------|
| **名称校验** | 2-50 位,仅含字母/数字/下划线/中划线 |
| **状态校验** | 仅支持 0(正常)、1(禁用) |
### 35.4 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/example/demo/detail/{id}` | module_example:demo:query | 详情 |
| GET | `/example/demo/list` | module_example:demo:query | 列表 |
| POST | `/example/demo/create` | module_example:demo:create | 创建 |
| PUT | `/example/demo/update/{id}` | module_example:demo:update | 更新 |
| DELETE | `/example/demo/delete` | module_example:demo:delete | 删除 |
| PATCH | `/example/demo/status/batch` | module_example:demo:patch | 批量设置状态 |
#### Demo01
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/example/demo01/detail/{id}` | module_example:demo01:query | 详情 |
| GET | `/example/demo01/list` | module_example:demo01:query | 列表 |
| POST | `/example/demo01/create` | module_example:demo01:create | 创建 |
| PUT | `/example/demo01/update/{id}` | module_example:demo01:update | 更新 |
| DELETE | `/example/demo01/delete` | module_example:demo01:delete | 删除 |
| PATCH | `/example/demo01/status/batch` | module_example:demo01:patch | 批量设置状态 |
---
## 36. Monitor 监控模块(module_monitor
### 36.1 业务描述
监控模块提供系统运行状态的实时监控能力,包括在线用户追踪、Redis 缓存监控、服务器资源监控和文件系统管理。该模块属于平台级功能,不受租户隔离限制,超级管理员可查看所有数据。
### 36.2 在线用户(online
#### 36.2.1 业务描述
在线用户监控来自 Redis 存储的会话数据,实时追踪当前登录用户。数据不按租户隔离,超级管理员可查看所有在线用户。
#### 36.2.2 数据模型
**OnlineOutSchemaRedis 数据结构)**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `user_id` | int | NOT NULL | 用户ID |
| `tenant_id` | int | NOT NULL | 租户ID |
| `user_name` | str | NOT NULL | 用户名 |
| `name` | str | NOT NULL | 用户名称 |
| `session_id` | str | NOT NULL | 会话编号 |
| `is_super_admin` | bool | NOT NULL, default=False | 是否超管 |
| `ipaddr` | str | nullable | 登录IP |
| `login_location` | str | nullable | 登录地 |
| `os` | str | nullable | 操作系统 |
| `browser` | str | nullable | 浏览器 |
| `login_time` | DateTime | nullable | 登录时间 |
| `login_type` | str | nullable | 登录类型(PC/移动) |
#### 36.2.3 业务规则
| 规则 | 说明 |
|------|------|
| **数据来源** | 数据存储在 Redis,会话过期自动移除 |
| **强制下线** | 超级管理员可强制指定用户下线 |
| **清空全部** | 超级管理员可清空所有在线用户会话 |
#### 36.2.4 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/monitor/online/list` | module_monitor:online:query | 在线用户列表 |
| DELETE | `/monitor/online/delete` | module_monitor:online:delete | 强制下线 |
| DELETE | `/monitor/online/clear` | module_monitor:online:delete | 清空所有在线用户 |
---
### 36.3 缓存监控(cache
#### 36.3.1 业务描述
Redis 缓存监控,提供缓存统计信息、缓存名称列表、键值查看和清除功能。数据不按租户隔离,属于平台级功能。
#### 36.3.2 数据模型
**CacheMonitorSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `command_stats` | list[dict] | NOT NULL, default=[] | Redis 命令统计 |
| `db_size` | int | NOT NULL, default=0 | Key 总数 |
| `info` | dict | NOT NULL, default={} | Redis 服务器信息 |
**CacheInfoSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `cache_key` | str | NOT NULL | 缓存键名 |
| `cache_name` | str | NOT NULL | 缓存名称 |
| `cache_value` | Any | nullable | 缓存值 |
| `remark` | str | nullable | 备注说明 |
#### 36.3.3 业务规则
| 规则 | 说明 |
|------|------|
| **统计信息** | 获取 Redis 命令统计和服务器信息 |
| **键值管理** | 支持查看和清除指定缓存 |
| **批量清除** | 支持按名称清除和清空所有缓存 |
#### 36.3.4 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/monitor/cache/info` | module_monitor:cache:query | 获取缓存监控统计 |
| GET | `/monitor/cache/get/names` | module_monitor:cache:query | 获取缓存名称列表 |
| GET | `/monitor/cache/get/keys/{cache_name}` | module_monitor:cache:query | 获取缓存键名列表 |
| GET | `/monitor/cache/get/value/{cache_name}/{cache_key}` | module_monitor:cache:query | 获取缓存值 |
| DELETE | `/monitor/cache/delete/name/{cache_name}` | module_monitor:cache:delete | 清除指定缓存名称 |
| DELETE | `/monitor/cache/delete/key/{cache_key}` | module_monitor:cache:delete | 清除指定缓存键 |
| DELETE | `/monitor/cache/clear` | module_monitor:cache:delete | 清除所有缓存 |
---
### 36.4 资源管理(resource
#### 36.4.1 业务描述
资源文件管理,提供服务器文件系统的浏览、上传、下载、删除、移动、复制、重命名、创建目录等操作。支持文件列表分页、关键词搜索和 Excel 导出。
#### 36.4.2 数据模型
**ResourceItemSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | str | NOT NULL | 文件名 |
| `file_url` | str | NOT NULL | 文件URL路径 |
| `relative_path` | str | NOT NULL | 相对路径 |
| `is_file` | bool | NOT NULL | 是否为文件 |
| `is_dir` | bool | NOT NULL | 是否为目录 |
| `size` | int | nullable | 文件大小(字节) |
| `created_time` | DateTime | nullable | 创建时间 |
| `modified_time` | DateTime | nullable | 修改时间 |
| `is_hidden` | bool | NOT NULL, default=False | 是否隐藏文件 |
**ResourceUploadSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `filename` | str | NOT NULL | 文件名 |
| `file_url` | str | NOT NULL | 访问URL |
| `file_size` | int | NOT NULL | 文件大小 |
| `upload_time` | DateTime | NOT NULL | 上传时间 |
**ResourceMoveSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `source_path` | str | NOT NULL | 源路径 |
| `target_path` | str | NOT NULL | 目标路径 |
| `overwrite` | bool | NOT NULL, default=False | 是否覆盖 |
**ResourceRenameSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `old_path` | str | NOT NULL | 原路径 |
| `new_name` | str | NOT NULL, max_length=255 | 新名称 |
**ResourceCreateDirSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `parent_path` | str | NOT NULL | 父目录路径 |
| `dir_name` | str | NOT NULL, max_length=255 | 目录名称 |
#### 36.4.3 业务规则
| 规则 | 说明 |
|------|------|
| **路径安全** | 禁止路径遍历(`..`),防止越权访问 |
| **文件/目录互斥** | 不能同时为文件和目录 |
| **隐藏文件** | 以 `.` 开头的文件自动标记为隐藏 |
| **分页查询** | 目录列表支持分页和关键词搜索 |
| **上传限制** | 仅 resource 类型支持指定目标目录 |
| **导出功能** | 支持将资源列表导出为 Excel |
#### 36.4.4 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/monitor/resource/list` | module_monitor:resource:query | 目录列表(分页) |
| POST | `/monitor/resource/upload` | module_monitor:resource:upload | 上传文件 |
| GET | `/monitor/resource/download` | module_monitor:resource:download | 下载文件 |
| DELETE | `/monitor/resource/delete` | module_monitor:resource:delete | 删除文件 |
| POST | `/monitor/resource/move` | module_monitor:resource:move | 移动文件 |
| POST | `/monitor/resource/copy` | module_monitor:resource:copy | 复制文件 |
| POST | `/monitor/resource/rename` | module_monitor:resource:rename | 重命名文件 |
| POST | `/monitor/resource/mkdir` | module_monitor:resource:mkdir | 创建目录 |
| POST | `/monitor/resource/export` | module_monitor:resource:export | 导出资源列表 |
---
### 36.5 服务器监控(server
#### 36.5.1 业务描述
服务器监控,采集服务器运行时的 CPU、内存、磁盘、Python 进程等信息,供运维人员了解系统资源使用情况。
#### 36.5.2 数据模型
**CpuInfoSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `cpu_num` | int | NOT NULL | CPU 核心数 |
| `used` | float | NOT NULL, 0-100 | 用户使用率(%) |
| `sys` | float | NOT NULL, 0-100 | 系统使用率(%) |
| `free` | float | NOT NULL, 0-100 | 空闲率(%) |
**MemoryInfoSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `total` | str | NOT NULL | 内存总量 |
| `used` | str | NOT NULL | 已用内存 |
| `free` | str | NOT NULL | 剩余内存 |
| `usage` | float | NOT NULL, 0-100 | 使用率(%) |
**SysInfoSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `computer_ip` | str | NOT NULL | 服务器IP |
| `computer_name` | str | NOT NULL | 服务器名称 |
| `os_arch` | str | NOT NULL | 系统架构 |
| `os_name` | str | NOT NULL | 操作系统 |
| `user_dir` | str | NOT NULL | 项目路径 |
**PyInfoSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `name` | str | NOT NULL | Python 名称 |
| `version` | str | NOT NULL | Python 版本 |
| `start_time` | str | NOT NULL | 启动时间 |
| `run_time` | str | NOT NULL | 运行时长 |
| `home` | str | NOT NULL | 安装路径 |
| `memory_used` | str | NOT NULL | 内存占用 |
| `memory_usage` | float | NOT NULL, 0-100 | 内存使用率(%) |
| `memory_total` | str | NOT NULL | 总内存 |
| `memory_free` | str | NOT NULL | 剩余内存 |
**DiskInfoSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `dir_name` | str | NOT NULL | 磁盘路径 |
| `sys_type_name` | str | NOT NULL | 文件系统类型 |
| `type_name` | str | NOT NULL | 磁盘类型 |
| `total` | str | NOT NULL | 总容量 |
| `used` | str | NOT NULL | 已用容量 |
| `free` | str | NOT NULL | 可用容量 |
| `usage` | float | NOT NULL, 0-100 | 使用率(%) |
**ServerMonitorSchema**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `cpu` | CpuInfoSchema | NOT NULL | CPU 信息 |
| `mem` | MemoryInfoSchema | NOT NULL | 内存信息 |
| `py` | PyInfoSchema | NOT NULL | Python 信息 |
| `sys` | SysInfoSchema | NOT NULL | 系统信息 |
| `disks` | list[DiskInfoSchema] | NOT NULL | 磁盘信息列表 |
#### 36.5.3 业务规则
| 规则 | 说明 |
|------|------|
| **实时采集** | 每次请求实时采集系统信息 |
| **百分比范围** | 使用率字段限制在 0-100 范围 |
#### 36.5.4 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/monitor/server/info` | module_monitor:server:query | 服务器监控信息 |
---
## 37. Common 公共模块(module_common
### 37.1 业务描述
公共模块提供跨模块复用的基础服务,包括统一文件上传下载、健康检查和指标监控。该模块属于平台级基础设施,不受租户隔离限制。
### 37.2 文件管理(file
#### 37.2.1 业务描述
统一文件上传下载服务,支持多种上传类型(通用文件、头像、参数配置、监控资源),支持指定目标目录。预留 Excel 导入功能,待后续实现。
#### 37.2.2 数据模型
**上传响应数据**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `file_name` | str | NOT NULL | 文件名 |
| `file_url` | str | NOT NULL | 访问URL |
| `file_size` | int | NOT NULL | 文件大小 |
| `upload_time` | DateTime | NOT NULL | 上传时间 |
**预留:Excel导入字段映射模型(ImportFieldModel**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `base_column` | str | nullable | 数据库字段名 |
| `excel_column` | str | nullable | Excel 字段名 |
| `default_value` | str | nullable | 默认值 |
| `is_required` | bool | nullable | 是否必传 |
| `selected` | bool | nullable | 是否勾选 |
**预留:Excel导入请求模型(ImportModel**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `table_name` | str | nullable | 目标表名 |
| `sheet_name` | str | nullable | Sheet 名 |
| `filed_info` | list[ImportFieldModel] | nullable | 字段映射列表 |
| `file_name` | str | nullable | 文件名 |
#### 37.2.3 业务规则
| 规则 | 说明 |
|------|------|
| **上传类型** | file=通用, avatar=头像, param=参数配置, resource=监控资源 |
| **目标目录** | 仅 resource 类型支持指定 target_path |
| **下载选项** | 支持下载后自动删除源文件 |
| **Excel导入预留** | ImportFieldModel 和 ImportModel 为预留功能,当前未实现对应接口 |
#### 37.2.4 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| POST | `/common/file/upload` | module_common:file:upload | 上传文件 |
| POST | `/common/file/download` | module_common:file:download | 下载文件 |
---
### 37.3 健康检查(health
#### 37.3.1 业务描述
三级健康检查体系,用于不同场景的健康探测:
- `/health`: 基础健康检查(负载均衡器探测)
- `/health/live`: 存活探针(K8s livenessProbe
- `/health/ready`: 就绪探针(K8s readinessProbe,检测数据库和 Redis
#### 37.3.2 数据模型
**健康检查响应**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `status` | str | NOT NULL | healthy/alive/ready/not_ready |
| `timestamp` | DateTime | NOT NULL | 检查时间戳 |
| `version` | str | NOT NULL | 系统版本 |
| `uptime_seconds` | float | NOT NULL | 运行时间(秒) |
| `dependencies` | dict | nullable | 依赖检查结果 |
| `disk_usage` | float | nullable | 磁盘使用率(%) |
**依赖检查结果**
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `database` | dict | NOT NULL | 数据库状态 {status, latency_ms} |
| `redis` | dict | NOT NULL | Redis 状态 {status, latency_ms} |
#### 37.3.3 业务规则
| 规则 | 说明 |
|------|------|
| **基础检查** | 仅检查进程是否存活,返回 healthy |
| **存活探针** | 进程已启动即可返回 200 |
| **就绪探针** | 检测数据库和 Redis 连接,失败返回 503 |
| **依赖状态** | up=正常, down=异常, disabled=已禁用 |
#### 37.3.4 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/health` | — | 基础健康检查 |
| GET | `/health/live` | — | 存活探针 |
| GET | `/health/ready` | — | 就绪探针 |
---
### 37.4 指标监控(metrics
#### 37.4.1 业务描述
Prometheus 指标监控,集成 prometheus-fastapi-instrumentator 自动采集 HTTP 请求指标,暴露 `/metrics` 端点供 Prometheus 抓取。
#### 37.4.2 采集指标
| 指标名称 | 类型 | 说明 |
|---------|------|------|
| `http_requests_total` | Counter | HTTP 请求总数(按 method/endpoint/status 分组) |
| `http_request_duration_seconds` | Histogram | 请求延迟直方图 |
| `http_requests_in_progress` | Gauge | 当前处理中的请求数 |
| `http_request_size_bytes` | Histogram | 请求体大小 |
| `http_response_size_bytes` | Histogram | 响应体大小 |
#### 37.4.3 排除端点
以下端点不纳入指标采集:
- `/metrics`: Prometheus 抓取端点
- `/health`, `/health/live`, `/health/ready`: 健康检查端点
- `/docs`, `/redoc`, `/openapi.json`: API 文档
- `/static/*`, `/favicon.ico`: 静态资源
#### 37.4.4 API 端点
| 方法 | 路径 | 权限标识 | 说明 |
|------|------|---------|------|
| GET | `/metrics` | — | Prometheus 指标端点 |
---
# Part 5:商业运营模块
---
## 38. Invoice 发票管理模块
### 38.1 业务描述
发票管理是中国 B2B SaaS 的法律合规要求。租户在完成订单支付后可申请开具电子发票(增值税普通发票/增值税专用发票),平台审核后对接第三方开票 API(如百望云/票通)生成电子发票,支持下载 PDF。
### 38.2 发票类型
| 类型 | 编码 | 适用场景 | 税率 |
|------|------|---------|------|
| **增值税普通发票** | `vat_normal` | 个人/小规模纳税人,不可抵扣 | 1%/3%/6% |
| **增值税专用发票** | `vat_special` | 一般纳税人,可抵扣进项税额 | 6%/13% |
### 38.3 数据模型
#### platform_invoice
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `invoice_no` | String(32) | NOT NULL, UNIQUE | 发票号码(平台自增号) |
| `order_id` | FK→platform_order.id | NOT NULL, UNIQUE | 关联订单(一单一票) |
| `tenant_id` | FK→platform_tenant.id | NOT NULL | 开票租户 |
| `invoice_type` | String(20) | NOT NULL | 类型:`vat_normal`(普票) `vat_special`(专票) |
| `title` | String(200) | NOT NULL | 发票抬头(公司全称/个人姓名) |
| `tax_no` | String(50) | nullable | 纳税人识别号(普票可选,专票必填) |
| `bank_info` | Text | nullable | 开户行及账号(专票必填) |
| `address_info` | Text | nullable | 注册地址及电话(专票必填) |
| `amount` | Integer | NOT NULL | 发票金额(分) |
| `tax_amount` | Integer | NOT NULL, default=0 | 税额(分) |
| `status` | Integer | NOT NULL, default=0 | 0=待开票 1=已开票 2=开票失败 3=已作废 |
| `pdf_url` | String(500) | nullable | 电子发票 PDF 下载地址 |
| `api_response` | Text | nullable | 第三方开票 API 原始响应 |
| `remark` | Text | nullable | 备注 |
### 38.4 业务流程
```
租户申请开票
├── POST /tenant/invoice/apply
│ body: {order_id, invoice_type, title, tax_no?, bank_info?, address_info?}
├── 校验:订单已支付(status=1)、未开过票(order_id UNIQUE)
├── 专票额外校验:tax_no/bank_info/address_info 必填
├── 创建 platform_invoice 记录(status=0 待开票)
└── 返回申请成功
超管审核开票
├── GET /platform/invoice/list(待开票列表)
├── PUT /platform/invoice/issue/{id}
│ ├── 调用第三方开票 API(百望云等)
│ ├── 成功 → 更新 status=1、pdf_url、api_response
│ ├── 失败 → 更新 status=2、记录错误信息
│ └── 通知租户(站内信 + 邮件,含下载链接)
└── PUT /platform/invoice/void/{id}(发票作废,仅已开票可作废)
```
### 38.5 业务规则
| 规则 | 说明 |
|------|------|
| **一单一票** | 每个订单仅可开具一张发票(order_id UNIQUE),杜绝重复开票 |
| **开票时限** | 订单支付后 30 天内可申请,超期不再支持(税务合规) |
| **金额匹配** | 发票金额必须等于订单实付金额 |
| **专票校验** | 增值税专用发票必须填写税号+开户行+地址,缺一不可 |
| **第三方对接** | 对接百望云/票通等电子发票平台,API 调用失败时自动重试 3 次后标记失败 |
| **PDF 存储** | 电子发票 PDF 上传至文件服务(§37.2),按 `invoice/{tenant_id}/{invoice_no}.pdf` 路径存储 |
| **作废规则** | 当月开具的发票可作废,跨月发票需冲红(暂不支持,预留扩展) |
### 38.6 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| POST | `/tenant/invoice/apply` | 申请开票 | 租户管理员 |
| GET | `/tenant/invoice/list` | 我的发票列表 | 租户管理员 |
| GET | `/tenant/invoice/{id}/download` | 下载发票 PDF | 租户管理员 |
| GET | `/platform/invoice/list` | 全部发票列表(支持筛选) | 超管 |
| PUT | `/platform/invoice/issue/{id}` | 开具发票(调用第三方 API) | 超管 |
| PUT | `/platform/invoice/void/{id}` | 作废发票 | 超管 |
---
## 39. AuditLog 租户审计日志模块
### 39.1 业务描述
记录平台级和租户级的关键管理操作,形成不可篡改的审计轨迹。满足企业内部合规审查、SOC2/ISO27001 认证中的数据追溯要求。审计日志与操作日志(§14)的区别在于:操作日志面向业务操作的查询追踪,审计日志面向合规要求的不可否认性记录。
### 39.2 操作分类
| 分类 | 说明 | 示例 |
|------|------|------|
| **租户管理** | 租户生命周期操作 | 创建/启禁用/冻结/删除/变更套餐 |
| **权限变更** | 角色/菜单/授权操作 | 角色创建/删除、菜单分配/回收 |
| **套餐变更** | 套餐相关操作 | 套餐价格修改、套餐菜单变更 |
| **支付与退款** | 财务相关操作 | 订单创建、退款批准/驳回 |
| **发票管理** | 开票相关操作 | 开具发票、作废发票 |
| **用户邀请** | 团队管理操作 | 发送邀请、取消邀请 |
### 39.3 数据模型
#### platform_audit_log
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `action` | String(50) | NOT NULL | 操作类型:`tenant.create`/`tenant.package_change`/`role.delete` 等 |
| `target_type` | String(50) | NOT NULL | 操作对象类型:`tenant`/`package`/`role`/`order`/`invoice` |
| `target_id` | Integer | NOT NULL | 操作对象 ID |
| `target_name` | String(200) | nullable | 操作对象名称(冗余存储,防删除后无法追溯) |
| `tenant_id` | FK→platform_tenant.id | nullable | 关联租户(平台级操作可为 null) |
| `operator_id` | FK→sys_user.id | NOT NULL | 操作人 |
| `operator_name` | String(50) | NOT NULL | 操作人名称(冗余) |
| `detail` | JSON | NOT NULL | 操作详情(变更前/后的关键字段) |
| `ip_address` | String(45) | nullable | 操作 IP |
| `user_agent` | String(500) | nullable | 浏览器 UA |
| **设计要点** | 说明 |
|------|------|
| **不可篡改** | 无 Update/Delete API,仅支持 Insert 和 Read |
| **变更对比** | `detail` JSON 字段存储 `{"before": {...}, "after": {...}}` 格式的变更对比 |
| **冗余存储** | target_name/operator_name 冗余存储,确保删除关联数据后仍可追溯 |
| **保留策略** | 默认保留 3 年,超期归档至冷存储(S3/OSS),支持按需导出 CSV/JSON |
### 39.4 审计事件清单
| action | target_type | 触发场景 | detail 示例 |
|--------|-------------|---------|------------|
| `tenant.create` | tenant | 创建租户 | `{after: {code, name, package_id}}` |
| `tenant.status_change` | tenant | 启禁/冻结/归档 | `{before: {status}, after: {status}}` |
| `tenant.package_change` | tenant | 变更套餐 | `{before: {package_id, name}, after: {package_id, name}}` |
| `tenant.quota_change` | tenant | 调整配额 | `{before: {max_users}, after: {max_users}}` |
| `tenant.delete` | tenant | 删除租户 | `{before: {code, name, deleted_at}}` |
| `package.price_change` | package | 修改套餐价格 | `{before: {price}, after: {price}}` |
| `package.menu_change` | package | 变更套餐菜单 | `{added: [...], removed: [...]}` |
| `role.delete` | role | 删除角色 | `{before: {name, user_count}}` |
| `order.refund_approve` | order | 批准退款 | `{order_no, amount, reason}` |
| `order.refund_reject` | order | 驳回退款 | `{order_no, amount, reject_reason}` |
| `invoice.issue` | invoice | 开具发票 | `{invoice_no, amount, type}` |
| `invoice.void` | invoice | 作废发票 | `{invoice_no, reason}` |
| `invite.send` | invite | 发送邀请 | `{invitee_email, target_role}` |
### 39.5 业务规则
| 规则 | 说明 |
|------|------|
| **全量记录** | 所有审计事件在业务操作的事务中同步写入,不依赖异步任务(防止丢失) |
| **不可删除** | 审计日志无 DELETE API,管理员不可手动删除(如需清理需走冷存储归档流程) |
| **权限** | 仅超管可查阅审计日志,租户端不可见 |
| **分页与筛选** | 支持按 action/target_type/tenant_id/operator_id/时间范围 多条件筛选分页查询 |
| **保留策略** | 定时任务 `archive_audit_logs` 每月扫描,将 3 年前的日志导出至 OSS 后从主表删除 |
### 39.6 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/platform/audit/list` | 审计日志列表(支持筛选/分页) | 超管 |
| GET | `/platform/audit/detail/{id}` | 审计日志详情 | 超管 |
| GET | `/platform/audit/export` | 导出审计日志(CSV/JSON | 超管 |
---
## 40. Dashboard 运营大盘
### 40.1 业务描述
平台运营数据可视化看板,基于已有数据(订单/支付/API用量/租户/工单)聚合展示核心运营指标,帮助超管快速掌握平台健康状况。
### 40.2 核心指标
| 指标 | 数据源 | 说明 |
|------|--------|------|
| **租户总数** | platform_tenant | 按状态分布(active/suspended/expired |
| **本月新增租户** | platform_tenant | 按月统计新建租户数 |
| **今日活跃租户** | platform_api_usage_daily | 当日有 API 调用的租户数 |
| **月收入(MRR** | platform_order | status=1 订单按月汇总金额 |
| **退款率** | platform_refund | 退款金额/总收入 × 100% |
| **API 调用总量** | platform_api_usage_daily | 按天/月聚合调用次数 |
| **待处理工单数** | platform_ticket | status=0/1 的工单数量 |
| **套餐分布** | platform_tenant JOIN platform_package | 各套餐租户数量(饼图) |
| **收入趋势** | platform_order | 近 12 个月收入折线图 |
### 40.3 API 端点
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | `/platform/dashboard/overview` | 运营概览(总览数据) | 超管 |
| GET | `/platform/dashboard/revenue` | 收入趋势(按月) | 超管 |
| GET | `/platform/dashboard/tenants` | 租户统计 | 超管 |
| GET | `/platform/dashboard/api-usage` | API 用量趋势 | 超管 |
---