# P10：租户管理后台 — tenant-admin-web

> 优先级：P10（依赖 P1 + P3，可与 P4-P9 并行）
> 预估工作量：中等

---

## 需求（Requirements）

### 用户故事

1. 作为租户管理员，我需要独立的 Web 管理后台来审核用户申请。
2. 作为租户管理员，我需要管理用户（编辑身份、店铺归属、关联助教）。
3. 作为租户管理员，我需要上传 Excel 数据（财务支出/团购收入/助教奖罚/充值业绩归属）。
4. 作为租户管理员，上传 Excel 时如果发现主键冲突，我需要逐条确认替换或保留（类似 diff 交互）。
5. 作为系统，租户管理员只能看到自己租户下的店铺数据。
6. 作为租户管理员，我需要查看每个客户的所有维客线索，并对线索进行修改、删除、隐藏操作。

### 验收标准

- AC1：租户管理后台是独立 Web 应用，独立登录入口
- AC1.1：租户管理员账号由系统管理后台（`apps/admin-web/`）创建，租户管理员不可自行注册
- AC2：租户管理员只能看到自己租户下的店铺和用户（由 Operator 分配的 `site_id` 列表决定）
- AC3：用户审核页面：申请列表、状态筛选、球房ID+手机号关联建议（同时匹配助教表+员工信息表）、审核操作
- AC4：Excel 上传校验：必填、金额精度、表头格式、类型合法
- AC5：主键冲突时前端展示 diff 交互，逐条确认后保存
- AC6：助教奖罚支持同一助教同月多笔
- AC7：维客线索管理页面：按客户列出全部线索，展示标签（大类）、摘要、提供人、备注原文等字段
- AC8：维客线索支持修改（编辑摘要/详情/标签）、删除（二次确认）、隐藏（不在小程序端展示但保留数据）操作

---

## 设计要点

### 技术栈

- 独立 Web 应用：React + Vite + Ant Design（与 `apps/admin-web/` 同技术栈）
- 部署路径：`apps/tenant-admin/`
- 后端复用 `apps/backend/` 的 FastAPI，新增租户管理路由模块

### 登录体系

- 独立凭据：每个租户有若干管理员账号（用户名+密码）
- 账号由系统管理后台（`apps/admin-web/`）的 Operator 创建和管理（见 admin-web-console 需求 11），租户管理员不可自行注册
- Operator 创建账号时指定：用户名、初始密码、所属租户、管辖球房 ID 列表（`site_id` 数组）
- 租户级管理员：管辖该租户下所有店铺
- 店铺级管理员：只能管理指定店铺（由 Operator 分配的 `site_id` 列表决定）
- 与系统管理后台（`apps/admin-web/`）完全隔离，登录入口独立

### Excel 4 种模板

#### 1. 财务支出（按月）

| 列名 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 月份 | YYYY-MM | 是 | 提交月份 |
| 支出类别 | 文本 | 是 | 房租/水电/物业/食品饮料进货/耗材/报销/固定人员工资/其他费用 |
| 金额 | 数值(2) | 是 | 支出金额 |
| 备注 | 文本 | 否 | |

主键：月份 + 支出类别

#### 2. 团购平台收入（按月）

| 列名 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 月份 | YYYY-MM | 是 | |
| 平台名称 | 文本 | 是 | |
| 收入金额 | 数值(2) | 是 | |
| 备注 | 文本 | 否 | |

主键：月份 + 平台名称

#### 3. 助教奖罚（按月）

| 列名 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 月份 | YYYY-MM | 是 | |
| 助教姓名 | 文本 | 是 | |
| 助教编号 | 文本 | 是 | |
| 类型 | 文本 | 是 | 扣款/奖金 |
| 金额 | 数值(2) | 是 | |
| 原因 | 文本 | 是 | |

主键：月份 + 助教姓名 + 助教编号 + 类型 + 原因（同一助教同月可多笔）

#### 4. 充值业绩归属（按月）

