diff --git a/.ai/domain/domain-map.md b/.ai/domain/domain-map.md index c439728..82b4109 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 | Draft | +| Content_Safety | ContentSafety · CrisisCare | ContentSafety **ECR-019**(FilterRule);CrisisCare Draft | | Ask_Ops | AskOperations · AICoreConfig | AskOperations **ECR-017**(只读);AICoreConfig Draft | | Ops_CMS_NoUGC | OpsCMS | Draft | | Community | — | **Forbidden** | diff --git a/.ai/domain/entity-catalog.md b/.ai/domain/entity-catalog.md index ad9d96a..c73fd6f 100644 --- a/.ai/domain/entity-catalog.md +++ b/.ai/domain/entity-catalog.md @@ -28,7 +28,7 @@ Status: `Draft` | HomeTool | Ops_Content | ExploreConfig | 已存在 Ops-C | | ScalePublishState | Ops_Content | ExploreConfig | 已存在 | | AnalyticsSession / Event | Analytics_OpsB | GrowthInsights | 已存在 Ops-B | -| FilterRule | Content_Safety | ContentSafety | 后置 | +| FilterRule | Content_Safety | ContentSafety | **ECR-019** 只读 + evaluate | | ModerationCase | Content_Safety | ContentSafety | 后置 | | CrisisEvent / CrisisPolicy | Content_Safety | CrisisCare | 后置 | | SystemPrompt / Knowledge* / ToolDefinition | Ask_Ops | AICoreConfig | 后置 | diff --git a/.ai/product/feature-map.md b/.ai/product/feature-map.md index 7363f8e..ab00f2f 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…017 Closed** → **ECR-018** Entitlement(Loop)。`docs/WAVE0/LOOP_AUTHORIZATION.md`。 | +| Phase D+ | **Contract-First**:**ECR-013A…018 Closed** → **ECR-019** ContentSafety(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 33b8a41..af18cd7 100644 --- a/.ai/product/feature-spec/README.md +++ b/.ai/product/feature-spec/README.md @@ -28,6 +28,7 @@ | [ops-user-intelligence.md](ops-user-intelligence.md) | 用户洞察 UserIntelligence | §7 | `admin-h5` 用户详情「洞察」· `GET /admin/users/:id/insight` | Ops-D · **ECR-016 Closed** | | [ops-ask-operations.md](ops-ask-operations.md) | 问答运营 AskOperations | §7 | `admin-h5` `/ask` · `GET /admin/ask/threads*` | Ops-D · **ECR-017 Closed** | | [ops-entitlement.md](ops-entitlement.md) | 用户权益 Entitlement | §7 | `admin-h5` 用户详情「权益」· `GET /admin/users/:id/entitlements` | Ops-D · **ECR-018 Closed** | +| [ops-content-safety.md](ops-content-safety.md) | 内容安全 ContentSafety | §7 | `admin-h5` `/safety` · `GET /admin/content-safety/*` | Ops-D · **ECR-019 Closed** | 新功能:复制 `_TEMPLATE.md` → 填满 → 在本表登记 → 再编码。 diff --git a/.ai/product/feature-spec/ops-admin.md b/.ai/product/feature-spec/ops-admin.md index c43f0f5..c656b48 100644 --- a/.ai/product/feature-spec/ops-admin.md +++ b/.ai/product/feature-spec/ops-admin.md @@ -215,6 +215,7 @@ Phase A 可先 `console`/本地;不挡验收。 | **I(ECR-016 Closed)** | UserIntelligence — Spec `ops-user-intelligence.md` | | **J(ECR-017 Closed)** | AskOperations — Spec `ops-ask-operations.md` | | **K(ECR-018 Closed)** | Entitlement — Spec `ops-entitlement.md` | -| 后置 | ContentSafety / QualityFeedback / AICoreConfig(Loop 续跑) | +| **L(ECR-019 Closed)** | ContentSafety FilterRule — Spec `ops-content-safety.md` | +| 后置 | QualityFeedback / AICoreConfig / CrisisCare(Loop 续跑) | | D | 订单筛选 · 展示价 · 退款只读(另开 ECR) | | 后置 | 封禁加深(Account_Risk)· 推送占位 | diff --git a/.ai/product/feature-spec/ops-content-safety.md b/.ai/product/feature-spec/ops-content-safety.md new file mode 100644 index 0000000..fd376c0 --- /dev/null +++ b/.ai/product/feature-spec/ops-content-safety.md @@ -0,0 +1,42 @@ +# Feature Spec: ContentSafety · FilterRule(Ops · ECR-019) + +> Status: `Active`(Loop continuous · **ECR-019 Closed**) +> Parent: WAVE0-FROZEN · Predecessor: ECR-018 Closed +> Capability: `ContentSafety` · BC: `Content_Safety` +> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md` + +## Non-goals + +ModerationCase 写回 · CrisisPolicy 配置 · 真 NLP/厂商审核 · UGC · 真支付 · 用户侧硬拦截上线(本切片仅运营只读 + 试匹配) + +## L2 Domain + +| 概念 | 语义 | +|------|------| +| `FilterRule` | code 唯一;category ∈ {crisis,abuse,spam,pii};action ∈ {flag,block,escalate};system 种子不可本切片删除 | + +## L3 API + +| Method | Path | 权限 | 语义 | +|--------|------|------|------| +| GET | `/admin/content-safety/filter-rules` | `admin.content_safety.read` | 列表 | +| GET | `/admin/content-safety/filter-rules/:id` | 同上 | 详情 | +| POST | `/admin/content-safety/evaluate` | 同上 | 试匹配(不写工单) | + +## Migration + +`000020`:`filter_rules` + 种子规则 + 授予 `admin.content_safety.read` + +## L4 AC + +| ID | Then | +|----|------| +| AC-F-01 | list 含 system 种子 ≥1 | +| AC-F-02 | evaluate 命中种子 pattern → matches 非空 | +| AC-F-03 | get 未知 id → 404 | +| AC-S-01 | 无 Admin → 401 | +| AC-S-02 | 无 content_safety.read → 403 | +| AC-P-01 | list < 500ms | +| AC-O-01 | evaluate 不写 Audit(只读试匹配) | + +contract_diff: `docs/CONTRACT_DIFF/ECR-019.yaml` diff --git a/apps/admin-h5/src/api/client.ts b/apps/admin-h5/src/api/client.ts index ca3992d..e67177d 100644 --- a/apps/admin-h5/src/api/client.ts +++ b/apps/admin-h5/src/api/client.ts @@ -243,6 +243,38 @@ export const adminApi = { updated_at: string messages: Array<{ id: string; role: string; content: string; created_at: string }> }>('GET', `/ask/threads/${id}`), + filterRules: () => + request<{ + items: Array<{ + id: string + code: string + title: string + category: string + pattern: string + action: string + active: boolean + system: boolean + updated_at: string + }> + }>('GET', '/content-safety/filter-rules'), + filterRule: (id: string) => + request<{ + id: string + code: string + title: string + category: string + pattern: string + action: string + active: boolean + system: boolean + updated_at: string + }>('GET', `/content-safety/filter-rules/${id}`), + evaluateContent: (text: string) => + request<{ matches: Array<{ code: string; title: string; category: string; action: string }> }>( + 'POST', + '/content-safety/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 3d9aed1..84464b4 100644 --- a/apps/admin-h5/src/layouts/AdminShell.vue +++ b/apps/admin-h5/src/layouts/AdminShell.vue @@ -31,6 +31,7 @@ async function onLogout() { 套餐 兑换码 问答 + 安全 订单 审计 diff --git a/apps/admin-h5/src/pages/SafetyPage.vue b/apps/admin-h5/src/pages/SafetyPage.vue new file mode 100644 index 0000000..167afb7 --- /dev/null +++ b/apps/admin-h5/src/pages/SafetyPage.vue @@ -0,0 +1,100 @@ + + +