From de224184a6cede9b82e9db9e975aa6efba0696da Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Sat, 8 Aug 2026 01:28:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(ECR-022):=20CrisisCare=20CrisisPolicy=20?= =?UTF-8?q?=E5=8F=AA=E8=AF=BB=E5=B9=B6=20Closed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 crisis_policies、admin.crisis.read、列表/试匹配 API 与 admin-h5「危机」页;禁 CrisisEvent 写/UGC/真支付。 Co-authored-by: Cursor --- .ai/domain/domain-map.md | 2 +- .ai/domain/entity-catalog.md | 2 +- .ai/product/feature-map.md | 2 +- .ai/product/feature-spec/README.md | 1 + .ai/product/feature-spec/ops-admin.md | 3 +- .ai/product/feature-spec/ops-crisis-care.md | 42 +++++++ apps/admin-h5/src/api/client.ts | 38 +++++++ apps/admin-h5/src/layouts/AdminShell.vue | 1 + apps/admin-h5/src/pages/CrisisPage.vue | 103 ++++++++++++++++++ apps/admin-h5/src/router/index.ts | 1 + apps/api/internal/handler/admin.go | 1 + apps/api/internal/handler/admin_crisis.go | 63 +++++++++++ apps/api/internal/integration/crisis_test.go | 103 ++++++++++++++++++ apps/api/internal/repository/crisis_repo.go | 102 +++++++++++++++++ apps/api/internal/service/admin/crisis.go | 39 +++++++ apps/api/internal/service/admin/rbac.go | 3 +- .../000023_crisis_policies.down.sql | 4 + .../migrations/000023_crisis_policies.up.sql | 49 +++++++++ .../BACKEND_DESIGN/BD-2026-022-crisis-care.md | 25 +++++ docs/CHANGELOG.md | 1 + docs/CODE_REVIEW/ECR-022.md | 8 ++ docs/CONTRACT_DIFF/ECR-022.yaml | 26 +++++ docs/ECR/ECR-022-crisis-care.md | 19 ++++ docs/ENGINEERING_SPEC/ECR-022-crisis-care.md | 7 ++ docs/HANDOFF/ECR-022-architect-to-engineer.md | 3 + docs/HANDOFF/ECR-022-engineer-to-reviewer.md | 3 + docs/PRODUCT_SPEC/ECR-022-crisis-care.md | 3 + docs/STATE/ECR-022.md | 6 + docs/TASKS/TASK-022-ECR022.yaml | 12 ++ docs/TEST_REPORT/ECR-022.md | 32 ++++++ docs/TRACEABILITY.md | 1 + docs/WAVE0/LOOP_AUTHORIZATION.md | 2 +- proto/openapi.yaml | 45 ++++++++ 33 files changed, 746 insertions(+), 6 deletions(-) create mode 100644 .ai/product/feature-spec/ops-crisis-care.md create mode 100644 apps/admin-h5/src/pages/CrisisPage.vue create mode 100644 apps/api/internal/handler/admin_crisis.go create mode 100644 apps/api/internal/integration/crisis_test.go create mode 100644 apps/api/internal/repository/crisis_repo.go create mode 100644 apps/api/internal/service/admin/crisis.go create mode 100644 apps/api/migrations/000023_crisis_policies.down.sql create mode 100644 apps/api/migrations/000023_crisis_policies.up.sql create mode 100644 docs/BACKEND_DESIGN/BD-2026-022-crisis-care.md create mode 100644 docs/CODE_REVIEW/ECR-022.md create mode 100644 docs/CONTRACT_DIFF/ECR-022.yaml create mode 100644 docs/ECR/ECR-022-crisis-care.md create mode 100644 docs/ENGINEERING_SPEC/ECR-022-crisis-care.md create mode 100644 docs/HANDOFF/ECR-022-architect-to-engineer.md create mode 100644 docs/HANDOFF/ECR-022-engineer-to-reviewer.md create mode 100644 docs/PRODUCT_SPEC/ECR-022-crisis-care.md create mode 100644 docs/STATE/ECR-022.md create mode 100644 docs/TASKS/TASK-022-ECR022.yaml create mode 100644 docs/TEST_REPORT/ECR-022.md diff --git a/.ai/domain/domain-map.md b/.ai/domain/domain-map.md index f5b7254..018b897 100644 --- a/.ai/domain/domain-map.md +++ b/.ai/domain/domain-map.md @@ -36,7 +36,7 @@ | Analytics_OpsB | GrowthInsights / UserIntelligence(read) | Shipped Ops-B | | Ops_Content | ExploreConfig (partial) | Shipped Ops-C | | Account_Risk | AccountLifecycle | Spec via ECR-013B(Identity_Profile owns UserStatus) | -| Content_Safety | ContentSafety · CrisisCare | ContentSafety **ECR-019**(FilterRule);CrisisCare Draft | +| Content_Safety | ContentSafety · CrisisCare | ContentSafety **ECR-019**;CrisisCare **ECR-022**(CrisisPolicy 只读) | | Ask_Ops | AskOperations · AICoreConfig | AskOperations **ECR-017/020**;AICoreConfig **ECR-021**(SystemPrompt 只读) | | Ops_CMS_NoUGC | OpsCMS | Draft | | Community | — | **Forbidden** | diff --git a/.ai/domain/entity-catalog.md b/.ai/domain/entity-catalog.md index df782e8..bc78d1a 100644 --- a/.ai/domain/entity-catalog.md +++ b/.ai/domain/entity-catalog.md @@ -30,7 +30,7 @@ Status: `Draft` | AnalyticsSession / Event | Analytics_OpsB | GrowthInsights | 已存在 Ops-B | | FilterRule | Content_Safety | ContentSafety | **ECR-019** 只读 + evaluate | | ModerationCase | Content_Safety | ContentSafety | 后置 | -| CrisisEvent / CrisisPolicy | Content_Safety | CrisisCare | 后置 | +| CrisisEvent / CrisisPolicy | Content_Safety | CrisisCare | **ECR-022** CrisisPolicy 只读;CrisisEvent 后置 | | SystemPrompt / Knowledge* / ToolDefinition | Ask_Ops | AICoreConfig | **ECR-021** SystemPrompt 只读;Knowledge/Tools 后置 | | AskSessionView / QualityFeedback | Ask_Ops | AskOperations | **ECR-017** AskSessionView;**ECR-020** QualityFeedback | | Banner / FeedSlot | Ops_CMS_NoUGC | OpsCMS | 后置 | diff --git a/.ai/product/feature-map.md b/.ai/product/feature-map.md index 0c63e2e..17c75d5 100644 --- a/.ai/product/feature-map.md +++ b/.ai/product/feature-map.md @@ -235,7 +235,7 @@ UI **不出现「塔罗」**。禁止神谕吉凶、恐吓话术。 | Phase A `[Ops]` | 登录 · 用户/订单查询 · 会员授予 · 审计 · `apps/admin-h5`(ECR-006 Closed) | | Phase B `[Ops]` | 行为分析:自有埋点 + 管理端「数据」看板(**ECR-007 Closed** · Spec `ops-analytics.md`) | | Phase C `[Ops]` | 内容:首页宫格 CRUD · 测评上下架(**ECR-008 Closed** · Spec `ops-content.md`) | -| Phase D+ | **Contract-First**:**ECR-013A…020 Closed** → **ECR-021** AICoreConfig(Loop)。`docs/WAVE0/LOOP_AUTHORIZATION.md`。 | +| Phase D+ | **Contract-First**:**ECR-013A…021 Closed** → **ECR-022** CrisisCare(Loop)。`docs/WAVE0/LOOP_AUTHORIZATION.md`。 | | 排除 | **UGC / 社区广场**(M10.2)仍 `[No]`;真支付最后 | 不计入 P1 Complete;不进入五 Tab。 diff --git a/.ai/product/feature-spec/README.md b/.ai/product/feature-spec/README.md index f3225f0..b1cf4b9 100644 --- a/.ai/product/feature-spec/README.md +++ b/.ai/product/feature-spec/README.md @@ -31,6 +31,7 @@ | [ops-content-safety.md](ops-content-safety.md) | 内容安全 ContentSafety | §7 | `admin-h5` `/safety` · `GET /admin/content-safety/*` | Ops-D · **ECR-019 Closed** | | [ops-quality-feedback.md](ops-quality-feedback.md) | 问答质量反馈 QualityFeedback | §7 | `admin-h5` `/ask` · `GET/POST /admin/ask/feedback*` | Ops-D · **ECR-020 Closed** | | [ops-ai-core-config.md](ops-ai-core-config.md) | AI 核心配置 AICoreConfig | §7 | `admin-h5` `/ai` · `GET /admin/ai/system-prompts*` | Ops-D · **ECR-021 Closed** | +| [ops-crisis-care.md](ops-crisis-care.md) | 危机关怀 CrisisCare | §7 | `admin-h5` `/crisis` · `GET /admin/crisis/policies*` | Ops-D · **ECR-022 Closed** | 新功能:复制 `_TEMPLATE.md` → 填满 → 在本表登记 → 再编码。 diff --git a/.ai/product/feature-spec/ops-admin.md b/.ai/product/feature-spec/ops-admin.md index e689fe1..aa00980 100644 --- a/.ai/product/feature-spec/ops-admin.md +++ b/.ai/product/feature-spec/ops-admin.md @@ -218,6 +218,7 @@ Phase A 可先 `console`/本地;不挡验收。 | **L(ECR-019 Closed)** | ContentSafety FilterRule — Spec `ops-content-safety.md` | | **M(ECR-020 Closed)** | QualityFeedback — Spec `ops-quality-feedback.md` | | **N(ECR-021 Closed)** | AICoreConfig SystemPrompt — Spec `ops-ai-core-config.md` | -| 后置 | CrisisCare / Knowledge·Tools 写面(Loop 续跑) | +| **O(ECR-022 Closed)** | CrisisCare CrisisPolicy — Spec `ops-crisis-care.md` | +| 后置 | Knowledge·Tools 写面 / CrisisEvent / 真支付(Loop 续跑) | | D | 订单筛选 · 展示价 · 退款只读(另开 ECR) | | 后置 | 封禁加深(Account_Risk)· 推送占位 | diff --git a/.ai/product/feature-spec/ops-crisis-care.md b/.ai/product/feature-spec/ops-crisis-care.md new file mode 100644 index 0000000..0ba759f --- /dev/null +++ b/.ai/product/feature-spec/ops-crisis-care.md @@ -0,0 +1,42 @@ +# Feature Spec: CrisisCare · CrisisPolicy(Ops · ECR-022) + +> Status: `Active`(Loop continuous · **ECR-022 Closed**) +> Parent: WAVE0-FROZEN · Predecessor: ECR-021 Closed +> Capability: `CrisisCare` · BC: `Content_Safety` +> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md` + +## Non-goals + +CrisisEvent 入库工单 · InterventionOutcome · 策略在线编辑 · 医疗诊断结论 · UGC · 真支付 · 用户侧硬熔断上线 + +## L2 Domain + +| 概念 | 语义 | +|------|------| +| `CrisisPolicy` | code 唯一;severity ∈ {high,critical};action ∈ {escalate,block,show_helpline};helpline_text 可选;本切片只读 | + +## L3 API + +| Method | Path | 权限 | 语义 | +|--------|------|------|------| +| GET | `/admin/crisis/policies` | `admin.crisis.read` | 列表 | +| GET | `/admin/crisis/policies/:id` | 同上 | 详情 | +| POST | `/admin/crisis/evaluate` | 同上 | 试匹配(不写 CrisisEvent) | + +## Migration + +`000023`:`crisis_policies` + 种子 + 授予权限 + +## L4 AC + +| ID | Then | +|----|------| +| AC-F-01 | list 含 system 种子 ≥1 | +| AC-F-02 | evaluate 命中 pattern → matches 非空 | +| AC-F-03 | 未知 id → 404 | +| AC-S-01 | 无 Admin → 401 | +| AC-S-02 | 无 crisis.read → 403 | +| AC-P-01 | list < 500ms | +| AC-O-01 | evaluate 不写 Audit/Event | + +contract_diff: `docs/CONTRACT_DIFF/ECR-022.yaml` diff --git a/apps/admin-h5/src/api/client.ts b/apps/admin-h5/src/api/client.ts index aafb0e8..69b7339 100644 --- a/apps/admin-h5/src/api/client.ts +++ b/apps/admin-h5/src/api/client.ts @@ -321,6 +321,44 @@ export const adminApi = { system: boolean updated_at: string }>('GET', `/ai/system-prompts/${id}`), + crisisPolicies: () => + request<{ + items: Array<{ + id: string + code: string + title: string + severity: string + pattern: string + action: string + helpline_text?: string + active: boolean + system: boolean + updated_at: string + }> + }>('GET', '/crisis/policies'), + crisisPolicy: (id: string) => + request<{ + id: string + code: string + title: string + severity: string + pattern: string + action: string + helpline_text?: string + active: boolean + system: boolean + updated_at: string + }>('GET', `/crisis/policies/${id}`), + evaluateCrisis: (text: string) => + request<{ + matches: Array<{ + code: string + title: string + severity: string + action: string + helpline_text?: string + }> + }>('POST', '/crisis/evaluate', { text }), orders: () => request<{ items: Array<{ diff --git a/apps/admin-h5/src/layouts/AdminShell.vue b/apps/admin-h5/src/layouts/AdminShell.vue index 4268626..e44b610 100644 --- a/apps/admin-h5/src/layouts/AdminShell.vue +++ b/apps/admin-h5/src/layouts/AdminShell.vue @@ -33,6 +33,7 @@ async function onLogout() { 问答 安全 AI + 危机 订单 审计 diff --git a/apps/admin-h5/src/pages/CrisisPage.vue b/apps/admin-h5/src/pages/CrisisPage.vue new file mode 100644 index 0000000..d5d47d1 --- /dev/null +++ b/apps/admin-h5/src/pages/CrisisPage.vue @@ -0,0 +1,103 @@ + + +