Files
Neo-ZQYY/.kiro/specs/tenant-admin-web/requirements.md
2026-03-20 09:03:11 +08:00

242 lines
22 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.
# 需求文档租户管理后台tenant-admin-web
## 简介
构建独立的租户管理 Web 应用(`apps/tenant-admin/`面向租户管理员提供用户审核与管理、Excel 数据上传4 种模板)、维客线索管理三大功能模块。前端采用 React + Vite + Ant Design`apps/admin-web/` 同技术栈),后端复用 `apps/backend/` FastAPI 新增 4 个路由模块。认证体系与小程序完全隔离,使用独立的 `auth.tenant_admins` 表、用户名+密码登录、不同 JWT audience。所有数据查询附加 `site_id IN (管辖列表)` 条件实现多门店数据隔离。
## 术语表
- **Tenant_Admin_Web**:租户管理后台前端应用,部署在 `apps/tenant-admin/`
- **Backend_API**FastAPI 后端服务,部署在 `apps/backend/`,为 Tenant_Admin_Web 提供 RESTful API
- **Tenant_Admin**:租户管理员,由系统管理后台 Operator 创建的账号,使用用户名+密码登录租户管理后台
- **Operator**:系统管理后台操作员,在 `apps/admin-web/` 中管理租户管理员账号
- **Site**:门店,通过 `site_id` 标识,是多门店数据隔离的基本单位
- **Site_Code**:球房编号,用户申请时输入的门店标识码,通过 `auth.site_code_mapping` 映射到 `site_id`
- **User_Application**:用户入驻申请,小程序用户提交的申请记录,存储在 `auth.user_applications`
- **Retention_Clue**:维客线索,助教为会员记录的销售/维护线索,存储在 `public.member_retention_clue`
- **Staging_Table**暂存表Excel 上传数据先写入业务库暂存表,再由 ETL 同步到 DWS 层
- **Upload_Batch**:上传批次,一次 Excel 上传操作的记录,存储在 `biz.excel_upload_log`
- **Salary_Adjustment**:助教奖罚明细,通过 Excel 上传写入 `biz.salary_adjustments`
- **Managed_Site_Ids**管辖门店列表Tenant_Admin 被授权管理的 `site_id` 数组
## 需求
### 需求 1租户管理员认证
**用户故事:** 作为 Tenant_Admin我希望通过用户名和密码登录租户管理后台以便安全地执行用户审核、数据上传等管理操作。
#### 验收标准
1. WHEN Tenant_Admin 提交有效的用户名和密码, THE Backend_API SHALL 验证凭据bcrypt 哈希比对),签发 JWT 访问令牌audience 为 `tenant-admin`,与小程序 `xcx` 隔离),并返回访问令牌和刷新令牌
2. WHEN Tenant_Admin 提交无效的用户名或密码, THE Backend_API SHALL 返回 401 状态码和错误描述,不泄露具体是用户名还是密码错误
3. IF Tenant_Admin 账号状态为禁用(`is_active = false`, THEN THE Backend_API SHALL 返回 403 状态码并提示账号已被禁用
4. WHILE Tenant_Admin 持有有效的 JWT 令牌, THE Backend_API SHALL 允许访问 `/api/tenant/*` 路径下的受保护端点
5. WHEN JWT 访问令牌过期, THE Tenant_Admin_Web SHALL 使用刷新令牌自动获取新的访问令牌WHEN 刷新令牌也过期, THE Tenant_Admin_Web SHALL 将 Tenant_Admin 重定向到登录页面
6. THE Backend_API SHALL 通过 JWT 中的 `aud` 字段区分租户管理员和小程序用户,使用独立的认证依赖注入(`require_tenant_admin()`),拒绝小程序 JWT 访问租户管理端点
### 需求 2数据隔离与权限控制
**用户故事:** 作为 Tenant_Admin我希望只能查看和操作自己管辖门店的数据以确保多门店之间的数据安全隔离。
#### 验收标准
1. THE Backend_API SHALL 在所有租户管理端点的数据查询中附加 `site_id IN (managed_site_ids)` 条件,其中 `managed_site_ids` 从当前 Tenant_Admin 的 JWT 令牌或 `auth.tenant_admins` 记录中获取
2. WHEN Tenant_Admin 的 `managed_site_ids` 包含多个 site_id, THE Backend_API SHALL 支持跨门店聚合查询(合并多个 site_id 的结果)
3. IF Tenant_Admin 尝试访问不在其 `managed_site_ids` 范围内的数据, THEN THE Backend_API SHALL 返回 403 状态码
4. THE Tenant_Admin_Web SHALL 在页面顶部提供门店筛选器,允许 Tenant_Admin 在管辖范围内切换或选择门店
### 需求 3用户申请审核
**用户故事:** 作为 Tenant_Admin我希望查看和审核小程序用户的入驻申请以便控制哪些用户可以使用系统。
#### 验收标准
1. WHEN Tenant_Admin 打开用户审核页面, THE Tenant_Admin_Web SHALL 从 Backend_API 获取管辖门店范围内的用户申请列表,支持按状态筛选(全部 / 待审核 pending / 已通过 approved / 已拒绝 rejected支持分页
2. THE Backend_API SHALL 返回每条申请的以下信息申请人昵称、手机号、球房编号site_code、申请角色文本、员工编号、申请时间、当前状态
3. WHEN Tenant_Admin 查看某条待审核申请的关联建议, THE Backend_API SHALL 根据申请中的球房编号通过 `auth.site_code_mapping` 查得 `site_id`,再并行匹配 `fdw_etl.v_dim_assistant`phone 匹配,`scd2_is_current=1`)和 `fdw_etl.v_dim_staff` + `v_dim_staff_ex`phone 匹配),返回匹配建议列表
4. WHEN Tenant_Admin 审核通过一条申请, THE Backend_API SHALL 接受角色(助教/管理者/员工)、关联助教 ID可选、关联员工 ID可选执行以下操作更新 `auth.users.status = 'approved'`、写入 `auth.user_site_roles`(分配角色)、写入 `auth.user_assistant_binding`(关联助教/员工,含 staff_id、更新 `auth.user_applications.status = 'approved'` 及审核人和审核时间
5. WHEN Tenant_Admin 审核拒绝一条申请, THE Backend_API SHALL 接受拒绝原因(必填),更新 `auth.user_applications.status = 'rejected'``review_note` 和审核时间
6. IF 申请状态不是 pending, THEN THE Backend_API SHALL 拒绝审核操作并返回 409 状态码
### 需求 4用户管理
**用户故事:** 作为 Tenant_Admin我希望管理已通过审核的用户信息包括修改角色、店铺归属和助教绑定关系以便维护用户数据的准确性。
#### 验收标准
1. WHEN Tenant_Admin 打开用户管理页面, THE Tenant_Admin_Web SHALL 展示管辖门店范围内已通过审核的用户列表,包含姓名、角色、关联助教姓名、所属门店、账号状态,支持按角色筛选和关键词搜索,支持分页
2. WHEN Tenant_Admin 编辑用户信息, THE Backend_API SHALL 允许修改以下字段:角色(助教/管理者/员工)、所属门店(`site_id`,限管辖范围内)、账号状态(启用/禁用)
3. WHEN Tenant_Admin 修改用户的助教/员工绑定关系, THE Backend_API SHALL 更新 `auth.user_assistant_binding` 记录,接受新的 `assistant_id` 和/或 `staff_id`
4. WHEN Tenant_Admin 禁用某用户账号, THE Backend_API SHALL 将 `auth.users.status` 设为 `disabled`,该用户后续登录小程序时 SHALL 被拒绝
5. IF Tenant_Admin 尝试将用户的 site_id 修改为不在其管辖范围内的值, THEN THE Backend_API SHALL 返回 403 状态码
### 需求 5Excel 上传 — 文件解析与格式校验
**用户故事:** 作为 Tenant_Admin我希望上传 Excel 文件并获得即时的格式校验反馈,以便在数据写入前发现和修正错误。
#### 验收标准
1. WHEN Tenant_Admin 选择模板类型并上传 Excel 文件, THE Backend_API SHALL 解析文件内容,按对应模板的列定义进行格式校验,并返回校验结果
2. THE Backend_API SHALL 支持 4 种模板类型的格式校验:
- 财务支出expense月份YYYY-MM不超过当前月、支出类别枚举 8 值:房租/水电/物业/食品饮料进货/耗材/报销/固定人员工资/其他费用)、金额(> 0精度 2 位小数)、备注(可选,最长 500 字符)
- 团购收入platform_income月份YYYY-MM、平台名称非空、收入金额> 0、备注可选最长 500 字符)
- 助教奖罚salary_adj月份YYYY-MM、助教姓名非空、助教编号非空、类型枚举扣款/奖金)、金额(> 0、原因非空最长 200 字符)
- 充值业绩归属recharge_commission充值日期YYYY-MM-DD、会员名称非空、充值金额> 0、归属助教非空、奖励金额≥ 0
3. WHEN 校验发现格式错误行, THE Backend_API SHALL 返回错误行号、列名和具体错误描述Tenant_Admin_Web SHALL 标红展示错误行
4. WHEN 校验全部通过, THE Backend_API SHALL 创建 `biz.excel_upload_log` 记录(状态为 pending返回 upload_id 和解析后的数据预览
5. IF 上传文件不是有效的 Excel 格式(.xlsx/.xls, THEN THE Backend_API SHALL 返回 400 状态码并提示文件格式错误
### 需求 6Excel 上传 — 人员匹配校验
**用户故事:** 作为 Tenant_Admin我希望上传助教奖罚和充值业绩归属数据时系统能自动校验助教信息的准确性以减少数据录入错误。
#### 验收标准
1. WHEN 上传助教奖罚salary_adj或充值业绩归属recharge_commission模板时, THE Backend_API SHALL 对每行数据中的助教姓名+助教编号执行匹配校验
2. THE Backend_API SHALL 按以下顺序匹配:先查 `fdw_etl.v_dim_assistant`nickname + assistant_number`scd2_is_current=1`),如不匹配再查 `fdw_etl.v_dim_staff` + `v_dim_staff_ex`name + staff_number
3. WHEN 匹配成功, THE Backend_API SHALL 在返回数据中填充 `assistant_id`
4. WHEN 匹配失败, THE Backend_API SHALL 标记该行为校验警告warning不阻断上传流程但在前端以黄色高亮提示 Tenant_Admin 确认
5. THE Tenant_Admin_Web SHALL 在校验结果页面汇总展示:通过行数、警告行数、错误行数
### 需求 7Excel 上传 — 冲突检测与解决
**用户故事:** 作为 Tenant_Admin我希望在数据写入前看到与已有数据的冲突情况并能逐行选择处理方式以避免误覆盖历史数据。
#### 验收标准
1. WHEN 格式校验通过后, THE Backend_API SHALL 按各模板的主键规则检测冲突:
- 财务支出:月份 + 支出类别
- 团购收入:月份 + 平台名称
- 助教奖罚:月份 + 助教姓名 + 助教编号 + 类型 + 原因
- 充值业绩归属:充值日期 + 会员名称 + 归属助教
2. WHEN 检测到冲突行(主键已存在), THE Backend_API SHALL 返回 diff 数据(旧值 vs 新值,按字段逐一对比)
3. THE Tenant_Admin_Web SHALL 展示 diff 交互表格,每行显示字段名、旧值、新值和操作选项(替换/保留),支持"全部替换"和"全部保留"快捷操作
4. WHEN Tenant_Admin 确认冲突解决方案并提交, THE Backend_API SHALL 接受 upload_id 和每行的解决方案resolutions 数组),按选择执行写入
5. WHEN 无冲突行, THE Backend_API SHALL 直接标记为"待写入"Tenant_Admin 确认后写入
### 需求 8Excel 上传 — 数据写入与记录
**用户故事:** 作为 Tenant_Admin我希望确认后的数据能正确写入目标表并保留上传历史记录以便追溯。
#### 验收标准
1. WHEN Tenant_Admin 确认写入, THE Backend_API SHALL 将数据写入对应目标表:
- 助教奖罚 → `biz.salary_adjustments`(直接写入业务库)
- 财务支出 → `biz.stg_finance_expense`staging 表,由 ETL 同步到 DWS
- 团购收入 → `biz.stg_platform_income`staging 表)
- 充值业绩归属 → `biz.stg_recharge_commission`staging 表)
2. THE Backend_API SHALL 在写入完成后更新 `biz.excel_upload_log` 记录:状态设为 `confirmed`、记录实际写入行数、冲突解决数、确认时间
3. IF 写入过程中发生数据库错误, THEN THE Backend_API SHALL 回滚整个批次的写入,将 `biz.excel_upload_log` 状态设为 `failed`,记录错误详情到 `error_detail` 字段
4. WHEN Tenant_Admin 查看上传记录页面, THE Backend_API SHALL 返回管辖门店范围内的历史上传记录列表,包含模板类型、文件名、上传人、上传时间、行数、冲突数、状态,支持分页
5. WHEN Tenant_Admin 下载空白模板, THE Backend_API SHALL 返回对应模板类型的 Excel 文件(含表头和格式说明)
### 需求 9维客线索 — 客户搜索
**用户故事:** 作为 Tenant_Admin我希望通过客户姓名或手机号搜索客户以便查看和管理其维客线索。
#### 验收标准
1. WHEN Tenant_Admin 输入搜索关键词, THE Backend_API SHALL 在管辖门店范围内搜索客户,匹配 `fdw_etl.v_dim_member``nickname`(模糊匹配)或 `mobile`精确匹配返回客户列表member_id、姓名、手机号脱敏、所属门店
2. THE Backend_API SHALL 通过 `member_id` JOIN `fdw_etl.v_dim_member``scd2_is_current=1`获取客户姓名和手机号不使用结算单上的冗余字段DQ-6 规则)
3. WHEN Tenant_Admin 选择门店筛选条件, THE Backend_API SHALL 在搜索结果中仅返回该门店的客户
4. IF 搜索结果为空, THEN THE Tenant_Admin_Web SHALL 展示"未找到匹配客户"的提示
### 需求 10维客线索 — 线索列表与展示
**用户故事:** 作为 Tenant_Admin我希望查看某客户的全部维客线索以便了解客户画像和历史记录。
#### 验收标准
1. WHEN Tenant_Admin 选择某客户, THE Backend_API SHALL 返回该客户在管辖门店范围内的全部维客线索列表,包含:线索 ID、大类标签category、摘要summary、详情detail、提供人recorded_by_name、来源sourcemanual / ai_consumption / ai_note、记录时间recorded_at、隐藏状态is_hidden
2. THE Tenant_Admin_Web SHALL 按大类标签分组展示线索,支持按来源和隐藏状态筛选
3. THE Tenant_Admin_Web SHALL 对已隐藏的线索以灰色或删除线样式区分展示
### 需求 11维客线索 — 编辑
**用户故事:** 作为 Tenant_Admin我希望修改维客线索的标签、摘要和详情以便纠正错误或补充信息。
#### 验收标准
1. WHEN Tenant_Admin 编辑某条线索, THE Backend_API SHALL 接受修改后的 category枚举 6 值:客户基础/消费习惯/玩法偏好/促销偏好/社交关系/重要反馈、summary非空最长 200 字符、detail可选
2. THE Backend_API SHALL 验证 category 值在枚举范围内summary 非空且不超过长度限制
3. IF 线索 ID 不存在或不在管辖门店范围内, THEN THE Backend_API SHALL 返回 404 状态码
### 需求 12维客线索 — 删除
**用户故事:** 作为 Tenant_Admin我希望删除错误或无效的维客线索以保持数据整洁。
#### 验收标准
1. WHEN Tenant_Admin 请求删除某条线索, THE Tenant_Admin_Web SHALL 弹出二次确认对话框,明确提示"删除后不可恢复"
2. WHEN Tenant_Admin 确认删除, THE Backend_API SHALL 对该线索执行物理删除(`DELETE FROM public.member_retention_clue WHERE id = ?`
3. IF 线索 ID 不存在或不在管辖门店范围内, THEN THE Backend_API SHALL 返回 404 状态码
### 需求 13维客线索 — 隐藏与显示
**用户故事:** 作为 Tenant_Admin我希望隐藏某些线索使其不在小程序端展示同时保留在管理后台可见以便灵活控制线索的可见性。
#### 验收标准
1. WHEN Tenant_Admin 切换某条线索的隐藏状态, THE Backend_API SHALL 更新 `public.member_retention_clue.is_hidden` 字段true=隐藏false=显示)
2. WHILE 线索的 `is_hidden = true`, THE Backend_API SHALL 确保小程序端查询线索时通过 `WHERE is_hidden = false` 条件过滤该线索
3. THE Tenant_Admin_Web SHALL 允许 Tenant_Admin 将已隐藏的线索恢复为显示状态(`is_hidden = false`
4. IF 线索 ID 不存在或不在管辖门店范围内, THEN THE Backend_API SHALL 返回 404 状态码
### 需求 14管理后台 — 租户管理员账号管理
**用户故事:** 作为 Operator我希望在系统管理后台admin-web中创建、编辑和管理租户管理员账号以便控制谁可以登录租户管理后台。
#### 验收标准
1. WHEN Operator 打开租户管理员管理页面, THE admin-web SHALL 展示所有租户管理员列表,包含用户名、显示名称、管辖门店、账号状态(启用/禁用)、创建时间、最后登录时间,支持分页和关键词搜索
2. WHEN Operator 创建新的租户管理员, THE Backend_API SHALL 接受用户名唯一、初始密码、显示名称、tenant_id、managed_site_ids门店列表将密码 bcrypt 哈希后写入 `auth.tenant_admins`,记录 `created_by` 为当前 Operator 的 user_id
3. IF 用户名已存在, THEN THE Backend_API SHALL 返回 409 状态码并提示"用户名已存在"
4. WHEN Operator 编辑租户管理员信息, THE Backend_API SHALL 允许修改以下字段显示名称、managed_site_ids、is_active启用/禁用)
5. WHEN Operator 重置租户管理员密码, THE Backend_API SHALL 接受新密码bcrypt 哈希后更新 `auth.tenant_admins.password_hash`
6. IF Operator 禁用某租户管理员is_active=false, THEN 该管理员后续登录租户管理后台时 SHALL 被拒绝(返回 403
7. THE Backend_API SHALL 要求 Operator 具有 site_admin 或 tenant_admin 角色才能访问租户管理员管理端点
### 需求 15数据库变更 — 新建表
**用户故事:** 作为开发者,我需要创建 NS4 所需的新数据库表,以支撑租户管理后台的全部功能。
#### 验收标准
1. THE 迁移脚本 SHALL 在 `auth` Schema 中创建 `tenant_admins`包含字段idBIGSERIAL PK、usernameVARCHAR(50) UNIQUE NOT NULL、password_hashVARCHAR(255) NOT NULL、display_nameVARCHAR(100)、tenant_idBIGINT NOT NULL、managed_site_idsBIGINT[] NOT NULL、is_activeBOOLEAN DEFAULT true、created_byBIGINT、created_atTIMESTAMPTZ DEFAULT NOW()、last_login_atTIMESTAMPTZ并创建 tenant_id 索引
2. THE 迁移脚本 SHALL 在 `biz` Schema 中创建 `salary_adjustments`包含字段idBIGSERIAL PK、site_idBIGINT NOT NULL、assistant_idBIGINT 可空、assistant_nameVARCHAR(100) NOT NULL、assistant_numberVARCHAR(50) NOT NULL、salary_monthVARCHAR(7) NOT NULL、adjustment_typeCHECK IN deduction/bonus、amountNUMERIC(12,2) > 0、reasonVARCHAR(200) NOT NULL、upload_batch_idFK → excel_upload_log、created_at、created_by并创建 (site_id, salary_month) 和 (assistant_id, salary_month) 索引
3. THE 迁移脚本 SHALL 在 `biz` Schema 中创建 `excel_upload_log`包含字段idBIGSERIAL PK、site_idBIGINT NOT NULL、upload_typeCHECK IN expense/platform_income/salary_adj/recharge_commission、file_nameVARCHAR(255) NOT NULL、uploaded_byBIGINT NOT NULL、row_countINTEGER DEFAULT 0、conflict_countINTEGER DEFAULT 0、resolved_countINTEGER DEFAULT 0、statusCHECK IN pending/confirmed/failed、error_detailJSONB、created_at、confirmed_at并创建 (site_id, created_at DESC) 索引
4. THE 迁移脚本 SHALL 在 `biz` Schema 中创建 3 张 staging 表:`stg_finance_expense``stg_platform_income``stg_recharge_commission`,各表包含 site_id、业务字段、upload_batch_idFK、synced_atTIMESTAMPTZ 可空NULL 表示未同步、created_at
### 需求 16数据库变更 — 表结构修改
**用户故事:** 作为开发者,我需要为现有表添加 NS4 所需的字段,以支持维客线索隐藏功能。
#### 验收标准
1. THE 迁移脚本 SHALL 为 `public.member_retention_clue` 表添加 `is_hidden`BOOLEAN NOT NULL DEFAULT false并添加列注释说明"是否隐藏true=管理后台保留但小程序不展示)"
2. THE 迁移脚本 SHALL 确保已有数据的 `is_hidden` 值为 false通过 DEFAULT 约束保证)
3. THE 小程序端现有的线索查询 API SHALL 在查询条件中增加 `WHERE is_hidden = false`,确保隐藏线索不在小程序端展示
### 需求 17前端应用骨架
**用户故事:** 作为开发者,我需要搭建租户管理后台的前端项目骨架,以便后续功能页面的开发。
#### 验收标准
1. THE 前端项目 SHALL 在 `apps/tenant-admin/` 目录下创建,使用 React + Vite + Ant Design 技术栈,包含 `package.json``vite.config.ts``tsconfig.json` 等配置文件
2. THE Tenant_Admin_Web SHALL 提供侧边栏导航包含四个功能模块入口用户审核、用户管理、Excel 上传、维客线索管理
3. THE Tenant_Admin_Web SHALL 实现登录页面,未认证时自动重定向到登录页
4. THE Tenant_Admin_Web SHALL 封装统一的 API 调用层(`services/api.ts`),处理 JWT 令牌的自动附加、刷新和过期重定向
5. THE Tenant_Admin_Web SHALL 使用与 Backend_API 一致的响应格式(`{ code: 0, data: ... }`Pydantic 使用 `alias_generator=to_camel` 实现驼峰命名转换
### 需求 18后端路由模块
**用户故事:** 作为开发者,我需要在 FastAPI 后端中创建租户管理专用的路由模块,以提供 NS4 所需的全部 API 端点。
#### 验收标准
1. THE Backend_API SHALL 在 `apps/backend/app/routers/` 下创建 4 个路由文件:`tenant_auth.py`(登录/JWT 签发/鉴权)、`tenant_users.py`(用户审核+用户管理)、`tenant_excel.py`Excel 上传/校验/冲突处理)、`tenant_clues.py`(维客线索管理),以及 1 个管理端路由文件 `admin_tenant_admins.py`(租户管理员 CRUD
2. THE Backend_API SHALL 将所有租户管理端点注册在 `/api/tenant/` 路径前缀下,管理端租户管理员 CRUD 端点注册在 `/api/admin/tenant-admins/` 路径前缀下,与小程序端点(`/api/xcx/`)隔离
3. THE Backend_API SHALL 为所有 `/api/tenant/*` 端点(登录接口除外)添加 `require_tenant_admin()` 认证依赖,验证 JWT 的 `aud` 字段为 `tenant-admin`;为 `/api/admin/tenant-admins/*` 端点添加 `_require_admin()` 认证依赖,验证 site_admin 或 tenant_admin 角色
4. THE Backend_API SHALL 遵循现有的 API 响应格式约定:成功返回 `{ code: 0, data: ... }`,失败返回 `{ code: number, message: string }`,分页返回 `{ items: T[], total: number, page: number, pageSize: number }`