| 列名 | 类型 | 必填 | 说明 |
|------|------|------|------|
| 充值日期 | YYYY-MM-DD | 是 | |
| 会员名称 | 文本 | 是 | |
| 充值金额 | 数值(2) | 是 | |
| 归属助教 | 文本 | 是 | |
| 奖励金额 | 数值(2) | 是 | |

主键：充值日期 + 会员名称 + 归属助教

### 冲突处理流程

```
上传 Excel → 后端解析+校验 → 返回校验结果
  ├── 无冲突 → 直接写入
  └── 有冲突 → 返回冲突列表（旧值 vs 新值）
        → 前端展示 diff 交互
        → 用户逐条选择"替换"或"保留"
        → 提交确认 → 后端按选择写入
```

### 维客线索管理

#### 页面功能

- 客户搜索/筛选：按客户姓名、手机号搜索，按门店筛选
- 线索列表：展示该客户的全部维客线索，字段包括：
  - 标签（大类枚举：客户基础/消费习惯/玩法偏好/促销接受/社交关系/重要反馈，与 P5 应用 8 输出一致）
  - 摘要
  - 详情
  - 提供人（recorded_by_name）
  - 来源（人工录入 `manual` / AI消费分析 `ai_consumption`（应用 3）/ AI备注分析 `ai_note`（应用 6），实际写入 `member_retention_clue` 的线索由应用 8 整合后统一落库）
  - 记录时间
- 操作：
  - 修改：编辑标签、摘要、详情
  - 删除：二次确认后物理删除
  - 隐藏：设置 `is_hidden = true`，小程序端不展示但管理后台保留（需新增 `is_hidden` 字段，待后续 DB 变更时一并实现）

#### 数据源

- `zqyy_app.public.member_retention_clue`（已建）
- 客户信息通过 FDW 读取 `dwd.dim_member`

#### 待实现的 DB 变更（SPEC 记录，暂不执行）

- `member_retention_clue` 新增 `is_hidden BOOLEAN DEFAULT false`：控制小程序端是否展示
- `member_retention_clue` 新增 `source VARCHAR(20) DEFAULT 'manual'`：区分线索来源（`manual` 人工录入 / `ai_consumption` 应用 3 消费分析 / `ai_note` 应用 6 备注分析；应用 8 整合时保留原始 source）

### 新建表

```
biz.salary_adjustments
  - id, site_id, assistant_id, assistant_name, assistant_number
  - salary_month, adjustment_type (deduction/bonus)
  - amount, reason
  - upload_batch_id, created_at

biz.excel_upload_log
  - id, site_id, upload_type (expense/platform_income/salary_adj/recharge_commission)
  - file_name, uploaded_by, row_count
  - conflict_count, resolved_count
  - status (pending/confirmed/failed)
  - created_at
```

---

## 任务清单

- [ ] T1：创建 `apps/tenant-admin/` 项目骨架（React + Vite + Ant Design）
- [ ] T2：实现租户管理员登录（独立凭据）
- [ ] T3：实现用户审核页面（申请列表 + 状态筛选 + 关联建议 + 审核操作）
- [ ] T4：实现用户管理页面（用户列表 + 身份编辑 + 店铺归属）
- [ ] T5：创建 `biz.salary_adjustments` + `biz.excel_upload_log` 表
- [ ] T6：实现 Excel 上传后端（解析 + 校验 + 冲突检测）
- [ ] T7：实现 Excel 上传前端（模板下载 + 上传 + diff 交互 + 确认）
- [ ] T8：实现 4 种 Excel 模板的校验规则
- [ ] T9：实现人员姓名+编号匹配校验（同时对照 `dim_assistant` 助教表和 `dim_staff` + `dim_staff_ex` 员工信息表）
- [ ] T10：实现维客线索管理页面（客户搜索 + 线索列表 + 标签/摘要/提供人/备注原文展示）
- [ ] T11：实现维客线索操作 API（修改/删除/隐藏）+ 后端路由扩展
