# 变更审计记录：店铺筛选 + 时间格式 + 姓名格式 + 李小燕确认

| 字段 | 值 |
|------|-----|
| 日期 | 2026-03-23 17:34:31 |
| Prompt-ID | P20260323-171648 |
| Session-ID | 84d2f92d |
| Session 路径 | docs/audit/session_logs/2026-03/23/19_abb5b8d0_165704 |

## 操作摘要
TASK 11：对 `tenant_users.py` 进行四项改动——tenant_admin 按 tenant_id 查所有店铺（解决新建店铺不在 JWT 中的问题）、申请时间格式化去除微秒和时区后缀、助教姓名组装"昵称（真实姓名）"格式、员工入职日期格式化。同时验证了李小燕在列表中存在（非 bug，列表较长未滚动到）。

## 变更内容

### 1. `list_my_sites` — tenant_admin 按 tenant_id 查所有店铺
- 原逻辑：只查 JWT `managed_site_ids` 范围内的店铺
- 新逻辑：tenant_admin 先查 `biz.tenants` 获取内部 PK，再按 `tenant_id` 查 `biz.sites` 所有活跃店铺
- 原因：admin-web 新建店铺后，tenant_admin 的 JWT 不含新店铺 site_id，导致下拉缺失
- site_admin 逻辑不变（仍按 managed_site_ids）

### 2. `list_applications` — 申请时间格式化
- `ua.created_at::text` → `to_char(ua.created_at, 'YYYY-MM-DD HH24:MI:SS')`
- 原因：`::text` 输出含微秒和时区后缀（如 `.123456+08`），前端显示多余内容

### 3. `list_site_staff` coach 分支 — 助教姓名格式
- SQL 新增 `nickname` 字段
- 新增 `_format_display_name(nickname, real_name)` 辅助函数
- 格式：`昵称（真实姓名）`，如 `闹闹（卢思静）`；昵称=真实姓名时只显示一个
- 原因：用户要求显示花名+真实姓名

### 4. `list_site_staff` staff 分支 — 员工入职日期格式化
- `entry_time::text` → `to_char(entry_time, 'YYYY-MM-DD')`
- 与 coach 分支保持一致

### 5. 李小燕确认
- 数据库确认：`scd2_is_current=1`，`site_id` 正确，`level=40`（星级）
- SQL 无 LIMIT，80 条全返回
- Playwright 搜索"小燕"找到：`星级 - 小燕（李小燕） - 17802081334 - 入职日期 2025-11-14`
- 结论：非 bug，列表较长用户可能未滚动到

## 影响范围
- 后端：`tenant_users.py`（`list_my_sites`、`list_site_staff`、`list_applications`）
- 前端：无改动（后端返回的 name 字段已包含格式化后的姓名）

## 验证
- Playwright 验证店铺下拉含 `朗朗桌球 (LL0001)` 和 `朗朗桌球2店 (LLX999)` ✅
- Playwright 验证申请时间显示 `2026-03-23 16:59:01`（无多余内容）✅
- Playwright 验证助教姓名格式 `初级 - 闹闹（卢思静） - 18124475671 - 入职日期 2026-03-18` ✅
- Playwright 搜索"小燕"找到李小燕 ✅

## 改动注解

### `apps/backend/app/routers/tenant_users.py`
- 变更类型：修改
- 原始原因：用户反馈三个问题——(1) tenant_admin 新建店铺后下拉不显示新店铺（JWT 中 managed_site_ids 不含新店铺）；(2) 申请时间显示含微秒和时区后缀；(3) 助教列表只显示真实姓名，用户要求同时显示花名
- 思路分析：
  - `list_my_sites`：对 tenant_admin 角色改为按 `tenant_id` 查 `biz.sites`，绕过 JWT 中 `managed_site_ids` 的限制。site_admin 保持原逻辑不变，确保权限隔离
  - 时间格式化：统一使用 PostgreSQL `to_char()` 在 SQL 层完成格式化，避免前端额外处理
  - 姓名格式：新增 `_format_display_name(nickname, real_name)` 辅助函数，当昵称与真实姓名不同时组装为"昵称（真实姓名）"格式，相同时只显示一个。SQL 层新增 `nickname` 字段查询
  - 员工 `entry_time` 同步使用 `to_char()` 格式化，与 coach 分支保持一致
- 修改结果：tenant_admin 可看到所有活跃店铺（含新建的）；时间显示干净无冗余；助教姓名格式符合用户预期。前端无需改动，后端返回值已包含格式化结果

### `apps/backend/docs/API-REFERENCE.md`
- 变更类型：修改
- 简要说明：同步更新接口文档，反映 `list_my_sites`、`list_applications`、`list_site_staff` 的返回值变更

### `NeoZQYY.code-workspace`
- 变更类型：修改
- 简要说明：workspace 配置调整，非逻辑变更

## 本次对话文件变更

### 新增文件
- `docs/audit/changes/2026-03-23__review-modal-phone-display-auto-match-identity-label.md`
- `docs/audit/prompt_logs/prompt_log_20260323_171648.md`
- `docs/audit/session_logs/2026-03/23/19_abb5b8d0_165704/main_01_84d2f92d.md`

### 删除文件
- `docs/audit/session_logs/2026-03/23/19_abb5b8d0_165704/main_01_2f435f10.md`（被 84d2f92d supersede）

## 合规检查

| 检查项 | 状态 |
|--------|------|
| 新增迁移 SQL | 无 |
| DDL 基线 | 不涉及 |
| OpenAPI spec | ⚠️ 接口代码已变更但 OpenAPI spec 未同步（见下方说明） |
| DB 文档 | 不涉及 |

### OpenAPI Spec 同步
接口代码（`tenant_users.py`）已变更，`compliance.openapi_spec_stale` 为 true。需手动执行：
```bash
python scripts/ops/_export_openapi.py
```
导出成功后重连 OpenAPI Power 的 MCP server 以加载新 spec。

## 回滚
- `list_my_sites`：恢复为只查 `managed_site_ids`
- 时间格式：`to_char(...)` → `ua.created_at::text`
- 姓名格式：移除 `nickname` 字段和 `_format_display_name` 函数，恢复 `real_name` 直接赋值
