Compare commits

108 Commits
Author SHA1 Message Date
jackyu66gitandCursor 7155b8b53a feat(api): 接入微信登录并原生实现咨询域(ECR-049/050)
小程序可在 Go 上完成微信手机号登录、测评、预约和下单,不再反代 Java。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 00:26:29 +08:00
jackyu66gitandCursor db4d118f9a fix(h5): 小程序 nh=1 模式保留子页返回按钮
原生顶栏隐藏 logo 顶栏时,非首页/我的仍显示 BackButton。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 04:43:07 +08:00
jackyu66gitandCursor fc53be22f8 fix(h5): 统一 nh=1 与 sbh 顶距逻辑,与 index.html 首屏一致
applyMpEmbedDom 在原生顶栏模式读取 session sbh;补充 nh+sbh 单测。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 04:21:52 +08:00
jackyu66gitandCursor e7290ed726 fix(h5): 首屏清 title + 桃色顶区对齐小程序 nav-bar(nh=1)
index.html 在 Vue 前清 document.title 并预应用 mp-native-header;
CSS 桃色顶区、隐藏 AppHeader,减轻黑字与渐变冲顶。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 04:09:12 +08:00
jackyu66git db3669c595 Revert "fix(h5): 小程序 nh=1 原生顶栏模式与 sbh 顶距(配 202608278)"
This reverts commit 580ba85b61.
2026-08-25 03:29:37 +08:00
jackyu66gitandCursor 580ba85b61 fix(h5): 小程序 nh=1 原生顶栏模式与 sbh 顶距(配 202608278)
H5 在 nh=1 时隐藏 AppHeader,支持 sbh 控制内容顶距,首屏 inline 预应用。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 03:24:42 +08:00
jackyu66gitandCursor 57687de360 feat(h5): 小程序原生顶栏 + 本地联调与一键部署
nh=1 隐藏 H5 顶栏并由 cover 承载 logo;H5 仅 4px 顶距避免重复留白。新增 dev:mp、deploy:h5 与联调文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 01:38:32 +08:00
jackyu66gitandCursor f4bd57ce1b fix(h5): 修复 WebView 顶栏 logo 与内容重叠
embed 模式 header 改文档流、移除负 margin;持久化 sbh 并在路由中保留,避免胶囊区与档案条重叠。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 01:22:39 +08:00
jackyu66git 9d4fb52d29 fix(h5): 小程序 WebView 通过 sbh 参数铺满刘海区
WebView 内 safe-area-inset-top 常为 0,改由小程序传入 statusBarHeight;
全屏洗底层上延并同步原生页背景色,修复刘海露白。
2026-08-25 01:18:16 +08:00
jackyu66gitandCursor 96292d0e95 fix(h5): 小程序 WebView 刘海区背景与顶栏安全区
Logo 下移 safe-area、embed 隐藏 HTML 标题;暖色渐变铺满刘海,页面背景上延与全屏洗底层对齐。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 01:14:07 +08:00
jackyu66gitandCursor 635a83fe11 fix(h5): 修复小程序 WebView 内 logo 与备案号展示
Logo 改用 BASE_URL 路径并在 embed 模式始终显示顶部栏;备案号改为页面底部文档流,避免悬浮遮挡内容。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 01:04:18 +08:00
jackyu66gitandCursor ebc2d3ad80 docs: 补充 Ubuntu+MySQL 并存与 PostgreSQL 部署指南
DEPLOY-MINI-PROGRAM-H5 增加 PG 安装三种方式、Go API systemd、Checklist 与双库架构说明。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 00:46:58 +08:00
jackyu66gitandCursor 8d856fb56c feat(h5): 小程序 embed 模式与全站 ICP 备案页脚
H5 支持 ?mp=1 嵌入微信 WebView;user-h5/admin-h5 底部展示蜀ICP备2025140386号-2 并链至工信部;补充小程序+H5 部署说明。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-25 00:41:14 +08:00
jackyu66gitandCursor d33a25a663 docs(BD-2026-044): rhythm-config-write 标记 Implemented · Closed
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 22:49:28 +08:00
jackyu66gitandCursor c693ac4bbc docs(ECR-048): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 22:51:42 +08:00
jackyu66gitandCursor e0f605558b feat(ECR-048): ReportTemplate 写面闭环并 Closed
growth.write POST/PUT · migration 000057 · 无新 C 端 · Loop STOP

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 22:51:42 +08:00
jackyu66gitandCursor aa197b719b docs(ECR-047): Freeze/Review 收口 → FROZEN WAIT
单刀授权硬边界 · 禁自动 ECR-048 · origin 齐平后冻结

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 22:32:25 +08:00
jackyu66gitandCursor 580f5ea3f0 docs(ECR-047): Human authorize Closed(不含 push)
Unauthorized Scope 经 authorize ECR-047 Closed 纳入正式 Closed;Loop STOP

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 22:21:54 +08:00
jackyu66gitandCursor 69cb99600f docs(ECR-047): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 22:14:21 +08:00
jackyu66gitandCursor 1db5fcc018 feat(ECR-047): FunnelDefinition 写面闭环并 Closed
growth.write POST/PUT · migration 000056 · 无新 C 端 · Loop STOP

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 22:14:21 +08:00
jackyu66gitandCursor 258deb0184 docs(ECR-046): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 21:56:11 +08:00
jackyu66gitandCursor b3fe4363fd feat(ECR-046): ScaleDefinition 元数据写面闭环并 Closed
explore.write POST/PUT · create→draft · status 仍 ECR-008 · migration 000055 · Loop STOP

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 21:56:11 +08:00
jackyu66gitandCursor fa8b0ba963 docs(ECR-045): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 21:29:43 +08:00
jackyu66gitandCursor 0158036341 feat(ECR-045): ImageCardDeck 写面闭环并 Closed
复用 admin.explore.write、POST/PUT+审计、C端 GET /cards/decks、
H5 无 active 空态/失败回退;migration 000054。无牌面内容编辑。
ExploreConfig Loop STOP,禁自动 ECR-046。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 21:29:43 +08:00
jackyu66gitandCursor dc93999f9e docs(ECR-044): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:43:08 +08:00
jackyu66gitandCursor 87b463b2bb feat(ECR-044): RhythmConfig 写面闭环并 Closed
复用 admin.explore.write、POST/PUT+审计、C端 GET /rhythm/configs、
H5 无 active 空态/失败回退;migration 000053。ExploreConfig Loop STOP,禁自动 ECR-045。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:43:08 +08:00
jackyu66gitandCursor fcc7667ba3 docs(ECR-044): Candidate Review 批准 RhythmConfig
首刀定为 RhythmConfig;Spec/Loop/Coding 未开;Auth 草案 PENDING;
排除 Prompt/Knowledge/Chunk,不预设 SystemPrompt。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:37:35 +08:00
jackyu66gitandCursor 19091bff12 docs(ECR-043): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:24:50 +08:00
jackyu66gitandCursor 77907f79ee feat(ECR-043): StarConfig 写面闭环并 Closed
加法权限 admin.explore.write、POST/PUT+审计、C端 GET /star/configs、
H5 无 active 空态/失败回退;migration 000052。ExploreConfig Loop STOP,禁自动 ECR-044。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:23:38 +08:00
jackyu66gitandCursor 2b295472a4 docs: feature-spec 索引补 ops-star-config-write
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:08:17 +08:00
jackyu66gitandCursor f492088237 docs(ECR-043): StarConfig 写面 Spec Ready(先不写码)
Human 批准 ExploreConfig Loop 仅限 043;Closed 后 STOP 禁自动 044;边界排除 Rhythm/Card/Scale/Prompt。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:07:58 +08:00
jackyu66gitandCursor cd3ac2c9ab docs: 恢复 TRACEABILITY 中 ECR-041/042 Closed 行
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:04:47 +08:00
jackyu66gitandCursor ec20e9744e docs(ECR-043): ExploreConfig 写面候选评审;收窄 Continuous Loop
CMS 041–042 锁定 Closed;下一刀不进 SystemPrompt;推荐首刀 StarConfig;禁止直接写码。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:04:36 +08:00
jackyu66gitandCursor aff6621ff8 docs(ECR-042): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:02:01 +08:00
jackyu66gitandCursor 56f661748a feat(ECR-042): OpsCMS FeedSlot 薄写面
Admin POST/PUT 复用 admin.cms.write;C 端 GET /home/feed-slots;首页无 active 槽隐藏推荐区、失败回退展示;无新 migration。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 20:02:01 +08:00
jackyu66gitandCursor 655141980a docs(ECR-041): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 19:55:27 +08:00
jackyu66gitandCursor 9707b72808 feat(ECR-041): OpsCMS Banner 薄写面(Write-Wave 首刀)
Admin POST/PUT + admin.cms.write/审计;C 端 GET /home/banners;首页投影回退静态 homeFeeds;migration 000051;不碰 FeedSlot/支付/UGC。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 19:55:27 +08:00
jackyu66gitandCursor ad45d17dff chore(repo): 仓侧治理门禁 — ECR/migration 身份唯一
将编号与 schema 完整性留在 Repository Governance,不扩 ESS/Loop;TRACEABILITY 锚定 Next ECR=041、Max Migration=000050。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 14:26:30 +08:00
jackyu66gitandCursor d81e8364cc fix(h5): 星座分享卡置顶;首页头像进个人档案
星座 ShareSheet/落地页卡片改到上方;首页顶栏个人入口从愈心解码改为 /profile。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 02:34:58 +08:00
jackyu66gitandCursor b7b0b18802 fix(db): 重编号合并后撞号 migration,并修 admin e2e base
将 015–023 双文件冲突线性化为 015–050;提供已有库 schema_migrations 修复脚本;admin Playwright 对齐 /psy/admin/ 预览路径。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 02:14:52 +08:00
jackyu66gitandCursor 0d50c0ee73 fix(admin): 收紧 isSuper、plan-prices RBAC 与封禁状态机
避免 roles.write 绕过全部 can();定价读写挂 membership.plans 权限;去掉快捷封禁双路径并让 ban/unban 走 lifecycle。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 01:56:00 +08:00
jackyu66gitandCursor 62cd8c45dd chore: 合入 stash Ops hardening 与 migration 000041
Ask/catalog 权限与审计加固、量表读权限统一,以及未提交的 ops hardening 变更。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 01:46:34 +08:00
jackyu66gitandCursor 4889ff5916 merge: 合入本地 Ops 扩展与 origin/main(ECR-009–016)
保留远程用户侧 ECR-009–016 与本地 Ops 目录/RBAC/CMS/危机等能力;文档标注分叉期间 ECR 编号冲突。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 01:45:53 +08:00
jackyu66gitandCursor 89756f65b4 feat(ECR-012–016): 合规、题库、时辰刷新、头像、MBTI OEJTS 与埋点
落地输入合规、探索题库、报告日/时辰刷新、账号头像、OEJTS 量表,并补齐 H5 埋点与 Admin 漏斗;同步 ESS 工件、切至自建 Git、清理 GitHub Actions。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 01:27:58 +08:00
jackyu66gitandCursor 13860bf1ef feat(ECR-011): 昵称、首页贴士与档案 Self 唯一/合盘交叉校验
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s
每账号仅一条 self(migration 40902);合盘只选 TA;账号昵称可改;首页穿衣/颜色/养生贴士。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-12 22:05:00 +08:00
jackyu66gitandCursor 5ceb3ce749 feat(ECR-010): Ops-E 系统运营;修复登出解绑;P2 Complete
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s
落地管理员 RBAC/封禁/推送任务 stub,logout 解绑 device 并统一各页 ensureAccount,同时收口 P2 生日生成与状态文档。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 18:54:59 +08:00
jackyu66gitandCursor 1026cd0596 docs(ECR-040): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 03:20:42 +08:00
jackyu66gitandCursor e9c11cdf1d feat(ECR-040): ExploreConfig ScaleDefinition 只读投影并 Closed
复用 scales 表只读投影;admin-h5 /catalogs 聚合 026–040 目录;Loop 队列 STOP。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 03:20:42 +08:00
jackyu66git 432c090046 docs(ECR-039): TEST_REPORT 补 commit sha 2026-08-08 03:17:12 +08:00
jackyu66git e955f1e6a2 feat(ECR-039): GrowthInsights FunnelDefinition 只读并 Closed
FunnelDefinition catalog (000040) · Loop continuous.
2026-08-08 03:17:12 +08:00
jackyu66git 63742c1d66 docs(ECR-038): TEST_REPORT 补 commit sha 2026-08-08 03:16:57 +08:00
jackyu66git 5de9a7bbe5 feat(ECR-038): GrowthInsights ReportTemplate 只读并 Closed
ReportTemplate catalog (000039) · Loop continuous.
2026-08-08 03:16:57 +08:00
jackyu66git b34b4f281f docs(ECR-037): TEST_REPORT 补 commit sha 2026-08-08 03:16:42 +08:00
jackyu66git 5c462ecb2a feat(ECR-037): ExploreConfig ImageCardDeck 只读并 Closed
ImageCardDeck catalog (000038) · Loop continuous.
2026-08-08 03:16:42 +08:00
jackyu66git 0fa6da2446 docs(ECR-036): TEST_REPORT 补 commit sha 2026-08-08 03:16:26 +08:00
jackyu66git bd9e13497f feat(ECR-036): ExploreConfig RhythmConfig 只读并 Closed
RhythmConfig catalog (000037) · Loop continuous.
2026-08-08 03:16:26 +08:00
jackyu66git 57acfa6105 docs(ECR-035): TEST_REPORT 补 commit sha 2026-08-08 03:16:12 +08:00
jackyu66git ebc93b6c7e feat(ECR-035): ExploreConfig StarConfig 只读并 Closed
StarConfig catalog (000036) · Loop continuous.
2026-08-08 03:16:12 +08:00
jackyu66git 09b9c4daab docs(ECR-034): TEST_REPORT 补 commit sha 2026-08-08 03:15:59 +08:00
jackyu66git 45e98f6872 feat(ECR-034): AdminGovernance PrivacyRequest 只读并 Closed
PrivacyRequest catalog (000035) · Loop continuous.
2026-08-08 03:15:59 +08:00
jackyu66git aad6cdf7ea docs(ECR-033): TEST_REPORT 补 commit sha 2026-08-08 03:15:45 +08:00
jackyu66git 487bee78d1 feat(ECR-033): AskOperations HandoffCase 只读并 Closed
HandoffCase catalog (000034) · Loop continuous.
2026-08-08 03:15:45 +08:00
jackyu66git 01063208a1 docs(ECR-032): TEST_REPORT 补 commit sha 2026-08-08 03:15:30 +08:00
jackyu66git 447fb6da16 feat(ECR-032): CrisisCare InterventionOutcome 只读并 Closed
InterventionOutcome catalog (000033) · Loop continuous.
2026-08-08 03:15:30 +08:00
jackyu66git 58eef7c02b docs(ECR-031): TEST_REPORT 补 commit sha 2026-08-08 03:15:16 +08:00
jackyu66git 24a115297b feat(ECR-031): CrisisCare CrisisEvent 只读并 Closed
CrisisEvent catalog (000032) · Loop continuous.
2026-08-08 03:15:16 +08:00
jackyu66git 0d4bc5054e docs(ECR-030): TEST_REPORT 补 commit sha 2026-08-08 03:15:00 +08:00
jackyu66git b904e6b04f feat(ECR-030): ContentSafety ModerationCase 只读并 Closed
ModerationCase catalog (000031) · Loop continuous.
2026-08-08 03:15:00 +08:00
jackyu66git 271e312669 docs(ECR-029): TEST_REPORT 补 commit sha 2026-08-08 03:14:45 +08:00
jackyu66git 5846d39d97 feat(ECR-029): ContentSafety BlockPolicy 只读并 Closed
BlockPolicy catalog (000030) · Loop continuous.
2026-08-08 03:14:45 +08:00
jackyu66git e6740a0326 docs(ECR-028): TEST_REPORT 补 commit sha 2026-08-08 03:14:30 +08:00
jackyu66git 00d9ed5610 feat(ECR-028): AICoreConfig ToolDefinition 只读并 Closed
ToolDefinition catalog (000029) · Loop continuous.
2026-08-08 03:14:30 +08:00
jackyu66git 1f572c4151 docs(ECR-027): TEST_REPORT 补 commit sha 2026-08-08 03:14:17 +08:00
jackyu66git c1a8a58488 feat(ECR-027): AICoreConfig KnowledgeChunk 只读并 Closed
KnowledgeChunk catalog (000028) · Loop continuous.
2026-08-08 03:14:17 +08:00
jackyu66gitandCursor 78857d5510 docs(ECR-026): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 03:13:36 +08:00
jackyu66gitandCursor b91806ba80 feat(ECR-026): OpsCMS ScheduledPublication 只读并 Closed
定时发布目录(ops_scheduled_publications),并加固 catalog 生成器。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 03:13:36 +08:00
jackyu66gitandCursor 8c50b3d925 docs(ECR-025): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 03:08:40 +08:00
jackyu66gitandCursor e32466357d feat(ECR-025): OpsCMS FeedSlot 只读并 Closed
栏目位目录(ops_feed_slots + /cms),复用 admin.cms.read。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 03:08:40 +08:00
jackyu66gitandCursor f51b2524c5 docs(ECR-024): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 03:05:55 +08:00
jackyu66gitandCursor 32ac559385 feat(ECR-024): OpsCMS Banner 只读并 Closed
运营横幅目录(ops_banners + admin /cms),锁定 024–040 全队列。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 03:05:55 +08:00
jackyu66gitandCursor ac1aec857d docs(ECR-023): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 01:49:58 +08:00
jackyu66gitandCursor bf1ae8bc53 feat(ECR-023): AICoreConfig KnowledgeSource 只读并 Closed
运营可观测知识源目录(knowledge_sources + admin /ai),不含 Chunk/Embedding。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 01:49:58 +08:00
jackyu66gitandCursor dd4d644eaa docs(ECR-022): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 01:28:45 +08:00
jackyu66gitandCursor de224184a6 feat(ECR-022): CrisisCare CrisisPolicy 只读并 Closed
新增 crisis_policies、admin.crisis.read、列表/试匹配 API 与 admin-h5「危机」页;禁 CrisisEvent 写/UGC/真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 01:28:45 +08:00
jackyu66gitandCursor 4b5e5bc63d docs(ECR-021): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 00:27:17 +08:00
jackyu66gitandCursor 140b08ca01 feat(ECR-021): AICoreConfig SystemPrompt 只读并 Closed
新增 system_prompts 目录、admin.ai_config.read 与 admin-h5「AI」页;本切片不改运行时 Prompt、禁写发布/UGC/真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 00:27:17 +08:00
jackyu66gitandCursor 22f9e358ae docs(ECR-020): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 23:02:13 +08:00
jackyu66gitandCursor 93227d3316 feat(ECR-020): QualityFeedback 问答质量反馈并 Closed
新增 ask_quality_feedback、运营/C端评分 API 与 admin-h5 问答反馈区;禁改消息/UGC/真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 23:02:13 +08:00
jackyu66gitandCursor e270a38393 docs(ECR-019): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 22:13:23 +08:00
jackyu66gitandCursor cae3380cbf feat(ECR-019): ContentSafety FilterRule 只读并 Closed
新增 filter_rules、admin.content_safety.read、列表/详情/试匹配 API 与 admin-h5「安全」页;禁审核写/UGC/真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 22:13:22 +08:00
jackyu66gitandCursor de025d72bc docs(ECR-018): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 19:26:16 +08:00
jackyu66gitandCursor c81f57a7d1 feat(ECR-018): Entitlement 用户权益只读并 Closed
聚合 GET /admin/users/:id/entitlements(Membership∪DeepAccess∪问答额度)与 admin-h5 权益 Tab;无 migration / 无真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 19:26:15 +08:00
jackyu66gitandCursor 37b91e51b8 docs(ECR-017): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 18:39:38 +08:00
jackyu66gitandCursor 00a798bd85 feat(ECR-017): AskOperations 问答会话只读并 Closed
新增 admin.ask.read、GET /admin/ask/threads*(AskSessionView)与 admin-h5「问答」页;禁改消息/UGC/真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 18:39:37 +08:00
jackyu66gitandCursor 1c157a0e46 docs(ECR-016): TEST_REPORT 补 commit sha
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 18:31:40 +08:00
jackyu66gitandCursor 61ae3b0451 feat(ECR-016): UserIntelligence 用户洞察只读切片并 Closed
聚合 GET /admin/users/:id/insight(报告类型/派生标签/行为快照),admin-h5 洞察 Tab;无 migration / 无 UGC / 无真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 18:31:06 +08:00
jackyu66gitandCursor 1afda1d389 docs: 队列指向 ECR-016 UserIntelligence
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 18:17:06 +08:00
jackyu66gitandCursor 1eeb0b00e7 feat(ECR-015): RedemptionCode 兑换码并 Closed
批次生成/作废、C 端兑码延长会员;admin-h5 /codes。
Loop continuous。Next:ECR-016 UserIntelligence。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 18:16:13 +08:00
jackyu66gitandCursor 0e26aabef8 docs: 同步 ECR-014 Closed 与 Loop 队列指向 ECR-015
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 18:04:43 +08:00
jackyu66gitandCursor 882c01d81a feat(ECR-014): MembershipPlan 套餐配置并 Closed
membership_plans 表、admin 套餐页、Grant/CreateOrder 读表;
Loop continuous 自动 Approve/Closed。Next:ECR-015 RedemptionCode。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 18:03:38 +08:00
jackyu66gitandCursor e25cd94b0c feat(ECR-013B): AccountLifecycle Closed;启用 Loop 连续执行
UserStatus 迁移、DeviceAuth 拒绝非 active、admin-h5 CTA;
Reviewer Closed。Human 授权 LOOP_AUTHORIZATION(免逐闸确认)。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 17:47:18 +08:00
jackyu66gitandCursor f75b42397f docs(ECR-013B): 启动 AccountLifecycle 契约定义
Human Start Authorization:UserStatus 状态机 Spec/BD/contract_diff;
禁止 coding,待 Approve + coding Start Authorization。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 17:23:04 +08:00
jackyu66gitandCursor b5a05941d9 feat(ECR-013A): Admin RBAC 实现并 Closed
角色权限、RequirePermission、/me permissions 与 migration 000015;
Reviewer Approve → Closed。Next:ECR-013B Contract Definition。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 17:19:45 +08:00
jackyu66gitandCursor 85ed0901bb docs: record WAVE0 Human Review FREEZE
Human Decision=FREEZE on foundation 27f27a1; next gate ECR-013A contract only after Start Authorization.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 16:31:42 +08:00
jackyu66gitandCursor 27f27a1cb2 docs: freeze WAVE0 domain foundation and contract governance
Mark WAVE0 FROZEN CANDIDATE for Human Review; docs + .ai domain/product only — no apps or migrations.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 16:20:11 +08:00
jackyu66gitandCursor d33c8fdfe9 feat(ECR-012): 星座对齐收口,并 Closed ECR-007/008
对齐 outlook/分享 type=star/报告页运势面板与测试;流程上关闭 Ops-B/C 两张 ECR。真支付仍后置。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 15:48:19 +08:00
jackyu66gitandCursor 0ee4d4f5ae docs(loop): approve and close LOOP-RUN-002 Ops-D sample
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 02:47:39 +08:00
jackyu66gitandCursor 31a0ec721a docs(ECR-009): dedupe TRACEABILITY
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-07 02:46:22 +08:00
1101 changed files with 46391 additions and 2011 deletions
+29
View File
@@ -0,0 +1,29 @@
# ADR-0008 — Go 为唯一用户后台,咨询域过渡反代
- Status: Accepted
- Date: 2026-09-14
- Tags: identity, miniprogram, consult, gateway
## Context
愈心谷 C 端同时打两套后台:咨询/测评在 Java + MySQL,愈心魔方在 Go + PostgreSQL。
`wx.login``code` 只能用一次,无法先后换两套 token。小程序需要单一身份与单一请求入口。
## Decision
1. **Go + PostgreSQL 是唯一用户身份源。** 微信 openid / 手机号写在 `users`
2. **Java 过渡期为被调方**Go 用 openid/phone 调内网 `issue-mini-app` 换 Java Redis token,并反代 `/app-api``/admin-api`
3. **咨询业务表本 ADR 不迁**;支付与档期另开 ECR。
4. **Admin 登录独立**,不走本决策。
## Consequences
- 小程序只配置 Go 域名(现网经 `/psy/api/` 反代)。
- 下线 Java 必须另开 ECR,并先迁支付。
- 历史魔方账密用户按手机号合并到 `wx_openid`
## Alternatives considered
- 只做 SSO、双库长期并存:否决(用户要求合库合服务)。
- 以 Java 吸收魔方:否决(栈已锁定 Go + PG)。
- 客户端双 token:否决(code 单次使用,且拖住前端统一接入)。
+2 -1
View File
@@ -180,8 +180,9 @@ Level 0 alone ≠ Backend/Frontend Done。
| **Vertical Slice Demo** | 主路径可演示;缺测/缺态/缺功能仍可存在 |
| **Feature Complete** | 单功能满足本文件 §17 |
| **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)
---
+6 -1
View File
@@ -3,5 +3,10 @@
| File | Purpose |
|---|---|
| [../domain.md](../domain.md) | Tech ↔ 用户名 |
| [domain-map.md](domain-map.md) | Bounded contexts冻结 |
| [domain-map.md](domain-map.md) | Bounded contextsP1 冻结 + Ops Wave 0 Draft |
| [erd.md](erd.md) | P1 表结构草案(冻结) |
| [boundary-rules.md](boundary-rules.md) | BC owns / allowed / forbidden**P0** · Wave 0 |
| [glossary.yaml](glossary.yaml) | 工程/API 唯一词(与 lexicon 双轨) |
| [entity-catalog.md](entity-catalog.md) | 概念级实体目录(无 migration |
Ops Contract-First 归档:`docs/WAVE0/`
+239
View File
@@ -0,0 +1,239 @@
# Bounded Context Boundary Rules
> Wave 0 · **P0 资产** · AI / Engineer 只读;变更须 Architect + Human
> 运行时权威副本:`.ai/domain/boundary-rules.md`
Status: `Draft`Human Review 后 Frozen
---
## 如何读
- **owns**:该 BC **唯一写权威**(修改入口)。
- **does_not_own**:明示禁止「顺手改」。
- **allowed**:允许的依赖(多为读或经门面)。
- **forbidden**:禁止的依赖(含跨 BC 写支付/会员等)。
违反本文件 = Reviewer **BLOCK**(架构回归)。
---
## Admin_Auth_Audit
```yaml
Admin_Auth_Audit:
owns:
- AdminAccount
- AdminSession
- AuditLog
does_not_own:
- UserStatus
- MembershipPlan
- Entitlement
allowed:
- Admin_RBAC.read
forbidden:
- Payment.write
- C端业务聚合根的隐式写入
```
## Admin_RBAC
```yaml
Admin_RBAC:
owns:
- AdminRole
- AdminPermission
does_not_own:
- User
- Profile
- Membership
allowed:
- Admin_Auth_Audit.write_audit
forbidden:
- Payment
- Membership.write
- DeviceAuth.user_token_issue
```
## Identity_Profile
```yaml
Identity_Profile:
owns:
- User
- Profile
- DeviceIdentity
- UserStatus
- BehaviorSnapshot
- PsychologicalTagSet
does_not_own:
- MembershipPlan
- Entitlement
- Order
- Payment
- RedemptionCode
allowed:
- Analytics_OpsB.read
forbidden:
- Payment
- Membership.write
```
## Membership_Orders
```yaml
Membership_Orders:
owns:
- MembershipPlan
- Entitlement
- Membership
- Order
- DeepAccess
- RedemptionCode
- Payment # mock 今;真支付适配器后置,仍归本 BC
does_not_own:
- UserStatus
- AdminRole
- FilterRule
allowed:
- Identity_Profile.read
forbidden:
- Ask_Ops.write_prompt
- Content_Safety.rule_mutate_via_order
```
## Analytics_OpsB
```yaml
Analytics_OpsB:
owns:
- AnalyticsSession
- AnalyticsEvent
does_not_own:
- User
- Membership
allowed:
- Identity_Profile.read_ids_only
forbidden:
- Payment
- 采集 PII 正文(生日/问答全文等)
```
## Ops_Content
```yaml
Ops_Content:
owns:
- HomeTool
- ScalePublishState
does_not_own:
- Scale 题目正文编辑器(未开 ECR 前禁止扩张)
allowed:
- Explore_Reports.publish_gate
forbidden:
- Community
- Payment
```
## Account_Risk
```yaml
Account_Risk:
owns:
- RiskFlag
- BanRecord
does_not_own:
- MembershipPlan
allowed:
- Identity_Profile.UserStatus.transition # 经明确应用服务
- Admin_Auth_Audit.write_audit
forbidden:
- Payment.refund_silent
```
## Content_Safety
```yaml
Content_Safety:
owns:
- FilterRule
- ModerationCase
- BlockPolicy
- CrisisEvent
- CrisisPolicy
does_not_own:
- Membership
- Payment
- AdminRole
allowed:
- Identity_Profile.read
- Ask_Ops.read_session_meta
forbidden:
- Membership.write
- Payment
```
## Ask_Ops
```yaml
Ask_Ops:
owns:
- AskSessionView
- QualityFeedback
- HandoffCase
- SystemPrompt
- KnowledgeSource
- KnowledgeChunk
- ToolDefinition
does_not_own:
- Payment
- MembershipPlan
- UserStatus
allowed:
- Identity_Profile.read
- Content_Safety.evaluate
forbidden:
- Payment
- Membership.write
- Admin_RBAC.write
```
## Ops_CMS_NoUGC
```yaml
Ops_CMS_NoUGC:
owns:
- Banner
- FeedSlot
- ScheduledPublication
does_not_own:
- UGC Post
- CommentGraph
- TrustScore
allowed:
- Admin_RBAC.check
forbidden:
- Community
- 用户生成内容入库为主路径
```
## Community
```yaml
Community:
status: Forbidden
owns: []
note: feature-map UGC 广场 [No];开启须 L3 ADR + 新 Capability
```
---
## 反模式(禁止)
```text
AskService → load User → update Membership → charge Payment
UserService.updateMembership()
Content_Safety 直接改 Entitlement
Ops_CMS 引入「用户帖子」表却声称 NoUGC
```
+20
View File
@@ -23,6 +23,26 @@
---
## Ops Platform Bounded ContextsWave 0 Draft
> 详细 owns/forbidden[`boundary-rules.md`](boundary-rules.md) · 词表:[`glossary.yaml`](glossary.yaml) · 实体:[`entity-catalog.md`](entity-catalog.md)
> 归档:`docs/WAVE0/domain/` · Capability`docs/WAVE0/capability/ops-capability-map.md`
> **不修改**上方 P1 不变量;Ops BC 实现须独立 ECR。
| Context | Capability | Status |
|---|---|---|
| Admin_Auth_Audit | AdminGovernance | Shipped Ops-A |
| Admin_RBAC | AdminGovernance | Draft → ECR-013A |
| Analytics_OpsB | GrowthInsights / UserIntelligence(read) | Shipped Ops-B |
| Ops_Content | ExploreConfig (partial) | Shipped Ops-C |
| Account_Risk | AccountLifecycle | Spec via ECR-013BIdentity_Profile owns UserStatus |
| 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** |
---
## Context diagram
```
+43
View File
@@ -0,0 +1,43 @@
# Entity CatalogWave 0 · 概念级)
> 仅登记名称、归属 BC、Capability、生命周期备注。**不建表、不写 migration。**
> 与 [`glossary.yaml`](glossary.yaml) · [`boundary-rules.md`](boundary-rules.md) 一致。
Status: `Draft`
| Entity | BC | Capability | Notes |
|--------|----|------------|-------|
| AdminAccount | Admin_Auth_Audit | AdminGovernance | 已存在 Ops-A |
| AdminSession | Admin_Auth_Audit | AdminGovernance | 已存在 |
| AuditLog | Admin_Auth_Audit | AdminGovernance | 已存在;只追加 |
| AdminRole | Admin_RBAC | AdminGovernance | **ECR-013A** |
| AdminPermission | Admin_RBAC | AdminGovernance | **ECR-013A** |
| User | Identity_Profile | AccountLifecycle | 已存在 |
| Profile | Identity_Profile | UserIntelligence | 已存在 |
| UserStatus | Identity_Profile | AccountLifecycle | **ECR-013B** 状态机 |
| AccountStateTransition | Identity_Profile | AccountLifecycle | **ECR-013B** |
| BehaviorSnapshot | Identity_Profile | UserIntelligence | **ECR-016** 读模型(analytics 聚合) |
| PsychologicalTagSet | Identity_Profile | UserIntelligence | **ECR-016** 由报告 type 派生 |
| MembershipPlan | Membership_Orders | CommerceEntitlement | **ECR-014 Closed** |
| Entitlement | Membership_Orders | CommerceEntitlement | **ECR-018** 读模型(MembershipDeepAccess |
| RedemptionCode | Membership_Orders | CommerceEntitlement | **ECR-015 Closed** |
| Membership | Membership_Orders | CommerceEntitlement | 已存在 |
| Order | Membership_Orders | CommerceEntitlement | 已存在 |
| DeepAccess | Membership_Orders | CommerceEntitlement | 已存在 |
| Payment | Membership_Orders | CommerceEntitlement | mock;真支付最后 |
| HomeTool | Ops_Content | ExploreConfig | 已存在 Ops-C |
| ScalePublishState | Ops_Content | ExploreConfig | 已存在 |
| AnalyticsSession / Event | Analytics_OpsB | GrowthInsights | 已存在 Ops-B |
| FilterRule | Content_Safety | ContentSafety | **ECR-019** 只读 + evaluate |
| ModerationCase | Content_Safety | ContentSafety | 后置 |
| 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 | 后置 |
| UGC* | Community | — | **Forbidden** |
## 状态机(ECR-013B · 契约已开)
`UserStatus`: 见 `.ai/product/feature-spec/ops-account-lifecycle.md`
`deleted` soft-delete **不在 013B**(另开)。
实现轮前:**禁止** migration / DeviceAuth 改动。
+116
View File
@@ -0,0 +1,116 @@
# domain glossary — engineering / API unique terms
# Wave 0 Draft. Runtime copy: .ai/domain/glossary.yaml
# User-facing copy stays in .ai/product/lexicon.md
# Conflict: Architect + Human resolve; do not invent synonyms in code.
version: "0.1.0"
status: draft
terms:
AdminRole:
zh: 管理员角色
bc: Admin_RBAC
forbidden: [admin_group, privilege_group]
AdminPermission:
zh: 管理员权限点
bc: Admin_RBAC
forbidden: [acl_flag, authz_bit]
AuditLog:
zh: 操作审计日志
bc: Admin_Auth_Audit
forbidden: [admin_history]
UserStatus:
zh: 用户账户状态
bc: Identity_Profile
values: [active, disabled, banned, suspended]
forbidden: [user_flag, account_level]
AccountStateTransition:
zh: 账户状态迁移
bc: Identity_Profile
forbidden: [status_patch_log]
MembershipPlan:
zh: 会员套餐
bc: Membership_Orders
forbidden: [package, vip_level, 订阅档, sku_vip]
Entitlement:
zh: 权益
bc: Membership_Orders
forbidden: [privilege_pack, benefit_bundle]
RedemptionCode:
zh: 兑换码
bc: Membership_Orders
forbidden: [gift_card, activation_key_alias]
DeepAccess:
zh: 深度版
bc: Membership_Orders
forbidden: [Unlock, unlock_report]
BehaviorSnapshot:
zh: 行为快照
bc: Identity_Profile
forbidden: [user_track_dump]
PsychologicalTagSet:
zh: 心理标签集
bc: Identity_Profile
forbidden: [psy_labels_raw]
FilterRule:
zh: 过滤规则
bc: Content_Safety
forbidden: [badword_only]
ModerationCase:
zh: 审核工单
bc: Content_Safety
forbidden: [review_ticket_ugc]
CrisisEvent:
zh: 危机事件
bc: Content_Safety
forbidden: [suicide_flag_public]
CrisisPolicy:
zh: 危机策略
bc: Content_Safety
forbidden: [kill_switch_generic]
SystemPrompt:
zh: 系统提示词
bc: Ask_Ops
forbidden: [god_prompt]
KnowledgeSource:
zh: 知识源
bc: Ask_Ops
forbidden: [rag_file]
KnowledgeChunk:
zh: 知识块
bc: Ask_Ops
forbidden: [embedding_row_ui]
ToolDefinition:
zh: 工具定义
bc: Ask_Ops
forbidden: [function_call_config_loose]
Banner:
zh: 运营横幅
bc: Ops_CMS_NoUGC
forbidden: [story_feed_ugc]
Community:
zh: (禁止能力)
bc: Community
status: forbidden
forbidden: [ugc_plaza, 社区广场]
+2 -1
View File
@@ -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) · 星 / 身心节律 / 意象卡片。
+2 -3
View File
@@ -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 等)**。
+14 -11
View File
@@ -212,11 +212,11 @@ UI **不出现「塔罗」**。禁止神谕吉凶、恐吓话术。
**不含** 2.62.8P2 三模块不计入 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]`
@@ -227,15 +227,18 @@ UI **不出现「塔罗」**。禁止神谕吉凶、恐吓话术。
## 7. 运营后台(内部 · 非 C 端 Tab)
详见 Spec[feature-spec/ops-admin.md](feature-spec/ops-admin.md) · ECR-006
详见 Spec[feature-spec/ops-admin.md](feature-spec/ops-admin.md) · ECR-006
**演进规范(Wave 0):** [`docs/WAVE0/`](../../docs/WAVE0/) · Capability Map · boundary-rules · Contract-First(禁止 Feature 堆砌进实现)
| 分期 | 内容 |
|---|---|
| Phase A `[Ops]` | 登录 · 用户/订单查询 · 会员授予 · 审计 · `apps/admin-h5`ECR-006 Closed |
| Phase B `[Ops]` | 行为分析:自有埋点 + 管理端「数据」看板(**ECR-007 Implemented** · Spec `ops-analytics.md` |
| Phase C `[Ops]` | 内容:首页宫格 CRUD · 测评上下架(**ECR-008 Implemented** · Spec `ops-content.md` |
| Phase B `[Ops]` | 行为分析:自有埋点 + 管理端「数据」看板(**ECR-007** · Spec `ops-analytics.md` |
| Phase C `[Ops]` | 内容:首页宫格 CRUD · 测评上下架(**ECR-008** · 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 D+ Contract | **Contract-First** 扩展目录:**ECR-013A…040**RBAC/兑换码/问答运营/安全/危机/CMS/ExploreConfig 等)。见 `docs/WAVE0/` |
| Phase F+ / Write-Wave | **写面加深**(首刀 **ECR-041 Banner** · `docs/WAVE0/WRITE_WAVE_AUTHORIZATION.md`);封禁细策略 · 真推送;**UGC 仍 `[No]`****真支付最后** |
不计入 P1 Complete;不进入五 Tab。
@@ -257,11 +260,11 @@ UI **不出现「塔罗」**。禁止神谕吉凶、恐吓话术。
| `/scales/:slug` | 探索测试作答 | |
| `/reports` | 成长报告列表 | |
| `/reports/:id` | 成长报告详情 | |
| `/star` | 星座 | P2 |
| `/synastry` | 合盘 | P2 |
| `/synastry/invite/:token` | 合盘邀请 | P2 |
| `/rhythm` | 身心节律 | P2 draft |
| `/cards` | 意象卡片 | P2 draft |
| `/star` | 星座 | P2 Complete |
| `/synastry` | 合盘 | P2 Complete |
| `/synastry/invite/:token` | 合盘邀请 | P2 Complete |
| `/rhythm` | 身心节律 | P2 Complete |
| `/cards` | 意象卡片 | P2 Complete |
兼容重定向:`/decode``/portrait`(仅技术兼容,UI 不出现 decode 文案)。
+6 -5
View File
@@ -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 |
| 真支付 | 部署阶段 |
| L0L3 验证 | Done(见 p2-status |
| 真支付 | 部署阶段(不挡 P2 Complete |
+45 -7
View File
@@ -15,19 +15,57 @@
| [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-analytics.md](ops-analytics.md) | 运营行为分析(埋点+数据看板) | §7 | `admin-h5` `/analytics` · H5 track | Ops-B · ECR-007 |
| [ops-content.md](ops-content.md) | 运营内容(宫格+测评上下架) | §7 | `admin-h5` `/content` · `GET /home/tools` | Ops-C · ECR-008 |
| [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 |
| [input-compliance.md](input-compliance.md) | 用户文字输入合规 | 横切 · 安全 | 各写接口 · code 40060 | P2 · ECR-012 |
| [ops-rbac.md](ops-rbac.md) | 运营 RBAC | §7 | `admin-h5` `/me` permissions · `/admin/roles*` | Ops · **ECR-013A** |
| [ops-redemption-code.md](ops-redemption-code.md) | 兑换码 RedemptionCode | §7 | `admin-h5` `/codes` · `POST /membership/redeem` | Ops · **ECR-015(local)** |
| [ops-user-intelligence.md](ops-user-intelligence.md) | 用户洞察 UserIntelligence | §7 | `admin-h5` 用户详情「洞察」 | Ops · **ECR-016(local)** |
| [ops-ask-operations.md](ops-ask-operations.md) | 问答运营 AskOperations | §7 | `admin-h5` `/ask` | Ops · **ECR-017** |
| [ops-entitlement.md](ops-entitlement.md) | 用户权益 Entitlement | §7 | `admin-h5` 用户详情「权益」 | Ops · **ECR-018** |
| [ops-content-safety.md](ops-content-safety.md) | 内容安全 ContentSafety | §7 | `admin-h5` `/safety` | Ops · **ECR-019** |
| [ops-quality-feedback.md](ops-quality-feedback.md) | 问答质量反馈 QualityFeedback | §7 | `admin-h5` `/ask` | Ops · **ECR-020** |
| [ops-ai-core-config.md](ops-ai-core-config.md) | AI 核心配置 AICoreConfig | §7 | `admin-h5` `/ai` | Ops · **ECR-021** |
| [ops-crisis-care.md](ops-crisis-care.md) | 危机关怀 CrisisCare | §7 | `admin-h5` `/crisis` | Ops · **ECR-022** |
| [ops-knowledge-source.md](ops-knowledge-source.md) | AI 知识源 KnowledgeSource | §7 | `admin-h5` `/ai` | Ops · **ECR-023** |
| [ops-banner.md](ops-banner.md) | OpsCMS Banner(只读基线) | §7 | `admin-h5` `/cms` | Ops · **ECR-024** |
| [ops-banner-write.md](ops-banner-write.md) | OpsCMS Banner **写面** | §7 | admin CMS 写 · `GET /home/banners` | Write-Wave · **ECR-041** |
| [ops-feed-slot.md](ops-feed-slot.md) | OpsCMS FeedSlot(只读基线) | §7 | `admin-h5` `/cms` | Ops · **ECR-025** |
| [ops-feed-slot-write.md](ops-feed-slot-write.md) | OpsCMS FeedSlot **写面** | §7 | admin CMS 写 · `GET /home/feed-slots` | Write-Wave · **ECR-042** |
| [ops-scheduled-publication.md](ops-scheduled-publication.md) | OpsCMS ScheduledPublication | §7 | `/admin/cms/publications*` | Ops · **ECR-026** |
| [ops-knowledge-chunk.md](ops-knowledge-chunk.md) | AICoreConfig KnowledgeChunk | §7 | `/admin/ai/knowledge-chunks*` | Ops · **ECR-027** |
| [ops-tool-definition.md](ops-tool-definition.md) | AICoreConfig ToolDefinition | §7 | `/admin/ai/tools*` | Ops · **ECR-028** |
| [ops-block-policy.md](ops-block-policy.md) | ContentSafety BlockPolicy | §7 | `/admin/content-safety/block-policies*` | Ops · **ECR-029** |
| [ops-moderation-case.md](ops-moderation-case.md) | ContentSafety ModerationCase | §7 | `/admin/content-safety/cases*` | Ops · **ECR-030** |
| [ops-crisis-event.md](ops-crisis-event.md) | CrisisCare CrisisEvent | §7 | `/admin/crisis/events*` | Ops · **ECR-031** |
| [ops-intervention-outcome.md](ops-intervention-outcome.md) | CrisisCare InterventionOutcome | §7 | `/admin/crisis/interventions*` | Ops · **ECR-032** |
| [ops-handoff-case.md](ops-handoff-case.md) | AskOperations HandoffCase | §7 | `/admin/ask/handoffs*` | Ops · **ECR-033** |
| [ops-privacy-request.md](ops-privacy-request.md) | AdminGovernance PrivacyRequest | §7 | `/admin/privacy/requests*` | Ops · **ECR-034** |
| [ops-star-config.md](ops-star-config.md) | ExploreConfig StarConfig(只读) | §7 | `/admin/explore/star-configs*` | Ops · **ECR-035** |
| [ops-star-config-write.md](ops-star-config-write.md) | StarConfig **写面** | §7 | admin 写 · `GET /star/configs` | ExploreConfig · **ECR-043** |
| [ops-rhythm-config.md](ops-rhythm-config.md) | ExploreConfig RhythmConfig(只读) | §7 | `/admin/explore/rhythm-configs*` | Ops · **ECR-036** |
| [ops-rhythm-config-write.md](ops-rhythm-config-write.md) | RhythmConfig **写面** | §7 | admin 写 · `GET /rhythm/configs` | ExploreConfig · **ECR-044** |
| [ops-image-card-deck.md](ops-image-card-deck.md) | ExploreConfig ImageCardDeck(只读) | §7 | `/admin/explore/image-card-decks*` | Ops · **ECR-037** |
| [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-report-template-write.md](ops-report-template-write.md) | ReportTemplate **写面** | §7 | admin POST/PUT · growth.write | GrowthInsights · **ECR-048** |
| [ops-funnel-definition.md](ops-funnel-definition.md) | GrowthInsights FunnelDefinition(只读) | §7 | `/admin/analytics/funnel-definitions*` | Ops · **ECR-039** |
| [ops-funnel-definition-write.md](ops-funnel-definition-write.md) | FunnelDefinition **写面** | §7 | admin POST/PUT · growth.write | GrowthInsights · **ECR-047** |
| [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` → 填满 → 在本表登记 → 再编码。
**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)
+42 -13
View File
@@ -10,13 +10,13 @@
| 字段 | 内容 |
|---|---|
| Name | 账号登录(手机号+密码 |
| Name | 账号登录(小程序微信;H5 过渡账密 |
| Purpose | 跨设备持久用户身份;未登录不可生成/查看生日衍生结果 |
| Business Goal | 数据归属清晰;为会员与深度版付费打底 |
| Business Goal | 数据归属清晰;咨询与魔方同一人;为会员与深度版付费打底 |
| In | Out |
|---|---|
| 注册 / 登录 / 登出 / me | 微信 OAuth、短信 OTP(后续) |
| 小程序微信登录 / 登出 / me | 短信 OTP |
| 设备身份绑定到已注册账号 | 游客可看完整报告 |
| Bearer Session | 运营 Admin 登录(独立) |
@@ -45,7 +45,7 @@
```text
进入结果页或建档
未登录? → /login 注册或登录
未登录? → 小程序 login-pop(微信)或 H5 /login(过渡账密)
Device 绑定到账号 User
@@ -58,7 +58,8 @@ Device 绑定到账号 User
| 路由 | 页面 |
|---|---|
| `/login` | LoginPage注册/登录切换 |
| `/login` | H5 LoginPage过渡账密;小程序已删除此页 |
| 小程序 `login-pop` | 微信手机号授权(唯一 C 端登录) |
| `/mine` | 展示账号手机号尾号 + 退出 |
---
@@ -77,19 +78,23 @@ Device 绑定到账号 User
| ID | Rule |
|---|---|
| R1 | **临时开放** 任意非空手机号 + 任意密码均可登录;未注册则自动建号写入 DB |
| R2 | 登录/注册统一走 OpenLogin;不校验历史密码,仍会更新 password_hash 记录本次输入 |
| R1 | **小程序** 微信 `code` + 手机号授权登录;按 `wx_openid` 命中,否则按手机号合并历史账密账号;未命中则建号 |
| R2 | **H5 过渡:** OpenLogin(任意非空手机号+密码)仍可用,避免 H5 断服;小程序不再提供账密页。正式关掉账密须另改本 Spec |
| R3 | 登录成功后:签发 sessiondevice_identities.user_id 改绑到账号 |
| R4 | 生成/查看档案与报告 API 必须已注册(users.phone 非空)且有效 session 或已绑设备账号 |
| R5 | 登出作废 session;设备可再登录其他账号 |
| R6 | 正式环境恢复校验前须改回本 Spec |
| R7 | 新账号未填昵称时,服务端用「意象词+场景词」词库(100×100)随机生成,如心语岛、微光谷 |
| R8 | 账号昵称可在个人档案页修改(PATCH `/auth/me`);首页与各页「自己」展示位统一用昵称(合盘/问答/星座/档案卡) |
| R9 | 账号头像可在「我的」页更换:相册或拍照上传;服务端持久化;GET `/auth/me` 返回 `avatar_url`;未设置时 UI 用愈心小人正面默认头像(`public/default-avatar.png`,源图 `public/logo/愈心小人1.png` |
---
## 8. 数据模型影响
- `users.phone` UNIQUE · `users.password_hash` · `users.nickname`
- `users.phone` UNIQUE · `users.wx_openid` UNIQUE · `users.wx_unionid` · `users.java_platform_user_id` · `users.password_hash` · `users.nickname` · `users.avatar_url`
- `user_sessions(token, user_id, expires_at)`
- 本地文件:`data/avatars/{user_id}.{jpg|png|webp}`(进程相对路径)
---
@@ -97,10 +102,14 @@ Device 绑定到账号 User
| Method | Path | 说明 |
|---|---|---|
| POST | `/api/v1/auth/register` | 注册 |
| POST | `/api/v1/auth/login` | 登录 |
| POST | `/api/v1/auth/wechat` | 小程序微信登录 `{ code, encryptedData, iv }` |
| POST | `/api/v1/auth/register` | H5 过渡注册(可带 nickname |
| POST | `/api/v1/auth/login` | H5 过渡登录 |
| POST | `/api/v1/auth/logout` | 登出 |
| GET | `/api/v1/auth/me` | 当前账号 |
| GET | `/api/v1/auth/me` | 当前账号(含 avatar_url |
| PATCH | `/api/v1/auth/me` | 更新昵称 `{ nickname }` |
| POST | `/api/v1/auth/me/avatar` | multipart `file`jpeg/png/webp;≤2MB → 更新 avatar_url |
| GET | `/api/v1/media/avatars/:file` | 公开读头像文件 |
---
@@ -110,12 +119,30 @@ Device 绑定到账号 User
|---|---|---|
| register/login | ✓ | ✓ |
| profiles / reports 写读 | ✗ | ✓ |
| 上传头像 | ✗ | ✓ |
| 读头像媒体 | ✓ | ✓ |
---
## 11. 埋点
`auth_register` · `auth_login` · `auth_logout`
经统一 `track()`(见 [analytics.md](analytics.md) · [ops-analytics.md](ops-analytics.md))。
**非** P1 核心漏斗 5 事件;接入后走自有 `POST /analytics/events`(及可选 GA)。
禁止参数含手机号、明文密码、头像绝对 URL / `user_id`
| Event | 触发 | 可选 params |
|---|---|---|
| `auth_register` | 账号新建成功(含 OpenLogin 自动建号,`is_new=true` | `source`: login_page |
| `auth_login` | 已有账号登录成功(`is_new=false` | `source`: login_page |
| `auth_logout` | 用户在「我的」点退出且本地 session 已清 | `surface`: mine |
| `avatar_sheet_opened` | 「我的」打开更换头像 sheet | `surface`: mine |
| `avatar_upload_succeeded` | 头像上传 API 成功且 UI 刷新为新图 | `source`: album \| camera |
| `avatar_upload_failed` | 头像上传失败(校验/网络/服务端) | `source`: album \| camera · `reason`: too_large \| bad_type \| network \| server \| unknown |
| `nickname_updated` | PATCH 昵称成功 | `surface`: profile |
`page_view``/login` · `/mine` 由路由自动采集,本 Spec 不重复定义。
**实现状态:** 事件名以本表为准;H5 已挂 Login / Mine / Profile`track` → 自有 ingest + 可选 GA)。
---
@@ -126,10 +153,12 @@ Device 绑定到账号 User
| 新设备 | 未登录 POST portrait | 401 |
| 注册成功 | 建档 | 200 且生成报告包 |
| 登录另一设备 | list profiles | 见同一账号数据 |
| 已登录设备 | POST logout 后无 Bearer 再 GET /auth/me | 401(设备已解绑,刷新不再显示登录态) |
---
## 13. AI 开发前检查
- [x] lexicon:登录/注册/账号
- [x] 不碰 Admin Auth
- [x] 不碰 Admin Auth
- [x] 咨询域见 Spec `consult-miniprogram`ECR-050
+2
View File
@@ -65,6 +65,8 @@
```text
/ask
├── 愈心 AI / 顾问 Tab
├── 顾问列表 → 顾问专属对话(?advisor=key · 顶栏返回回列表)
├── Empty(无档案)
├── Loading(发送中)
├── Normal(历史 + 输入)
+12 -7
View File
@@ -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)
---
@@ -111,10 +111,15 @@ Tab 陪伴 → /companion
## 11. 埋点
| Event | 触发 |
|---|---|
| `companion_viewed` | 打开 |
| `mood_saved` | P2 |
| Event | 触发 | 可选 params |
|---|---|---|
| `companion_viewed` | 打开 `/companion` | — |
| `mood_saved` | 心情保存成功 | `score`(整数分,非正文) |
| `growth_plan_viewed` | 打开 `/growth-plan` 且列表加载结束 | — |
| `growth_plan_created` | 创建计划成功 | — |
| `growth_plan_checkin` | 今日打卡成功 | — |
禁止参数含计划 title/focus/note 明文。
---
@@ -142,6 +147,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 CompleteP2:节气 + 心情 + 近七日轨迹;成长计划见 `/growth-plan` |
| Process Review | 2026-08-02 [P1-PROCESS-REVIEW](P1-PROCESS-REVIEW.md) · 心情已按产品「其他先做」落地 |
@@ -0,0 +1,22 @@
# Feature Spec: 小程序咨询域(原生 Go)
> Status: `Active` · ECR-050
## 1. 功能定义
小程序首页/咨询/测评/订单/我的资料不再打 Java。Go 用 PostgreSQL 提供与原 `/psychic/*` 相同的字段合同。
## 2. Business Rules
| ID | Rule |
|---|---|
| C1 | 未登录可看 banner、资讯、测评列表、咨询师、档期、协议 |
| C2 | 登录后才能交卷、关注、下单、改资料、反馈 |
| C3 | 下单锁时段;取消未支付订单释放时段 |
| C4 | 金额单位:分;支付 openid = `users.wx_openid` |
| C5 | 测评仅单选计分,按分数区间匹配结果 |
| C6 | 协议路径保持历史拼写 `procotol` |
## 3. API
全部挂在 `/api/v1/psychic/*`,字段与原 Java App VO 一致(见 ECR-050 BD)。
+48 -17
View File
@@ -45,9 +45,9 @@
进入 /scales/:slug
加载题目
逐题作答(须全部完成)
已有结果? → 直接展示最近结果 +「重新测试」
(无结果 / 点重测)
加载题目 · 逐题作答(须全部完成)
无 Self → 引导建档
@@ -60,10 +60,13 @@ POST result → 展示丰富结果
| 子能力 | P1 |
|---|---|
| 测试入口(探索 Tab | ✓ |
| 测试列表 | ✓ |
| 测试入口(探索 Tab · 首页宫格) | ✓ · 产品向 `mbti-lite`;探索主路径为 curated 题库(A 集 |
| 测试列表 | ✓ · 探索「题库精选」宫格(每类 1~2)+「题库分类」;分类页 `/explore/bank/:key` |
| 题库范围(A) | ✓ 人格 / 情绪能力 / 智能 / 生活与关系;**不含**抑郁焦虑强迫躁狂双相等临床筛查 |
| 问题流程 | ✓ |
| 答题保存(服务端结果落库) | ✓ |
| 最近结果回看 | ✓ GET `/scales/{slug}/result`;有结果进页不重复作答 |
| 重新测试 | ✓ 结果页按钮 → 清空草稿再答;新结果落库并覆盖展示 |
| 中断恢复 | ✓ localStorage 草稿(`scaleDraft`);提交后清除 |
| 结果生成(丰富结构) | ✓ |
| 免费层 | ✓ 全量 result |
@@ -79,8 +82,10 @@ POST result → 展示丰富结果
| 路由 | 页面 |
|---|---|
| `/explore` | ExplorePage |
| `/scales/:slug` | ScalePage |
| `/explore` | ExplorePage(题库精选宫格 + 题库分类 + 其它工具) |
| `/explore/bank/:category` | ExploreBankCategoryPage · curated 题库分类列表 |
| `/explore/:category` | ExploreCategoryPage · 产品工具分类(解码/星座等) |
| `/scales/:slug` | ScalePage(含 bank slug |
```text
/scales/:slug
@@ -116,6 +121,13 @@ POST result → 展示丰富结果
| R5 | result 含 label/summary/overview/dimensions/tips/scripts/growth_plan/faq |
| R6 | 结果「不是固定标签」需在文案中提示 |
| R7 | 作答过程写入本地草稿(按 slug);提交成功后清除;刷新可恢复未提交答案 |
| R8 | 同一用户同一 slug(含 curated 题库 slug)若已有 `scale_results`,再次进入直接展示**最近一次**结果,并提供「重新测试」;未点重测不得自动进入答题 |
| R9 | `mbti-lite` 产品展示名统一为「MBTI测试」(首页宫格 / 探索入口 / 量表 title) |
| R10 | **标准版 `mbti-lite`**:开源 OEJTS 1.2 · **32 题**(四维各 8)· 双极 1–5 Likert;按维累加,阈值判出 E/I·S/N·T/F·J/P 一侧,合成四字母类型;**免费** |
| R11 | **完整版 `mbti-full`**:60 题(四维各 15)· 须**成长会员**;未开通时 GET 返回 `locked=true` 且无题目;提交返回 403 |
| R12 | 禁止使用官方 MBTI® 版权题干;结果须提示「不是固定标签」;题库重大更新须 soft-delete 旧 `scale_results` |
| R13 | curated 题库(`scalebank`)仅含 A 集非临床探索量表;文案须声明「非诊断」;临床类(抑郁/焦虑/强迫等)不得进入 C 端 |
| R14 | curated 题库作答走同一 `/scales/:slug`;完成后再次点击入口须遵守 R8(只看结果或显式重测),不得每次从头作答 |
---
@@ -134,6 +146,7 @@ POST result → 展示丰富结果
|---|---|---|
| GET | `/api/v1/scales` | 列表 |
| GET | `/api/v1/scales/{slug}` | 题目 |
| GET | `/api/v1/scales/{slug}/result` | 当前用户该量表最近一次结果;无则 404 |
| POST | `/api/v1/scales/{slug}/result` | 提交计分 |
---
@@ -144,18 +157,27 @@ POST result → 展示丰富结果
|---|---|---|
| 看列表/题目 | ✓ | ✓ |
| 提交结果 | ✗ | ✓ |
| `mbti-full` 作答 | ✗(须成长会员) | ✓(会员 active) |
---
## 11. 埋点
| Event | 触发 |
|---|---|
| `scale_list_viewed` | 探索列表 |
| `scale_started` | 进入作答 |
| `scale_completed` | 出结果 |
| `scale_share_clicked` | 分享 |
| `scale_cta_relation` / `scale_cta_ask` | 导流 |
经统一 `track()`;非 P1 核心 5 事件。禁止参数含题干全文、答案明文、档案 ID。
| Event | 触发 | 可选 params |
|---|---|---|
| `scale_list_viewed` | 打开 `/explore` 且题库 catalog 加载成功 | — |
| `scale_bank_category_viewed` | 打开 `/explore/bank/:category` 且分类加载成功 | `category`key |
| `scale_started` | 用户点开始进入作答(含「重新测试」后再开始) | `slug` · `access`: free\|member |
| `scale_completed` | 提交计分成功并展示结果 | `slug` · `access`: free\|member |
| `scale_result_reopened` | 再次进入已有结果直接展示(R8,未重测) | `slug` |
| `scale_retake_clicked` | 点「重新测试」 | `slug` |
| `scale_locked_viewed` | 会员锁页展示(如 mbti-full | `slug` |
| `scale_share_clicked` | 结果页打开分享 | `slug` |
| `scale_cta_relation` / `scale_cta_ask` | 结果导流(若 UI 有) | `slug` |
`page_view``/explore` · `/explore/bank/*` · `/scales/*` 由路由自动采集。
---
@@ -165,6 +187,14 @@ POST result → 展示丰富结果
**When** 答完提交
**Then** 返回 label 与 overview/tips 等丰富字段
**Given** 已提交过结果
**When** 再次进入 `/scales/{slug}`
**Then** 直接展示最近结果与「重新测试」;不进入答题
**Given** 结果页点「重新测试」并完成提交
**When** 再次进入
**Then** 展示新结果
**Given** 未答完
**When** 提交
**Then** 前端拦截;不成功落库
@@ -186,7 +216,8 @@ POST result → 展示丰富结果
| 项 | 内容 |
|---|---|
| Packages | `internal/scale` · `service/scale` · `ExplorePage` · `ScalePage` |
| Seeds | communication-style · emotion-pattern |
| Seeds | communication-style · emotion-pattern · **mbti-liteOEJTS 32** · **mbti-full60·会员)** |
| Gaps | 结果付费墙若要做须先改 Spec;全量 §11 埋点未挂(核心漏斗见 analytics) |
| Tests | score/result L1`scaleDraft.spec`Scale 流程组件测 |
| Process Review | 草稿恢复已实现 · P1 收口 |
| Tests | score/result L1`ScoreJungian``scaleDraft.spec`Scale 流程组件测 |
| Process Review | OEJTS 标准/完整双轨 · 最近结果回看 · P1 收口 |
| Credit | OEJTS / Open Psychometrics · openjung MIT |
+35 -9
View File
@@ -62,9 +62,10 @@
```text
/
├── Hero / 品牌
├── 建档 CTA
├── 功能入口
├── 全局顶栏:透明愈心谷 Logo(AppHeader,全站)
├── 「自己」卡片标题展示账号昵称(默认意象词+场景词;档案页可改)
├── 穿衣指数 · 颜色搭配 · 养生推荐
├── 功能入口(4 列固定网格 · 多行换行 · 禁止横向滑动)
└── (可选)推荐内容
```
@@ -90,27 +91,36 @@ Empty:首页本身是入口,不因无档案空白死页。
| R2 | 不展示内部运维信息(API 连接状态等) |
| R3 | 入口文案符合 lexicon(个人画像/关系理解/成长会员…) |
| R4 | Tab 五名冻结:首页·探索·问答·陪伴·我的 |
| R5 | 「自己」卡片:穿衣指数 / 颜色搭配 / 养生推荐;有 Self 生日时按生日+**本地日历**+**十二时辰**易经种子生成;**每一时辰准点切换**;无档案或 LLM 不可用则本地回退 |
| R6 | 文案禁止占卜/算命恐吓与医疗疗效承诺;卦象仅作节律意象 |
| R7 | 贴士按 `user × 时辰起点` 持久缓存;GET 优先读缓存**即时返回**;缓存未命中时先回退/旧值,后台异步生成写入,禁止每次打开同步等 LLM |
| R8 | 响应含 `shichen` / `shichen_name` / `valid_until`(下一时辰起点);前端在 `valid_until` 自动静默刷新 |
---
## 8. 数据模型影响
经首页建档时写 `profiles`(见 profile Spec
经首页建档时写 `profiles``home_daily_tips(user_id, shichen_start, tips jsonb)` 缓存时辰贴士
---
## 9. API 需求
复用 `POST /profiles``POST /reports/portrait`;无专用 home API。
| Method | Path | 说明 |
|---|---|---|
| GET | `/api/v1/home/tools` | 宫格(已有) |
| GET | `/api/v1/home/daily-tips` | 自己卡片贴士(读缓存;含 valid_until |
| — | 复用 | `POST /profiles``POST /reports/portrait` |
---
## 10. 权限设计
| 能力 | Visitor |
|---|---|
| 浏览首页 | ✓ |
| 建档并生成画像 | ✓ |
| 能力 | Visitor | User |
|---|---|---|
| 浏览首页 | ✓ | ✓ |
| 建档并生成画像 | 需登录 | ✓ |
| daily-tips(个性化) | 回退文案 | 有 Self 生日则 LLM |
---
@@ -134,10 +144,26 @@ Empty:首页本身是入口,不因无档案空白死页。
**When** 提交主 CTA
**Then** 进入画像基础结果路径
**Given** 已登录且有 Self 生日、该时辰已有缓存
**When** GET `/home/daily-tips`
**Then** 立即返回缓存(含 clothing/palette/wellness / valid_until),不阻塞 LLM
**Given** 时辰切换到点
**When** 前端到达 `valid_until`
**Then** 静默刷新;穿衣/颜色/养生随新时辰更新
**Given** 无档案或无 LLM
**When** GET `/home/daily-tips`
**Then** `source=fallback` 且页面不白屏
---
## 13. AI 开发前检查
- [x] lexicon
- [x] 不恐吓 / 不算命话术
- [x] OpenAPI 已登记 `/home/daily-tips`
- [x] Spec 齐全
---
+5 -4
View File
@@ -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 CompleteP2 |
@@ -0,0 +1,123 @@
# Feature Spec: 用户文字输入合规校验
> Status: `Active` · Map: `横切 · 安全/合规` · Phase: `P2`
> 关联:[lexicon.md](../lexicon.md) · [.ai/security.md](../../security.md) · account-auth / profile / ask / companion
---
## 1. 功能定义
| 字段 | 内容 |
|---|---|
| Name | 文字输入合规(Text Compliance |
| Purpose | 所有用户提交的**自由文字**在服务端统一做合法性/合规校验;前端可做即时提示,**以后端拒绝为准** |
| Business Goal | 降低 XSS/注入、恐吓与医疗疗效话术、垃圾刷屏进入 DB 与模型上下文的风险 |
| In | Out |
|---|---|
| 昵称 · 档案显示名 · 问答 content · 心情/打卡 note · 成长计划 title/focus · 邀请 display_name · 意象场景自填 | 纯数字字段(生日年月日、分数、经纬度) |
| 长度 · 格式清洗 · XSS/控制字符 · lexicon 硬禁止 · 简易刷屏特征 | 完整内容审核平台 / 人工审 / 语音 |
| 错误码统一 | 改开放登录策略本身 |
---
## 2. 用户价值
1. 避免有害/违规文案入库与进模型。
2. 提交失败时得到可读中文原因。
3. N/A(安全横切,非付费面)。
---
## 3. 用户角色
Visitor / User / Admin 凡提交自由文字均适用(Admin 运营文案可后续加强;本 Spec **先 C 端用户 API**)。
---
## 4. 用户流程
```text
用户输入文字 →(可选)前端即时校验
→ API Handler/Service 调用 textsafe.Check(kind, text)
→ OK:规范化后入库/进模型
→ 拒绝:HTTP 400 · code 40060 · message 可读原因
```
---
## 56. 页面 / 状态
各既有表单;Error 态展示 `message`。无新页面。
---
## 7. Business Rules
| ID | Rule |
|---|---|
| R1 | **后端必须校验**所有用户自由文字;前端校验不得替代后端 |
| R2 | 种类与上限(rune):`nickname` 116`display_name` 124`ask_content` 12000`note` 0200`title` 140`focus` 040`scene` 180 |
| R3 | 拒绝控制字符(允许消息内 `\n` `\t`);拒绝 HTML/脚本片段(如 `<script``javascript:` |
| R4 | 命中 lexicon 硬禁止子串(占卜/算命/改命恐吓/疗效承诺/「测测」品牌自称等)→ 拒绝 |
| R5 | 刷屏特征:连续相同字 ≥8,或可见字符全相同且长度 ≥6 → 拒绝 |
| R6 | 密码字段不做 lexicon,仅拒空字节;手机号走既有数字规则 |
| R7 | 校验通过后写库的是 **Normalize** 后的文本(Trim;压缩首尾空白) |
| R8 | 错误码:`40060`;文案过 lexicon(勿用恐吓语气) |
---
## 8. 数据模型
无新表。进程内可选短时计数(本版刷屏以内容特征为主,不做跨请求配额表)。
---
## 9. API
既有写接口;拒绝时统一:
```json
{ "code": 40060, "message": "文案不合规:…", "data": null }
```
---
## 10. 权限
同各业务接口鉴权;校验在鉴权之后、写库之前。
---
## 11. 埋点
可选:`text_rejected{kind}`(本版可不接)。
---
## 12. 验收
| Given | When | Then |
|---|---|---|
| nickname 含「算命」 | PATCH /auth/me | 40060 |
| ask content 含 `<script` | POST messages | 40060 |
| display_name 正常「小愈」 | POST profile | 200 |
| note=`啊啊啊啊啊啊啊啊` | POST mood | 40060 |
---
## 13. AI 检查
- [x] lexicon
- [x] 后端权威
- [x] 不扩开放登录
---
## 14. Implementation
| 项 | 内容 |
|---|---|
| Package | `apps/api/internal/textsafe` |
| Mirror | `@yuxingu/utils` `validateUserText`(即时提示) |
| Wire | auth · profile · ask · companion · growth · synastry accept |
+15 -2
View File
@@ -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)
> 竞品对照:命理体系之生活向(能力同构,禁止算命/流年吉凶叙事)
@@ -98,6 +98,10 @@ CTA:节气陪伴 / 问答「生活节奏」/ 分享
| R5 | 推荐用语:体质倾向、生活建议、节律、平衡 |
| R6 | 可展示五行隐喻,须标注探索/非医疗 |
| R7 | 与星象性格、个人画像内容可互相链,不互相覆盖主路径 |
| R8 | **体质/五行倾向**随生日确定;**今日/本周生活建议**按**自然日(CST)**刷新(本周焦点随星期变) |
| R9 | GET `/reports/latest` 与 GET `/reports/{id}`type=rhythm):`as_of` ≠ 当日则**原 id 原地更新**时效段并落库;同日即时返回 |
| R10 | `summary``as_of` · `valid_until`(次日 00:00 CST);前端到点静默刷新;**不按十二时辰** |
| R11 | `summary.wuxing` 必含五行分布 `bars`(与愈心解码同源算法)及主/辅倾向;缺字段时 GET 原地补全 |
---
@@ -108,6 +112,7 @@ CTA:节气陪伴 / 问答「生活节奏」/ 分享
| `growth_reports.type` | `rhythm` / `life_rhythm` |
| 或 Constitution 专用表 | 若需独立聚合,实现时 ADR |
| 禁止 | `luck_score`、病名诊断字段 |
| 报告 JSON | `as_of` · `valid_until` · `today_tip` · `week_focus` |
---
@@ -155,13 +160,21 @@ CTA:节气陪伴 / 问答「生活节奏」/ 分享
**When** 扫 summary+detail
**Then** 符合 lexicon
**Given** 已有 rhythm 报告且 `as_of` 为昨日
**When** GET latest
**Then** `today_tip` / `week_focus` 按今日更新;报告 `id` 不变
**Given** 用户停留在 `/rhythm` 跨自然日
**When** 到达 `valid_until`
**Then** 前端静默刷新建议文案
---
## 13. AI 开发前检查
- [x] Spec 齐全 · 与 2.5 关系已写清
- [x] lexicon LifeRhythm
- [ ] OpenAPI/erd(编码时)
- [x] OpenAPI/erd(编码时)
---
@@ -0,0 +1,156 @@
# Feature Spec: 账户生命周期 / UserStatusOps · ECR-013B
> Status: `Active`**Closed** · Feature Complete)· Map: `§7 运营后台` · Phase: `Ops-D`
> Parent: **WAVE0-FROZEN** (`27f27a1`) · Predecessor: **ECR-013A Closed**
> ESS: `docs/ECR/ECR-013B-account-lifecycle.md`**Closed**
> Capability: `AccountLifecycle` · BC: `Identity_Profile`
> 模板:`docs/WAVE0/contracts/OPS-CONTRACT-TEMPLATE.md`
---
## ESS 门禁
1. Change Level = **L2** → 已 Approved + Coding auth + Implemented + **Closed**
2. ~~Contract Definition only~~ — 已完成
3. 实现证据:`docs/TEST_REPORT/ECR-013B.md` · `docs/CODE_REVIEW/ECR-013B.md`
---
## L0 Capability
| 字段 | 内容 |
|------|------|
| Capability ID | `AccountLifecycle` |
| Purpose | 以可审计状态机管理 C 端用户账户启停与封禁,阻断违规会话 |
| Why now | `users.status` 字段已存在但无运营迁移与强制拒绝;013A 权限面就绪后可挂状态写权限 |
| Non-goals | soft-delete / `deleted`;UGC 社区封禁;真支付;Admin 账号启停(已有 admin.status);推送通知 |
---
## L1 Bounded Context
| 字段 | 内容 |
|------|------|
| Primary BC | `Identity_Profile` |
| owns | `User` · `UserStatus` · `AccountStateTransition` |
| does_not_own | `AdminRole` · `Membership` · `Payment` · `BanRecord`Account_Risk 后置加深) |
| allowed | `Admin_Auth_Audit.write_audit`(经 admin 调用) |
| forbidden | `Payment` · `Membership.write` · soft-delete User |
权威:`.ai/domain/boundary-rules.md` · `Account_Risk` 仅允许经明确服务触发 `UserStatus.transition`
---
## 1. 功能定义
| 字段 | 内容 |
|------|------|
| Name | Account Lifecycle / UserStatus |
| Purpose | 运营可迁移用户状态;非 active 会话在 C 端被拒绝 |
| Business Goal | 风险处置最小闭环,不引入社区/UGC |
| In | Out |
|---|---|
| 状态机 `active/disabled/banned/suspended` | soft-delete / GDPR 擦除 |
| Admin 迁移 API + AuditLog + Transition 记录 | 站内推送 / 短信 |
| DeviceAuth / Bearer 对非 active 拒绝 | AdminAccount.status013A 外) |
| 权限码 `admin.users.status.write` | 行级数据 ACL |
---
## L2 Domain
| Entity | 不变式 / 状态机 |
|--------|----------------|
| `UserStatus` | 取值冻结:`active` · `disabled` · `banned` · `suspended`;非法值拒写 |
| `User.status` | 与 `UserStatus` 同值;默认 `active`(已有列) |
| `AccountStateTransition` | 只追加;记录 from→to · admin_id · reason · created_at |
### 合法迁移
```text
active → disabled | banned | suspended
disabled → active | banned
suspended → active | banned | disabled
banned → active | disabled
```
其它边 → **400**。同状态写 → **400**(幂等拒绝,避免空审计噪音)。
### C 端效应
| Status | DeviceAuth / 已登录 Bearer |
|--------|------------------------------|
| `active` | 放行 |
| `disabled` / `banned` / `suspended` | **401**(或 403 统一码,实现轮定一)+ 不可发新 session |
---
## L3 API Contract(意图 · 实现轮同步 OpenAPI)
前缀:`/api/v1/admin` · AdminAuth · 信封 `{code,message,data}`
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| POST | `/users/:id/status` | `admin.users.status.write` | 迁移 UserStatus;写 Transition + AuditLog |
| GET | `/users/:id/status-transitions` | `admin.users.read` | 最近迁移列表(limit |
既有 `GET /users` · `GET /users/:id` 已暴露 `status` — 保持;实现轮确认枚举文档化。
`contract_diff``docs/CONTRACT_DIFF/ECR-013B.yaml`
RBAC catalog **additive**`admin.users.status.write` → 种子写入 `super_admin`migration)。
---
## L4 Acceptance Criteria
### Functional
| ID | Given | When | Then |
|----|-------|------|------|
| AC-F-01 | 用户 `active` | POST status=`banned` + reason | 200GET user.status=`banned` |
| AC-F-02 | 用户 `banned` | POST status=`active` | 200;可再次 DeviceAuth |
| AC-F-03 | 非法边(如 `active``active` | POST | **400** |
| AC-F-04 | GET status-transitions | — | 含最近 from/to/admin/reason |
### Security
| ID | Given | When | Then |
|----|-------|------|------|
| AC-S-01 | Admin 无 `admin.users.status.write` | POST status | **403** + deny audit |
| AC-S-02 | 用户 `banned` | C 端带原 Bearer 访问受保护 API | **401/403** |
| AC-S-03 | 仅 DeviceAuth 无 Admin | POST `/admin/users/:id/status` | **401** |
| AC-S-04 | 无 Admin session | GET transitions | **401** |
### Performance
| ID | Given | When | Then |
|----|-------|------|------|
| AC-P-01 | transitions ≤1000 行/用户 | GET transitions limit=50 | 本机 P95 **&lt; 500ms** |
### Observability
| ID | Given | When | Then |
|----|-------|------|------|
| AC-O-01 | 迁移成功 | — | AuditLog action=`users.status.transition` |
| AC-O-02 | 迁移成功 | — | `account_state_transitions` 有对应行 |
---
## Forbidden(本切片)
- soft-delete / `deleted` 状态
- 真支付 · UGC · Prompt · 兑换码 · Crisis 深化
- 改 Admin RBAC 模型(仅 **additive** 一枚 permission
- 自动开下一 ECR
---
## Implementation Notes(实现轮才执行)
| 项 | 内容 |
|----|------|
| Migration | YEStransitions 表;permission 种子;必要时 CHECK/注释枚举) |
| Packages | service account/lifecycle · DeviceAuth/session 拒绝 · admin handler · admin-h5 用户详情最小 CTA |
| Depends | ECR-013A Closedpermission middleware |
+18 -4
View File
@@ -20,9 +20,11 @@
| 用户列表 / 详情(档案摘要) | 首页运营位 / 工具配置 CMS |
| 订单列表 | 优惠券 · 退款工单流 |
| 成长会员只读 + **授予/延长**(mock 履约) | 真支付渠道配置 |
| 操作审计日志 | 细粒度 RBAC(多角色矩阵) |
| 操作审计日志 | 真支付渠道配置 |
| `apps/admin-h5` 桌面友好壳 | 小程序后台 |
> Phase EECR-010):RBAC / 封禁 / 推送占位见 [ops-system.md](ops-system.md)。
---
## 2. 用户价值
@@ -206,7 +208,19 @@ Phase A 可先 `console`/本地;不挡验收。
| Phase | 内容 |
|---|---|
| **A(本 ECR** | 登录 · 用户 · 订单 · 授予会员 · 审计 · admin-h5 壳 |
| **BECR-007** | 自有埋点 · 管理端「数据」看板 — Spec `ops-analytics.md` |
| **CECR-008** | 首页宫格 CRUD · 测评上下架 — Spec `ops-content.md`Approved |
| **BECR-007 Closed** | 自有埋点 · 管理端「数据」看板 — Spec `ops-analytics.md` |
| **CECR-008 Closed** | 首页宫格 CRUD · 测评上下架 — Spec `ops-content.md` |
| **EECR-013A Closed** | Admin RBAC — Spec `ops-rbac.md` · Parent WAVE0-FROZEN |
| **FECR-013B Closed** | AccountLifecycle — Spec `ops-account-lifecycle.md` |
| **GECR-014 Closed** | MembershipPlan — Spec `ops-membership-plan.md` |
| **HECR-015 Closed** | RedemptionCode — Spec `ops-redemption-code.md` |
| **IECR-016 Closed** | UserIntelligence — Spec `ops-user-intelligence.md` |
| **JECR-017 Closed** | AskOperations — Spec `ops-ask-operations.md` |
| **KECR-018 Closed** | Entitlement — Spec `ops-entitlement.md` |
| **LECR-019 Closed** | ContentSafety FilterRule — Spec `ops-content-safety.md` |
| **MECR-020 Closed** | QualityFeedback — Spec `ops-quality-feedback.md` |
| **NECR-021 Closed** | AICoreConfig SystemPrompt — Spec `ops-ai-core-config.md` |
| **OECR-022 Closed** | CrisisCare CrisisPolicy — Spec `ops-crisis-care.md` |
| 后置 | Knowledge·Tools 写面 / CrisisEvent / 真支付(Loop 续跑) |
| D | 订单筛选 · 展示价 · 退款只读(另开 ECR) |
| E | RBAC · 封禁 · 推送占位(另开 ECR) |
| 后置 | 封禁加深(Account_Risk)· 推送占位 |
@@ -0,0 +1,41 @@
# Feature Spec: AICoreConfig · SystemPromptOps · ECR-021
> Status: `Active`Loop continuous · **ECR-021 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-020 Closed
> Capability: `AICoreConfig` · BC: `Ask_Ops`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
Prompt 在线编辑/发布 · KnowledgeSource/Chunk · ToolDefinition · 切换运行时引擎 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `SystemPrompt` | code 唯一;body 为模板文本;active/system;本切片只读 |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/ai/system-prompts` | `admin.ai_config.read` | 列表 |
| GET | `/admin/ai/system-prompts/:id` | 同上 | 详情(含 body |
## Migration
`000022``system_prompts` + 种子 `ask_default` + 授予权限
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含 ask_default |
| AC-F-02 | get 返回 body 非空 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无 ai_config.read → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-021.yaml`
+2 -2
View File
@@ -1,7 +1,7 @@
# Feature Spec: 运营行为分析(Ops-B
> Status: `Active` · Map: `§7 运营后台` · Phase: `Ops-B`
> ESS`docs/ECR/ECR-007-ops-analytics.md`Approved · L2)· BD-2026-007 Approved
> ESS`docs/ECR/ECR-007-ops-analytics.md`**Closed** · L2)· BD-2026-007 Approved
> 关联:[ops-admin.md](ops-admin.md) · [analytics.md](analytics.md) · [cece-frontend-re/complete-design/40-ops-commerce.md](cece-frontend-re/complete-design/40-ops-commerce.md)
---
@@ -118,7 +118,7 @@ analytics_daily_page(可选汇总)
- 卡片:DAU、新增、会话数、人均会话时长
- 图:日活趋势;页面均停留 TOP
- 表:页面明细、退出页 TOP、点击 TOP
- 漏斗:画像完成 → 深度 CTA → 支付成功
- 漏斗:探索列表 → 开始作答 → 量表完成 → 画像完成 → 深度 CTA → 支付成功
视觉:愈心谷色系;品牌仅 logo,无「运营后台」字样。
@@ -0,0 +1,42 @@
# Feature Spec: AskOperationsOps · ECR-017
> Status: `Active`Loop continuous · **ECR-017 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-016 Closed
> Capability: `AskOperations` · BC: `Ask_Ops`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
改写/删除消息正文 · SystemPrompt/Knowledge 配置 · QualityFeedback 写回 · AICoreConfig · 真支付 · UGC
## L2 读模型
| 概念 | 语义 |
|------|------|
| `AskSessionView` | 线程元数据 + message_count(列表) |
| 会话详情 | 只读消息序列(role/content/created_at |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/ask/threads` | `admin.ask.read` | 列表(可选 `user_id` |
| GET | `/admin/ask/threads/:id` | `admin.ask.read` | 会话 meta(不含原文) |
| GET | `/admin/ask/threads/:id/messages` | `admin.ask.transcript.read` | 对话原文(审计 `ask.transcript.read` |
## Migration
`000019`:为 `super_admin` 授予 `admin.ask.read`(无新业务表)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | 有消息线程 → list 含该 thread · message_count≥1 |
| AC-F-02 | detail 返回 user+assistant 消息 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无 ask.read → 403 |
| AC-P-01 | list &lt; 500ms 本机 |
| AC-O-01 | N/A 只读(无写审计强制) |
contract_diff: `docs/CONTRACT_DIFF/ECR-017.yaml`
@@ -0,0 +1,112 @@
# Feature Spec: OpsCMS · Banner 写面(Ops · ECR-041
> Status: `Active`**ECR-041 Closed** · Feature Complete
> Map: `§7 运营后台` · Capability: `OpsCMS` · BC: `Ops_CMS_NoUGC`
> Parent: WAVE0-FROZEN · Write-Wave: `docs/WAVE0/WRITE_WAVE_AUTHORIZATION.md`
> Predecessor: ECR-024 ClosedBanner 只读)
> 关联:[ops-banner.md](ops-banner.md)(只读基线)· [ops-content.md](ops-content.md) · [home.md](home.md)
---
## 1. 目标
让运营可 **创建 / 更新 / 上下架** BannerC 端首页对 `placement=home` 的 active Banner **生效**;写操作鉴权 + 审计;失败可回退静态。
验证 Write-Wave 首刀闭环,**不**做 FeedSlot、真支付、UGC。
## 2. In / Out
| In | Out |
|----|-----|
| AdminBanner create / update / 设 `active` | **FeedSlot** 一切写与 API |
| 加法权限 `admin.cms.write`(既有 RBAC | soft-delete 物理删行(`system=true` 禁止删) |
| C 端:`GET` active banners(至少 `placement=home` | ScheduledPublication 写 · 外链任意 URL |
| 首页推荐区:API Banner 投影优先,空则回退静态 `homeFeeds` | 把 FeedSlot 与 Banner 合并为一 ECR |
| `admin_audit_logs` 记录写操作 | Crisis / Handoff / ContentSafety 写 · 真支付 · UGC |
| OpenAPI · contract_diff · integration | 新权限子系统 · 改引擎分层 |
### 与 `homeFeeds` 的关系(诚实边界)
- ECR-008 将「首页 Feeds CMS」后置;表意信息流位属 **FeedSlot**ECR-025 只读,写面另 ECR)。
- **本切片**:用 **Bannerplacement=home** 驱动首页推荐区展示(投影字段见下);**不**写 `ops_feed_slots`
- 完整 Feeds 槽位运营(多 slot、排期)→ 后续 FeedSlot 写面 ECR。
## 3. Domain
| 概念 | 语义 |
|------|------|
| `Banner` | `code` 唯一;`placement` ∈ {home,explore,ask}`active``system` |
| 写语义 | upsert 字段;`active=false` = 下架;禁止 soft-delete 列/物理删 system 行 |
| C 端投影 | 仅 `active=true`;按 `sort_order` 升序 |
### 首页投影映射(Banner → 原 homeFeeds 卡)
| homeFeeds 字段 | 来源 |
|----------------|------|
| `to` | `link_path`(须站内 `/` 相对路径) |
| `title` | `title` |
| `meta` | 可选:无则空串或固定「运营推荐」 |
| `icon` / `tone` / `stat` / `tag` | 本切片可用缺省;**不**扩表除非 migration 明确(默认缺省,避免 scope creep |
## 4. API
### Admin(既有前缀 `/api/v1/admin/cms`
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/banners` · `/banners/{id}` | `admin.cms.read` | 保持 ECR-024 |
| POST | `/banners` | `admin.cms.write` | 创建(code 唯一) |
| PUT | `/banners/{id}` | `admin.cms.write` | 更新 title/placement/image_url/link_path/sort_order/active |
| — | DELETE | — | **不做**(下架用 `active=false` |
校验:`link_path` 若非空须以 `/` 开头、禁 `http(s):``placement` 枚举;`system=true` 行不可改 `code`、不可删。
### C 端
| Method | Path | Auth | 语义 |
|--------|------|------|------|
| GET | `/api/v1/home/banners` | DeviceAuth(与 `/home/tools` 同级) | 仅 `active`query `placement` 默认 `home` |
## 5. Permission / Audit
- Migration 授予 `super_admin``admin.cms.write`(加法码,非新 RBAC 模型)。
- 每次 POST/PUT 写 `admin_audit_logs`actor · action · target id/code · diff 摘要)。
## 6. Migration
- 若仅需权限种子:`000051_ops_banner_write`(以 TRACEABILITY Max+1 为准,编码时再锁)。
- **表结构**:默认复用 `ops_banners`;本切片 **不**为 FeedSlot 改表。若投影缺省不够且 Human 未扩 scope,不得私自加列。
## 7. UI
| 端 | 行为 |
|----|------|
| admin-h5 `/cms` | Banner 列表 + 新建/编辑/上下架(只动 Banner;FeedSlot 仍只读展示可保留) |
| user-h5 首页 | `GET /home/banners` → 推荐区;失败或空 → 静态 `homeFeeds` |
## 8. AC
| ID | Then |
|----|------|
| AC-F-01 | POST 合法 Banner → list/get 可见 |
| AC-F-02 | PUT `active=false` → C 端 list 不含 |
| AC-F-03 | PUT `active=true` + placement=home → `GET /home/banners` 含该项 |
| AC-F-04 | 重复 `code` → 4xx |
| AC-F-05 | 非法 `link_path`(外链)→ 4xx |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 仅 read 无 write → POST/PUT 403 |
| AC-A-01 | 写操作产生审计行 |
| AC-C-01 | H5 首页:有 active home Banner 时推荐区非纯静态 |
| AC-C-02 | API 失败时首页不白屏(回退静态) |
| AC-O-01 | 不触碰 FeedSlot 写路径 · 无 soft-delete · 无支付 |
## 9. Non-goalsSTOP 对齐)
FeedSlot 写 · ScheduledPublication 写 · UGC · 真支付 · soft-delete · Crisis/Handoff/ContentSafety 写 · 扩大为本 ECR 合并多概念。
## 10. Trace
- ECR: `docs/ECR/ECR-041-banner-write.md`
- BD: `docs/BACKEND_DESIGN/BD-2026-041-banner-write.md`
- PRODUCT_SPEC · CONTRACT_DIFF · TEST_REPORT · CODE_REVIEW(实现阶段)
- Auth: `docs/WAVE0/WRITE_WAVE_AUTHORIZATION.md`
+42
View File
@@ -0,0 +1,42 @@
# Feature Spec: OpsCMS · BannerOps · ECR-024 只读基线)
> Status: `Active`**ECR-024 Closed** · 只读)
> Parent: WAVE0-FROZEN · Predecessor: ECR-023 Closed
> Capability: `OpsCMS` · BC: `Ops_CMS_NoUGC`
> 授权(只读队列):`docs/WAVE0/LOOP_AUTHORIZATION.md`
> **写面:** [ops-banner-write.md](ops-banner-write.md)**ECR-041** · Write-Wave
## Non-goals(本只读 Spec
Banner 写发布(见 ECR-041)· FeedSlot · ScheduledPublication · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `Banner` | code 唯一;placement ∈ {home,explore,ask}active/system;本切片只读 |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/cms/banners` | `admin.cms.read` | 只读 |
| GET | `/admin/cms/banners/{id}` | `admin.cms.read` | 只读 |
## Migration
`000025`:表 + 种子(若有) + 授予 admin.cms.read
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-024.yaml`
@@ -0,0 +1,41 @@
# Feature Spec: ContentSafety · BlockPolicyOps · ECR-029
> Status: `Active`Loop continuous · **ECR-029 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-028 Closed
> Capability: `ContentSafety` · BC: `Content_Safety`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
策略写发布 · 用户侧硬拦截上线 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `BlockPolicy` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/content-safety/block-policies` | `admin.content_safety.read` | 只读 |
| GET | `/admin/content-safety/block-policies/{id}` | `admin.content_safety.read` | 只读 |
## Migration
`000030`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-029.yaml`
@@ -0,0 +1,42 @@
# Feature Spec: ContentSafety · FilterRuleOps · 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 &lt; 500ms |
| AC-O-01 | evaluate 不写 Audit(只读试匹配) |
contract_diff: `docs/CONTRACT_DIFF/ECR-019.yaml`
+2 -2
View File
@@ -1,7 +1,7 @@
# Feature Spec: 运营内容配置(Ops-C
> Status: `Active` · Map: `§7 运营后台` · Phase: `Ops-C`
> ESS`docs/ECR/ECR-008-ops-content.md`Approved · L2)· BD-2026-008 Approved
> ESS`docs/ECR/ECR-008-ops-content.md`**Closed** · L2)· BD-2026-008 Approved
> 关联:[ops-admin.md](ops-admin.md) · [ops-analytics.md](ops-analytics.md) · [home.md](home.md) · [explore-test.md](explore-test.md)
---
@@ -27,7 +27,7 @@
| In | Out |
|---|---|
| 首页宫格 CRUD(最多约 24 项;默认 12 | 首页 Feeds 信息流 CMS(本 ECR 后置,仍用静态 `homeFeeds` |
| 首页宫格 CRUD(最多约 24 项;默认 12 | 首页 Feeds/推荐区运营:ECR-008 后置;**Banner 写面见 ECR-041**;完整 FeedSlot 写另 ECR |
| 工具 path / icon 白名单校验 | 任意外链、自定义 SVG 上传 |
| scales 列表 + 上架/下架 + 审计 | 新建量表题目编辑器 · 探索目录全量 CMS |
| Admin「内容」页 | RBAC / 封禁 / 推送(Ops-E |
@@ -0,0 +1,42 @@
# Feature Spec: CrisisCare · CrisisPolicyOps · 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 &lt; 500ms |
| AC-O-01 | evaluate 不写 Audit/Event |
contract_diff: `docs/CONTRACT_DIFF/ECR-022.yaml`
@@ -0,0 +1,41 @@
# Feature Spec: CrisisCare · CrisisEventOps · ECR-031
> Status: `Active`Loop continuous · **ECR-031 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-030 Closed
> Capability: `CrisisCare` · BC: `Content_Safety`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
事件写入工单流 · 医疗诊断 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `CrisisEvent` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/crisis/events` | `admin.crisis.read` | 只读 |
| GET | `/admin/crisis/events/{id}` | `admin.crisis.read` | 只读 |
## Migration
`000032`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-031.yaml`
@@ -0,0 +1,36 @@
# Feature Spec: EntitlementOps · ECR-018
> Status: `Active`Loop continuous · **ECR-018 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-017 Closed
> Capability: `CommerceEntitlement` · BC: `Membership_Orders`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
真支付 · 改订单/退款 · 新建权益矩阵表 · UGC · 改报告正文 · ask_pack SKU
## L2 读模型(无 migration
| 概念 | 来源 |
|------|------|
| `Entitlement` | Membership + DeepAccess 列表 + ask_paid_quota 聚合 |
| flags | `report_detail_via_membership` · `deep_access_count` |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/users/:id/entitlements` | `admin.users.read` | 用户权益只读视图 |
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | 授予会员后 flags.report_detail_via_membership=true |
| AC-F-02 | 有 deep_access 时 deep_accesses 非空或 count≥1 |
| AC-F-03 | 无会员无深度 → membership.active=false 且 count=0 仍 200 |
| AC-S-01 | 无 Admin → 401 |
| AC-P-01 | GET &lt; 500ms 本机 |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-018.yaml`
@@ -0,0 +1,40 @@
# Feature Spec: OpsCMS · FeedSlot 写面(Ops · ECR-042
> Status: `Active`**ECR-042 Closed**
> Map: `§7` · Capability: `OpsCMS` · BC: `Ops_CMS_NoUGC`
> Write-Wave: `docs/WAVE0/WRITE_WAVE_AUTHORIZATION.md`
> Predecessor: ECR-025 Closed · ECR-041 Closed
## Goal
运营可创建/更新/上下架 FeedSlot;C 端可读 active 槽位;首页主信息流区按 `home.main`(或 placement=home 的 active 槽)显隐;失败回退「仍展示推荐区」。
## In / Out
| In | Out |
|----|-----|
| Admin POST/PUT feed-slots · `admin.cms.write` | Banner 再扩 · ScheduledPublication 写 |
| C 端 GET `/home/feed-slots` | UGC 内容流正文 CMS · soft-delete |
| 首页:无 active home 槽 → 隐藏推荐区;API 失败 → 仍显示 | 真支付 · Crisis/Handoff · 新权限模型 |
## Domain
复用 `ops_feed_slots``code` 唯一 · `slot_key` · `placement` ∈ {home,explore,ask} · `active` · `system`system 不可改 code)。
## API
| Method | Path | Perm |
|--------|------|------|
| GET | `/admin/cms/feed-slots*` | cms.read |
| POST/PUT | `/admin/cms/feed-slots*` | cms.write |
| GET | `/home/feed-slots?placement=home` | DeviceAuth |
无 DELETE;下架 `active=false`
## AC
AC-F create/update/conflict/invalid · AC-S 401/403 · AC-A audit · AC-C 首页显隐与失败回退 · AC-O 无 Banner 合并 / 无 soft-delete
## Migration
无新表;权限已在 `000051`。本切片 **不新增 migration 文件**Max 保持 `000051`)。
+41
View File
@@ -0,0 +1,41 @@
# Feature Spec: OpsCMS · FeedSlotOps · ECR-025
> Status: `Active`Loop continuous · **ECR-025 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-024 Closed
> Capability: `OpsCMS` · BC: `Ops_CMS_NoUGC`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
FeedSlot 写发布 · ScheduledPublication · UGC · 真支付 · Banner 写
## L2 Domain
| 概念 | 语义 |
|------|------|
| `FeedSlot` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/cms/feed-slots` | `admin.cms.read` | 只读 |
| GET | `/admin/cms/feed-slots/{id}` | `admin.cms.read` | 只读 |
## Migration
`000026`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-025.yaml`
@@ -0,0 +1,72 @@
# Feature Spec: GrowthInsights · FunnelDefinition 写面(Ops · ECR-047
> Status: `Active`**Implemented · ECR-047 Closed**
> Map: `§7` · Capability: `GrowthInsights` · BC: `Analytics_OpsB`
> Auth: `docs/WAVE0/ECR-047_WRITE_AUTHORIZATION.md`**仅 ECR-047**
> Predecessor: ECR-039 Closed(只读)· ECR-046 Closed
> 关联:[ops-funnel-definition.md](ops-funnel-definition.md)
---
## 1. 目标
运营可 **创建 / 更新 / 上下架** `FunnelDefinition`(漏斗目录);写鉴权 + 审计;**无**新 C 端路由。
**Closed 后立即 STOP**,不自动 ECR-048。
## 2. In / Out
| In | Out |
|----|-----|
| Admin POST/PUT `/admin/analytics/funnel-definitions*` | ReportTemplate 写 |
| 加法 `admin.growth.write` | SystemPrompt · Knowledge · Chunk |
| 复用表 `funnel_definitions` | soft-delete · 真支付 · UGC |
| 审计 | 题干 · Crisis/Handoff |
| | 自动 ECR-048 · 新 C 端路由 |
## 3. Domain
| 字段 | 规则 |
|------|------|
| `code` | 唯一;`^[a-z][a-z0-9_]{1,62}$``system=true` **不可改 code** |
| `title` | 必填 · ≤128 |
| `active` | `false` = 下架(唯一下架手段) |
| `system` | 种子行;禁止物理删除 |
## 4. API
| Method | Path | 权限 |
|--------|------|------|
| GET | `/admin/analytics/funnel-definitions` · `/{id}` | `admin.growth.read` |
| POST | `/admin/analytics/funnel-definitions` | `admin.growth.write` |
| PUT | `/admin/analytics/funnel-definitions/{id}` | `admin.growth.write` |
无 DELETE · 无新 C 端路径。
## 5. Permission / Migration
- Migration `000056_ops_funnel_definition_write`:授予 `super_admin``admin.growth.write`
- 编码时占号(Max=`000055``000056`
## 6. UI
admin-h5 漏斗定义页:列表 + 新建/编辑/上下架。
## 7. AC
| ID | Then |
|----|------|
| AC-F-01 | POST → list/get 可见 |
| AC-F-02 | PUT active=false → list 仍可见且 active=false |
| AC-F-03 | 重复 code → 409 |
| AC-S-01/02 | 401;仅 growth.read → POST 403 |
| AC-A-01 | 写产生审计 |
| AC-O-01 | 无 soft-delete · 无 ReportTemplate/Prompt 写路径 |
## 8. Closed 后
**立即 STOP**。ECR-048 须新 Candidate Review + Human 拍板。
## 9. Trace
ECR-047 · BD-2026-047 · CONTRACT_DIFF · ECR-047_WRITE_AUTHORIZATION
@@ -0,0 +1,42 @@
# Feature Spec: GrowthInsights · FunnelDefinitionOps · ECR-039
> Status: `Active`Loop continuous · **ECR-039 Closed**
> **写面:** [ops-funnel-definition-write.md](ops-funnel-definition-write.md)**ECR-047 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-038 Closed
> Capability: `GrowthInsights` · BC: `Analytics_OpsB`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
漏斗写配置 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `FunnelDefinition` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/analytics/funnel-definitions` | `admin.growth.read` | 只读 |
| GET | `/admin/analytics/funnel-definitions/{id}` | `admin.growth.read` | 只读 |
## Migration
`000040`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-039.yaml`
@@ -0,0 +1,41 @@
# Feature Spec: AskOperations · HandoffCaseOps · ECR-033
> Status: `Active`Loop continuous · **ECR-033 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-032 Closed
> Capability: `AskOperations` · BC: `Ask_Ops`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
转人工写流 · 顾问执业 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `HandoffCase` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/ask/handoffs` | `admin.ask.read` | 只读 |
| GET | `/admin/ask/handoffs/{id}` | `admin.ask.read` | 只读 |
## Migration
`000034`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-033.yaml`
@@ -0,0 +1,95 @@
# Feature Spec: ExploreConfig · ImageCardDeck 写面(Ops · ECR-045
> Status: `Active`**Implemented · ECR-045 Closed**
> Map: `§7` · Capability: `ExploreConfig` · BC: `Explore_Reports`
> Auth: `docs/WAVE0/ECR-045_WRITE_AUTHORIZATION.md`Human 批准 · **仅 ECR-045**
> Predecessor: ECR-037 Closed(只读)· ECR-044 ClosedRhythm 写)
> 关联:[ops-image-card-deck.md](ops-image-card-deck.md)
---
## 1. 目标
运营可 **创建 / 更新 / 上下架** `ImageCardDeck`(牌组目录);写操作鉴权 + 审计;C 端可读 active deck 并影响 `/cards` 可用性;失败回退「现网可抽」。
**Closed 后立即 STOP**,不自动 ECR-046。
## 2. In / Out
| In | Out |
|----|-----|
| Admin POST/PUT `/admin/explore/image-card-decks*` | **牌面/单卡内容 CRUD** · Star / Rhythm / Scale |
| 复用 `admin.explore.write` | SystemPrompt · Knowledge · Chunk |
| 复用表 `image_card_decks` | soft-delete · 真支付 · UGC |
| C 端 GET active decks | Crisis / Handoff 写 |
| `/cards`:无 active → 空态禁抽卡 CTA;失败 → 回退可抽 | 自动 ECR-046 |
| 审计 | 新权限子系统 |
## 3. Domain
| 字段 | 规则 |
|------|------|
| `code` | 唯一;`^[a-z][a-z0-9_]{1,62}$``system=true` 不可改 code |
| `title` | 必填 · ≤128 |
| `active` | `false` = 下架(唯一下架手段) |
| `system` | 种子行;禁止物理删除 |
## 4. API
### Admin
| Method | Path | 权限 |
|--------|------|------|
| GET | `/admin/explore/image-card-decks` · `/{id}` | `admin.explore.read` |
| POST | `/admin/explore/image-card-decks` | `admin.explore.write` |
| PUT | `/admin/explore/image-card-decks/{id}` | `admin.explore.write` |
无 DELETE。
### C 端
| Method | Path | Auth | 语义 |
|--------|------|------|------|
| GET | `/api/v1/cards/decks` | DeviceAuth | 仅 `active=true`;按 code 排序 |
## 5. C 端生效
| 条件 | user-h5 `/cards` |
|------|------------------|
| ≥1 active | 保持现网抽卡 |
| 成功且 0 active | 「意象牌组暂未开放」空态;禁用抽卡 CTA |
| 请求失败 | **回退**不阻断抽卡 |
## 6. Permission / Migration
- Migration `000054_ops_image_card_deck_write`:幂等确认 `super_admin``admin.explore.write`
- 编码时占号(Max=`000053`
## 7. UI
| 端 | 行为 |
|----|------|
| admin-h5 意象牌组页 | 列表 + 新建/编辑/上下架(仅 Deck) |
| user-h5 `/cards` | 按 §5 |
## 8. AC
| ID | Then |
|----|------|
| AC-F-01 | POST → list/get 可见 |
| AC-F-02 | PUT active=false → C 端不含 |
| AC-F-03 | 有 active → C 端非空 |
| AC-F-04 | 重复 code → 409 |
| AC-S-01/02 | 401;仅 read → POST 403 |
| AC-A-01 | 写产生审计 |
| AC-C-01 | 全下架后空态/禁 CTA |
| AC-C-02 | API 失败回退可抽 |
| AC-O-01 | 无牌面写路径 · 无 soft-delete |
## 9. Closed 后
**立即 STOP**。ECR-046 须新 Candidate Review + Human 拍板。
## 10. Trace
ECR-045 · BD-2026-045 · CONTRACT_DIFF · ECR-045_WRITE_AUTHORIZATION
@@ -0,0 +1,42 @@
# Feature Spec: ExploreConfig · ImageCardDeckOps · ECR-037
> Status: `Active`Loop continuous · **ECR-037 Closed**
> **写面:** [ops-image-card-deck-write.md](ops-image-card-deck-write.md)**ECR-045 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-036 Closed
> Capability: `ExploreConfig` · BC: `Explore_Reports`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
牌组写发布 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `ImageCardDeck` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/explore/image-card-decks` | `admin.explore.read` | 只读 |
| GET | `/admin/explore/image-card-decks/{id}` | `admin.explore.read` | 只读 |
## Migration
`000038`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-037.yaml`
@@ -0,0 +1,41 @@
# Feature Spec: CrisisCare · InterventionOutcomeOps · ECR-032
> Status: `Active`Loop continuous · **ECR-032 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-031 Closed
> Capability: `CrisisCare` · BC: `Content_Safety`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
干预写回 · 医疗诊断 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `InterventionOutcome` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/crisis/interventions` | `admin.crisis.read` | 只读 |
| GET | `/admin/crisis/interventions/{id}` | `admin.crisis.read` | 只读 |
## Migration
`000033`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-032.yaml`
@@ -0,0 +1,41 @@
# Feature Spec: AICoreConfig · KnowledgeChunkOps · ECR-027
> Status: `Active`Loop continuous · **ECR-027 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-026 Closed
> Capability: `AICoreConfig` · BC: `Ask_Ops`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
Embedding · 上传切块 · 运行时 RAG 接线 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `KnowledgeChunk` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/ai/knowledge-chunks` | `admin.ai_config.read` | 只读 |
| GET | `/admin/ai/knowledge-chunks/{id}` | `admin.ai_config.read` | 只读 |
## Migration
`000028`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-027.yaml`
@@ -0,0 +1,41 @@
# Feature Spec: AICoreConfig · KnowledgeSourceOps · ECR-023
> Status: `Active`Loop continuous · **ECR-023 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-022 Closed
> Capability: `AICoreConfig` · BC: `Ask_Ops`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
KnowledgeChunk / Embedding · 源文件上传 · 在线编辑发布 · ToolDefinition · 运行时 RAG 接线 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `KnowledgeSource` | code 唯一;source_kind ∈ {faq,policy,guide}active/system;本切片只读目录,不含 Chunk |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/ai/knowledge-sources` | `admin.ai_config.read` | 列表 |
| GET | `/admin/ai/knowledge-sources/:id` | 同上 | 详情 |
## Migration
`000024``knowledge_sources` + 种子 `ask_grounding`(权限已由 000022 授予)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含 ask_grounding |
| AC-F-02 | get 返回 source_kind 合法 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无 ai_config.read → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-023.yaml`
@@ -0,0 +1,77 @@
# Feature Spec: 会员套餐 MembershipPlanOps · ECR-014
> Status: `Active`Loop continuous · Approved · coding)· Map: `§7` · Phase: `Ops-D`
> Parent: **WAVE0-FROZEN** · Predecessor: **ECR-013B Closed**
> ESS: `docs/ECR/ECR-014-membership-plan.md`
> Capability: `CommerceEntitlement` · BC: `Membership_Orders`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
---
## ESS 门禁
1. L2 · Loop continuous:契约齐 → 自动 Approve + coding
2. **不含** 真支付 · 兑换码 · Entitlement 矩阵 · ask_pack 价目表
---
## L0 Capability
| 字段 | 内容 |
|------|------|
| Capability ID | `CommerceEntitlement` |
| Purpose | 运营可配置成长会员套餐时长与标价(mock 履约仍用既有支付) |
| Why now | planDays/金额硬编码;013B 完成后进入 Commerce 配置面最小切片 |
| Non-goals | 真支付网关 · RedemptionCode · Entitlement 细权 · ask_pack |
---
## L1 Bounded Context
| Primary BC | `Membership_Orders` |
| owns | `MembershipPlan` |
| does_not_own | `UserStatus` · `Payment` 适配器 · `AdminRole` |
| allowed | `Admin_Auth_Audit.write_audit` |
| forbidden | 真支付 · UGC |
---
## L2 Domain
| Entity | 不变式 |
|--------|--------|
| `MembershipPlan` | `code` ∈ {month,quarter,year} 本切片冻结;`duration_days`>0`amount_cents`≥0`active` 布尔 |
---
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/membership-plans` | `admin.membership.plans.read` | 列表 |
| GET | `/admin/membership-plans/:code` | `admin.membership.plans.read` | 详情 |
| PUT | `/admin/membership-plans/:code` | `admin.membership.plans.write` | 更新 title/days/amount/active + AuditLog |
履约:`GrantMembership` / membership `CreateOrder` 读表(缺行回退旧硬编码)。
`contract_diff``docs/CONTRACT_DIFF/ECR-014.yaml`
---
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | GET plans 含 month/quarter/year |
| AC-F-02 | PUT month days/amount → GET 一致 |
| AC-F-03 | GrantMembership 使用表内 duration_days |
| AC-S-01 | 无 write 权限 PUT → 403 |
| AC-S-02 | 无 Admin → 401 |
| AC-P-01 | GET list P95 &lt; 500ms 本机 |
| AC-O-01 | PUT 成功 → AuditLog `membership.plans.update` |
---
## Implementation Notes
Migration `membership_plans` + RBAC additive permissions · admin-h5 最小列表编辑页
@@ -0,0 +1,41 @@
# Feature Spec: ContentSafety · ModerationCaseOps · ECR-030
> Status: `Active`Loop continuous · **ECR-030 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-029 Closed
> Capability: `ContentSafety` · BC: `Content_Safety`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
审核写回 · 真 NLP 厂商 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `ModerationCase` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/content-safety/cases` | `admin.content_safety.read` | 只读 |
| GET | `/admin/content-safety/cases/{id}` | `admin.content_safety.read` | 只读 |
## Migration
`000031`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-030.yaml`
@@ -0,0 +1,41 @@
# Feature Spec: AdminGovernance · PrivacyRequestOps · ECR-034
> Status: `Active`Loop continuous · **ECR-034 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-033 Closed
> Capability: `AdminGovernance` · BC: `Admin_Auth_Audit`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
隐私请求履约写 · soft-delete · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `PrivacyRequest` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/privacy/requests` | `admin.privacy.read` | 只读 |
| GET | `/admin/privacy/requests/{id}` | `admin.privacy.read` | 只读 |
## Migration
`000035`:表 + 种子(若有) + 授予 admin.privacy.read
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-034.yaml`
@@ -0,0 +1,42 @@
# Feature Spec: QualityFeedbackOps · ECR-020
> Status: `Active`Loop continuous · **ECR-020 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-019 Closed
> Capability: `AskOperations` · BC: `Ask_Ops`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
改写消息正文 · SystemPrompt/Knowledge · HandoffCase 全量 · AICoreConfig · UGC 广场 · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `QualityFeedback` | 绑定 thread(可选 message);rating 15tag ∈ {helpful,off_topic,unsafe,other}source ∈ {admin,user} |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/ask/feedback` | `admin.ask.read` | 列表 |
| POST | `/admin/ask/threads/:id/feedback` | `admin.ask.feedback.write` | 运营提交 |
| POST | `/ask/threads/:id/feedback` | DeviceAuth 用户 | C 端评分 |
## Migration
`000021``ask_quality_feedback` + 授予 `admin.ask.feedback.write`
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | admin POST feedback → 200 · list 可见 |
| AC-F-02 | user POST feedback → 200 |
| AC-F-03 | 无效 rating → 400 |
| AC-S-01 | 无 Admin → admin list 401 |
| AC-S-02 | 无 feedback.write → admin POST 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | admin 提交写 AuditLog `ask.feedback.create` |
contract_diff: `docs/CONTRACT_DIFF/ECR-020.yaml`
+151
View File
@@ -0,0 +1,151 @@
# Feature Spec: 运营后台 RBACOps · ECR-013A
> Status: `Active`**Closed** · Feature Complete)· Map: `§7 运营后台` · Phase: `Ops-D`
> Parent: **WAVE0-FROZEN** (`27f27a1`)
> ESS: `docs/ECR/ECR-013A-admin-rbac.md`**Closed**
> Capability: `AdminGovernance` · BC: `Admin_RBAC`
> 模板:`docs/WAVE0/contracts/OPS-CONTRACT-TEMPLATE.md`
---
## ESS 门禁
1. Change Level = **L2** → 已 Approved + Coding auth + Implemented + **Closed**
2. ~~Contract Definition only~~ — 已完成
3. 实现证据:`docs/TEST_REPORT/ECR-013A.md` · `docs/CODE_REVIEW/ECR-013A.md`
---
## L0 Capability
| 字段 | 内容 |
|------|------|
| Capability ID | `AdminGovernance` |
| Purpose | 以最小权限原则约束管理员可执行的运营动作 |
| Why now | Ops-A 全员等价权限;进入 M1+ 前必须有权限面,否则越权不可治理 |
| Non-goals | 用户账户启停(ECR-013B);兑换码;CMSAsk Prompt;真支付;UGC |
---
## L1 Bounded Context
| 字段 | 内容 |
|------|------|
| Primary BC | `Admin_RBAC` |
| owns | `AdminRole` · `AdminPermission` |
| does_not_own | `User` · `Profile` · `UserStatus` · `Membership` · `Payment` |
| allowed | `Admin_Auth_Audit.write_audit` |
| forbidden | `Payment` · `Membership.write` · `DeviceAuth.user_token_issue` |
权威:`.ai/domain/boundary-rules.md`
---
## 1. 功能定义
| 字段 | 内容 |
|------|------|
| Name | Admin RBAC |
| Purpose | 角色与权限点绑定;API 级强制鉴权;变更可审计 |
| Business Goal | 支撑多运营角色,而不扩大 C 端攻击面 |
| In | Out |
|---|---|
| Role CRUD(最小:list/get/update permissions | UserStatus / 封禁(013B |
| `GET /admin/me` 返回 permissions | 细粒度数据行级 ACL |
| 既有 admin 写操作挂 permission 检查 | 新业务模块权限爆炸式新增(另 ECR) |
---
## L2 Domain
| Entity | 不变式 |
|--------|--------|
| `AdminRole` | `name` 唯一;系统角色 `super_admin` 不可删除 |
| `AdminPermission` | 稳定字符串码(见下表);只增不改语义 |
| `AdminAccount.role_id` | 每个账号恰好一个角色(本切片);无角色视为拒绝写操作 |
### Permission catalog(本切片冻结)
| Code | 覆盖既有能力 |
|------|----------------|
| `admin.users.read` | GET users / users/:id / stats |
| `admin.users.membership.grant` | POST membership/grant |
| `admin.users.ask_quota.grant` | POST ask-quota/grant |
| `admin.orders.read` | GET orders |
| `admin.audit.read` | GET audit-logs |
| `admin.analytics.read` | GET analytics/*Ops-B |
| `admin.content.write` | home tools / scales publishOps-C |
| `admin.roles.read` | GET roles |
| `admin.roles.write` | 变更角色权限 |
`super_admin` 种子拥有上表全部。
---
## L3 API Contract(意图 · 实现轮同步 OpenAPI)
前缀:`/api/v1/admin` · AdminAuth · 信封 `{code,message,data}`
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/roles` | `admin.roles.read` | 角色列表 |
| GET | `/roles/:id` | `admin.roles.read` | 角色 + permissions[] |
| PUT | `/roles/:id/permissions` | `admin.roles.write` | 全量替换权限集;写 AuditLog |
| GET | `/me` | (已登录) | **扩展**返回 `role` + `permissions[]` |
既有写接口:在实现轮挂上表 permission;缺权 → **403** + AuditLog(尝试记录)。
`contract_diff``docs/CONTRACT_DIFF/ECR-013A.yaml`
---
## L4 Acceptance Criteria
### Functional
| ID | Given | When | Then |
|----|-------|------|------|
| AC-F-01 | super_admin 已登录 | GET `/roles` | 200,含 `super_admin` |
| AC-F-02 | 角色存在 | PUT permissions 合法集合 | 200;再次 GET 一致 |
| AC-F-03 | GET `/me` | — | data 含 `permissions` 数组 |
### Security
| ID | Given | When | Then |
|----|-------|------|------|
| AC-S-01 | Admin 无 `admin.roles.write` | PUT `/roles/:id/permissions` | **403**;AuditLog 有拒绝或尝试记录 |
| AC-S-02 | Admin 无 `admin.users.membership.grant` | POST membership/grant | **403** |
| AC-S-03 | 仅 DeviceAuth | 访问 `/admin/roles` | **401** |
| AC-S-04 | 删除 `super_admin` 角色 | 任意 API | **拒绝**4xx |
### Performance
| ID | Given | When | Then |
|----|-------|------|------|
| AC-P-01 | 角色数 N≤100 | GET `/roles` | 本机 integration 环境下 P95 **&lt; 500ms** |
### Observability
| ID | Given | When | Then |
|----|-------|------|------|
| AC-O-01 | PUT permissions 成功 | — | AuditLog action 含 `roles.permissions.update` |
| AC-O-02 | AC-S-01 触发 | — | AuditLog 可追溯 admin_id + path |
---
## Forbidden(本切片)
- UserStatus / AccountLifecycle**ECR-013B**
- 真支付 · UGC · Prompt/RAG · 兑换码 · CMS Banner 新表
- 改 DeviceAuth / C 端五 Tab
---
## Implementation Notes(实现轮才执行)
| 项 | 内容 |
|----|------|
| Migration | YESroles / permissions 关联;accounts.role_id;种子 super_admin |
| Packages | `service/admin` · repo · middleware permission check · admin-h5 只读展示权限(最小) |
| Parent | WAVE0-FROZEN |
@@ -0,0 +1,43 @@
# Feature Spec: 兑换码 RedemptionCodeOps · ECR-015
> Status: `Active`Loop continuous · Approved · coding
> Parent: WAVE0-FROZEN · Predecessor: ECR-014 Closed
> Capability: `CommerceEntitlement` · BC: `Membership_Orders`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
真支付 · Entitlement 细权 · ask_pack 兑换 · UGC
## L2
| Entity | 不变式 |
|--------|--------|
| `RedemptionBatch` | label · plan_code∈membership_plans · quantity 1..100 |
| `RedemptionCode` | code 唯一;status unused→redeemed\|disabledredeemed 不可再兑 |
## L3 API
| Method | Path | Auth | 语义 |
|--------|------|------|------|
| POST | `/admin/redemption-batches` | `admin.membership.codes.write` | 批量生成 |
| GET | `/admin/redemption-batches` | `admin.membership.codes.read` | 批次列表 |
| GET | `/admin/redemption-batches/:id/codes` | `admin.membership.codes.read` | 码列表 |
| POST | `/admin/redemption-codes/:id/disable` | `admin.membership.codes.write` | 作废 unused |
| POST | `/membership/redeem` | DeviceAuth+已注册 | 兑码→延长会员 |
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | POST batch quantity=3 → 3 unused codes |
| AC-F-02 | C端 redeem → membership active;码=redeemed |
| AC-F-03 | 再兑同一码 → 400 |
| AC-F-04 | disable unused → status=disabled;兑 → 400 |
| AC-S-01 | 无 write → POST batch 403 |
| AC-S-02 | 未登录兑码 → 401 |
| AC-P-01 | GET batches &lt; 500ms |
| AC-O-01 | 生成 AuditLog `redemption.batch.create` |
| AC-O-02 | 兑换可追溯 redeemed_by |
contract_diff: `docs/CONTRACT_DIFF/ECR-015.yaml`
@@ -0,0 +1,73 @@
# Feature Spec: GrowthInsights · ReportTemplate 写面(Ops · ECR-048
> Status: `Active`**Implemented · ECR-048 Closed**
> Map: `§7` · Capability: `GrowthInsights` · BC: `Explore_Reports`
> Auth: `docs/WAVE0/ECR-048_WRITE_AUTHORIZATION.md`**仅 ECR-048**
> Predecessor: ECR-038 Closed(只读)· ECR-047 ClosedFunnel 写)
> 关联:[ops-report-template.md](ops-report-template.md)
---
## 1. 目标
运营可 **创建 / 更新 / 上下架** `ReportTemplate`(报告模板目录);写鉴权 + 审计;**无**新 C 端路由。
**Closed 后立即 STOP**,不自动 ECR-049。
## 2. In / Out
| In | Out |
|----|-----|
| Admin POST/PUT `/admin/growth/report-templates*` | Funnel 再写 |
| 复用 `admin.growth.write` | SystemPrompt · Knowledge · Chunk |
| 复用表 `report_templates` | soft-delete · 真支付 · UGC |
| 审计 | 题干 · Crisis/Handoff |
| | 自动 ECR-049 · 新 C 端路由 |
## 3. Domain
| 字段 | 规则 |
|------|------|
| `code` | 唯一;`^[a-z][a-z0-9_]{1,62}$``system=true` **不可改 code** |
| `title` | 必填 · ≤128 |
| `scene` | 必填;`^[a-z][a-z0-9_]{0,30}$` · ≤32 |
| `active` | `false` = 下架 |
| `system` | 种子行;禁止物理删除 |
## 4. API
| Method | Path | 权限 |
|--------|------|------|
| GET | `/admin/growth/report-templates` · `/{id}` | `admin.growth.read` |
| POST | `/admin/growth/report-templates` | `admin.growth.write` |
| PUT | `/admin/growth/report-templates/{id}` | `admin.growth.write` |
无 DELETE · 无新 C 端路径。
## 5. Permission / Migration
- Migration `000057_ops_report_template_write`:幂等确认 `super_admin``admin.growth.write`
- 编码时占号(Max=`000056``000057`
## 6. UI
admin-h5 报告模板页:列表 + 新建/编辑/上下架。
## 7. AC
| ID | Then |
|----|------|
| AC-F-01 | POST → list/get 可见 |
| AC-F-02 | PUT active=false → list 仍可见且 active=false |
| AC-F-03 | 重复 code → 409 |
| AC-S-01/02 | 401;仅 growth.read → POST 403 |
| AC-A-01 | 写产生审计 |
| AC-O-01 | 无 soft-delete · 无 Funnel/Prompt 写路径 |
## 8. Closed 后
**立即 STOP**。ECR-049 须新 Candidate Review + Human 拍板。
## 9. Trace
ECR-048 · BD-2026-048 · CONTRACT_DIFF · ECR-048_WRITE_AUTHORIZATION
@@ -0,0 +1,42 @@
# Feature Spec: GrowthInsights · ReportTemplateOps · ECR-038
> Status: `Active`Loop continuous · **ECR-038 Closed**
> **写面:** [ops-report-template-write.md](ops-report-template-write.md)**ECR-048 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-037 Closed
> Capability: `GrowthInsights` · BC: `Explore_Reports`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
模板写发布 · 广告投放 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `ReportTemplate` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/growth/report-templates` | `admin.growth.read` | 只读 |
| GET | `/admin/growth/report-templates/{id}` | `admin.growth.read` | 只读 |
## Migration
`000039`:表 + 种子(若有) + 授予 admin.growth.read
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-038.yaml`
@@ -0,0 +1,98 @@
# Feature Spec: ExploreConfig · RhythmConfig 写面(Ops · ECR-044
> Status: `Active`**Implemented · ECR-044 Closed**
> Map: `§7` · Capability: `ExploreConfig` · BC: `Explore_Reports`
> Auth: `docs/WAVE0/ECR-044_WRITE_AUTHORIZATION.md`Human 批准 · **仅 ECR-044**
> Predecessor: ECR-036 Closed(只读)· ECR-043 ClosedStarConfig 写)
> 关联:[ops-rhythm-config.md](ops-rhythm-config.md) · [life-rhythm.md](life-rhythm.md)
---
## 1. 目标
运营可 **创建 / 更新 / 上下架** `RhythmConfig`;写操作鉴权 + 审计;C 端可读 active 配置并影响节律入口可用性;失败回退「现网可生成」。
验证 ExploreConfig 写面同构复用;**Closed 后立即 STOP**,不自动 ECR-045。
## 2. In / Out
| In | Out |
|----|-----|
| Admin POST/PUT `/admin/explore/rhythm-configs*` | **StarConfig / ImageCardDeck / ScaleDefinition** 一切 |
| 复用权限 `admin.explore.write` | SystemPrompt · KnowledgeSource · Chunk · ToolDefinition |
| 复用表 `rhythm_configs`code/title/active/system | soft-delete · 改节律引擎 · 真支付 · UGC |
| C 端 GET active rhythm configs | Crisis / Handoff / ContentSafety 写 |
| 节律页:无 active → 温和不可用态;API 失败 → 回退可生成 | 合并多概念 · 自动开 ECR-045 |
| 审计 `admin_audit_logs` | 新权限子系统 |
## 3. Domain
| 字段 | 规则 |
|------|------|
| `code` | 唯一;`^[a-z][a-z0-9_]{1,62}$``system=true` 不可改 code |
| `title` | 必填 · ≤128 |
| `active` | `false` = 下架(唯一下架手段) |
| `system` | 种子行;禁止物理删除 |
## 4. API
### Admin
| Method | Path | 权限 |
|--------|------|------|
| GET | `/admin/explore/rhythm-configs` · `/{id}` | `admin.explore.read`(保持) |
| POST | `/admin/explore/rhythm-configs` | `admin.explore.write` |
| PUT | `/admin/explore/rhythm-configs/{id}` | `admin.explore.write` |
无 DELETE。
### C 端
| Method | Path | Auth | 语义 |
|--------|------|------|------|
| GET | `/api/v1/rhythm/configs` | DeviceAuth | 仅 `active=true`;按 code 排序 |
## 5. C 端生效(可观察)
| 条件 | user-h5 `/rhythm` |
|------|-------------------|
| 至少 1 条 active | 保持现网生成路径 |
| 成功且 0 条 active | 展示「节律配置暂未开放」类空态;禁用主生成 CTA |
| 请求失败 | **回退**:不阻断现网生成 |
不改节律引擎、不改报告 JSON 契约。
## 6. Permission / Migration
- Migration `000053_ops_rhythm_config_write`:幂等确认 `super_admin``admin.explore.write`043 已授)
- 编码时占号(当前 Max=`000052`
## 7. UI
| 端 | 行为 |
|----|------|
| admin-h5 节律配置页 | 列表 + 新建/编辑/上下架(仅 RhythmConfig |
| user-h5 `/rhythm` | 按 §5 显隐 CTA |
## 8. AC
| ID | Then |
|----|------|
| AC-F-01 | POST 合法 → list/get 可见 |
| AC-F-02 | PUT `active=false` → C 端 list 不含 |
| AC-F-03 | 有 active → C 端 list 非空;节律页可生成 |
| AC-F-04 | 重复 code → 409 |
| AC-S-01/02 | 401;仅 read → POST 403 |
| AC-A-01 | 写产生审计 |
| AC-C-01 | 全下架后节律页空态/禁 CTA |
| AC-C-02 | C 端 API 失败不白屏、仍可走回退生成 |
| AC-O-01 | 无 Star/Card/Scale/Prompt 写路径;无 soft-delete |
## 9. Closed 后
**立即 STOP** Continuous Loop。
ECR-045 必须重新 Candidate Review + Human 单独拍板。
## 10. Trace
ECR-044 · BD-2026-044 · PRODUCT_SPEC · CONTRACT_DIFF · ECR-044_WRITE_AUTHORIZATION
@@ -0,0 +1,42 @@
# Feature Spec: ExploreConfig · RhythmConfigOps · ECR-036
> Status: `Active`Loop continuous · **ECR-036 Closed**
> **写面:** [ops-rhythm-config-write.md](ops-rhythm-config-write.md)**ECR-044**
> Parent: WAVE0-FROZEN · Predecessor: ECR-035 Closed
> Capability: `ExploreConfig` · BC: `Explore_Reports`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
配置写发布 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `RhythmConfig` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/explore/rhythm-configs` | `admin.explore.read` | 只读 |
| GET | `/admin/explore/rhythm-configs/{id}` | `admin.explore.read` | 只读 |
## Migration
`000037`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-036.yaml`
@@ -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 listPATCH 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
@@ -0,0 +1,43 @@
# Feature Spec: ExploreConfig · ScaleDefinitionOps · 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`
## Non-goals
PATCH status(已有 content.write)· 题干编辑 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `ScaleDefinition` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/explore/scales` | `admin.explore.read``admin.content.write` | 只读 |
| GET | `/admin/explore/scales/{id}` | `admin.explore.read``admin.content.write` | 只读 |
| GET | `/admin/scales` | `admin.explore.read``admin.content.write` | 只读(与 explore 同源) |
## Migration
Migration NO:复用 `scales` 表只读投影
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-040.yaml`
@@ -0,0 +1,41 @@
# Feature Spec: OpsCMS · ScheduledPublicationOps · ECR-026
> Status: `Active`Loop continuous · **ECR-026 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-025 Closed
> Capability: `OpsCMS` · BC: `Ops_CMS_NoUGC`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
定时发布写操作 · UGC · 真支付 · Banner/FeedSlot 写
## L2 Domain
| 概念 | 语义 |
|------|------|
| `ScheduledPublication` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/cms/publications` | `admin.cms.read` | 只读 |
| GET | `/admin/cms/publications/{id}` | `admin.cms.read` | 只读 |
## Migration
`000027`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-026.yaml`
@@ -0,0 +1,99 @@
# Feature Spec: ExploreConfig · StarConfig 写面(Ops · ECR-043
> Status: `Active`**Implemented · ECR-043 Closed**
> Map: `§7` · Capability: `ExploreConfig` · BC: `Explore_Reports`
> Auth: `docs/WAVE0/EXPLORE_CONFIG_WRITE_AUTHORIZATION.md`Human 批准 · **仅 ECR-043**
> Predecessor: ECR-035 Closed(只读)· CMS 041042 Closed
> 关联:[ops-star-config.md](ops-star-config.md) · [star-profile.md](star-profile.md)
---
## 1. 目标
运营可 **创建 / 更新 / 上下架** `StarConfig`;写操作鉴权 + 审计;C 端可读 active 配置并影响星座入口可用性;失败回退「现网可生成」。
验证 ExploreConfig 写面与 CMS 同类闭环;**Closed 后立即 STOP**,不自动 ECR-044。
## 2. In / Out
| In | Out |
|----|-----|
| Admin POST/PUT `/admin/explore/star-configs*` | **RhythmConfig / ImageCardDeck / ScaleDefinition** 一切 |
| 加法权限 `admin.explore.write` | SystemPrompt · KnowledgeSource · Chunk · ToolDefinition |
| 复用表 `star_configs`code/title/active/system | soft-delete · 改引擎/宫位制 · 真支付 · UGC |
| C 端 GET active star configs | Crisis / Handoff / ContentSafety 写 |
| 星座页:无 active → 温和不可用态;API 失败 → 回退可生成 | 合并多概念 · 自动开 ECR-044 |
| 审计 `admin_audit_logs` | 新权限子系统 |
## 3. Domain
| 字段 | 规则 |
|------|------|
| `code` | 唯一;`^[a-z][a-z0-9_]{1,62}$``system=true` 不可改 code |
| `title` | 必填 · ≤128 |
| `active` | `false` = 下架(唯一下架手段) |
| `system` | 种子行;禁止物理删除 |
## 4. API
### Admin
| Method | Path | 权限 |
|--------|------|------|
| GET | `/admin/explore/star-configs` · `/{id}` | `admin.explore.read`(保持) |
| POST | `/admin/explore/star-configs` | `admin.explore.write` |
| PUT | `/admin/explore/star-configs/{id}` | `admin.explore.write` |
无 DELETE。
### C 端
| Method | Path | Auth | 语义 |
|--------|------|------|------|
| GET | `/api/v1/star/configs` | DeviceAuth | 仅 `active=true`;按 code 排序 |
## 5. C 端生效(可观察)
| 条件 | user-h5 `/star` |
|------|-----------------|
| 至少 1 条 active | 保持现网生成路径 |
| 成功且 0 条 active | 展示「星座配置暂未开放」类空态;禁用主生成 CTA |
| 请求失败 | **回退**:不阻断现网生成(与 Banner/FeedSlot 失败回退同哲学) |
不改星历引擎、不改报告 JSON 契约。
## 6. Permission / Migration
- Migration `000052_ops_star_config_write`:授予 `super_admin``admin.explore.write`
- 编码时占号(当前 Max=`000051`
- `knownPermissions` 注册加法码
## 7. UI
| 端 | 行为 |
|----|------|
| admin-h5 探索/星座配置页 | 列表 + 新建/编辑/上下架(仅 StarConfig |
| user-h5 `/star` | 按 §5 显隐 CTA |
## 8. AC
| ID | Then |
|----|------|
| AC-F-01 | POST 合法 → list/get 可见 |
| AC-F-02 | PUT `active=false` → C 端 list 不含 |
| AC-F-03 | 有 active → C 端 list 非空;星座页可生成 |
| AC-F-04 | 重复 code → 409 |
| AC-S-01/02 | 401;仅 read → POST 403 |
| AC-A-01 | 写产生审计 |
| AC-C-01 | 全下架后星座页空态/禁 CTA |
| AC-C-02 | C 端 API 失败不白屏、仍可走回退生成 |
| AC-O-01 | 无 Rhythm/Card/Scale/Prompt 写路径;无 soft-delete |
## 9. Closed 后
**立即 STOP** Continuous Loop。
ECR-044 必须重新 Candidate Review + Human 单独拍板。
## 10. Trace
ECR-043 · BD-2026-043 · PRODUCT_SPEC · CONTRACT_DIFF(实现时)· EXPLORE_CONFIG_WRITE_AUTHORIZATION
@@ -0,0 +1,42 @@
# Feature Spec: ExploreConfig · StarConfigOps · ECR-035 只读基线)
> Status: `Active`**ECR-035 Closed** · 只读)
> **写面:** [ops-star-config-write.md](ops-star-config-write.md)**ECR-043 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-034 Closed
> Capability: `ExploreConfig` · BC: `Explore_Reports`
> 授权(只读队列):`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals(本只读 Spec
配置写发布(见 ECR-043)· 引擎改分层 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `StarConfig` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/explore/star-configs` | `admin.explore.read` | 只读 |
| GET | `/admin/explore/star-configs/{id}` | `admin.explore.read` | 只读 |
## Migration
`000036`:表 + 种子(若有) + 授予 admin.explore.read
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-035.yaml`
+170
View File
@@ -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-ADECR-006009
---
## 1. 功能定义
| 字段 | 内容 |
|---|---|
| Name | 运营系统 Ops-E |
| Purpose | 两角色 RBAC、C 端用户封禁、推送任务占位,支撑安全运维 |
| Business Goal | 限制写权限;可处置违规账号;预留推送运营入口(不下发) |
| In | Out |
|---|---|
| `admin_accounts.role``super` \| `ops` | 细粒度权限矩阵 / 动态权限表 |
| 用户封禁 / 解封(`users.status` active↔banned | 设备级 / IP 封禁 |
| `push_jobs` 草稿 CRUDdraft / 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 CompleteOps-E MVP |
@@ -0,0 +1,41 @@
# Feature Spec: AICoreConfig · ToolDefinitionOps · ECR-028
> Status: `Active`Loop continuous · **ECR-028 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-027 Closed
> Capability: `AICoreConfig` · BC: `Ask_Ops`
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
工具在线编辑 · 运行时绑定 · UGC · 真支付
## L2 Domain
| 概念 | 语义 |
|------|------|
| `ToolDefinition` | 本切片只读目录;code 唯一(若适用) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/ai/tools` | `admin.ai_config.read` | 只读 |
| GET | `/admin/ai/tools/{id}` | `admin.ai_config.read` | 只读 |
## Migration
`000029`:表 + 种子(若有)(权限复用)
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | list 含种子或空列表合法 |
| AC-F-02 | 已知 id get 200 |
| AC-F-03 | 未知 id → 404 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无权限 → 403 |
| AC-P-01 | list &lt; 500ms |
| AC-O-01 | N/A 只读 |
contract_diff: `docs/CONTRACT_DIFF/ECR-028.yaml`
@@ -0,0 +1,39 @@
# Feature Spec: 用户洞察 UserIntelligenceOps · ECR-016
> Status: `Active`Loop continuous · **ECR-016 Closed**
> Parent: WAVE0-FROZEN · Predecessor: ECR-015 Closed
> Capability: `UserIntelligence` · BC: `Identity_Profile`(读)+ `Analytics_OpsB`(读)
> 授权:`docs/WAVE0/LOOP_AUTHORIZATION.md`
## Non-goals
写标签 · 改报告正文 · Ask 全文运营编辑 · UGC · 真支付 · 新表(本切片纯读模型聚合)
## L2 读模型(无 migration
| 概念 | 来源 |
|------|------|
| `UserProfileView` | profiles 摘要(已有 detail |
| `InsightReport` | growth_reports 类型计数 + 最近列表 |
| `PsychologicalTagSet` | 由报告 type 派生的稳定标签(非 NLP) |
| `BehaviorSnapshot` | analytics_events 最近页面/事件(若有) |
## L3 API
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/users/:id/insight` | `admin.users.read` | 聚合洞察只读 |
## L4 AC
| ID | Then |
|----|------|
| AC-F-01 | 有报告用户 → insight.reports_by_type 非空或 reports 列表 |
| AC-F-02 | tags 含报告类型映射标签 |
| AC-F-03 | 无埋点时 behavior.events=[] 仍 200 |
| AC-S-01 | 无 Admin → 401 |
| AC-S-02 | 无 users.read → 403 |
| AC-P-01 | GET insight &lt; 500ms 本机 |
| AC-O-01 | N/A 只读(无写审计) |
contract_diff: `docs/CONTRACT_DIFF/ECR-016.yaml`
@@ -16,7 +16,7 @@
| In | Out |
|---|---|
| Self / Other 档案创建与生日变更触发 | 无档案凭空出合盘 |
| 单人:portrait · star · rhythm | 意象卡片抽牌(配额玩法,不预生成) |
| 单人:portrait · star · rhythmstar/rhythm 运势与今日建议按自然日在 GET 时原地刷新,见各 Spec) | 意象卡片抽牌(配额玩法,不预生成) |
| 有 Self+Otherrelation + synastry 成对覆盖 | 附近的人合盘(进页再算) |
---
+11
View File
@@ -92,6 +92,8 @@
| R4 | 删除策略:任务未要求硬删 PII 时按实现(软删优先);级联影响报告见服务规则 |
| R5 | Ask / 画像 / 关系必须挂合法 profile_id |
| R6 | 文案用「个人档案」,不用命盘 |
| R7 | **每账号至多一条** `relation=self`(唯一索引);再建 self → 409;解码/星盘等复用或更新既有 Self,禁止重复造「我」 |
| R8 | 合盘/匹配对象列表只展示 `other` + 附近的人;不得把多余 self 当作 TA |
---
@@ -100,6 +102,7 @@
| 表 | 字段要点 |
|---|---|
| `profiles` | id, user_id, relation, display_name, birth_date, birth_time?, birth_place?, gender?, relation_type? |
| 约束 | 部分唯一:`(user_id) WHERE relation='self' AND deleted_at IS NULL` |
---
@@ -143,10 +146,18 @@
**When** 添加 TA
**Then** relation=other 且可用于关系理解
**Given** 已有 Self
**When** 再次 POST relation=self
**Then** 409 conflict;列表仍仅一条 Self
**Given** 他人 id
**When** PATCH
**Then** 失败
**Given** 同账号多条历史 self(脏数据)
**When** 迁移/启动
**Then** 仅保留最新一条,其余软删;合盘 TA 侧不再出现多个「我」
---
## 13. AI 开发前检查
+18 -4
View File
@@ -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)
> 竞品对照:测测星座体系(本命排盘 · 运势大全 · 连线合盘)
@@ -106,6 +106,10 @@
| R8 | 合盘与人格匹配分流:`/synastry` 盘型;`/relation` 行为风格 |
| R9 | 附近的人默认 `geo_visible=false`;仅可见已开启位置的 self 档案 |
| R10 | 合盘星历走 Swiss Ephemeris(默认 Moshier);宫位制 Whole Sign |
| R11 | 合盘交叉校验:档案 A 为本账号 Self;档案 B 须为本账号 `other` 或附近可见他人 Self;拒绝 self×self / 同 id |
| R12 | **本命层**(轮盘/行星/相位/太阳月亮上升)随生日确定,不因打开时间重算;**运势层**(日/周/月/年 outlook · 行运)按**自然日(CST**刷新 |
| R13 | GET `/reports/latest` 与 GET `/reports/{id}`type=star):若 `summary.as_of` ≠ 当日则**原报告 id 原地更新**时效段并落库;同日再读即时返回;禁止为换日运新建报告 id(以免深度权益丢失) |
| R14 | `summary``as_of`YYYY-MM-DD)与 `valid_until`(次日 00:00 CST RFC3339);前端到点静默刷新;**不按十二时辰**切换 |
---
@@ -118,7 +122,7 @@
| `profiles.birth_place` | 可选;常用城市表解析经纬度 |
| `profiles.geo_lat/lng/visible` | 附近的人 |
| `synastry_invites` | 邀请 token · host/guest · report_id |
| 报告 JSON | `charts.*`(五主盘+推运)· `as_of` · 三指数 · fortune/transits |
| 报告 JSON | `charts.*`(五主盘+推运)· `as_of` · `valid_until` · 三指数 · fortune/transits |
详见 erd;实现切片同步 OpenAPI。
@@ -152,7 +156,7 @@
| Event | 触发 |
|---|---|
| `star_completed` / portrait_completed source=star | 基础结果展示 |
| `star_completed` | 基础结果展示 |
| `star_wheel_viewed` | 点选轮盘行星 |
| `synastry_completed` | 合盘结果展示 |
| `synastry_invite_created` / `synastry_invite_accepted` | 邀请链路 |
@@ -165,7 +169,7 @@
**Given** Self 有生日
**When** 生成星座报告
**Then** 展示轮盘、太阳/月亮/上升、相位速览、fortune 日运与行运;无「占卜」「算命」恐吓
**Then** 展示轮盘、太阳/月亮/上升、相位速览、outlook 日/周/月/年/一生与行运;无「占卜」「算命」恐吓
**Given** 两份档案
**When** POST synastry
@@ -183,6 +187,14 @@
**When** 打开 `/star`
**Then** 页内生日表单可生成
**Given** 已有 star 报告且 `as_of` 为昨日
**When** GET latest
**Then** 日运等 outlook 按今日更新;报告 `id` 不变;响应含当日 `as_of``valid_until`
**Given** 用户停留在 `/star` 跨自然日
**When** 到达 `valid_until`
**Then** 前端静默刷新运势 Tab 内容
---
## 13. AI 开发前检查
@@ -202,3 +214,5 @@
| Packages | `internal/star` · `ephemeris` · `natal` · `fortune` · `synastry` |
| 星历 | Swiss EphemerisCGO,默认 Moshier);热带 + Whole Sign |
| 实现 | `POST /reports/star` · `POST /reports/synastry` · nearby/invites · H5 `/star` · `/synastry` · invite |
| Gaps | ECR-012 收口:报告页复用 StarFortunePanel;分享 type=starfortuneBundle 仅读 outlook 且剥离 luckye2e mock 含 outlook |
| ESS | `docs/ECR/ECR-012-star-alignment.md`**Approved** · L2)· BD-2026-012 Approved |
+1 -1
View File
@@ -41,7 +41,7 @@ P1 必做(feature-map):用户档案 · 性格探索/个人画像 · 人格
| 探索测试草稿恢复 | `scaleDraft` localStorage |
| L0L3 | 全绿(收口时) |
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)
---
+55
View File
@@ -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 | PASS38 |
| L3 e2e auth/profiles mock | PASS2 |
| 状态真源对齐 | 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
```
+2 -1
View File
@@ -7,7 +7,8 @@
- DB: PostgreSQL
- Migrations: goose (or golang-migrate) under `apps/api/migrations/`
- Log: structured (zap or slog wrapper). No `fmt.Println` in business paths.
- Module path: `github.com/yuxingu/digital-psychology/apps/api`
- Module path: `github.com/yuxingu/digital-psychology/apps/api`Go import 身份;**不**表示托管在 GitHub)
- Git host: `https://git.jackyu66.com/jack/digital-psychology.git`(唯一远程 `origin`;无 GitHub Actions
## User H5
+20
View File
@@ -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"
}
+53
View File
@@ -0,0 +1,53 @@
{
"ecr": "ECR-012",
"result": "BLOCK",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": false,
"tests": false
},
"violations": [
{
"rule": "TR-REVIEW-001",
"description": "Backend change requires CODE_REVIEW / code-review handoff artifact",
"fix_required": [
"Write reviewer decision artifact",
"Re-run gate"
],
"detail": "ECR-012",
"check": "traceability-check"
},
{
"rule": "TR-COMMIT-001",
"description": "Trace chain requires implementation commit evidence",
"fix_required": [
"Record commit sha in TRACEABILITY or TEST_REPORT",
"Re-run gate"
],
"detail": "ECR-012",
"check": "traceability-check"
},
{
"rule": "TE-MISSING-001",
"description": "Missing TEST_REPORT for implemented change",
"fix_required": [
"Add docs/TEST_REPORT/ECR-xxx.md",
"Re-run gate"
],
"detail": "ECR-012",
"check": "test-evidence-check"
}
],
"errors": [
"[traceability-check] BLOCK [TR-REVIEW-001]: Backend change requires CODE_REVIEW / code-review handoff artifact \u2014 ECR-012",
"[traceability-check] BLOCK [TR-COMMIT-001]: Trace chain requires implementation commit evidence \u2014 ECR-012",
"[test-evidence-check] BLOCK [TE-MISSING-001]: Missing TEST_REPORT for implemented change \u2014 ECR-012"
],
"warnings": [],
"timestamp": "2026-08-07T06:51:10Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-016",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T10:30:16Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-017",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T10:39:01Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-018",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T11:26:01Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-019",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T14:13:03Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-020",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T15:01:53Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-021",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T16:26:53Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-022",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T17:28:27Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-023",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T17:48:29Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-024",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:05:41Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-025",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:08:40Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-026",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:13:36Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-027",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:14:17Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-028",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:14:30Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-029",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:14:44Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-030",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:15:00Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-031",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:15:15Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-032",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:15:30Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-033",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:15:45Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-034",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:15:59Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-035",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:16:12Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-036",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:16:26Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-037",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:16:42Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-038",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:16:57Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-039",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:17:12Z"
}
+18
View File
@@ -0,0 +1,18 @@
{
"ecr": "ECR-040",
"result": "PASS",
"ess_version": "v1.0",
"gate_version": "0.1.2",
"project_profile": "unknown",
"checks": {
"artifact": true,
"role_boundary": true,
"backend_boundary": true,
"traceability": true,
"tests": true
},
"violations": [],
"errors": [],
"warnings": [],
"timestamp": "2026-08-07T19:20:29Z"
}
-58
View File
@@ -1,58 +0,0 @@
name: ci
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
h5:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- run: npm ci
- run: npm run build:h5
- run: npm run test:h5
- name: Install Playwright Chromium
working-directory: apps/user-h5
run: npx playwright install --with-deps chromium
- run: npm run test:e2e -w @yuxingu/user-h5
env:
CI: true
api:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Unit tests (no integration DB)
working-directory: apps/api
run: go test $(go list ./... | grep -v /internal/integration) -count=1
ess-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: ESS artifact presence (active ECR)
run: |
test -f docs/ECR/ECR-005-h5-openapi-hygiene.md
test -f docs/TASKS/TASK-20260805-ECR005.yaml
test -f docs/TEST_REPORT/ECR-005.md
test -f docs/STATE/ECR-005.md
echo "ESS docs gate OK"
- name: ess-validate when ESS_ROOT available
env:
ESS_ROOT: ${{ vars.ESS_ROOT }}
run: |
if [ -n "$ESS_ROOT" ] && [ -f "$ESS_ROOT/scripts/ess-validate.py" ]; then
python scripts/ess-validate.py --phase coding --ecr ECR-005 --task docs/TASKS/TASK-20260805-ECR005.yaml
else
echo "ESS_ROOT not configured in CI — skipped full ess-validate"
fi
+6
View File
@@ -3,6 +3,7 @@ __pycache__/
.venv/
*.log
.env
deploy.env
.DS_Store
# ESS — machine-local root pointer(仓库内用 scripts 包装器)
@@ -17,6 +18,7 @@ dist/
apps/api/bin/
apps/api/yuxingu-api
apps/api/tmp/
apps/api/data/
*.exe
# IDE
@@ -40,3 +42,7 @@ Thumbs.db
# Local secrets (never commit)
apps/api/config.local.yaml
# Playwright / Vitest artifacts
**/test-results/
**/playwright-report/
+43 -21
View File
@@ -4,40 +4,62 @@ You are the software engineer for **愈心谷 (YuXinGu)**.
This file is for AI agents.
## 0. ESS Intake Bootstrap(任何改码任务最先做)
`$ESS_ROOT/governance/SESSION_BOOTSTRAP.md`**先输出 ESS Mode 块**,再改文件或扩 Scope。
```text
ESS Mode: L0 | L1 | L2 | L2-BLOCKED | L3 | RETRO
Change: <one line>
ECR: <id|NONE>
Phase: <ARCHITECT|ENGINEER|REVIEWER|none>
Execution Context: <ECR id|TASK id|NONE>
Allowed: <...>
Forbidden: <...>
Action: CONTINUE | STOP
Reason: <...>
```
- `Action: STOP``L2-BLOCKED` → **禁止**改 `apps/` / `packages/` / `proto/` / `migrations/`
- 本仓 `ess_intake: strict`(见 `docs/PROJECT_RULES.md`):Retro ECR 须四字段;新主题 OUT OF SCOPE
- Solo multi-role 允许同 Session 串行换 Phase**同一 review turn 禁止 Approve + Modify**
## Load order (every task)
1. `.ai/ai-contract.md`
2. `.ai/constitution.md`
3. `.ai/architecture.md`
4. `.ai/domain.md` (+ product/API tasks: `domain/domain-map.md`)
5. `.ai/forbidden.md`
6. `.ai/definition-of-done.md`**完成标准(强制)**Done ≠ build 通过
7. `.ai/environment.md` + `.ai/development.md`(本地)· 部署任务再读 `deployment.md` / `docker.md`
8. `.ai/file-map.md` + `.ai/workflow.md`
9. Task rules: `.ai/coding.md` / `api.md` / `database.md` / `ui.md` / `security.md` / `testing.md`
10. **Product scope:** `.ai/product/lexicon.md` + `feature-map.md` + **`feature-design.md`** + 对应 **`feature-spec/<id>.md`** + `user-journey.md` + `page-tree.md` · 状态 `product/p1-status.md`
11. **Competitor parity / reverse engineering:** `.ai/design/reverse-engineering-spec.md`(强制 STEP 118 · Evidence First · 禁止 Demo 式省略)→ 再映射 lexicon / feature-spec
12. **Any UI work:** `.ai/design/design-system.md` + `component-catalog.md` + matching `design/platform/*.md` (H5 default)
13. Matching **ADR** in `.ai/adr/` before changing stack or API style
14. Prefer **patterns/** + **examples/** + **playbooks/**(含 `feature-spec` over inventing structure
15. Before claiming Done: `.ai/definition-of-done.md` + `.ai/review.md` + `.ai/checklists/*` → 输出 **Review Report**
16. Verify via `.ai/commands.md`(本地默认本机 Go/Vite + compose.dev 仅 DB
17. `prompts/` are optional helpers — not a substitute for rules above
18. **ESS process (when L2+ / architecture / release):** `docs/PROJECT_PROFILE.md` → role from `$ESS_ROOT/agents/``docs/ECR/` · `docs/TASKS/` · `docs/HANDOFF/`;勿把 ESS 整树复制进仓
1. **§0 ESS Mode 块**(上)→ `docs/PROJECT_PROFILE.md` · `docs/PROJECT_RULES.md`
2. `.ai/ai-contract.md`
3. `.ai/constitution.md`
4. `.ai/architecture.md`
5. `.ai/domain.md` (+ product/API tasks: `domain/domain-map.md`)
6. `.ai/forbidden.md`
7. `.ai/definition-of-done.md`**完成标准(强制)**Done ≠ build 通过
8. `.ai/environment.md` + `.ai/development.md`(本地)· 部署任务再读 `deployment.md` / `docker.md`
9. `.ai/file-map.md` + `.ai/workflow.md`
10. Task rules: `.ai/coding.md` / `api.md` / `database.md` / `ui.md` / `security.md` / `testing.md`
11. **Product scope:** `.ai/product/lexicon.md` + `feature-map.md` + **`feature-design.md`** + 对应 **`feature-spec/<id>.md`** + `user-journey.md` + `page-tree.md` · 状态 `product/p1-status.md`
12. **Competitor parity / reverse engineering:** `.ai/design/reverse-engineering-spec.md`(强制 STEP 118 · Evidence First · 禁止 Demo 式省略)→ 再映射 lexicon / feature-spec
13. **Any UI work:** `.ai/design/design-system.md` + `component-catalog.md` + matching `design/platform/*.md` (H5 default)
14. Matching **ADR** in `.ai/adr/` before changing stack or API style
15. Prefer **patterns/** + **examples/** + **playbooks/**(含 `feature-spec` over inventing structure
16. Before claiming Done: `.ai/definition-of-done.md` + `.ai/review.md` + `.ai/checklists/*` → 输出 **Review Report**
17. Verify via `.ai/commands.md`(本地默认本机 Go/Vite + compose.dev 仅 DB
18. `prompts/` are optional helpers — not a substitute for rules above
19. **ESS process (L2+ / architecture / release):** `$ESS_ROOT/SKILL.md` · `SESSION_BOOTSTRAP` · role from `$ESS_ROOT/agents/``docs/ECR/` · `docs/TASKS/` · `docs/HANDOFF/`;勿把 ESS 整树复制进仓
## ESS dual-track
| Concern | Source of truth |
|---------|-----------------|
| Lexicon · Feature Spec · DoD · domain · architecture freeze | `.ai/` |
| Roles · ECR · Task Contract · Handoff · validate gates | ESS + `docs/` |
| Intake Mode · Roles · ECR · Task · Handoff · validate | ESS + `docs/` |
```text
Load Agent Profile: ARCHITECT | ENGINEER | REVIEWER | RELEASE_MANAGER
```
- Cursor → ARCHITECT / REVIEWER(禁改生产实现)
- Engineer 实现前:Active Feature Spec +L2+Approved ECR + HANDOFF
- Cursor Session 可 Solo 串行三角色;**当前 Phase 权限唯一**
- ARCHITECT / REVIEWER phase:禁改生产实现;REVIEWER 禁 Approve+Modify 同 turn
- EngineerActive Feature Spec +L2+Approved ECR + HANDOFF;无 ECR → `L2-BLOCKED`
- `python scripts/ess-validate.py --phase <phase> --ecr ECR-xxx`
## Hard constraints
+34 -3
View File
@@ -21,7 +21,22 @@ test('admin login users grant and audit with mocked API', async ({ page }) => {
await ok({
token: 'adm_e2e_token',
expires_at: new Date(Date.now() + 3600_000).toISOString(),
admin: { id: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', username: 'admin' },
admin: {
id: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
username: 'admin',
role: 'super_admin',
permissions: [
'admin.users.read',
'admin.users.membership.grant',
'admin.users.ask_quota.grant',
'admin.orders.read',
'admin.audit.read',
'admin.analytics.read',
'admin.content.write',
'admin.roles.read',
'admin.roles.write',
],
},
})
return
}
@@ -55,7 +70,22 @@ test('admin login users grant and audit with mocked API', async ({ page }) => {
return
}
if (url.endsWith('/me') || url.includes('/me?')) {
await ok({ id: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', username: 'admin' })
await ok({
id: 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa',
username: 'admin',
role: 'super_admin',
permissions: [
'admin.users.read',
'admin.users.membership.grant',
'admin.users.ask_quota.grant',
'admin.orders.read',
'admin.audit.read',
'admin.analytics.read',
'admin.content.write',
'admin.roles.read',
'admin.roles.write',
],
})
return
}
if (url.includes(`/users/${userId}/ask-quota/grant`) && method === 'POST') {
@@ -131,7 +161,8 @@ test('admin login users grant and audit with mocked API', async ({ page }) => {
await ok({})
})
await page.goto('/login')
// Leading `/login` would resolve to host root and miss Vite base `/psy/admin/`.
await page.goto('login')
await expect(page.getByRole('heading', { name: '愈心谷' })).toBeVisible()
await page.locator('input[type="password"]').fill('change-me')
await page.getByRole('button', { name: '登录' }).click()
+4 -2
View File
@@ -2,6 +2,7 @@ import { defineConfig, devices } from '@playwright/test'
/**
* Ops admin L3 smoke: Vite preview + mocked /api/v1/admin (no Go required).
* Build uses base `/psy/admin/` baseURL must include that prefix.
* Run: npm run test:e2e -w @yuxingu/admin-h5
*/
export default defineConfig({
@@ -10,7 +11,8 @@ export default defineConfig({
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0,
use: {
baseURL: 'http://127.0.0.1:4174',
// Trailing slash so relative goto('login') resolves under /psy/admin/
baseURL: 'http://127.0.0.1:4174/psy/admin/',
trace: 'on-first-retry',
},
projects: [
@@ -24,7 +26,7 @@ export default defineConfig({
],
webServer: {
command: 'npm run build && npm run preview -- --host 127.0.0.1 --port 4174',
url: 'http://127.0.0.1:4174',
url: 'http://127.0.0.1:4174/psy/admin/',
reuseExistingServer: false,
timeout: 120_000,
},

Some files were not shown because too many files have changed in this diff Show More