From b3fe4363fd90791af445e4ec8851495898a44edb Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Thu, 13 Aug 2026 21:56:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(ECR-046):=20ScaleDefinition=20=E5=85=83?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=86=99=E9=9D=A2=E9=97=AD=E7=8E=AF=E5=B9=B6?= =?UTF-8?q?=20Closed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit explore.write POST/PUT · create→draft · status 仍 ECR-008 · migration 000055 · Loop STOP Co-authored-by: Cursor --- .ai/product/feature-spec/README.md | 3 +- .../ops-scale-definition-write.md | 68 ++++++++ .../feature-spec/ops-scale-definition.md | 1 + apps/admin-h5/src/api/client.ts | 37 ++++- apps/admin-h5/src/layouts/AdminShell.vue | 1 + .../src/pages/ScaleDefinitionPage.vue | 156 ++++++++++++++++++ apps/admin-h5/src/router/index.ts | 6 + .../internal/handler/admin_explore_scales.go | 65 ++++++++ .../scale_definition_write_test.go | 116 +++++++++++++ .../repository/scale_definition_write.go | 106 ++++++++++++ .../service/admin/scale_definition_write.go | 81 +++++++++ ...000055_ops_scale_definition_write.down.sql | 2 + .../000055_ops_scale_definition_write.up.sql | 7 + .../BD-2026-046-scale-definition-write.md | 22 +++ docs/CHANGELOG.md | 3 + docs/CODE_REVIEW/ECR-046.md | 3 + docs/CONTRACT_DIFF/ECR-046.yaml | 20 +++ docs/ECR/ECR-046-scale-definition-write.md | 20 +++ docs/HANDOFF/ECR-046-architect-to-engineer.md | 5 + docs/HANDOFF/ECR-046-engineer-to-reviewer.md | 4 + .../ECR-046-scale-definition-write.md | 3 + docs/PROJECT_PROFILE.md | 10 +- docs/STATE/ECR-046.md | 7 + docs/TEST_REPORT/ECR-046.md | 11 ++ docs/TRACEABILITY.md | 5 +- docs/WAVE0/ECR-046-CANDIDATE_REVIEW.md | 58 +++++++ docs/WAVE0/ECR-046_WRITE_AUTHORIZATION.md | 21 +++ proto/openapi.yaml | 37 +++++ 28 files changed, 868 insertions(+), 10 deletions(-) create mode 100644 .ai/product/feature-spec/ops-scale-definition-write.md create mode 100644 apps/admin-h5/src/pages/ScaleDefinitionPage.vue create mode 100644 apps/api/internal/integration/scale_definition_write_test.go create mode 100644 apps/api/internal/repository/scale_definition_write.go create mode 100644 apps/api/internal/service/admin/scale_definition_write.go create mode 100644 apps/api/migrations/000055_ops_scale_definition_write.down.sql create mode 100644 apps/api/migrations/000055_ops_scale_definition_write.up.sql create mode 100644 docs/BACKEND_DESIGN/BD-2026-046-scale-definition-write.md create mode 100644 docs/CODE_REVIEW/ECR-046.md create mode 100644 docs/CONTRACT_DIFF/ECR-046.yaml create mode 100644 docs/ECR/ECR-046-scale-definition-write.md create mode 100644 docs/HANDOFF/ECR-046-architect-to-engineer.md create mode 100644 docs/HANDOFF/ECR-046-engineer-to-reviewer.md create mode 100644 docs/PRODUCT_SPEC/ECR-046-scale-definition-write.md create mode 100644 docs/STATE/ECR-046.md create mode 100644 docs/TEST_REPORT/ECR-046.md create mode 100644 docs/WAVE0/ECR-046-CANDIDATE_REVIEW.md create mode 100644 docs/WAVE0/ECR-046_WRITE_AUTHORIZATION.md diff --git a/.ai/product/feature-spec/README.md b/.ai/product/feature-spec/README.md index 6e14337..03e8da0 100644 --- a/.ai/product/feature-spec/README.md +++ b/.ai/product/feature-spec/README.md @@ -57,7 +57,8 @@ | [ops-image-card-deck-write.md](ops-image-card-deck-write.md) | ImageCardDeck **写面** | §7 | admin 写 · `GET /cards/decks` | ExploreConfig · **ECR-045** | | [ops-report-template.md](ops-report-template.md) | GrowthInsights ReportTemplate | §7 | `/admin/growth/report-templates*` | Ops · **ECR-038** | | [ops-funnel-definition.md](ops-funnel-definition.md) | GrowthInsights FunnelDefinition | §7 | `/admin/analytics/funnel-definitions*` | Ops · **ECR-039** | -| [ops-scale-definition.md](ops-scale-definition.md) | ExploreConfig ScaleDefinition | §7 | `/admin/explore/scales*` | Ops · **ECR-040** | +| [ops-scale-definition.md](ops-scale-definition.md) | ExploreConfig ScaleDefinition(只读) | §7 | `/admin/explore/scales*` | Ops · **ECR-040** | +| [ops-scale-definition-write.md](ops-scale-definition-write.md) | ScaleDefinition **写面** | §7 | admin POST/PUT · status 仍 ECR-008 | ExploreConfig · **ECR-046** | 新功能:复制 `_TEMPLATE.md` → 填满 → 在本表登记 → 再编码。 diff --git a/.ai/product/feature-spec/ops-scale-definition-write.md b/.ai/product/feature-spec/ops-scale-definition-write.md new file mode 100644 index 0000000..ca52a27 --- /dev/null +++ b/.ai/product/feature-spec/ops-scale-definition-write.md @@ -0,0 +1,68 @@ +# Feature Spec: ExploreConfig · ScaleDefinition 写面(Ops · ECR-046) + +> Status: `Active`(**Implemented · ECR-046 Closed**) +> Auth: `docs/WAVE0/ECR-046_WRITE_AUTHORIZATION.md`(**仅 ECR-046**) +> Predecessor: ECR-040 只读 · ECR-008 status PATCH · ECR-045 Closed +> 关联:[ops-scale-definition.md](ops-scale-definition.md) · [ops-content.md](ops-content.md) + +## 1. 目标 + +运营可在 Explore 侧 **创建/更新量表元数据**(slug/title/description);**上下架仍走 ECR-008**。Closed 后 STOP。 + +## 2. In / Out + +| In | Out | +|----|-----| +| POST/PUT `/admin/explore/scales*` | **PATCH status**(ECR-008 独占) | +| 复用 `admin.explore.write` | 题干 / scale_questions | +| 复用表 `scales` | soft-delete · Prompt/Knowledge/Chunk | +| 创建默认 `status=draft` | 支付 · UGC · 自动 ECR-047 | +| 审计 | 新权限子系统 | + +## 3. 双通道矩阵(强制) + +| API | 权限 | 字段 | +|-----|------|------| +| `POST/PUT /admin/explore/scales*` | `admin.explore.write` | slug · title · description | +| `PATCH /admin/scales/{id}` | `admin.content.write` | **仅 status**(既有) | + +## 4. Domain + +| 字段 | 规则 | +|------|------| +| `slug` | 唯一;`^[a-z][a-z0-9-]{1,62}$` | +| `title` | 必填 · ≤128 | +| `description` | ≤512 · 可空串 | +| `status` | 创建固定 `draft`;本刀 PUT **不得**改 status | + +## 5. C 端可观察 + +| 条件 | 行为 | +|------|------| +| 新建 draft | C 端 published list **不含** | +| ECR-008 PATCH → published | C 端 list **含**(既有 `/scales`) | +| 不新增 C 端路由 | — | + +## 6. Migration + +`000054`→`000055_ops_scale_definition_write`:幂等确认 `admin.explore.write` + +## 7. UI + +admin-h5:量表元数据页(新建/编辑 slug·title·description);上下架仍在「内容」页。 + +## 8. AC + +| ID | Then | +|----|------| +| AC-F-01 | POST → list/get 可见 · status=draft | +| AC-F-02 | PUT 改 title → get 反映;status 不变 | +| AC-F-03 | 重复 slug → 409 | +| AC-F-04 | draft 不在 C 端 published list;PATCH published 后出现 | +| AC-S-01/02 | 401;仅 read → POST 403 | +| AC-A-01 | 写产生审计 | +| AC-O-01 | explore PUT 带 status 被忽略或不接受;无题干写;无 soft-delete | + +## 9. Closed 后 + +立即 STOP · 禁自动 ECR-047 diff --git a/.ai/product/feature-spec/ops-scale-definition.md b/.ai/product/feature-spec/ops-scale-definition.md index 198a204..15c75fb 100644 --- a/.ai/product/feature-spec/ops-scale-definition.md +++ b/.ai/product/feature-spec/ops-scale-definition.md @@ -1,6 +1,7 @@ # Feature Spec: ExploreConfig · ScaleDefinition(Ops · ECR-040) > Status: `Active`(Loop continuous · **ECR-040 Closed**) +> **写面:** [ops-scale-definition-write.md](ops-scale-definition-write.md)(**ECR-046 Closed**) > Parent: WAVE0-FROZEN · Predecessor: ECR-039 Closed > Capability: `ExploreConfig` · BC: `Explore_Reports` > 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md` diff --git a/apps/admin-h5/src/api/client.ts b/apps/admin-h5/src/api/client.ts index 3fb2c03..d8facbc 100644 --- a/apps/admin-h5/src/api/client.ts +++ b/apps/admin-h5/src/api/client.ts @@ -726,9 +726,42 @@ export const adminApi = { funnelDefinition: (id: string) => request>('GET', `/analytics/funnel-definitions/${id}`), exploreScales: () => - request<{ items: Array> }>('GET', '/explore/scales'), + request<{ + items: Array<{ + id: string + slug: string + title: string + description: string + status: string + }> + }>('GET', '/explore/scales'), exploreScale: (id: string) => - request>('GET', `/explore/scales/${id}`), + request<{ + id: string + slug: string + title: string + description: string + status: string + }>('GET', `/explore/scales/${id}`), + createExploreScale: (body: { slug: string; title: string; description: string }) => + request<{ + id: string + slug: string + title: string + description: string + status: string + }>('POST', '/explore/scales', body), + updateExploreScale: ( + id: string, + body: { slug: string; title: string; description: string }, + ) => + request<{ + id: string + slug: string + title: string + description: string + status: string + }>('PUT', `/explore/scales/${id}`, body), orders: (params?: { status?: string kind?: string diff --git a/apps/admin-h5/src/layouts/AdminShell.vue b/apps/admin-h5/src/layouts/AdminShell.vue index 4c8b6ff..06e740f 100644 --- a/apps/admin-h5/src/layouts/AdminShell.vue +++ b/apps/admin-h5/src/layouts/AdminShell.vue @@ -38,6 +38,7 @@ async function onLogout() { 星座配置 节律配置 意象牌组 + 量表元数据 目录仓 订单 定价 diff --git a/apps/admin-h5/src/pages/ScaleDefinitionPage.vue b/apps/admin-h5/src/pages/ScaleDefinitionPage.vue new file mode 100644 index 0000000..fb32ff5 --- /dev/null +++ b/apps/admin-h5/src/pages/ScaleDefinitionPage.vue @@ -0,0 +1,156 @@ + + +