# genbi-backend **Repository Path**: uyynot_admin/genbi-backend ## Basic Information - **Project Name**: genbi-backend - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: release/dev_feat_multi_entity - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-01-07 - **Last Updated**: 2026-03-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README AI 智能问数平台 · 系统开发设计书(V1.0) ## 🚀 快速开始 如果你想快速启动项目,请跳转到 [快速开始](#快速开始) 部分。 ## 📚 目录 - [快速开始](#快速开始) - [背景与目标](#一背景与目标) - [整体架构设计](#二整体架构设计) - [数据模型与存储设计](#三数据模型与存储设计sqlmodel--sqlalchemy) - [权限设计](#四权限设计casbin) ### 一、背景与目标 - 基于产品《智能问数产品需求说明书(PRD)V1.0》,本设计书面向研发与测试团队,给出端到端的系统设计与落地方案。 - 技术栈约定: - FastAPI + MySQL + Redis; - ORM 采用 SQLAlchemy 2.0; - 认证采用 JWT; - 权限控制采用 Casbin(RBAC with Domains); - 日志使用 loguru; - 依赖管理使用 uv; - 配置集中在项目根目录 .env; - 提供容器化(Dockerfile 与 docker-compose)方案; - 全链路统一返回结构与请求级 trace_id 贯穿日志与响应头。 ### 二、整体架构设计 为了更好地理解系统结构,下面展示了系统的整体架构图: ```mermaid graph TB A[客户端/浏览器] --> B[API网关/Nginx] B --> C[FastAPI应用] C --> D[(MySQL数据库)] C --> E[(Redis缓存)] C --> F[Casbin权限引擎] F --> D G[SSO认证服务器] -.-> C H[外部数据源] -.-> C subgraph 应用层 C end subgraph 存储层 D E end subgraph 安全层 F G end subgraph 外部系统 H end ``` 1) #### 分层架构(对齐 FastAPI 官方推荐组织方式) - main.py:应用入口、路由聚合、生命周期事件、定时任务初始化 - core:通用核心能力(配置、安全、加解密、时区、常量、错误码) - api:API 路由层(按业务域分模块,例如 system、workspace、usage、enhance、modeling 等) - schemas:Pydantic 模型(请求/响应 DTO、分页、统一返回包装) - services:领域服务层(业务编排、事务边界、跨仓储组合) - repositories:数据访问层(SQLAlchemy 2.0 仓储、读写分离、查询封装) - models:ORM 实体定义(SQLAlchemy 2.0 Models) - casbin:权限模型与适配器(model.conf、policy 存储适配、初始化) - middlewares:中间件(日志 Trace、JWT 鉴权、异常处理、统一返回封装) - common:工具库(雪花/UUID、哈希、分页、ID 生成、ContextVar) - tasks:定时任务与异步任务(调度器、枚举值同步、SQL 校验、批量导入处理) - migrations/:数据库迁移(Alembic) 2) #### 运行时组件 - FastAPI 应用:Uvicorn 运行(生产建议搭配 Uvicorn + Gunicorn,多进程) - MySQL:主存储,事务一致性保障 - Redis:缓存(会话黑名单、频控、短期数据)、分布式锁、延迟队列 - Casbin:权限判定引擎(策略存储使用 MySQL) - Loguru:结构化日志(JSON/文本)、按大小/日期滚动、异常落盘 3) #### 核心非功能设计点 - 统一返回结构:所有成功/失败响应包装为一致结构;异常通过全局异常处理器转换 - 请求级 trace_id:请求进入即生成,写入日志上下文,回写响应头 X-Trace-Id - 数据隔离与权限:基于租户与工作空间的强隔离;接口与数据双维度访问控制 - 可观测性:关键路径埋点、慢查询日志、SQL 与外部调用时长统计 - 可扩展性:多租户、多空间、多角色;按工作空间扩展增强配置与知识库 ### 三、数据模型与存储设计(SQLAlchemy 2.0) 1) 主要实体 - 用户 User:id、工号、姓名、职位、部门、状态、创建人/时间 - 租户 Tenant:id、名称、描述、团队码、状态、创建人/时间 - 租户成员 TenantUser(用户-租户关系):id、tenant_id、user_id、角色(TenantAdmin/Member)、创建时间 - 工作空间 Workspace:id、tenant_id、名称、logo、描述、状态、创建/修改人及时间 - 工作空间成员 WorkspaceMember:id、workspace_id、user_id、权限(Manager/User)、创建时间 - SQL 问答对 SqlQa:id、workspace_id、问题、答案SQL、SQL有效性(正确/错误/未知)、执行比对结果(一致/不一致/未知)、启用状态、创建人/时间 - 待优化问答对 PendingQa:id、workspace_id、问题、SQL、SQL有效性、最后操作人/时间、来源(点踩/复制) - 用户提问记录 QuestionLog:id、workspace_id、user_id(或工号)、问题、回答、反馈(赞/踩/未反馈)、SQL、提问时间 2) 关键关系与约束 - User 与 Tenant:多对多(TenantUser),记录用户在租户维度的角色 - User 与 Workspace:多对多(WorkspaceMember),记录用户在工作空间维度的权限 - Workspace 与 Tenant:多对一,所有 Workspace 下资源均带 tenant_id 与 workspace_id - DataModel、Category、SqlQa、PendingQa、QuestionLog 等全部强关联 workspace_id(并可冗余 tenant_id 以提升过滤效率) - 删除约束:租户删除需先清理其工作空间;工作空间删除需先清理其下配置项与知识库(逻辑删除优先) - **维度配置唯一性规则**: - 维度配置(DimensionConfig)的唯一性由 `tenant_id + workspace_id + entity + tables(标准化后)` 组合确定 - tables 字段在比较时会进行标准化处理:按英文逗号分割、去除空格、去重、按字母顺序排序后用逗号连接 - 示例:`"table_a,table_b,table_c"` 和 `"table_c,table_b,table_a"` 被视为相同的 tables 值,因为标准化后都是 `"table_a,table_b,table_c"` - 示例:`"table_a,table_b,table_c"` 和 `"table_a,table_b"` 被视为不同的 tables 值 - 该规则同样适用于枚举值(enum)记录,枚举值通常继承其父维度的 tables 值 - 创建、更新、导入操作均会进行唯一性校验,防止重复数据 3) 审计字段自动更新 - 所有模型均继承 BaseModel,包含 created_by、updated_by、deleted_by 等审计字段 - 系统通过 SQLAlchemy 事件监听器自动维护这些字段,无需手动设置 - 创建记录时自动填充 created_by 和 updated_by 字段 - 更新记录时自动更新 updated_by 字段 - 删除记录时自动填充 deleted_by 字段(软删除) - 时间戳字段 created_at 和 updated_at 也自动维护: - created_at: 记录首次创建时间,创建后不再更改 - updated_at: 记录最后更新时间,每次更新都会重新设置 4) 迁移与版本管理 - 使用 Alembic 管理 SQLAlchemy 2.0 模型迁移;严格约束上线前迁移顺序;预发布环境回放验证 - 支持自动生成迁移脚本(autogenerate);手动审核迁移内容确保数据安全 - 迁移文件版本化管理/支持升级和回滚操作 ### 四、权限设计(Casbin) 权限系统采用 Casbin RBAC with Domains 模型,下面是权限架构图: ```mermaid graph TD A[用户] --> B[角色绑定] B --> C[系统角色] B --> D[租户角色] B --> E[工作空间角色] C --> F[系统级权限] D --> G[租户级权限] E --> H[工作空间级权限] F --> I[权限策略] G --> I H --> I I --> J[资源访问控制] subgraph 权限层级 C D E end subgraph 权限执行 I J end ``` 1) 权限粒度与分层 - 系统级(System):SuperAdmin、SystemViewer 等(跨租户操作能力) - 租户级(Tenant):TenantAdmin、TenantMember(仅作用于特定租户域) - 工作空间级(Workspace):WorkspaceManager、WorkspaceUser(仅作用于特定工作空间域) - 数据域:基于 tenant_id 与 workspace_id 的强数据隔离,A 租户看不到 B 租户;同一租户下 A 空间用户看不到 B 空间 2) 模型选择 - 采用 Casbin RBAC with Domains(多域 RBAC) - 主体(sub):用户或用户组(角色) - 域(dom):可取 system、tenant:{tenant_id}、workspace:{workspace_id} - 资源(obj):REST 资源标识(如 /api/tenants、/api/workspaces/{id}、业务域资源名) - 动作(act):HTTP 方法或业务动作(get、list、create、update、delete、enable、disable…) 3) 策略定义语义(示例以语义描述,不提供代码) - g 绑定:将用户绑定到角色,并指定域。例如:将用户 U 作为 WorkspaceManager 绑定到 workspace:W1 域 - p 策略:为角色在某个域内授予对资源的动作权限。例如:WorkspaceManager 在 workspace:* 域,对 /api/sqlqa 资源拥有 create/update 权限 - 数据域约束:matcher 中加入请求上下文的 tenant_id/workspace_id 与策略 dom 的匹配校验,确保跨域不可访问 4) 能力覆盖性判断 - 接口权限:通过域化 RBAC,满足系统/租户/空间三级不同粒度的权限控制 - 数据权限:通过 matcher 强制校验 dom 与请求上下文的 tenant_id/workspace_id 相等,天然实现数据隔离 - 角色层级:通过 g 的层级关系,可表达系统角色派生租户/空间角色的能力(如系统超管在任何域都拥有管理权限) - 结论:Casbin 完全可覆盖本期接口与数据权限需求,且具备良好可扩展性(新增角色/策略无需代码变更) 5) 核心执行点 - 鉴权中间件在 JWT 解析后构造 EnforcementContext(包含 user_id、system_roles、tenant_id、workspace_id、请求路径与动作) ### 五、IAM认证集成 系统支持与安踏IAM系统集成,提供基于OAuth 2.0的第三方登录功能。 #### 1) 接口说明 - `/auth/iam/token` - 根据授权码(code)获取访问令牌 - `/auth/iam/userinfo` - 根据访问令牌获取用户详细信息 - `/auth/iam/refresh` - 刷新访问令牌 #### 2) 认证流程 1. 用户在IAM系统登录后,会重定向回系统并携带授权码(code) 2. 系统使用授权码调用`/auth/iam/token`接口获取访问令牌 3. 使用访问令牌调用`/auth/iam/userinfo`接口获取用户详细信息 4. 系统根据用户信息创建或更新本地用户记录 5. 生成系统JWT令牌并返回给前端 #### 3) 缓存机制 为提高性能,系统对以下信息进行缓存: - IAM访问令牌:缓存时间根据令牌过期时间减去5分钟缓冲时间 - 用户信息:缓存时间为1小时 缓存支持Redis和本地内存两种方式,优先使用Redis。 #### 4) 错误处理 - 当IAM接口返回错误时,系统会将错误信息透传给客户端 - 对于令牌过期的情况,系统提供自动刷新机制 #### 5) 文件上传接口 系统提供统一的文件上传接口,支持文件类型验证和数据库存储。 **接口路径**: `/auth/upload-file/` **请求参数**: - `file` (必填): 上传的文件对象 - `allowed_types` (可选): 允许的文件类型列表,多个类型用逗号分隔 - 示例: `"image/jpeg,image/png,application/pdf"` - 不传则不做类型限制 **响应数据**: - `id`: 文件数据库ID - `filename`: 原始文件名 - `content`: 文件内容(base64编码) - `content_type`: 文件MIME类型 - `file_size`: 文件大小(字节) - `file_extension`: 文件扩展名 - `created_at`: 创建时间 **特性**: - 文件内容以base64编码存储在数据库中,不保存到服务器磁盘 - 支持文件类型白名单验证,上传不允许的类型会返回400错误 - 所有上传记录保存在`files`表中,支持软删除 - 遵循BaseModel规范,包含创建人、更新人等审计字段 ### 六、统一返回结构与错误处理 1) 响应统一包装 - 字段:code(整型错误码,0 表示成功)、msg(人类可读消息)、data(业务数据,可为对象/数组/分页对象) - 分页约定:page、page_size、total、items[] 2) 错误码规范(示意) - 0:成功 - 400xx:参数/校验错误(如 40001 无效参数,40002 缺失字段) - 401xx:认证错误(未登录、token 过期、签名错误) - 403xx:鉴权失败(无访问权限、越权访问) - 404xx:资源不存在 - 409xx:业务冲突(名称重复、状态不允许) - 422xx:语义/规则校验失败(SQL 校验错误等) - 500xx:系统内部错误 3) 全局异常处理 - 捕获 Pydantic 校验异常、HTTPException、数据库异常、Casbin 鉴权异常、未捕获异常 - 统一转换为上述结构并记录日志(含 trace_id、请求摘要、堆栈) ### 七、日志与追踪(loguru + trace_id) - 生成时机:请求进入第一中间件生成 trace_id(若请求头已有 X-Trace-Id 则沿用) - 贯穿:trace_id 注入 ContextVar,loguru logger 添加 filter 将 trace_id 自动加入日志行 - 输出:控制台与文件双通道;按日期/大小滚动;错误与异常单独错误文件;生产推荐 JSON 格式便于采集 - 关键日志:鉴权结果、SQL 执行耗时、外部接口调用、任务执行结果、异常与告警 ### 八、缓存与性能 - Redis 用途: - JWT 黑名单与会话状态 - 频控与防刷(按 IP/用户/接口维度滑动窗口) - 列表查询缓存(短期 TTL,带租户/空间维度) - 任务队列与去重(批量导入、SQL 校验) - 慢查询:记录 ORM SQL 耗时阈值超限日志 - 大对象分页:统一分页策略与上限保护(page_size 上限) ### 九、接口与模块设计(对齐 PRD) 为了更好地理解系统各模块之间的交互关系,下面展示了核心业务流程: ```mermaid graph TD A[用户] --> B[前端界面] B --> C[API路由层] C --> D[服务层] D --> E[仓储层] E --> F[(数据库)] D --> G[外部服务] H[定时任务] --> D I[异步任务] --> D subgraph 应用架构 B C D E end subgraph 数据层 F end subgraph 外部集成 G H I end ``` 1) 系统管理 - 租户管理:列表、创建、编辑、删除、启用/禁用、管理用户、租户切换 - 用户管理:列表、创建、编辑、删除、启用/禁用(区分系统超管与租户管理员的数据可见范围) 2) 工作空间 - 管理工作空间:列表、创建(校验重名)、编辑、删除(校验是否有关联配置)、详情 - 成员管理:列表、授权(搜索用户并分配权限)、删除(权限差异:所有者/管理者/普通用户) 3) 使用追踪 - 用户提问记录:列表过滤(问题、状态、工号、职位、部门)、详情、优化补充(转为样本或待优化) - 待优化问答对:列表(含 SQL 有效性)、优化(执行校验与结果记录)、转为样本、删除、详情 4) 增强配置 - SQL 问答对:列表(SQL 有效性、执行结果比对、启用/禁用)、创建、编辑(AI 结果比对流程)、复制、批量导入、命中测试(Top N 相似) 5) 系统对接 - 对外接口:接收智能体平台问答记录与问题反馈(鉴权、入库、去重、幂等) ### 十、请求与鉴权统一约定 - 认证:除登录/健康检查/静态资源,其他接口需 Bearer JWT - 鉴权:路由标注资源与动作;中间件解析域并调用 Casbin 判定 - 数据域:服务层强制注入 tenant_id/workspace_id 条件,仓储层额外兜底过滤(双保险) - 审计:重要操作写入 AuditLog,包含 actor、action、resource、结果、trace_id - 审计字段自动更新:系统自动维护 created_by、updated_by、deleted_by 等审计字段,无需手动设置 ### 十一、配置管理(.env) - 必备配置项(示意名称): - APP_ENV(dev/staging/prod) - APP_NAME、APP_HOST、APP_PORT、APP_LOG_LEVEL - MYSQL_HOST、MYSQL_PORT、MYSQL_DB、MYSQL_USER、MYSQL_PASSWORD、MYSQL_POOL_SIZE - REDIS_URL 或 REDIS_HOST/PORT/DB - JWT_SECRET、JWT_ALG、JWT_EXPIRE_MINUTES - LOG_DIR、LOG_ROTATION、LOG_RETENTION - CASBIN_MODEL_PATH、CASBIN_POLICY_ADAPTER(mysql) - PAGINATION_DEFAULT_SIZE、PAGINATION_MAX_SIZE ### 十二、依赖与本地开发(uv) - 依赖管理:使用 uv 管理 Python 依赖,版本锁定(uv.lock) - 开发流程: - 克隆仓库并创建 .env - 使用 uv 安装依赖:`uv sync` - 测试数据库连接:`uv run python scripts/run_tests.py db` - 生成并应用迁移:`uv run alembic revision --autogenerate -m "Initial migration"` 然后 `uv run alembic upgrade head` - 本地运行应用与调试:`uv run uvicorn main:app --reload` - 质量:pre-commit(可选)、ruff/flake8(可选)、mypy(可选)、pytest(可选) #### Docker 快速启动(推荐) 使用 Docker 可以快速启动完整的开发环境: ```bash # 1. 克隆项目 git clone cd genbi-backend # 2. 使用本地开发配置(包含 MySQL 和 Redis) docker-compose -f docker-compose.local.yml up -d # 3. 查看服务状态 docker-compose -f docker-compose.local.yml ps # 4. 访问应用 # API: http://localhost:8000 # 文档: http://localhost:8000/docs ``` **📚 部署文档**: **Docker 部署**: - [Docker 部署完整指南](docs/DOCKER_DEPLOYMENT.md) - 详细的部署说明和最佳实践 - [Docker 快速开始](docs/DOCKER_QUICKSTART.md) - 快速上手指南 - [Docker 配置说明](docs/DOCKER_CONFIG_NOTES.md) - 各环境配置详解和虚拟环境管理 - [部署问题排查](docs/DEPLOYMENT_NOTES.md) - 常见问题和解决方案 **Kubernetes 部署**: - [Kubernetes 部署指南](docs/K8S_DEPLOYMENT.md) - K8s 集群部署完整指南 - [Kubernetes 存储配置](docs/K8S_STORAGE_CONFIG.md) - 文件持久化与多副本共享解决方案 - [k8s_dev.yaml](k8s_dev.yaml) - 开发环境 K8s 配置文件 ### 十三、容器化与编排 系统提供完整的 Docker 容器化部署方案,支持生产环境和本地开发两种模式。 #### Kubernetes 存储配置 **重要**:在 Kubernetes 环境中部署时,文件上传功能需要特别注意存储配置: 1. **问题**: - 使用 `emptyDir` 时,Pod 重启后上传的文件会丢失 - 多副本部署时,文件只保存在一个 Pod 中,其他 Pod 无法访问 2. **解决方案**: - 使用 `PersistentVolumeClaim (PVC)` 配合 `ReadWriteMany` 访问模式 - 配置 NFS 或云存储服务作为后端存储 - 生产环境建议使用对象存储(OSS/S3/MinIO) 3. **详细配置**: - 参考 [Kubernetes 存储配置文档](docs/K8S_STORAGE_CONFIG.md) - 已在 `k8s_dev.yaml` 中配置好 PVC,根据集群情况调整 `storageClassName` #### 部署架构 **生产环境**(使用云服务): ```mermaid graph LR A[负载均衡器] --> B[应用容器1] A --> C[应用容器2] B --> D[云MySQL] B --> E[云Redis] C --> D C --> E ``` **本地开发环境**(容器化): ```mermaid graph LR A[开发者] --> B[应用容器] B --> C[MySQL容器] B --> D[Redis容器] ``` #### 核心特性 - ✅ **多阶段构建**: 优化镜像大小,仅包含运行时依赖 - ✅ **非 root 运行**: 提升容器安全性 - ✅ **健康检查**: 自动检测服务健康状态 - ✅ **自动迁移**: 启动时自动执行数据库迁移 - ✅ **资源限制**: 可配置 CPU 和内存限制 - ✅ **日志持久化**: 日志和上传文件持久化到宿主机 - ✅ **云服务集成**: 生产环境使用云 MySQL 和 Redis #### 快速开始 详细的部署步骤、配置说明、故障排查等,请查看: 👉 **[Docker 部署完整指南](docs/DOCKER_DEPLOYMENT.md)** #### 文件说明 - `Dockerfile`: 生产级多阶段构建配置 - `docker-compose.yml`: 生产环境编排配置(仅应用容器) - `docker-compose.local.yml`: 本地开发环境配置(含 MySQL、Redis) - `docker-entrypoint.sh`: 容器启动脚本(数据库迁移等) - `.dockerignore`: 优化构建效率 - `env.example`: 环境变量配置模板 ### 十四、关键业务流程设计补充 1) 用户登录与租户切换 - 登录签发 JWT,返回用户可见的最近租户;多租户用户登录后默认进入最近一次登录的租户 - 首次登录或未被加入租户:返回引导提示 2) 待优化问答对闭环 - 点踩数据自动进入待优化集合 - 进入优化:执行 SQL 校验并记录有效性;人工判定后保存 - 转为样本:仅当 SQL 有效性为正确且不重复方可转入 SQL 问答对 3) SQL 问答对编辑的结果比对 - 同时执行 AI 辅助生成 SQL 与答案 SQL,对结果字段集合与数据逐项比对 - 记录比对结论与执行时延;手动"标记为正确"需二次确认 ### 十五、安全与合规 - 身份认证:JWT 最小可行,后续支持与企业 SSO 对接 - 权限最小化:基于角色与域授予最小权限 - 输入校验与防护:参数校验、SQL 注入风险控制(ORM + 预编译)、XSS/CSRF(主要为 API,无表单场景) ### 十六、性能与稳定性设计 - 性能目标:接口均值 < 300ms;页面响应 < 3s;并发 10 万在线(通过横向扩展与缓存分流) - 限流与降级:Redis 频控、热点接口缓存;依赖失败熔断与重试(外部服务) - 可用性:多副本部署;静态配置健康检查;滚动升级 - 容灾:数据库备份策略;日志与审计外部持久化(如对象存储/ELK) ### 十七、监控与埋点(V1.0 侧重日志) - 日志为核心可观测手段;后续接入指标与分布式追踪(Prometheus/OpenTelemetry) - 关键埋点:登录、鉴权失败、SQL 校验、命中测试、导入成功/失败 ### 十八、开放问题与后续规划 - 与企业 SSO 的集成时序与协议细节(回调/单点登出) - 命中测试 TOP 相似实现细节(Embedding 引擎与召回策略,V1.0 可先采用朴素相似度) - SQL 执行资源配额与超时控制(防止重 SQL 影响主库) - 更细粒度的数据权限(列/行级,V1.1+) ### 十九、验收清单(V1.0) - 统一返回结构与异常处理落地,trace_id 贯穿并回写响应头 - JWT 登录鉴权可用,租户/空间切换规则符合 PRD - Casbin 策略与域鉴权覆盖系统/租户/空间 + 数据域 - 主要模块 API 可用:系统管理、工作空间、使用追踪、增强配置、业务建模 - 待优化问答对闭环可用,SQL 校验与样本转化规则生效 - 日志与错误落盘,Docker 与 Compose 可本地一键拉起 ### 二十、目录结构 ``` genbi-backend/ ├── api/ # API路由层 ├── core/ # 核心模块 │ ├── db.py # SQLModel数据库配置 │ ├── settings.py # 配置管理 │ └── auth/ # 权限认证 ├── schemas/ # Pydantic模型 ├── services/ # 业务服务层 ├── repositories/ # 数据访问层 ├── models/ # SQLModel数据模型 ├── middlewares/ # 中间件 ├── common/ # 工具库 ├── logging/ # 日志配置 ├── tasks/ # 定时任务与异步任务 │ ├── __init__.py # 任务模块初始化 │ ├── scheduler.py # 定时任务调度器 │ └── enum_sync_task.py # 枚举值自动同步任务 ├── migrations/ # Alembic迁移文件 │ ├── env.py # 迁移环境配置 │ ├── script.py.mako # 迁移脚本模板 │ └── versions/ # 迁移版本文件 ├── tests/ # 测试文件 │ ├── database/ # 数据库测试 │ ├── migration/ # 迁移测试 │ ├── integration/ # 集成测试 │ ├── run_tests.py # 测试运行器 │ ├── quick_test.py # 快速测试 │ └── README.md # 测试说明 ├── scripts/ # 脚本工具 │ ├── run_tests.py # 测试入口脚本 │ └── init_data.py # 数据初始化脚本 ├── docs/ # 项目文档 │ ├── MIGRATION_GUIDE.md # 迁移指南 │ └── MIGRATION_SUCCESS.md # 迁移成功报告 ├── .env # 环境配置(本地示例) ├── alembic.ini # Alembic配置文件 ├── pyproject.toml # 项目依赖配置 ├── uv.lock # 依赖锁定文件 ├── Dockerfile # Docker镜像构建 ├── docker-compose.yml # Docker编排配置 └── README.md # 项目文档 ``` ### 二十一、附录 附录 A · 统一返回结构约定(示意) - 成功:code=0,msg=OK,data=对象或列表 - 失败:code!=0,msg=错误提示,data=null 或附带错误上下文 附录 B · Casbin 策略定义语义(示意) - 角色绑定(g):user -> role @ domain - 权限授权(p):role @ domain -> obj + act - 匹配器要点: - 要求请求上下文中的 tenant_id/workspace_id 与策略域一致 - 支持基于路径前缀/资源名匹配与方法映射 - 系统超管可绕过域限制或具备 system 域全权 附录 C · 部署与运行(概述) - 准备 .env、构建镜像、启动 compose;首次启动执行数据库迁移(alembic upgrade head);访问健康检查端点确认成功 - 生产环境建议:只读根文件系统、最小权限、资源配额、滚动升级、外部日志收集 附录 D · .env 配置项建议与说明(示意) - 应用: - APP_ENV=dev/staging/prod(环境标识) - APP_NAME=ai-nlq-platform(服务名称) - APP_HOST=0.0.0.0,APP_PORT=8000(监听地址/端口) - APP_LOG_LEVEL=INFO(日志级别) - 数据库: - MYSQL_HOST、MYSQL_PORT、MYSQL_DB、MYSQL_USER、MYSQL_PASSWORD - MYSQL_POOL_SIZE=10(连接池大小) - Redis: - REDIS_HOST、REDIS_PORT、REDIS_DB、REDIS_PASSWORD(可选) - JWT: - JWT_SECRET(强随机密钥)、JWT_ALG=HS256、JWT_EXPIRE_MINUTES=60 - Casbin: - CASBIN_MODEL_PATH=casbin_config/model.conf - CASBIN_POLICY_ADAPTER=mysql(策略持久化适配器) - 日志: - LOG_DIR=/var/log/app,LOG_ROTATION=1 week,LOG_RETENTION=30 days - 其他: - PAGINATION_DEFAULT_SIZE=20、PAGINATION_MAX_SIZE=100 - RATE_LIMIT_ENABLE=true、RATE_LIMIT_QPS=50 说明:.env 不应提交到版本库,生产密钥通过安全渠道下发;可提供 .env.example 供本地参考。 附录 E · 请求/响应头与追踪约定 - 入站请求头: - Authorization: Bearer {JWT}(除登录/健康检查) - X-Request-Id(可选;若上游网关已生成则透传) - 出站响应头: - X-Trace-Id(本服务生成或透传的追踪 ID) - 追踪规则: - 若请求头包含 X-Request-Id,则作为 trace_id 使用;否则服务端生成 UUIDv4 - 全部日志记录均带 trace_id 字段,便于串联一次请求的完整链路 附录 F · 日志字段规范(loguru,文本或 JSON 均适用) - 时间戳:timestamp(ISO8601) - 级别:level(INFO/ERROR 等) - 追踪信息:trace_id - 请求信息:method、path、status_code、latency_ms、client_ip、user_agent - 业务上下文:user_id、tenant_id、workspace_id、action、resource - 错误上下文:exception、stack、error_code 说明:生产环境建议输出 JSON,便于 ELK/云原生日志采集;隐私字段脱敏。 附录 G · JWT 载荷字段建议(说明) - sub:用户唯一 ID - name:用户姓名 - staff_no:工号 - system_roles:系统级角色数组,如 ["SuperAdmin"] - tenant_roles:数组,元素含 {tenant_id, roles: [..]} - workspace_roles:数组,元素含 {workspace_id, roles: [..]} - iat/exp:签发/过期时间 - jti:令牌唯一 ID(支持加入黑名单) 说明:后端校验签名与 exp,有效期到达后需刷新;登出/强制下线将 jti 写入 Redis 黑名单。 附录 H · Casbin 持久化与表设计(说明) - 使用自定义 SQLModel Adapter,策略表通常包括:casbin_rules(ptype、v0..v5) - 约定: - p, sub, dom, obj, act(动作),必要时扩展到 v2+ 存储资源模式与数据域 - g, user, role, dom(域级角色绑定) - 模型匹配器建议:支持 keyMatch2/regex 等路径匹配;matcher 中引入租户/工作空间一致性校验 附录 I · Dockerfile / docker-compose 结构要点(不含代码) - Dockerfile: - 基于 python:3.11-slim;使用 uv 安装依赖 - 复制最小文件集(pyproject.toml、锁文件、app/ 源码) - 非 root 运行;设置 TZ;健康检查(/healthz) - ENTRYPOINT 采用 gunicorn + uvicorn workers 或 uvicorn(小规模) - docker-compose: - services:app、mysql、redis(可选 adminer/redis-commander) - volumes:持久化 MySQL 数据与日志目录 - env_file:加载 .env;depends_on 确保 DB/Redis 就绪 - 命令:启动前执行迁移(alembic upgrade head),再启动应用 附录 J · uv 常用命令与开发流程 - 安装依赖:uv sync(根据 pyproject.toml 与锁文件) - 运行应用:uv run uvicorn app.main:app --reload - 迁移管理:uv run alembic revision --autogenerate / uv run alembic upgrade head - 运行测试:uv run pytest(如使用) - 代码质量:uv run ruff check / mypy(如使用) 附录 K · API 统一响应示例(仅示意,非代码) - 成功: - { code: 0, msg: "OK", data: { ... }, } - 失败: - { code: 40301, msg: "无权限访问当前资源", data: null, } ## 数据模型说明 ### 核心实体关系 ```mermaid erDiagram User ||--o{ TenantUser : "belongs to" Tenant ||--o{ TenantUser : "has" Tenant ||--o{ Workspace : "owns" User ||--o{ WorkspaceMember : "member of" Workspace ||--o{ WorkspaceMember : "has" Workspace ||--o{ SqlQa : "contains" Workspace ||--o{ PendingQa : "contains" Workspace ||--o{ QuestionLog : "contains" User ||--o{ QuestionLog : "asks" ``` ### 权限模型 系统采用基于Casbin的RBAC with Domains权限模型: - **系统级角色**: SuperAdmin, SystemViewer - **租户级角色**: TenantAdmin, TenantMember - **工作空间级角色**: WorkspaceManager, WorkspaceUser ### SQLModel 模型定义规范 #### 1. 基础模型类 ``` from sqlmodel import SQLModel, Field from datetime import datetime from typing import Optional class TimestampMixin(SQLModel): """时间戳混入类""" created_at: datetime = Field(default_factory=datetime.now, description='创建时间') updated_at: datetime = Field(default_factory=datetime.now, description='更新时间') class BaseModel(TimestampMixin): """基础模型类,包含常用字段""" id: Optional[int] = Field(default=None, primary_key=True, description='主键ID') is_active: bool = Field(default=True, description='是否激活') created_by: Optional[str] = Field(default=None, max_length=50, description='创建人') updated_by: Optional[str] = Field(default=None, max_length=50, description='更新人') ``` #### 2. 实体模型示例 ``` from sqlmodel import SQLModel, Field, Relationship, Column from sqlalchemy import JSON, Text from typing import List, Optional class User(BaseModel, SoftDeleteMixin, table=True): __tablename__ = 'users' staff_no: str = Field(max_length=20, index=True, unique=True, description='工号') name: str = Field(max_length=50, description='姓名') # JSON字段使用Column(JSON) system_roles: List[str] = Field(default_factory=list, sa_column=Column(JSON), description='系统角色列表') # 关系字段 tenant_links: List["TenantUser"] = Relationship(back_populates="user") ``` #### 3. 特殊字段类型处理 - **JSON字段**: 使用 `sa_column=Column(JSON)` - **TEXT字段**: 使用 `sa_column=Column(Text)` - **外键字段**: 使用 `Field(foreign_key="table.id")` - **唯一约束**: 使用 `__table_args__ = (UniqueConstraint("field1", "field2"),)` ### 数据库操作规范 #### 1. 会话管理 ``` from core.db import get_session from sqlalchemy import select async def get_user_by_id(user_id: int): async with get_session() as session: result = await session.execute(select(User).where(User.id == user_id)) return result.scalar_one_or_none() ``` #### 2. 创建和更新 ``` async def create_user(user_data: dict): async with get_session() as session: user = User(**user_data) session.add(user) await session.commit() await session.refresh(user) return user ``` #### 3. 关系查询 ``` from sqlalchemy.orm import selectinload async def get_user_with_tenants(user_id: int): async with get_session() as session: result = await session.execute( select(User) .options(selectinload(User.tenant_links)) .where(User.id == user_id) ) return result.scalar_one_or_none() ``` ### 数据库初始化 1. **测试数据库连接**: ```bash uv run python scripts/run_tests.py db ``` 2. **生成初始迁移**: ```bash uv run alembic revision --autogenerate -m "Initial migration" ``` 3. **应用迁移**: ```bash uv run alembic upgrade head ``` 4. **查看迁移历史**: ```bash uv run alembic history ``` 5. **回滚迁移**: ```bash uv run alembic downgrade -1 ``` 6. **初始化演示数据**: ```bash uv run python scripts/init_data.py ``` ### Alembic 迁移管理 #### 1. 配置文件 - `alembic.ini`: Alembic主配置文件 - `migrations/env.py`: 迁移环境配置,支持动态数据库URL构建 - `migrations/script.py.mako`: 迁移脚本模板 #### 2. 常用命令 ```bash # 生成迁移(自动检测模型变化) uv run alembic revision --autogenerate -m "描述信息" # 手动创建空迁移 uv run alembic revision -m "描述信息" # 应用所有待执行迁移 uv run alembic upgrade head # 回滚到上一个版本 uv run alembic downgrade -1 # 回滚到指定版本 uv run alembic downgrade # 查看当前版本 uv run alembic current # 查看迁移历史 uv run alembic history --verbose ``` #### 3. 密码特殊字符处理 系统自动处理数据库密码中的特殊字符(如 `@`、`#` 等),通过URL编码确保连接正常: ```python from urllib.parse import quote_plus # 自动编码密码中的特殊字符 encoded_password = quote_plus(settings.mysql_password) database_url = f"mysql+aiomysql://{settings.mysql_user}:{encoded_password}@{settings.mysql_host}:{settings.mysql_port}/{settings.mysql_db}" ``` ### 默认账号 初始化后会创建以下默认账号: - **超级管理员**: admin / 系统管理员 - **演示用户1**: demo001 / 张三 (租户管理员 + 工作空间管理者) - **演示用户2**: demo002 / 李四 (租户成员 + 工作空间用户) ### 权限策略 权限策略定义在 `core/permissions.py` 中,包括: - 系统级权限:租户管理、用户管理 - 租户级权限:工作空间管理、租户用户管理 - 工作空间级权限:成员管理、SQL问答对管理、使用追踪等 ### 项目文件结构 ``` models/ # SQLModel 数据模型 ├── __init__.py # 模型导出 ├── base.py # 基础模型类(TimestampMixin, BaseModel, SoftDeleteMixin) ├── base_sqlalchemy.py # SQLAlchemy 2.0 基础模型类 ├── user.py # 用户模型 ├── user_sqlalchemy.py # 用户模型 (SQLAlchemy 2.0 风格) ├── tenant.py # 租户模型 ├── tenant_sqlalchemy.py # 租户模型 (SQLAlchemy 2.0 风格) ├── workspace.py # 工作空间模型 ├── workspace_sqlalchemy.py # 工作空间模型 (SQLAlchemy 2.0 风格) ├── sql_qa.py # SQL问答对模型 ├── sql_qa_sqlalchemy.py # SQL问答对模型 (SQLAlchemy 2.0 风格) ├── question_log.py # 用户提问记录模型 ├── question_log_sqlalchemy.py # 用户提问记录模型 (SQLAlchemy 2.0 风格) └── casbin_rule.py # Casbin规则模型 └── casbin_rule_sqlalchemy.py # Casbin规则模型 (SQLAlchemy 2.0 风格) core/ ├── db.py # 数据库连接和会话管理 ├── settings.py # 配置管理 └── auth/ # 权限认证模块 ├── __init__.py # 权限模块导出 ├── model.conf # Casbin权限模型配置 ├── adapter.py # Casbin SQLModel适配器 ├── enforcer.py # Casbin执行器 └── utils.py # Casbin工具函数 migrations/ # Alembic 数据库迁移 ├── env.py # 迁移环境配置 ├── script.py.mako # 迁移脚本模板 └── versions/ # 迁移版本文件 services/ ├── auth_service.py # 权限管理服务(已适配SQLModel) middlewares/ ├── auth.py # 权限认证中间件 alembic.ini # Alembic 配置文件 test_db_connection.py # 数据库连接测试脚本 test_migration.py # 迁移测试脚本 MIGRATION_GUIDE.md # 详细迁移指南 ``` ### 从 Tortoise ORM 迁移到 SQLModel 项目已完成从 Tortoise ORM 到 SQLModel 的迁移,主要变化: ### 从 SQLModel 迁移到 SQLAlchemy 2.0 项目现在支持 SQLAlchemy 2.0 风格的模型定义,这是一种更现代、更强大的 ORM 方式。主要变化: #### 1. 依赖变化 - **保持**: `sqlmodel`, `sqlalchemy[asyncio]`, `alembic` - **新增**: SQLAlchemy 2.0 特性支持 #### 2. 模型定义变化 - 继承 `Base` (SQLAlchemy 2.0 风格) 而非 `SQLModel` - 使用 `Mapped` 类型注解定义字段 - 使用 `mapped_column()` 定义字段属性 - 使用 `relationship()` 定义关系 #### 3. 示例对比 **SQLModel 风格**: ```python from sqlmodel import SQLModel, Field, Relationship class User(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) name: str = Field(max_length=50) tenant_links: List["TenantUser"] = Relationship(back_populates="user") ``` **SQLAlchemy 2.0 风格**: ```python from sqlalchemy.orm import Mapped, mapped_column, relationship class User(Base): # 继承 Base 而非 SQLModel __tablename__ = 'users' id: Mapped[Optional[int]] = mapped_column(Integer, primary_key=True) name: Mapped[str] = mapped_column(String(50)) tenant_links: Mapped[List["TenantUser"]] = relationship("TenantUser", back_populates="user") ``` #### 4. 迁移优势 - 更现代的 ORM 语法 - 更好的类型提示支持 - 更灵活的查询构建 - 更好的性能优化 - 与 SQLAlchemy 生态系统更好的集成 ### V1.0.1 本次迭代开发四大配置模块:全局规则配置、时间配置、维度配置、指标配置。包含完整的CRUD功能、批量导入导出、数据同步等特性。 - [技术方案](https://alidocs.dingtalk.com/i/nodes/kDnRL6jAJM3Ek196c3qwa9DDWyMoPYe1) - [数据库设计](https://alidocs.dingtalk.com/i/nodes/amweZ92PV6vkdR6mImqZyNX1VxEKBD6p?utm_scene=person_space&sideCollapsed=true&iframeQuery=utm_source%253Dportal%2526utm_medium%253Dportal_new_tab_open&corpId=dinge3c0722f4bd307cb35c2f4657eb6378f) - [开发排期](https://alidocs.dingtalk.com/i/nodes/qnYMoO1rWxDAop7vCd9MKENOW47Z3je9) ### V1.0.2.1 - 定时任务系统 新增定时任务调度系统,基于APScheduler实现。 #### 系统架构 **调度器组件** (`tasks/scheduler.py`): - 基于APScheduler的AsyncIOScheduler实现 - 支持多种触发器类型(cron、interval、date) - 自动管理任务生命周期 - 时区配置:Asia/Shanghai **任务管理**: - 任务注册:在应用启动时自动注册 - 任务监控:通过日志记录任务执行情况 - 任务控制:支持启动、停止、添加、移除任务 #### 已实现的定时任务 ##### 1. 枚举值自动同步任务 **任务名称**: `sync_enum_values` **执行时间**: 每天凌晨2点(可配置) **任务文件**: `tasks/enum_sync_task.py` **功能**: - 自动遍历所有活跃租户 - 遍历每个租户下所有活跃工作空间 - 对每个工作空间执行枚举值增量同步 - 记录详细的同步日志和统计信息 **配置项**: ```env # 是否启用定时任务调度器 ENABLE_SCHEDULER=true # 枚举值同步定时任务执行时间(小时,0-23) ENUM_SYNC_HOUR=2 # 枚举值同步定时任务执行时间(分钟,0-59) ENUM_SYNC_MINUTE=0 # 枚举值同步任务分布式锁超时时间(秒) ENUM_SYNC_LOCK_TIMEOUT=3600 ``` **分布式部署支持**: - ✅ 使用Redis分布式锁,完全支持多实例部署 - ✅ 自动协调,只有一个实例执行任务 - ✅ 其他实例自动跳过 - ✅ 无需手动配置主从关系 #### 添加新的定时任务 1. 在 `tasks/` 目录下创建任务文件,如 `tasks/my_task.py`: ```python from loguru import logger async def my_scheduled_task(): """我的定时任务""" logger.info("开始执行定时任务") # 任务逻辑 logger.info("定时任务执行完成") ``` 2. 在 `main.py` 的 `lifespan` 函数中注册任务: ```python from tasks.my_task import my_scheduled_task # 添加定时任务(每天凌晨3点执行) scheduler.add_job( func=my_scheduled_task, trigger='cron', job_id='my_task', name='我的定时任务', hour=3, minute=0, replace_existing=True ) ``` #### 任务监控 查看定时任务日志: ```bash # Docker部署 docker logs -f | grep "定时任务" # 本地运行 tail -f logs/app.log | grep "定时任务" ``` #### 注意事项 1. **时区设置**: 所有定时任务默认使用Asia/Shanghai时区 2. **任务并发**: AsyncIOScheduler支持异步任务,不会阻塞主线程 3. **错误处理**: 任务异常会被捕获并记录,不会影响调度器运行 4. **资源管理**: 应用关闭时会自动停止调度器并等待任务完成 ### V1.0.2 - 维度枚举值同步功能 新增从第三方接口自动同步维度枚举值的功能,支持定时自动同步和手动触发同步。 #### 功能说明 ##### 1. 定时自动同步(推荐) 系统已配置定时任务,默认每天凌晨2点自动同步所有租户下所有工作空间的枚举值。 **工作原理**: - 自动遍历所有非软删除的活跃租户 - 遍历每个租户下所有非软删除的活跃工作空间 - 对每个工作空间执行枚举值增量同步 - 记录详细的同步日志,包括成功/失败统计 **配置项** (在 `.env` 中配置): ``` # 是否启用定时任务调度器 ENABLE_SCHEDULER=true # 枚举值同步定时任务执行时间(小时,0-23) ENUM_SYNC_HOUR=2 # 枚举值同步定时任务执行时间(分钟,0-59) ENUM_SYNC_MINUTE=0 ``` **查看日志**: 定时任务执行日志会输出到应用日志中,可通过以下方式查看: ```bash # Docker部署 docker logs -f | grep "同步所有工作空间枚举值" # 本地运行 tail -f logs/app.log | grep "同步所有工作空间枚举值" ``` ##### 2. 手动触发同步 **接口路径**: `POST /tenants/{tenant_id}/workspaces/{workspace_id}/dimension-configs/sync-enum-values/` **功能描述**: 手动触发当前工作空间的枚举值同步,主要用于: - 新增维度后立即同步枚举值 - 紧急修复数据问题 - 测试同步功能 该接口会自动执行以下操作: 1. 查询当前工作空间下需要同步的维度记录(`field_type` 非空且 `entity_type='dimension'`) 2. 对每个维度,根据 `field_type` 调用第三方枚举接口获取枚举数据 3. 增量同步: - 上游有、本地没有 -> **新增** - 上游有、本地有 -> **更新** - 上游没有、本地有(data_source=1)-> **软删除** **转换规则**: - `parent_id`: 设置为对应维度的id - `entity`: 枚举值 + 维度的field_name (如: "许翠萍大店长姓名") - `entity_type`: 改为 'enum' - `comment`: 维度的expression,将 `{{name}}` 替换为枚举值 - `synonyms` / `original_value`: 枚举值 - `large_area_cd` / `agency_cd` / `cms_code`: 取接口返回的对应值 - `data_source`: True (表示API同步) - 其他字段继承维度 **请求参数**: - `brand_code` (可选): 品牌代码,默认根据维度的 brand 字段自动映射 - `clear_old_data` (可选): 同步模式,默认为 false - `false`: 增量同步模式,自动对比新增/更新/删除 - `true`: 全量替换模式,先清除所有旧数据再重新插入 **响应数据**: ```json { "code": 0, "msg": "同步完成: 10 个维度, 新增 100 条, 更新 50 条, 删除 10 条", "data": { "total_dimensions": 10, "synced_dimensions": 10, "total_enums": 150, "created_enums": 100, "updated_enums": 50, "deleted_enums": 10, "failed_dimensions": 0, "errors": [] } } ``` **配置项** (在 `.env` 中配置): ``` # 第三方枚举值同步接口配置 ENUM_API_URL=http://10.128.127.158:30443/api/enum/hierarchy/list ENUM_API_SECRET_CODE=2f2ce0fb ENUM_API_USERNAME=chat_bi ENUM_API_TIMEOUT=300 ENUM_API_BRAND_CODE=01 ENUM_SYNC_BATCH_SIZE=500 ``` **品牌代码映射**: 维度表中的 `brand` 字段会自动映射到第三方接口的 `brand_code`: | 维度brand | 接口brand_code | |-----------|----------------| | fila | 01 | | anta | 06 | **注意事项**: - 默认增量同步模式:自动检测新增/更新/删除 - 如果 `clear_old_data=true`,会切换为全量替换模式 - `data_source=1` 的枚举值(API同步)如果上游不存在会被软删除 - 支持大批量数据,会分批插入确保稳定性 - 系统会根据维度的 `brand` 字段自动选择对应的接口品牌代码