feat(ops): ECR-007 行为分析与 ECR-008 内容运营后台
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s

落地埋点 ingest/数据看板、首页宫格 CMS 与测评上下架;含账号引导、问答流式与免责声明去重,以及 review P1 审计同事务修复。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-07 02:26:16 +08:00
co-authored by Cursor
parent 7e9023f0a8
commit 7ab9add5dd
132 changed files with 8276 additions and 491 deletions
+40
View File
@@ -202,6 +202,43 @@
| meta | jsonb | |
| created_at | timestamptz | 只追加 |
### analytics_sessionsOps-B · ECR-007
| column | type | notes |
|---|---|---|
| session_id | text PK | 客户端 session_id |
| device_key | text | |
| user_id | uuid FK null | |
| started_at | timestamptz | |
| ended_at | timestamptz null | |
| exit_page | text null | |
| duration_ms | int null | |
### analytics_events
| column | type | notes |
|---|---|---|
| id | uuid PK | |
| session_id | text FK→analytics_sessions | |
| user_id | uuid FK null | |
| name | text | page_view / page_leave / … |
| page_path | text null | |
| props | jsonb | 白名单键;无 PII |
| client_ts | timestamptz | |
| received_at | timestamptz | 只追加 |
### home_toolsOps-C · ECR-008
| column | type | notes |
|---|---|---|
| id | uuid PK | |
| row_index | smallint | 1 \| 2 |
| sort_order | int | |
| path | text | 站内相对路径 |
| icon | text | HomeToolIcon 白名单 |
| label | text | ≤16 字 |
| badge | text null | |
| badge_tone | text null | hot \| new |
| enabled | bool | |
| updated_at | timestamptz | |
---
## Relationship sketch
@@ -217,4 +254,7 @@ profiles ── relation_insights ── profiles
users 1──* ask_threads 1──* ask_messages
admin_accounts 1──* admin_sessions
admin_accounts 1──* admin_audit_logs
users 1──* analytics_sessions 1──* analytics_events
home_tools(运营配置宫格)
scales.status published|draft
```
+4 -2
View File
@@ -231,8 +231,10 @@ UI **不出现「塔罗」**。禁止神谕吉凶、恐吓话术。
| 分期 | 内容 |
|---|---|
| Phase A `[Ops]` | 登录 · 用户/订单查询 · 会员授予 · 审计 · `apps/admin-h5` |
| Phase B+ | 内容配置 · 测评上下架 · 封禁 · RBAC · 推送(后置 |
| 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 D+ | 商业加深 · RBAC · 封禁 · 推送(各开独立 ECR) |
不计入 P1 Complete;不进入五 Tab。
+4
View File
@@ -6,6 +6,8 @@
|---|---|---|---|---|
| [home.md](home.md) | 首页 / 发现入口 | §1 首页 | `/` | P1 |
| [profile.md](profile.md) | 个人档案 | §1.1 · §5.1 | `/profile` | P1 |
| [account-auth.md](account-auth.md) | 账号登录 | §5.x | `/login` | P1 |
| [profile-bootstrap.md](profile-bootstrap.md) | 生日包生成 | §1.1 | profiles→reports | P1 |
| [portrait.md](portrait.md) | 个人画像 | §2.1 · §2.3 | `/portrait` | P1 |
| [relation.md](relation.md) | 关系理解 | §2.4 | `/relation` | P1 |
| [explore-test.md](explore-test.md) | 探索测试 | §2.2 | `/explore` · `/scales/:slug` | P1 |
@@ -19,6 +21,8 @@
| [life-rhythm.md](life-rhythm.md) | 身心节律 | §2.7 | `/rhythm` | P2 设计 |
| [image-card.md](image-card.md) | 意象卡片 | §2.8 | `/cards` | P2 设计 |
| [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 Approved |
新功能:复制 `_TEMPLATE.md` → 填满 → 在本表登记 → 再编码。
+135
View File
@@ -0,0 +1,135 @@
# Feature Spec: 账号登录
> Status: `Active` · Map: `5.x 账号 [P1+]` · Phase: `P1`
> 规范:[../feature-design.md](../feature-design.md)
> 关联:[profile-bootstrap.md](profile-bootstrap.md)
---
## 1. 功能定义
| 字段 | 内容 |
|---|---|
| Name | 账号登录(手机号+密码) |
| Purpose | 跨设备持久用户身份;未登录不可生成/查看生日衍生结果 |
| Business Goal | 数据归属清晰;为会员与深度版付费打底 |
| In | Out |
|---|---|
| 注册 / 登录 / 登出 / me | 微信 OAuth、短信 OTP(后续) |
| 设备身份绑定到已注册账号 | 游客可看完整报告 |
| Bearer Session | 运营 Admin 登录(独立) |
---
## 2. 用户价值
1. 换手机后仍能看到自己的档案与报告。
2. 登录后输入生日一次生成、各页直读。
3. N/A(登录本身免费)。
---
## 3. 用户角色
| Actor | 能力 |
|---|---|
| Visitor | 仅设备身份;可打开登录页;**不可**建档出结果 |
| User(已注册) | CRUD 本人档案与 TA;读已生成报告 |
| VIP / DeepAccess | 同前,权益不变 |
---
## 4. 用户流程
```text
进入结果页或建档
未登录? → /login 注册或登录
Device 绑定到账号 User
进入档案 / 结果页(读缓存报告)
```
---
## 5. 页面设计
| 路由 | 页面 |
|---|---|
| `/login` | LoginPage(注册/登录切换) |
| `/mine` | 展示账号手机号尾号 + 退出 |
---
## 6. 页面状态规范
| 状态 | UI |
|---|---|
| Loading | 登录中 |
| Error | 密码错误 / 手机号已注册 |
| Normal | 跳转 redirect 或 /mine |
---
## 7. Business Rules
| ID | Rule |
|---|---|
| R1 | **临时开放:** 任意非空手机号 + 任意密码均可登录;未注册则自动建号写入 DB |
| R2 | 登录/注册统一走 OpenLogin;不校验历史密码,仍会更新 password_hash 记录本次输入 |
| R3 | 登录成功后:签发 sessiondevice_identities.user_id 改绑到账号 |
| R4 | 生成/查看档案与报告 API 必须已注册(users.phone 非空)且有效 session 或已绑设备账号 |
| R5 | 登出作废 session;设备可再登录其他账号 |
| R6 | 正式环境恢复校验前须改回本 Spec |
---
## 8. 数据模型影响
- `users.phone` UNIQUE · `users.password_hash` · `users.nickname`
- `user_sessions(token, user_id, expires_at)`
---
## 9. API 需求
| Method | Path | 说明 |
|---|---|---|
| POST | `/api/v1/auth/register` | 注册 |
| POST | `/api/v1/auth/login` | 登录 |
| POST | `/api/v1/auth/logout` | 登出 |
| GET | `/api/v1/auth/me` | 当前账号 |
---
## 10. 权限设计
| 能力 | Visitor | User |
|---|---|---|
| register/login | ✓ | ✓ |
| profiles / reports 写读 | ✗ | ✓ |
---
## 11. 埋点
`auth_register` · `auth_login` · `auth_logout`
---
## 12. 测试验收标准
| Given | When | Then |
|---|---|---|
| 新设备 | 未登录 POST portrait | 401 |
| 注册成功 | 建档 | 200 且生成报告包 |
| 登录另一设备 | list profiles | 见同一账号数据 |
---
## 13. AI 开发前检查
- [x] lexicon:登录/注册/账号
- [x] 不碰 Admin Auth
+23 -13
View File
@@ -17,7 +17,7 @@
|---|---|
| 挂 profile 的多轮对话;场景入口 | 占卜/运势/预测未来 |
| DeepSeek(可配)+ 规则引擎降级 | 医疗诊断 |
| 免费次数 + 会员配额 | 无档案空聊(禁止) |
| 免费次数 + 额度包(ask_pack+ 会员配额 | 无档案空聊(禁止) |
---
@@ -25,7 +25,7 @@
1. **为何需要:** 看完画像仍有具体情境问题。
2. **完成后获得:** 结合档案的结构化建议与可执行小步骤。
3. **为何付费:** 免费次数用尽后会员提供更多回复。
3. **为何付费:** 免费次数用尽后可购买问答额度包,或开通成长会员获得更多回复。
---
@@ -49,10 +49,10 @@
选择档案(我 / TA+ 可选场景
输入问题 → 检查配额
├─ 耗尽 → 引导会员
└─ 有余 → 创建/续 thread → assistant 回复
├─ 耗尽 → 引导购买额度包 / 成长会员(mock 支付后立刻加次)
└─ 有余 → 创建/续 thread → assistant **SSE 流式**回复(delta → done
DeepSeek 失败/无 key → 规则引擎降级
DeepSeek 失败/无 key → 规则引擎降级(仍按字流式输出)
```
---
@@ -69,7 +69,7 @@
├── Loading(发送中)
├── Normal(历史 + 输入)
├── Error(发送失败可重试)
└── Quota Exhausted引导会员)
└── Quota Exhausted额度包 + 会员)
```
---
@@ -82,7 +82,7 @@
| Loading | 发送中禁用重复点 |
| Error | 失败 + 重试 |
| Normal | 消息列表 |
| Quota | 明确耗尽文案 + `/membership` |
| Quota | 明确耗尽文案 + 额度包购买 + `/membership` |
---
@@ -91,11 +91,12 @@
| ID | Rule |
|---|---|
| R1 | 每条用户消息必须绑定 `profile_id`self 或 other |
| R2 | 免费回复次数有上限(实现:如 3);会员另计配额 |
| R3 | 配额耗尽返回明确业务错误,不生成付费假回复 |
| R2 | 免费回复次数有上限(实现:如 3);可购 `ask_pack` 加次;会员另计配额 |
| R3 | 配额耗尽返回明确业务错误,不生成付费假回复;引导购买额度或会员 |
| R7 | 扣次顺序:会员配额 → 已购额度包 → 免费额度(非会员);购买额度不解锁报告深度版 |
| R4 | LLM 可选;失败降级规则引擎,仍须 lexicon 安全 |
| R5 | 禁止运势/吉凶/算命/合盘话术;结尾免责声明 |
| R6 | 回复宜有结构:回应用户结合档案 → 可执行建议(详见 system prompt |
| R5 | 禁止占卜/算命/吉凶恐吓/医疗诊断;可用愈心解码·星座·人格匹配等探索向用语 |
| R6 | 回复宜短(约 80160 字):回应当下1 条档案洞察 → 12 条可执行建议system prompt 须对齐 lexicon 与产品定位 |
---
@@ -106,6 +107,7 @@
| `ask_threads` | user_id, profile_id |
| `ask_messages` | role, content |
| `ask_quotas` / 等价 | 余量 |
| `users.ask_paid_quota_left` | 已购额度包余量 |
---
@@ -115,7 +117,11 @@
|---|---|---|
| GET | `/api/v1/ask/quota` | 余量 |
| POST | `/api/v1/ask/threads` | 创建线程(profile_id, scene? |
| POST | `/api/v1/ask/threads/{id}/messages` | 发消息拿回复 |
| POST | `/api/v1/ask/threads/{id}/messages` | 发消息拿回复(默认 JSON |
| POST | `/api/v1/ask/threads/{id}/messages?stream=1` | SSE`meta` / `delta` / `done` / `error` |
| DELETE | `/api/v1/ask/threads/{id}` | 清空该会话(软删除) |
| POST | `/api/v1/orders` kind=`ask_pack` | 购买额度包(plan: pack10/pack30/pack100 |
| POST | `/api/v1/orders/{id}/pay-mock` | 支付后增加 `ask_paid_quota_left` |
---
@@ -147,7 +153,11 @@
**Given** 配额为 0
**When** 再发送
**Then** 业务错误引导会员,无助手胡编
**Then** 业务错误引导购买额度/会员,无助手胡编
**Given** 免费额度耗尽
**When** pay-mock `ask_pack` pack10
**Then** GET `/ask/quota` remaining ≥ 10,可继续提问
**Given** 无 DeepSeek key
**When** 提问
+4 -2
View File
@@ -17,6 +17,7 @@
|---|---|
| 单报告 DeepAccess | 广告干扰主路径 |
| 订阅 Membership(月/季/年 mock | 塔罗商城 |
| 问答额度包 ask_pack(mock) | 与会员深度权益混绑(额度包不加深度版) |
| Server 侧权益生效 | 纯前端「解锁」 |
---
@@ -103,7 +104,8 @@ pay-mock → memberships 行 active + expires_at
| R2 | Membership active`expires_at > now` |
| R3 | 报告 detail 可见 ⇔ Membership active **OR** DeepAccess 存在 |
| R4 | 支付 P1 仅 mock;成功必须服务端落库后再改可见性 |
| R5 | Ask 配额:免费次数耗尽后依赖会员配额(见 ask Spec) |
| R5 | Ask 配额:免费次数耗尽后可购 ask_pack 或依赖会员配额(见 ask Spec) |
| R8 | `ask_pack`plan=`pack10|pack30|pack100` → 增加 `users.ask_paid_quota_left`;不改变 membership / deep_access |
| R6 | 订单归属必须校验 report 属于当前用户 |
| R7 | 文案:成长会员 / 深度版;禁用恐吓与疗效承诺 |
@@ -125,7 +127,7 @@ pay-mock → memberships 行 active + expires_at
| Method | Path | 意图 |
|---|---|---|
| GET | `/api/v1/membership/me` | 当前会员状态 |
| POST | `/api/v1/orders` | deep_access \| membership |
| POST | `/api/v1/orders` | deep_access \| membership \| ask_pack |
| POST | `/api/v1/orders/{id}/pay-mock` | 生效权益 |
---
+8 -4
View File
@@ -128,9 +128,11 @@ AdminShell
| POST | `/auth/login` | 登录,返回 token + account |
| POST | `/auth/logout` | 注销当前会话 |
| GET | `/me` | 当前管理员 |
| GET | `/users` | 列表 `?q=&limit=&offset=` |
| GET | `/users/:id` | 详情(含 membership · profiles 摘要 · 近订单) |
| GET | `/stats` | 仪表盘概览计数 |
| GET | `/users` | 列表 `?q=` 支持 UUID / 手机 / 昵称 |
| GET | `/users/:id` | 详情(档案 · 报告 · 会员 · 问答额度 · 近订单) |
| POST | `/users/:id/membership/grant` | 授予/延长会员 |
| POST | `/users/:id/ask-quota/grant` | 增加已购问答额度 `{delta}` |
| GET | `/orders` | 订单列表 |
| GET | `/audit-logs` | 审计列表 |
@@ -204,5 +206,7 @@ Phase A 可先 `console`/本地;不挡验收。
| Phase | 内容 |
|---|---|
| **A(本 ECR** | 登录 · 用户 · 订单 · 授予会员 · 审计 · admin-h5 壳 |
| B | 内容配置 / 测评上下架 / 封禁注销 |
| C | RBAC · 推送 · 优惠券 · 退款工单 |
| **BECR-007** | 自有埋点 · 管理端「数据」看板 — Spec `ops-analytics.md` |
| **CECR-008** | 首页宫格 CRUD · 测评上下架 — Spec `ops-content.md`Approved |
| D | 订单筛选 · 展示价 · 退款只读(另开 ECR) |
| E | RBAC · 封禁 · 推送占位(另开 ECR) |
+156
View File
@@ -0,0 +1,156 @@
# 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
> 关联:[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)
---
## ESS 门禁
按本仓 `docs/PROJECT_RULES.md` + ESS `CHANGE_MANAGEMENT.md`
1. Change Level = **L2** → ECR Approved + Feature Spec Active + BACKEND_DESIGN Approved — **已满足(2026-08-07**
2. 实现顺序以 `docs/ENGINEERING_SPEC/ECR-007-ops-analytics.md` 为准
3. 完成后:TEST_REPORT · CODE_REVIEW · `ess-validate --phase review` · DoD Review Report
(与 ECR/BACKEND_DESIGN 冲突时以已 Approved 的 ESS 工件为准。)
---
## 1. 目标
按**传统 App 运营**模式:
1. **采集**:页浏览、点击、停留时长、会话退出页等
2. **落库**:自有管道(可与 GA4 双写)
3. **展示**:管理端「数据」看板(DAU、页面、退出页、漏斗)
4. **分期**:先 Ops-B 分析,再内容 / 商业 / 权限全量模块
---
## 2. 分期总览
| 分期 | 主题 | 交付 |
|---|---|---|
| Ops-A | 已完成 | 登录 · 用户 · 订单 · 会员授予 · 审计 · 概览图 |
| **Ops-B** | **行为分析** | 自有埋点 + 管理端「数据」 |
| Ops-C | 内容运营 | 首页宫格/工具配置 · 测评上下架 |
| Ops-D | 商业加深 | 订单筛选 · 套餐价配置 · 退款只读 |
| Ops-E | 系统 | RBAC · 封禁 · 推送/站内信占位 |
UGC / 达人 / 真支付网关:**更远期**,不在本 Spec 承诺范围。
---
## 3. Ops-B 功能定义
| 字段 | 内容 |
|---|---|
| Name | App 行为分析(采集 + 运营看板) |
| Purpose | 用自有数据回答:谁来了、看了哪、停多久、从哪走、点了什么、漏斗掉哪 |
| Business Goal | 替代「只能看 GA / 改库」;支撑日常运营决策 |
| In | Out |
|---|---|
| session / page_view / page_leave(dwell) / ui_click / session_end(exit_page) | 热力图坐标级精确点击 |
| 漏斗事件写入同一管道 | 实时毫秒大屏 |
| Admin「数据」页聚合 | 用户轨迹录像 |
---
## 4. 采集字典(客户端)
扩展 `apps/user-h5/src/lib/analytics.ts`:通道 A = GA4(可选);通道 B = 自有 `POST /api/v1/analytics/events`
| Event | 触发 | 关键 props |
|---|---|---|
| `session_start` | 冷启动或可见性恢复(节流) | `session_id`, `cold`, `app_ver` |
| `page_view` | `router.afterEach` | `page_path`, `page_title`, `referrer_path` |
| `page_leave` | 路由离开 / `visibilitychange=hidden` / `pagehide` | `page_path`, **`dwell_ms`** |
| `ui_click` | `data-track` 或白名单委托 | `page_path`, `element_id` |
| `session_end` | 超时 / 关页(`sendBeacon` | `session_id`, `duration_ms`, **`exit_page`** |
| 既有漏斗 | 保持 | `home_cta_portrait` 等 |
规则:
- 批量上报(5–10s)+ 退出冲刷
- `session_id``sessionStorage`30min 无互动新开
- **禁止**生日、问答正文、手机号、完整档案 ID 明文
---
## 5. 数据模型
```text
analytics_sessions
id, device_key, user_id?, started_at, ended_at, exit_page, duration_ms
analytics_events
id, session_id, user_id?, name, page_path, props jsonb, client_ts, received_at
analytics_daily_page(可选汇总)
day, page_path, pv, uv, dwell_sum_ms, exit_count
```
---
## 6. API
| Method | Path | 意图 |
|---|---|---|
| POST | `/api/v1/analytics/events` | 批量 ingestDeviceAuth,限流) |
| GET | `/api/v1/admin/analytics/overview` | DAU / 会话 / 均时长 |
| GET | `/api/v1/admin/analytics/pages` | 页 PV/UV/avg_dwell/exit |
| GET | `/api/v1/admin/analytics/exits` | 退出页 TOP |
| GET | `/api/v1/admin/analytics/clicks` | 点击 TOP |
| GET | `/api/v1/admin/analytics/funnel` | 漏斗计数 |
查询均带 `from` / `to`(日)。
---
## 7. 管理端「数据」页
侧栏:概览 · **数据** · 用户 · 订单 · 审计
- 时间筛选:今日 / 7 日 / 30 日
- 卡片:DAU、新增、会话数、人均会话时长
- 图:日活趋势;页面均停留 TOP
- 表:页面明细、退出页 TOP、点击 TOP
- 漏斗:画像完成 → 深度 CTA → 支付成功
视觉:愈心谷色系;品牌仅 logo,无「运营后台」字样。
---
## 8. Ops-CE 边界(后置)
| 分期 | In |
|---|---|
| Ops-C | 首页宫格/工具 CRUDscales 上下架 |
| Ops-D | 订单多维筛选;membership 展示价;退款状态只读 |
| Ops-E | 用户封禁;admin 角色;推送任务占位 |
---
## 9. Ops-B 验收
**Given** H5 打开并切换两页后离开
**When** ingest 成功
**Then** 存在 `page_view`×2、`page_leave`(含 dwell_ms)、`session_end.exit_page`
**Given** Admin 登录「数据」近 7 日
**When** GET overview/pages/exits
**Then** 返回非空聚合且与事件量级一致
---
## 10. 实施顺序
1. 本 Spec 置 Active + 改 feature-map §7 / ops-admin 分期
2. Migration + ingest API
3. Admin 聚合 API
4. H5 SDK(会话/停留/点击/beacon
5. admin-h5「数据」页
6. 测试与部署
+92
View File
@@ -0,0 +1,92 @@
# 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
> 关联:[ops-admin.md](ops-admin.md) · [ops-analytics.md](ops-analytics.md) · [home.md](home.md) · [explore-test.md](explore-test.md)
---
## ESS 门禁
1. Change Level = **L2** → ECR Approved + Feature Spec Active + BACKEND_DESIGN Approved — **已满足(2026-08-07**
2. 实现顺序以 `docs/ENGINEERING_SPEC/ECR-008-ops-content.md` 为准
3. 完成后:TEST_REPORT · CODE_REVIEW · `ess-validate --phase review`
---
## 1. 目标
让运营在后台配置:
1. **首页宫格工具**:增删改排序、显隐、角标(替代写死 `homeCatalog.ts`
2. **测评上下架**`scales.status` = `published` / `draft`C 端列表只见已发布
---
## 2. In / Out
| In | Out |
|---|---|
| 首页宫格 CRUD(最多约 24 项;默认 12 | 首页 Feeds 信息流 CMS(本 ECR 后置,仍用静态 `homeFeeds` |
| 工具 path / icon 白名单校验 | 任意外链、自定义 SVG 上传 |
| scales 列表 + 上架/下架 + 审计 | 新建量表题目编辑器 · 探索目录全量 CMS |
| Admin「内容」页 | RBAC / 封禁 / 推送(Ops-E |
---
## 3. 数据模型
```text
home_tools
id, row_index (1|2), sort_order, path, icon, label,
badge?, badge_tone? (hot|new|null), enabled, updated_at
scales(已有)
status: published | draft
```
种子:与当前 `homeGridRow1/2` 一致。
---
## 4. API
| Method | Path | 意图 |
|---|---|---|
| GET | `/api/v1/home/tools` | C 端宫格(仅 enabledDeviceAuth |
| GET | `/api/v1/admin/home/tools` | 管理端全量 |
| PUT | `/api/v1/admin/home/tools` | 整表替换保存(排序+字段)+ 审计 |
| GET | `/api/v1/admin/scales` | 全部量表(含 draft |
| PATCH | `/api/v1/admin/scales/:id` | `{ status }` + 审计 |
C 端既有 `GET /scales` 继续只返回 published。
---
## 5. 管理端「内容」页
侧栏:概览 · 数据 · **内容** · 用户 · 订单 · 审计
- Tab「首页宫格」:两行列表、拖拽/上下移、编辑 label/path/icon/badge、启用开关、保存
- Tab「测评」:列表 · 上架/下架
---
## 6. C 端行为
- `useHomePage` 优先拉 `GET /home/tools`;失败回退静态 `homeCatalog`
- 下架 scale:列表不可见;直链 slug 返回 404/不可用(与现 Get 行为对齐)
---
## 7. 验收
**Given** Admin 将某宫格 disabled 并保存
**When** H5 刷新首页
**Then** 该项不出现
**Given** Admin 将 `mbti-lite` 置 draft
**When** GET `/scales`
**Then** 列表不含该 slug
---
@@ -0,0 +1,129 @@
# Feature Spec: 生日包生成(档案引导)
> Status: `Active` · Map: `1.1 个人档案` · Phase: `P1`
> 关联:[account-auth.md](account-auth.md) · [profile.md](profile.md) · [portrait.md](portrait.md) · [star-profile.md](star-profile.md)
---
## 1. 功能定义
| 字段 | 内容 |
|---|---|
| Name | 生日衍生数据一次生成与覆盖 |
| Purpose | 输入/修改生日后服务端一次性生成并落库;结果页优先读缓存 |
| Business Goal | 降低重复输入;保证本人与 TA 数据归属同一账号 |
| In | Out |
|---|---|
| Self / Other 档案创建与生日变更触发 | 无档案凭空出合盘 |
| 单人:portrait · star · rhythm | 意象卡片抽牌(配额玩法,不预生成) |
| 有 Self+Otherrelation + synastry 成对覆盖 | 附近的人合盘(进页再算) |
---
## 2. 用户价值
1. 填一次生日,解码/星座/节律可直接看。
2. 添加的 TA 挂在账号下;与本人关系/合盘一并备好。
3. 改生日自动覆盖旧结果。
---
## 3. 用户角色
**已登录 User**。Visitor 被鉴权拦截。
---
## 4. 用户流程
```text
登录 → 创建/编辑档案(含生日)
Server GenerateBundle(profile)
├─ 单人报告:portrait / star / rhythm(同 type 软删旧 → 新建)
└─ 若存在配对:self×each other → relation + synastry 覆盖
结果页:GET latest by profile+type(无则引导档案,不强制再填生日)
```
---
## 5. 页面设计
| 路由 | 行为变更 |
|---|---|
| `/portrait` `/star` `/rhythm` | 有 Self → 读 latest;无 → 引导 `/profile` 或登录 |
| `/relation` `/synastry` | 选已有 Self+TA → 读 latest pair;缺则引导加档案 |
| `/profile` | 创建/改生日后展示「已生成」提示 |
---
## 6. 页面状态规范
| 状态 | UI |
|---|---|
| Loading | 生成中/拉取中 |
| Empty | 无档案 → 去建档;未登录 → 去登录 |
| Normal | 展示缓存报告 |
| Error | 重试 |
---
## 7. Business Rules
| ID | Rule |
|---|---|
| R1 | 仅登录用户可写档案 |
| R2 | Create/Update 若 birth_date(或 time/place)相对旧值变化 → GenerateBundle |
| R3 | 覆盖=同 user + profile + type+ peer)软删旧报告再插入 |
| R4 | Other 档案 `user_id` 必须=当前账号;删除 Other 时软删其单人报告及含其 peer 的配对报告 |
| R5 | 结果页不得再要求重复输入已知生日,除非用户主动改档案 |
---
## 8. 数据模型影响
- `growth_reports.peer_profile_id` nullable(配对报告)
- 索引便于 latest 查询
---
## 9. API 需求
| Method | Path | 说明 |
|---|---|---|
| POST/PATCH | `/profiles` | 成功后服务端同步 GenerateBundle |
| GET | `/reports/latest` | `profile_id` · `type` · 可选 `peer_profile_id` |
现有 POST `/reports/*` 保留(幂等覆盖),供重算;主路径以 latest 为准。
---
## 10. 权限设计
同 account-auth:必须 User。
---
## 11. 埋点
`profile_bootstrap_done` · `report_cache_hit`
---
## 12. 测试验收标准
| Given | When | Then |
|---|---|---|
| 登录用户建 Self | POST profile | DB 有 portrait/star/rhythm |
| 再加 Other | POST other | 有 relation+synastry 相对 Self |
| 改 Self 生日 | PATCH | 旧报告 soft-deleted,新报告可读 |
| 登录用户开 /portrait | GET latest | 无需再填生日 |
---
## 13. AI 开发前检查
- [x] 合盘不造假 TA
- [x] lexicon:愈心解码 / 星座 / 合盘 / 个人档案
+1
View File
@@ -64,6 +64,7 @@
| Synastry | 合盘 | 五主盘+推运;与人格匹配分流 |
| LifeRhythm | 身心节律 | |
| ImageCard | 意象卡片 | 可并列塔罗别名 |
| Account / Auth | 账号 / 登录 / 注册 | 手机号+密码;未登录不可出结果 |
**Code aliases(可用):** Decode≈Portrait · Match≈RelationInsight · Zodiac≈StarProfile
+1
View File
@@ -16,6 +16,7 @@ IA Tab 冻结:首页 · 探索 · 问答 · 陪伴 · 我的
/ask AskPage AI成长助手
/companion CompanionPage 节气生活 · 心情记录
/mine MinePage 资产总览 → profile/reports/membership
/login LoginPage 注册/登录
/profile ProfilePage 个人档案管理
/portrait PortraitPage 个人画像(基础/深度版)
/relation RelationPage 关系理解