# 变更审计记录：租户用户审核 — 软删除恢复 upsert 修复

| 字段 | 值 |
|------|-----|
| 日期 | 2026-03-25 01:54:19 |
| Prompt-ID | P20260325-000112 |
| Session-ID | 4627fd68 |
| Session 路径 | docs/audit/session_logs/2026-03/24/79_de2a1755_235713 |

## 操作摘要

修复租户管理后台审核通过流程中 `ON CONFLICT DO NOTHING` 被已软删除记录阻塞的 bug。当用户曾被移除（`is_removed=true`）后重新申请并审核通过时，`user_site_roles` 写入被唯一约束上的已删除记录阻塞，导致 approved 用户无角色。改为 `DO UPDATE SET is_removed = false, removed_at = NULL` 恢复软删除记录。同步更新了前后端联调规范文档，将此模式记录为通用规则。

## 风险标签

`dir:backend`、`root-file`、`dir:admin-web`、`dir:etl`、`dir:miniprogram`、`dir:db`、`db-schema-change`

> 注：本次 session 实际只修改了 `tenant_users.py` 和联调文档。其余风险标签来自累积未审计的变更（DEMO-miniprogram 迁移、admin-web 重构、miniprogram 更新、ETL 调整等），这些变更在更早的提交中完成，本次审计一并覆盖标记清除。

## 本次对话文件变更

### 新增文件
- `docs/audit/prompt_logs/prompt_log_20260325_000112.md`
- `docs/audit/session_logs/2026-03/24/78_ec0569d7_234840/main_01_a5bb4c10.md`
- `docs/audit/session_logs/2026-03/24/79_de2a1755_235713/main_01_fe71cc2d.md`
- `docs/audit/session_logs/2026-03/24/79_de2a1755_235713/sub_01_fe71cc2d.md`

### 删除文件
- `docs/audit/session_logs/2026-03/24/78_ec0569d7_234840/main_01_df41de21.md`（被替换为新版本）

## 合规检查

- ⚠️ 接口代码已变更但 OpenAPI spec 未同步（`api_changed: true`, `openapi_spec_stale: true`）— 需手动运行 `python scripts/ops/_export_openapi.py` 重新导出
- ⚠️ DDL 基线待合并（`has_ddl_baseline: false`）— 累积变更中含 db-schema-change
- ⚠️ 文档同步缺失：`apps/backend/app/routers/tenant_users.py` 应同步更新 `apps/backend/docs/API-REFERENCE.md` 和 `docs/contracts/openapi/backend-api.json`
- ✅ 迁移 SQL：无新增迁移（`new_migration_sql` 为空）

## 改动注解

### `apps/backend/app/routers/tenant_users.py`
- 变更类型：修改
- 原始原因：用户反馈审核通过时，后端用 `applied_role_text` 匹配 `auth.roles` 失败导致 `user_site_roles` 未写入。实际问题是 `ON CONFLICT DO NOTHING` 被已软删除的记录阻塞——唯一约束 `(user_id, site_id, role_id)` 上存在 `is_removed=true` 的旧记录，INSERT 触发冲突但 DO NOTHING 跳过，导致用户审核通过后无角色。
- 思路分析：将 `approve_application` 中 `user_site_roles` 的 INSERT 语句从 `ON CONFLICT DO NOTHING` 改为 `ON CONFLICT (user_id, site_id, role_id) DO UPDATE SET is_removed = false, removed_at = NULL`。这是软删除 + 唯一约束的标准处理模式（已记录在前后端联调规范中）。不影响其他端点，不改变表结构。
- 修改结果：审核通过流程可正确恢复已软删除的角色记录。影响范围仅限 `POST /api/tenant/applications/{id}/approve` 端点。

### `docs/guides/FRONTEND-BACKEND-INTEGRATION.md`
- 变更类型：修改（简要）
- 同步更新联调规范，新增"软删除 + 唯一约束陷阱"通用规则条目。

### `.kiro/steering/frontend-backend-integration.md`
- 变更类型：修改（简要）
- steering 规则同步更新，引用联调规范中的新增条目。

## 回滚策略

代码回滚：revert `approve_application` 中的 upsert 改动，恢复为 `ON CONFLICT DO NOTHING`。
数据影响：若已有用户通过新逻辑恢复了角色，回滚后不影响已恢复的记录（`is_removed=false` 状态保持）。
