From 5ceb3ce7498019817f5ba75857c451f8fa2a5fb8 Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Tue, 11 Aug 2026 18:54:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(ECR-010):=20Ops-E=20=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E8=BF=90=E8=90=A5=EF=BC=9B=E4=BF=AE=E5=A4=8D=E7=99=BB=E5=87=BA?= =?UTF-8?q?=E8=A7=A3=E7=BB=91=EF=BC=9BP2=20Complete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 落地管理员 RBAC/封禁/推送任务 stub,logout 解绑 device 并统一各页 ensureAccount,同时收口 P2 生日生成与状态文档。 Co-authored-by: Cursor --- .ai/definition-of-done.md | 3 +- .ai/product/README.md | 3 +- .ai/product/STRATEGY.md | 5 +- .ai/product/feature-map.md | 11 +- .ai/product/feature-spec/P2-BACKLOG.md | 11 +- .ai/product/feature-spec/README.md | 12 +- .ai/product/feature-spec/account-auth.md | 1 + .ai/product/feature-spec/companion.md | 6 +- .ai/product/feature-spec/image-card.md | 9 +- .ai/product/feature-spec/life-rhythm.md | 4 +- .ai/product/feature-spec/ops-admin.md | 4 +- .ai/product/feature-spec/ops-system.md | 170 +++++++++++ .ai/product/feature-spec/star-profile.md | 2 +- .ai/product/p1-status.md | 2 +- .ai/product/p2-status.md | 55 ++++ .gates/ECR-010-report.json | 20 ++ apps/admin-h5/src/api/client.ts | 37 ++- apps/admin-h5/src/layouts/AdminShell.vue | 6 +- apps/admin-h5/src/pages/AdminsPage.vue | 68 +++++ apps/admin-h5/src/pages/ContentPage.vue | 13 +- apps/admin-h5/src/pages/PushJobsPage.vue | 97 ++++++ apps/admin-h5/src/pages/UserDetailPage.vue | 30 +- apps/admin-h5/src/router/index.ts | 12 +- apps/admin-h5/src/stores/auth.ts | 12 +- apps/api/internal/handler/admin.go | 13 + apps/api/internal/handler/admin_content.go | 8 + apps/api/internal/handler/admin_system.go | 201 +++++++++++++ apps/api/internal/handler/auth.go | 3 +- .../internal/integration/auth_logout_test.go | 25 ++ .../internal/integration/ops_system_test.go | 197 +++++++++++++ apps/api/internal/middleware/device.go | 31 ++ .../internal/repository/admin_ops_system.go | 276 ++++++++++++++++++ apps/api/internal/repository/admin_repo.go | 15 +- apps/api/internal/repository/auth_repo.go | 27 ++ apps/api/internal/service/admin/content.go | 6 + apps/api/internal/service/admin/service.go | 14 +- apps/api/internal/service/admin/system.go | 154 ++++++++++ apps/api/internal/service/auth/service.go | 13 +- .../api/migrations/000016_ops_system.down.sql | 7 + apps/api/migrations/000016_ops_system.up.sql | 34 +++ apps/user-h5/e2e/p2-explore-paths.spec.ts | 8 + apps/user-h5/e2e/portrait-main-path.spec.ts | 24 +- apps/user-h5/src/composables/useAskPage.ts | 8 +- .../src/composables/useImageCardPage.ts | 6 + .../src/composables/useLifeRhythmPage.ts | 2 +- .../src/composables/usePortraitPage.ts | 2 +- .../src/composables/useRelationPage.ts | 5 + apps/user-h5/src/composables/useReportPage.ts | 8 +- apps/user-h5/src/composables/useScalePage.ts | 8 +- .../src/composables/useStarProfilePage.ts | 2 +- .../src/composables/useSynastryPage.ts | 2 + apps/user-h5/src/lib/authSession.ts | 2 +- apps/user-h5/src/pages/AskPage.spec.ts | 6 +- apps/user-h5/src/pages/CompanionPage.spec.ts | 12 +- apps/user-h5/src/pages/CompanionPage.vue | 6 + .../user-h5/src/pages/ExploreCategoryPage.vue | 8 +- apps/user-h5/src/pages/ExplorePage.vue | 9 + apps/user-h5/src/pages/GrowthPlanPage.vue | 8 + apps/user-h5/src/pages/ImageCardPage.spec.ts | 6 +- apps/user-h5/src/pages/LifeRhythmPage.spec.ts | 13 +- apps/user-h5/src/pages/LoginPage.vue | 11 +- apps/user-h5/src/pages/MembershipPage.spec.ts | 6 +- apps/user-h5/src/pages/MembershipPage.vue | 9 + apps/user-h5/src/pages/MinePage.vue | 19 +- apps/user-h5/src/pages/PortraitPage.spec.ts | 9 +- apps/user-h5/src/pages/ProfilePage.spec.ts | 8 +- apps/user-h5/src/pages/RelationPage.spec.ts | 6 +- apps/user-h5/src/pages/ReportsPage.vue | 8 + apps/user-h5/src/pages/ScalePage.spec.ts | 5 +- .../user-h5/src/pages/StarProfilePage.spec.ts | 9 +- apps/user-h5/src/pages/SynastryInvitePage.vue | 6 + apps/user-h5/src/pages/SynastryPage.spec.ts | 4 +- docs/BACKEND_DESIGN/BD-2026-010-ops-system.md | 45 +++ docs/CODE_REVIEW/ECR-010.md | 32 ++ docs/ECR/ECR-010-ops-system.md | 49 ++++ docs/ENGINEERING_SPEC/ECR-010-ops-system.md | 6 + docs/HANDOFF/ECR-010-architect-to-engineer.md | 9 + docs/HANDOFF/ECR-010-engineer-to-reviewer.md | 9 + docs/PRODUCT_SPEC/ECR-010-ops-system.md | 24 ++ docs/PROJECT_PROFILE.md | 7 +- docs/STATE/ECR-010.md | 14 + docs/TASKS/TASK-010-ECR010.yaml | 9 + docs/TEST_REPORT/ECR-010.md | 30 ++ docs/TRACEABILITY.md | 1 + proto/openapi.yaml | 104 ++++++- 85 files changed, 2098 insertions(+), 103 deletions(-) create mode 100644 .ai/product/feature-spec/ops-system.md create mode 100644 .ai/product/p2-status.md create mode 100644 .gates/ECR-010-report.json create mode 100644 apps/admin-h5/src/pages/AdminsPage.vue create mode 100644 apps/admin-h5/src/pages/PushJobsPage.vue create mode 100644 apps/api/internal/handler/admin_system.go create mode 100644 apps/api/internal/integration/auth_logout_test.go create mode 100644 apps/api/internal/integration/ops_system_test.go create mode 100644 apps/api/internal/repository/admin_ops_system.go create mode 100644 apps/api/internal/service/admin/system.go create mode 100644 apps/api/migrations/000016_ops_system.down.sql create mode 100644 apps/api/migrations/000016_ops_system.up.sql create mode 100644 docs/BACKEND_DESIGN/BD-2026-010-ops-system.md create mode 100644 docs/CODE_REVIEW/ECR-010.md create mode 100644 docs/ECR/ECR-010-ops-system.md create mode 100644 docs/ENGINEERING_SPEC/ECR-010-ops-system.md create mode 100644 docs/HANDOFF/ECR-010-architect-to-engineer.md create mode 100644 docs/HANDOFF/ECR-010-engineer-to-reviewer.md create mode 100644 docs/PRODUCT_SPEC/ECR-010-ops-system.md create mode 100644 docs/STATE/ECR-010.md create mode 100644 docs/TASKS/TASK-010-ECR010.yaml create mode 100644 docs/TEST_REPORT/ECR-010.md diff --git a/.ai/definition-of-done.md b/.ai/definition-of-done.md index 41e72ea..6bd4701 100644 --- a/.ai/definition-of-done.md +++ b/.ai/definition-of-done.md @@ -180,8 +180,9 @@ Level 0 alone ≠ Backend/Frontend Done。 | **Vertical Slice Demo** | 主路径可演示;缺测/缺态/缺功能仍可存在 | | **Feature Complete** | 单功能满足本文件 §1–7 | | **P1 Complete** | feature-map P1 必做项全部 Feature Complete + L2 三流 + 至少 1 条 L3(或书面豁免经用户同意) | +| **P2 Complete** | feature-map P2 范围全部 Feature Complete + P2 L2 流 + 至少 1 条 P2 L3(真支付除外,见 feature-map) | -当前仓库默认状态见:[product/p1-status.md](product/p1-status.md)。 +当前仓库默认状态见:[product/p1-status.md](product/p1-status.md) · [product/p2-status.md](product/p2-status.md)。 --- diff --git a/.ai/product/README.md b/.ai/product/README.md index 2f72e33..36173b3 100644 --- a/.ai/product/README.md +++ b/.ai/product/README.md @@ -4,6 +4,7 @@ |---|---| | [ENGINEERING-FREEZE.md](ENGINEERING-FREEZE.md) | **P1 冻结清单** | | [p1-status.md](p1-status.md) | **P1 完成度(当前:P1 Complete)** | +| [p2-status.md](p2-status.md) | **P2 完成度(当前:P2 Complete)** | | [lexicon.md](lexicon.md) | 产品语言契约(最高优先级) | | [feature-design.md](feature-design.md) | **Feature Spec 强制规范** | | [feature-spec/](feature-spec/README.md) | **功能详细设计(单功能 HOW)** | @@ -21,4 +22,4 @@ 开发链路:`feature-map` →(对标时)逆向 STEP 1–18 → `feature-spec` → domain/OpenAPI → 实现 → 测试 → Review。 -P2 探索三模块(设计):[feature-spec/P2-BACKLOG.md](feature-spec/P2-BACKLOG.md) · 星象性格 / 身心节律 / 意象卡片。 +P2 探索收口:[p2-status.md](p2-status.md) · [feature-spec/P2-BACKLOG.md](feature-spec/P2-BACKLOG.md) · 星座 / 身心节律 / 意象卡片。 diff --git a/.ai/product/STRATEGY.md b/.ai/product/STRATEGY.md index aaae775..5ba45fc 100644 --- a/.ai/product/STRATEGY.md +++ b/.ai/product/STRATEGY.md @@ -30,11 +30,10 @@ ## 执行分期(与 feature-map 对齐) 1. **P1** — 档案 · 性格探索/画像 · 人格测评 · 关系理解 · 问答 · 深度版 · 会员 · 分享 · 埋点 -2. **P2** — 星象性格 · 身心节律 · 意象卡片 · 节气陪伴深化 · 心情记录 · 成长计划 +2. **P2** — 星象性格 · 身心节律 · 意象卡片 · 节气陪伴深化 · 心情记录 · 成长计划 → **P2 Complete**([p2-status.md](p2-status.md)) 3. **P3** — 长期记忆 · 成长数据库 · 真人顾问 -P2 三模块:**设计已立项**(Spec Active);**实现前**须保持 Spec 与 OpenAPI/erd 同步,另开编码切片。 -不进入 P1 Complete 判定。 +真支付仍属部署阶段,不进入 P1/P2 Complete 判定。 冲突时:**lexicon + feature-map > 历史 Vision 文档中的旧命名(解码/Unlock 等)**。 diff --git a/.ai/product/feature-map.md b/.ai/product/feature-map.md index 804f1e9..e0706bb 100644 --- a/.ai/product/feature-map.md +++ b/.ai/product/feature-map.md @@ -212,11 +212,11 @@ UI **不出现「塔罗」**。禁止神谕吉凶、恐吓话术。 **不含** 2.6–2.8;P2 三模块不计入 P1 Complete。 -### 第二阶段 `[P2]` — **合规全量探索进行中** +### 第二阶段 `[P2]` — **P2 Complete** 星象性格 · 身心节律 · 意象卡片 · 节气陪伴深化 · 心情记录 · 成长计划 · 探索三级目录 · 量表矩阵 -设计真源:`feature-spec/star-profile.md` · `life-rhythm.md` · `image-card.md` · [P2-BACKLOG.md](feature-spec/P2-BACKLOG.md) +状态真源:[p2-status.md](p2-status.md) · Spec:`feature-spec/star-profile.md` · `life-rhythm.md` · `image-card.md` · [P2-BACKLOG.md](feature-spec/P2-BACKLOG.md) 排除不变:消息/达人/UGC 广场/运势 Feed;真支付部署阶段再做。 ### 第三阶段 `[P3]` @@ -235,7 +235,8 @@ UI **不出现「塔罗」**。禁止神谕吉凶、恐吓话术。 | Phase B `[Ops]` | 行为分析:自有埋点 + 管理端「数据」看板(**ECR-007 Implemented** · Spec `ops-analytics.md`) | | Phase C `[Ops]` | 内容:首页宫格 CRUD · 测评上下架(**ECR-008 Implemented** · Spec `ops-content.md`) | | Phase D `[Ops]` | 商业加深:订单筛选 · 展示价 · 退款只读(**ECR-009** · Spec `ops-commerce.md`) | -| Phase E+ | RBAC · 封禁 · 推送(各开独立 ECR) | +| Phase E `[Ops]` | 系统:RBAC · 封禁 · 推送占位(**ECR-010** · Spec `ops-system.md`) | +| Phase F+ | 封禁细策略 · 真推送通道 · 更细权限矩阵(各开独立 ECR) | 不计入 P1 Complete;不进入五 Tab。 @@ -260,8 +261,8 @@ UI **不出现「塔罗」**。禁止神谕吉凶、恐吓话术。 | `/star` | 星座 | P2 | | `/synastry` | 合盘 | P2 | | `/synastry/invite/:token` | 合盘邀请 | P2 | -| `/rhythm` | 身心节律 | P2 draft | -| `/cards` | 意象卡片 | P2 draft | +| `/rhythm` | 身心节律 | P2 | +| `/cards` | 意象卡片 | P2 | 兼容重定向:`/decode` → `/portrait`(仅技术兼容,UI 不出现 decode 文案)。 diff --git a/.ai/product/feature-spec/P2-BACKLOG.md b/.ai/product/feature-spec/P2-BACKLOG.md index 593b4b1..4ca4498 100644 --- a/.ai/product/feature-spec/P2-BACKLOG.md +++ b/.ai/product/feature-spec/P2-BACKLOG.md @@ -1,6 +1,6 @@ # P2 Backlog — 探索三模块 + 合规全量目录 -**状态:** 合规全量探索进行中(Wave 0–4) +**状态:`P2 Complete`(见 [../p2-status.md](../p2-status.md))** **命名决策:** 星座 / 身心节律 / 意象卡片;UI 无「塔罗」主入口、无「算命/占卜」恐吓 **契约:** [../lexicon.md](../lexicon.md) · [../feature-map.md](../feature-map.md) · [../STRATEGY.md](../STRATEGY.md) @@ -13,11 +13,11 @@ | 顺序 | 模块 | Spec | 路由 | 状态 | |---|---|---|---|---| | 0 | 探索目录 | — | `/explore` · `/explore/:category` | Done | -| 1 | 星座 | [star-profile.md](star-profile.md) | `/star` | Done(星盘/运势/合盘对齐中) | -| 2 | 身心节律 | [life-rhythm.md](life-rhythm.md) | `/rhythm` | Done(今日/本周) | +| 1 | 星座 | [star-profile.md](star-profile.md) | `/star` · `/synastry` | Done | +| 2 | 身心节律 | [life-rhythm.md](life-rhythm.md) | `/rhythm` | Done | | 3 | 意象卡片 | [image-card.md](image-card.md) | `/cards` | Done(≥78 牌 · ≥8 场景) | | 4 | 量表矩阵 | [explore-test.md](explore-test.md) | `/scales/*` | Done(≥8) | -| 5 | 成长计划 | companion | `/growth-plan` | Done | +| 5 | 成长计划 | companion / growth | `/growth-plan` | Done | | 6 | 心情轨迹 | companion | `/companion` | Done | --- @@ -28,4 +28,5 @@ |---|---| | 设计三 Spec | Done | | 探索 catalog API | Done | -| 真支付 | 部署阶段 | +| L0–L3 验证 | Done(见 p2-status) | +| 真支付 | 部署阶段(不挡 P2 Complete) | diff --git a/.ai/product/feature-spec/README.md b/.ai/product/feature-spec/README.md index af03fd4..78cb3c3 100644 --- a/.ai/product/feature-spec/README.md +++ b/.ai/product/feature-spec/README.md @@ -15,19 +15,21 @@ | [membership.md](membership.md) | 深度版 / 成长会员 | §6 · §5.3 | `/membership` | P1 | | [reports.md](reports.md) | 成长报告列表/详情 | §2.1.2 · §5.2 | `/reports` · `/reports/:id` | P1 | | [share.md](share.md) | 分享卡 | journey 双引擎 | `/share` | P1 | -| [companion.md](companion.md) | 陪伴(节气壳) | §4 | `/companion` | P1 壳 / P2 | +| [companion.md](companion.md) | 陪伴(节气+心情) | §4 | `/companion` | P2 Complete | | [analytics.md](analytics.md) | 增长埋点最小集 | 横切 | H5 `track` | P1 | -| [star-profile.md](star-profile.md) | 星象性格 | §2.6 | `/star` | P2 设计 | -| [life-rhythm.md](life-rhythm.md) | 身心节律 | §2.7 | `/rhythm` | P2 设计 | -| [image-card.md](image-card.md) | 意象卡片 | §2.8 | `/cards` | P2 设计 | +| [star-profile.md](star-profile.md) | 星象性格 | §2.6 | `/star` | P2 Complete | +| [life-rhythm.md](life-rhythm.md) | 身心节律 | §2.7 | `/rhythm` | P2 Complete | +| [image-card.md](image-card.md) | 意象卡片 | §2.8 | `/cards` | P2 Complete | | [ops-admin.md](ops-admin.md) | 运营后台 | §7 | `admin-h5` `/` `/users/:id` … | Ops-A | | [ops-analytics.md](ops-analytics.md) | 运营行为分析(埋点+数据看板) | §7 | `admin-h5` `/analytics` · H5 track | Ops-B · ECR-007 Implemented | | [ops-content.md](ops-content.md) | 运营内容(宫格+测评上下架) | §7 | `admin-h5` `/content` · `GET /home/tools` | Ops-C · ECR-008 Implemented | | [ops-commerce.md](ops-commerce.md) | 运营商业加深(订单筛选+展示价+退款只读) | §7 | `admin-h5` `/orders` `/pricing` | Ops-D · ECR-009 | +| [ops-system.md](ops-system.md) | 运营系统(RBAC+封禁+推送占位) | §7 | `admin-h5` `/push` `/admins` | Ops-E · ECR-010 | 新功能:复制 `_TEMPLATE.md` → 填满 → 在本表登记 → 再编码。 **P1 全量流程走查:** [P1-PROCESS-REVIEW.md](P1-PROCESS-REVIEW.md) -**P2 三模块队列(设计立项 / 编码另批):** [P2-BACKLOG.md](P2-BACKLOG.md) +**P2 收口状态:** [../p2-status.md](../p2-status.md) · [P2-BACKLOG.md](P2-BACKLOG.md) + **竞品逆向(测测前端全量):** [cece-frontend-re/](cece-frontend-re/README.md) · **完整设计包:** [cece-frontend-re/complete-design/](cece-frontend-re/complete-design/README.md) · 方法见 [../../design/reverse-engineering-spec.md](../../design/reverse-engineering-spec.md) diff --git a/.ai/product/feature-spec/account-auth.md b/.ai/product/feature-spec/account-auth.md index 2367c89..0025f75 100644 --- a/.ai/product/feature-spec/account-auth.md +++ b/.ai/product/feature-spec/account-auth.md @@ -126,6 +126,7 @@ Device 绑定到账号 User | 新设备 | 未登录 POST portrait | 401 | | 注册成功 | 建档 | 200 且生成报告包 | | 登录另一设备 | list profiles | 见同一账号数据 | +| 已登录设备 | POST logout 后无 Bearer 再 GET /auth/me | 401(设备已解绑,刷新不再显示登录态) | --- diff --git a/.ai/product/feature-spec/companion.md b/.ai/product/feature-spec/companion.md index 00d154a..134a18f 100644 --- a/.ai/product/feature-spec/companion.md +++ b/.ai/product/feature-spec/companion.md @@ -1,6 +1,6 @@ # Feature Spec: 陪伴(节气壳) -> Status: `Active`(壳) · Map: `§4 陪伴 [P2 主做;P1 可占位]` · Phase: `P1 Shell / P2` +> Status: `Active` · Map: `§4 陪伴 [P2]` · Phase: `P2 Complete` > 规范:[../feature-design.md](../feature-design.md) --- @@ -142,6 +142,6 @@ Tab 陪伴 → /companion |---|---| | Packages | `CompanionPage` · `internal/companion` · moods 表 | | 实现 | GET `/solar-terms/today` · POST `/moods` · GET `/moods/today` · H5 心情 UI | -| Gaps | 成长计划、推送、心情趋势 | -| Label | 陪伴 P2 心情已接;真支付仍后置 | +| Gaps | 推送(后置 / Ops-E) | +| Label | Feature Complete(P2:节气 + 心情 + 近七日轨迹;成长计划见 `/growth-plan`) | | Process Review | 2026-08-02 [P1-PROCESS-REVIEW](P1-PROCESS-REVIEW.md) · 心情已按产品「其他先做」落地 | diff --git a/.ai/product/feature-spec/image-card.md b/.ai/product/feature-spec/image-card.md index a4e0d32..b7138aa 100644 --- a/.ai/product/feature-spec/image-card.md +++ b/.ai/product/feature-spec/image-card.md @@ -1,6 +1,6 @@ # Feature Spec: 意象卡片 -> Status: `Active`(设计)· Map: `2.8 意象卡片 [P2]` · Phase: `P2` +> Status: `Active` · Map: `2.8 意象卡片 [P2]` · Phase: `P2 Complete` > 规范:[../feature-design.md](../feature-design.md) · 队列:[P2-BACKLOG.md](P2-BACKLOG.md) > 竞品对照:塔罗体系(能力同构;**UI 禁止「塔罗」入口名**) @@ -170,7 +170,7 @@ - [x] Spec 齐全 · 禁用塔罗入口已写清 - [x] lexicon ImageCard -- [ ] 内容库 seed 与 OpenAPI(编码时) +- [x] 内容库 seed 与 OpenAPI(编码时) --- @@ -181,5 +181,6 @@ | 复用 | ShareSheet · membership/orders · Explore 入口 | | 工作量 | 卡面文案/视觉资产最大;宜先 22~78 张小库 MVP | | Packages | `internal/imagecard` · `ImageCardPage` `/cards` | -| 实现 | scenes/quota/draw API · 报告 type=`image_card` · 日配额表 · seed 12 卡 | -| 编码顺序 | P2-BACKLOG 切片 3 Done | +| 实现 | scenes/quota/draw API · 报告 type=`image_card` · 日配额 · ≥78 牌(手写底稿+确定性扩容)· ≥8 场景 | +| Gaps | 全手写美术资产;推送 | +| Label | Feature Complete(P2) | diff --git a/.ai/product/feature-spec/life-rhythm.md b/.ai/product/feature-spec/life-rhythm.md index 11a1f60..bcdcb06 100644 --- a/.ai/product/feature-spec/life-rhythm.md +++ b/.ai/product/feature-spec/life-rhythm.md @@ -1,6 +1,6 @@ # Feature Spec: 身心节律 -> Status: `Active`(设计)· Map: `2.7 身心节律 [P2]`(= `2.5 身心探索` 产品化主入口)· Phase: `P2` +> Status: `Active` · Map: `2.7 身心节律 [P2]`(= `2.5 身心探索` 产品化主入口)· Phase: `P2 Complete` > 规范:[../feature-design.md](../feature-design.md) · 队列:[P2-BACKLOG.md](P2-BACKLOG.md) > 竞品对照:命理体系之生活向(能力同构,禁止算命/流年吉凶叙事) @@ -161,7 +161,7 @@ CTA:节气陪伴 / 问答「生活节奏」/ 分享 - [x] Spec 齐全 · 与 2.5 关系已写清 - [x] lexicon LifeRhythm -- [ ] OpenAPI/erd(编码时) +- [x] OpenAPI/erd(编码时) --- diff --git a/.ai/product/feature-spec/ops-admin.md b/.ai/product/feature-spec/ops-admin.md index 9e80f88..60059ba 100644 --- a/.ai/product/feature-spec/ops-admin.md +++ b/.ai/product/feature-spec/ops-admin.md @@ -20,9 +20,11 @@ | 用户列表 / 详情(档案摘要) | 首页运营位 / 工具配置 CMS | | 订单列表 | 优惠券 · 退款工单流 | | 成长会员只读 + **授予/延长**(mock 履约) | 真支付渠道配置 | -| 操作审计日志 | 细粒度 RBAC(多角色矩阵) | +| 操作审计日志 | 真支付渠道配置 | | `apps/admin-h5` 桌面友好壳 | 小程序后台 | +> Phase E(ECR-010):RBAC / 封禁 / 推送占位见 [ops-system.md](ops-system.md)。 + --- ## 2. 用户价值 diff --git a/.ai/product/feature-spec/ops-system.md b/.ai/product/feature-spec/ops-system.md new file mode 100644 index 0000000..563ddb2 --- /dev/null +++ b/.ai/product/feature-spec/ops-system.md @@ -0,0 +1,170 @@ +# Feature Spec: 运营系统(Ops-E) + +> Status: `Active` · Map: `§7 Phase E` · Phase: `Ops-E` · ECR: `ECR-010` +> 规范:[../feature-design.md](../feature-design.md) +> 上游:Ops-A~D(ECR-006~009) + +--- + +## 1. 功能定义 + +| 字段 | 内容 | +|---|---| +| Name | 运营系统 Ops-E | +| Purpose | 两角色 RBAC、C 端用户封禁、推送任务占位,支撑安全运维 | +| Business Goal | 限制写权限;可处置违规账号;预留推送运营入口(不下发) | + +| In | Out | +|---|---| +| `admin_accounts.role`:`super` \| `ops` | 细粒度权限矩阵 / 动态权限表 | +| 用户封禁 / 解封(`users.status` active↔banned) | 设备级 / IP 封禁 | +| `push_jobs` 草稿 CRUD(draft / cancelled) | 真推送通道 · Worker · 站内信 | +| Admin API 动作级 403 + admin-h5 导航裁剪 | UGC · 真支付 | + +--- + +## 2. 用户价值 + +1. **为何需要:** 全员等同 Admin 风险高;无封禁只能改库。 +2. **完成后获得:** ops 可查可封可建推送草稿;写会员/定价/内容仅 super。 +3. **为何付费:** N/A(内部工具)。 + +--- + +## 3. 用户角色 + +| Actor | 能力 | +|---|---| +| Admin `super` | 全部读写;Admin 账号列表与改角色 | +| Admin `ops` | 读全模块;封禁/解封;推送草稿;**不可**授予会员、改价、内容写、管 Admin | +| C 端 User `banned` | DeviceAuth / 会话拒绝(40310) | +| Guest | 无后台入口 | + +--- + +## 4. 用户流程 + +```text +Admin 登录 → Me 含 role + ↓ +ops:导航隐藏「定价写 / 内容写入口仍可见只读?按表:内容写否」 + → 用户详情 → 封禁 → C 端下次请求 40310 + → 推送页 → 新建 draft → 可 cancelled +super:另开「管理员」改 role +失败:无权限 403;未登录 401 +``` + +--- + +## 5. 页面设计 + +| 路由 | 页面 | 说明 | +|---|---|---| +| `/users/:id` | UserDetailPage | 封禁 / 解封按钮 | +| `/push` | PushJobsPage | 列表 + 新建草稿 | +| `/admins` | AdminsPage | 仅 super:列表改角色 | + +```text +AdminShell Nav += 推送 | 管理员(super) +``` + +--- + +## 6. 状态 + +| 态 | 要求 | +|---|---| +| Loading | 列表/详情请求中 | +| Empty | 无推送任务 / 无管理员(不应发生) | +| Error | 403 提示无权限;封禁失败可感知 | +| Normal | 状态与角色展示正确 | + +--- + +## 7. 业务规则 + +1. Bootstrap / 存量 admin 默认 `super`。 +2. `ops` 调用 grant / PUT plan-prices / 内容写 → HTTP 403。 +3. 封禁写 `users.status='banned'` + audit `user.ban`;解封 `active` + `user.unban`。 +4. 被封用户:Bearer 会话与 DeviceAuth 解析后均拒绝(40310);可撤销 sessions。 +5. 推送仅 `draft`/`cancelled`;创建不下发。 +6. 仅 `super` 可 `PATCH` admin role;**禁止**降级最后一个 `super`(HTTP 409 / code 40901)。 +7. 推送标题非空且 ≤128 字符(rune),否则 400。 + +--- + +## 8. 数据 + +- `admin_accounts.role` varchar CHECK (`super`,`ops`) DEFAULT `super` +- `users.status` CHECK (`active`,`banned`)(存量补齐) +- `push_jobs(id, title, body, audience, status, created_by, created_at, updated_at)` + +--- + +## 9. API + +| Method | Path | 说明 | +|---|---|---| +| GET | `/api/v1/admin/me` | 响应含 `role` | +| POST | `/api/v1/admin/users/{id}/ban` | super+ops | +| POST | `/api/v1/admin/users/{id}/unban` | super+ops | +| GET | `/api/v1/admin/admins` | super | +| PATCH | `/api/v1/admin/admins/{id}` | super · body `{role}` | +| GET | `/api/v1/admin/push-jobs` | super+ops | +| POST | `/api/v1/admin/push-jobs` | super+ops · draft | +| PATCH | `/api/v1/admin/push-jobs/{id}` | 改文案或 cancelled | + +既有写接口对 `ops` 返回 403。 + +--- + +## 10. 埋点 + +N/A(内部);审计日志覆盖封禁与角色变更。 + +--- + +## 11. 事件 + +| Audit action | 何时 | +|---|---| +| `user.ban` / `user.unban` | 封禁变更 | +| `admin.role_update` | 改角色 | +| `push_job.create` / `push_job.update` | 推送占位 | + +--- + +## 12. 验收 + +**Given** ops 账号 +**When** PUT plan-prices 或 grant membership +**Then** 403 + +**Given** 用户被 ban +**When** C 端带该 user 的 DeviceAuth 请求 +**Then** 40310 + +**Given** 唯一 super +**When** PATCH role=ops +**Then** 40901,角色不变 + +**Given** push title 超 128 字 +**When** POST push-jobs +**Then** 40055 + +--- + +## 13. AI 开发前检查 + +- [x] Spec §4/7/9/12 +- [x] ECR-010 · BD-2026-010 + +--- + +## 14. Implementation Notes + +| 项 | 内容 | +|---|---| +| Packages | `middleware` · `service/admin` · `AdminRepo` · admin-h5 | +| Migration | `000016_ops_system` | +| Label | Feature Complete(Ops-E MVP) | diff --git a/.ai/product/feature-spec/star-profile.md b/.ai/product/feature-spec/star-profile.md index cfd6816..76ca44f 100644 --- a/.ai/product/feature-spec/star-profile.md +++ b/.ai/product/feature-spec/star-profile.md @@ -1,6 +1,6 @@ # Feature Spec: 星座 -> Status: `Active` · Map: `2.6 星座 [P2]` · Phase: `P2` +> Status: `Active` · Map: `2.6 星座 [P2]` · Phase: `P2 Complete` > 规范:[../feature-design.md](../feature-design.md) · 队列:[P2-BACKLOG.md](P2-BACKLOG.md) > 竞品对照:测测星座体系(本命排盘 · 运势大全 · 连线合盘) diff --git a/.ai/product/p1-status.md b/.ai/product/p1-status.md index 1d2bf78..113f1d8 100644 --- a/.ai/product/p1-status.md +++ b/.ai/product/p1-status.md @@ -41,7 +41,7 @@ P1 必做(feature-map):用户档案 · 性格探索/个人画像 · 人格 | 探索测试草稿恢复 | `scaleDraft` localStorage | | L0–L3 | 全绿(收口时) | -P2 设计队列:[`feature-spec/P2-BACKLOG.md`](feature-spec/P2-BACKLOG.md) +P2 收口:[`p2-status.md`](p2-status.md)(**P2 Complete**)· [`feature-spec/P2-BACKLOG.md`](feature-spec/P2-BACKLOG.md) --- diff --git a/.ai/product/p2-status.md b/.ai/product/p2-status.md new file mode 100644 index 0000000..b5e65de --- /dev/null +++ b/.ai/product/p2-status.md @@ -0,0 +1,55 @@ +# P2 Status(对照 Definition of Done) + +**当前标签:`P2 Complete`** + +判定依据:`.ai/definition-of-done.md` §9 +P2 范围(feature-map):星座 · 身心节律 · 意象卡片 · 探索三级目录 · 量表矩阵 · 节气陪伴深化 · 心情记录 · 成长计划 + +**不含:** 真支付网关 · 消息/达人/UGC · 独立运势 Feed · 推送 · P3 长期记忆/顾问 + +--- + +## Review Report + +- Feature: P2 合规全量探索收口 +- Scope label: **P2 Complete** +- Architecture: PASS — handler → service → repository;`star` / `rhythm` / `imagecard` / `companion` / `growth` 独立包 +- API: PASS — `proto/openapi.yaml` 覆盖 star / synastry / rhythm / image-cards / explore catalog / moods / growth plans +- Security: PASS — report detail 服务端按 DeepAccess 裁剪;配额与归属校验在服务端 +- Test (L0/L1/L2/L3): PASS + - L0: `go test ./...` · `npm run build:h5` · `npm run test:h5` + - L1: star / synastry / rhythm / imagecard / explore + - L2: `internal/integration` — p2_flows · synastry · explore_catalog · growth checkin + - L3: Playwright `e2e/p2-explore-paths.spec.ts` + `e2e/portrait-main-path.spec.ts` +- Frontend states (L/E/E/N): PASS — 主路径页具备加载/空/错/正常;深度版 Locked +- Documentation: PASS — Spec Active + P2-BACKLOG Done + 本文件 +- Environment: PASS — `commands.md` 本机 Go/Vite + compose.dev 仅 DB +- Known Issues: + - 真支付仍为 mock(部署阶段) + - 意象卡库:手写底稿 + 确定性扩至 ≥78(非全手写美术) + - 推送未做(Ops-E / 后置) + - Ask 无 key 时规则引擎降级 + +--- + +## 本轮收口变更 + +| 项 | 结果 | +|---|---| +| 生日表单生成误用 `getLatestReport` | 已改回 `createPortrait` / `createStar` / `createRhythm` | +| H5 单测 auth 门禁 mock | PASS(38) | +| L3 e2e auth/profiles mock | PASS(2) | +| 状态真源对齐 | P2-BACKLOG · feature-map · Spec 头 · DoD §9 | + +--- + +## 验收路径 + +探索目录 → `/star`(星盘/运势/合盘)→ `/rhythm` → `/cards` → `/companion`(心情+近七日)→ `/growth-plan` → 探索量表 + +```bash +npm run test:api +npm run test:api:integration +npm run test:h5 +npm run build:h5 && npm run test:e2e +``` diff --git a/.gates/ECR-010-report.json b/.gates/ECR-010-report.json new file mode 100644 index 0000000..1a68428 --- /dev/null +++ b/.gates/ECR-010-report.json @@ -0,0 +1,20 @@ +{ + "ecr": "ECR-010", + "result": "PASS", + "ess_version": "v1.0", + "gate_version": "0.1.2", + "project_profile": "2026.08", + "checks": { + "artifact": true, + "role_boundary": true, + "backend_boundary": true, + "traceability": true, + "tests": true + }, + "violations": [], + "errors": [], + "warnings": [ + "Code changed but CHANGELOG.md not modified \u2014 required for L2 Done" + ], + "timestamp": "2026-08-11T10:20:52Z" +} diff --git a/apps/admin-h5/src/api/client.ts b/apps/admin-h5/src/api/client.ts index 792fe89..873afbf 100644 --- a/apps/admin-h5/src/api/client.ts +++ b/apps/admin-h5/src/api/client.ts @@ -94,24 +94,57 @@ export type UserDetail = { }> } +export type AdminRole = 'super' | 'ops' + +export type AdminMe = { id: string; username: string; role: AdminRole } + +export type PushJob = { + id: string + title: string + body: string + audience: string + status: 'draft' | 'cancelled' + created_by: string + created_at: string + updated_at: string +} + +export type AdminListItem = { + id: string + username: string + role: AdminRole + status: string + created_at: string +} + export const adminApi = { login: (username: string, password: string) => - request<{ token: string; admin: { id: string; username: string } }>('POST', '/auth/login', { + request<{ token: string; admin: AdminMe }>('POST', '/auth/login', { username, password, }), logout: () => request<{ ok: boolean }>('POST', '/auth/logout'), - me: () => request<{ id: string; username: string }>('GET', '/me'), + me: () => request('GET', '/me'), stats: () => request('GET', '/stats'), users: (q = '') => request<{ items: UserListItem[] }>('GET', `/users?q=${encodeURIComponent(q)}`), user: (id: string) => request('GET', `/users/${id}`), + banUser: (id: string) => request<{ ok: boolean }>('POST', `/users/${id}/ban`), + unbanUser: (id: string) => request<{ ok: boolean }>('POST', `/users/${id}/unban`), grant: (id: string, plan: string) => request<{ ok: boolean }>('POST', `/users/${id}/membership/grant`, { plan }), grantAskQuota: (id: string, delta: number) => request<{ ok: boolean; ask_paid_quota_left: number }>('POST', `/users/${id}/ask-quota/grant`, { delta, }), + admins: () => request<{ items: AdminListItem[] }>('GET', '/admins'), + patchAdminRole: (id: string, role: AdminRole) => + request<{ ok: boolean }>('PATCH', `/admins/${id}`, { role }), + pushJobs: () => request<{ items: PushJob[] }>('GET', '/push-jobs'), + createPushJob: (body: { title: string; body?: string; audience?: string }) => + request('POST', '/push-jobs', body), + patchPushJob: (id: string, body: { title?: string; body?: string; status?: 'draft' | 'cancelled' }) => + request('PATCH', `/push-jobs/${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 1c3eac9..c25a3d1 100644 --- a/apps/admin-h5/src/layouts/AdminShell.vue +++ b/apps/admin-h5/src/layouts/AdminShell.vue @@ -29,11 +29,13 @@ async function onLogout() { 内容 用户 订单 - 定价 + 定价 + 推送 + 管理员 审计
- {{ auth.username || '管理员' }} + {{ auth.username || '管理员' }} · {{ auth.role }}
diff --git a/apps/admin-h5/src/pages/AdminsPage.vue b/apps/admin-h5/src/pages/AdminsPage.vue new file mode 100644 index 0000000..53c3cc7 --- /dev/null +++ b/apps/admin-h5/src/pages/AdminsPage.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/apps/admin-h5/src/pages/ContentPage.vue b/apps/admin-h5/src/pages/ContentPage.vue index 2f124fa..39f6ad0 100644 --- a/apps/admin-h5/src/pages/ContentPage.vue +++ b/apps/admin-h5/src/pages/ContentPage.vue @@ -1,7 +1,9 @@ + +