- tags
- [coucou, backend, api, design, auth]
- created
- 2026-06-11
- updated
- 2026-06-12
- status
- draft
- repo
- https://github.com/sentino-jp/coucou-server
coucou-server 架构 + 接口 + 实现级设计权威文档。从 coucou-19day-sprint PART 3 抽取架构/接口契约 + 合并 auth 转发实现级设计。sprint plan PART 3 保留原内容作排期上下文,本文档为接口契约权威版(DTO / 转发规则 / 上游契约对齐 / 边界 case)。
架构前提变更(2026-06-11):本次 sprint = 公开 launch 免费版(非 demo)。auth 从「H5 直连 workflow-api」改为「全部走 coucou-server 转发 + OAuth 回调接管」(方案 A),推翻 sprint plan 旧决策 #6。理由见 §5.0。
1. 架构总览¶
核心定位:coucou-server = 薄业务层 + auth gateway¶
| 层 | 谁 | 职责 |
|---|---|---|
workflow-api(studio.sentino.jp 同 host) |
dragonflow,prod alive | Sentino IdP + 多功能后端:JWT/OAuth2/User/注册/Workspace/订阅/Agent CRUD/Agora pipeline/图片上传/邮件 |
| api.sentino.jp | 公共 API 门面 | chat completions / chat history / memory / conversation(CouCou workspace API_KEY 调用) |
coucou-server(api-coucou.sentino.jp) |
新建 | ① auth gateway(转发 workflow-api /api/auth/* + 业务 hook)② CouCou-specific 业务 schema + endpoint |
调用关系(launch 版,auth 转发后)¶
[CouCou H5] ──── 全部只调 ────► coucou-server (api-coucou.sentino.jp)
只认一个 backend │
├── /api/coucou/auth/* ──转发──► workflow-api /api/auth/*
│ (X-Forwarded-Host 注入 + 业务 hook)
├── /api/coucou/* 业务自有
└── 业务转发 ──► api.sentino.jp /api/v1/* (chat/history/memory)
└── 文件上传转发 ──► workflow-api /api/media/upload
└── (sprint 后) ──► IoT bridge api-iot.sentino.jp
[Google OAuth] ──callback──► coucou-server(X-Forwarded-Host 让回调落 coucou 域)
[Studio workflow-web] ──Publish 调──► coucou-server /api/coucou/creator/agents/publish
[Stripe webhook] ──(7.15)──► coucou-server /api/coucou/webhooks/stripe
studio.sentino.jp 对 C 端浏览器完全不可见(launch 隐藏 dragonflow)。
数据边界¶
- 复用 dragonflow PG instance,独立
coucoudatabase(独立 user/权限),coucou-server 不读 dragonflowworkflow_*表 - user 不建第二套:dragonflow user 表即 fan/creator 本体;coucou-server 只建
user_profiles关联dragonflow_user_id存 capability flags(is_fan/is_creator/display_name) - uid 是穿透各子系统的 single source of truth:dragonflow uid → CouCou 业务 / Studio / 未来 IoT / 未来 Stripe customer
- Dogfood public API 纪律:业务调用(chat/memory/history)全走
api.sentino.jp公共 API + workspace API_KEY,coucou-server 跟外部 B 端客户平等,无直连 workflow-api 私有 endpoint / 数据库(auth 转发是唯一例外——IdP 基础设施层)
Studio ↔ CouCou 天然互通¶
同一 dragonflow user + 同一 JWT issuer:Mia 登 Studio 创建 agent(workspace OWNER/ADMIN)→ coucou-server login hook 检测到角色 → is_creator=true → CouCou H5 Creator 卡翻转。sprint 内真实现,不 mock。
2. 接口契约总表(27 业务 endpoint + ~14 auth 转发)¶
完整调用关系 + 职责边界。auth 转发组详见 §5,业务组 DTO 详见后续 module 章节。
A. coucou-server 提供的 endpoint¶
A.0 auth 转发组(M0/M1,~14 个,§5 详)¶
/api/coucou/auth/* 全部转发 workflow-api /api/auth/*:register / verify-email-code / login / refresh-token / logout / me / profile / change-password / registration-config / sessions(GET+DELETE) / oauth2(authorize/callback/bind)
A.1 Agent 元数据 + 角色列表¶
| 方法 | Endpoint | 调用方 | 用途 | 节点 |
|---|---|---|---|---|
| GET | /api/coucou/agents |
H5 | 当前 user 已订阅 agent 列表 | M1 |
| GET | /api/coucou/agents/:id |
H5 | 角色详情 | M1 |
| GET | /api/coucou/agents/:id/plans |
H5 | plan 列表(Plan Sheet) | M1 |
| POST | /api/coucou/admin/agents |
admin | seed agent metadata | M1 |
| POST | /api/coucou/admin/agents/:id/plans |
admin | 批量配 plan | M1 |
| POST | /api/coucou/admin/grant-subscription |
admin | demo 账号预订阅 | M1 |
| GET | /api/coucou/admin/memory?user=X&agent=Y |
admin | 转发 memex GET(debug) | M3 d3 |
| POST | /api/coucou/creator/agents/publish |
Studio | 一站式 metadata+plans | M4(郑国敏) |
| POST | /api/coucou/creator/agents/:id/plans |
CouCou app | Creator 配 plan | sprint 后 |
| PATCH | /api/coucou/creator/agents/:id |
H5 | 改 metadata | M6 nice |
A.2 Chat(转发 api-gateway)¶
| 方法 | Endpoint | 转发到 | 节点 |
|---|---|---|---|
| POST | /api/coucou/chat |
api.sentino.jp/api/v1/chat/completions(user=coucou_user_<uid>) |
M3 |
| GET | /api/coucou/chat/history |
api.sentino.jp/api/v1/chat/history + merge broadcast |
M3-M5 |
A.3 Subscription(Stripe — 全部 7.15 sprint,sprint 内 stub)¶
checkout / webhooks/stripe / list / cancel / portal-url —— sprint 内 Subscribe 按钮 stub "✨ 即将上线"
A.4 Broadcast(B 主力,§4 详)¶
| 方法 | Endpoint | 用途 | 节点 |
|---|---|---|---|
| POST | /api/coucou/broadcasts |
创建 text/voice broadcast | M5 |
| GET | /api/coucou/broadcasts?agent_id=X |
分发记录 | M5 |
| GET | /api/coucou/notifications/unread |
未读 broadcast(红点) | M5 |
| POST | /api/coucou/notifications/:broadcast_id/read |
mark read | M5 |
| POST | /api/coucou/creator/distribute |
A 框架 stub | M5 |
| GET | /api/coucou/creator/distributes |
A 框架历史 | M5 |
A.5 Creator Operation + Dashboard(真数据)¶
| 方法 | Endpoint | 用途 | 节点 |
|---|---|---|---|
| GET | /api/coucou/creator/operation |
Operation tab("本月到手" + Sense mock) | M5 |
| GET | /api/coucou/creator/operation/sense?agent=X |
Sense 真转发 | 7.15 |
| GET | /api/coucou/creator/dashboard/fans?agent_id=X |
fan 列表 | M5 |
| GET | /api/coucou/creator/dashboard/subscriptions?agent_id=X |
订阅 timeline | M5 |
| GET | /api/coucou/creator/dashboard/revenue-trend?agent_id=X&days=30 |
收入趋势 | M5 |
| GET | /api/coucou/creator/dashboard/interactions?agent_id=X |
互动次数 | M5 |
A.6 Creator Memory + Controls(M6,郑国敏)¶
notes CRUD ×4(GET/POST/PATCH/DELETE)+ controls GET/PATCH
B. api-gateway / workflow-api 提供的 endpoint(coucou-server 调用)¶
B.1 已有公共 API(api.sentino.jp)✓¶
/api/v1/chat/completions /api/v1/chat/history /api/v1/conversations*(语音 sprint 不做)/api/v1/memory/cleanup /api/v1/chat/cleanup
B.2 已有 workflow-api auth(coucou-server 转发,§1 实测契约 §5.1 详)✓¶
register/login/refresh/logout/me/profile/change-password/registration-config/sessions/oauth2*
B.3 Sprint 内 dragonflow team 必须新增 ⚠️¶
| 方法 | Endpoint | 截止 |
|---|---|---|
| POST | /api/auth/verify-email-code 新增 |
M1 d3 (6.14) |
| POST | /api/image/upload→/api/media/upload 改造接收 audio |
M4 d3 (6.23) |
| GET | /api/v1/memory/items?user=X&agent=Y 新增 |
M3 d3 (6.20) 前 |
| — | OAuth2Service 信任 X-Forwarded-Host(验证,§5.0) | Day-0 |
B.4 Sprint 后(Phase 3 Memory UI 前置)¶
POST/PATCH/DELETE /api/v1/memory/items + /api/v1/metering/sense(7.15)
3. Agent 发布方案(M4 deliverable,郑国敏 own)¶
职责分工¶
| 工具 | 职责 |
|---|---|
| Studio (workflow-web) | agent 本体创作 + Publish flow:一站式发 metadata + 3 plan |
| coucou-server | POST /creator/agents/publish(payload 含 metadata + plans 数组) |
coucou.coucou_agents schema¶
CREATE TABLE coucou.coucou_agents (
agent_id varchar(64) PRIMARY KEY, -- = api.sentino.jp 平台 agent UUID
name varchar(100), avatar_url varchar(500), description text,
by_creator_name varchar(100), category varchar(50), tags jsonb,
body_color varchar(10), accent_color varchar(10),
status varchar(20) DEFAULT 'draft', -- draft/published/paused/archived
owner_user_id varchar(64), -- creator 权限校验
ip_policy_id bigint, published_at timestamptz, created_at timestamptz DEFAULT now()
);
发布 flow(sprint 内一站式)¶
1. Creator 在 Studio 创建/编辑 agent
2. Studio 点"发布到 CouCou" → form 填 metadata + 3 plan (slot1 强制 free)
3. POST /api/coucou/creator/agents/publish
· coucou-server 校验权限(user 在该 agent workspace 是 OWNER/ADMIN)
· insert coucou_agents + coucou_agent_plans
· sprint 内不调 Stripe(plan stripe_product_id 留空),status='published'
4. CouCou 角色列表 / 搜索立即可见,fan 可看 plan(Subscribe stub)
详细 schema(coucou_agent_plans 等)见后续 §M3 module。
4. Broadcast 设计(M5,B 主力 + A 框架)¶
两种模式¶
| 模式 | 内容来源 | sprint 内 |
|---|---|---|
| B. Creator original | Creator 直接输入 text 或录音 | 完整实现(陈玉祥后端 + 伍天力录音 UX) |
| A. Agent distribute | Creator 跟 agent 聊 → mark AI 回复 distribute | stub endpoint + UI 框架,产品打磨 |
B 模式流程¶
Creator 端 → POST /api/coucou/broadcasts {agent_id, message_type, content_text/audio_url, tier_scope}
↓ 后端 insert broadcast_messages + fanout 写订阅 fan 的 fan_broadcast_receipts
Fan 端 Home 红点 + banner → 点进 Player Chat
↓ GET /chat/history merge broadcast_messages 按时间 DESC(陈玉祥最复杂业务逻辑,郑国敏 M3 algorithm doc)
↓ 文本=紫框气泡 / 语音=语音条 + "📢 by creator",进 chat 自动 POST /notifications/:id/read
B 模式技术规格¶
| 项 | 规格 |
|---|---|
| 录音 UX | 长按录音 + 松手发送 / 上滑取消(微信风格,伍天力独立完成) |
| 音频格式 | webm/opus(MediaRecorder) |
| 时长上限 | 60 秒(~200-500KB/条) |
| 上传 | /api/media/upload(workflow-api 扩 audio MIME,dragonflow team) |
| Tier scope | All / Companion+ / Inner Circle |
| 通知 sprint 内 | In-app 红点 + Home banner(不做 Web Push/email) |
| Chat merge | GET /chat/history join broadcast_messages 时间 DESC |
A 模式框架(stub)¶
POST /api/coucou/creator/distribute:mark chat message highlighted(不真推 fan),后端 0.3d。
5. auth 转发层设计(M0/M1,方案 A)¶
5.0 架构决策记录:auth 转发(推翻决策 #6)¶
旧决策(已推翻)¶
sprint plan 决策 #6 + v2 报告 §03:H5 直接调 studio.sentino.jp/api/auth/*,理由「auth=IdP 能力,直连合理(Stripe/Auth0 同模型),CORS 已允许,无中转价值,零工程」。前提是 6.30 = demo。
新决策(方案 A)¶
全部 auth 走 coucou-server 转发 + OAuth 回调接管。 前提变更为 6.30 = 公开 launch 免费版,三个动机在 launch 阶段全部成立:
- 统一入口:H5 只认
api-coucou.sentino.jp一个 backend,不暴露studio.sentino.jp - 隐藏 dragonflow:C 端浏览器不可见 Studio 域名(launch 阶段品牌 + 安全收益真实)
- auth 挂业务 hook:register/login/oauth callback 成功后同步建
user_profiles/ 检测is_creator/ 触发欢迎邮件
容量影响(staffing 视角,落陈玉祥)¶
| 项 | 工时 | 落点 |
|---|---|---|
| ~10 auth proxy endpoint | ~1d | M1 |
| 3 业务 hook(建 profile / 检测 creator / 发邮件) | ~0.5d | 本就要做,位置挪到 hook |
| OAuth 回调接管 | ~0.5d(比预估低,见下) | M1 |
| OpenAPI spec +10 endpoint | +0.5d | 6.13 EOD |
| 净增 | ~2-2.5d | 砸 M1 |
陈玉祥 sprint 内 ~15d + 3d buffer → ~17d + ~1d buffer(回到接近满载,但 launch 标准下值得)。
关键工程发现(降低了 OAuth 接管成本)¶
读 OAuth2Controller.java 源码:
- resolveBaseUrl() 用 X-Forwarded-Host header 决定回调域 → coucou-server 反向代理时注入 X-Forwarded-Host: api-coucou.sentino.jp,workflow-api 自动把 redirect_uri + 最终 302 都指向 coucou 域。OAuth 回调接管基本不需要改 workflow-api 源码,大幅降低跨团队依赖。
- callback 同时支持 GET + POST,成功后 302 跳 {baseUrl}/oauth/callback?token=...&refresh=...(已绑定)或 ?binding_token=...&provider=...&email=...(未绑定需 bind)。
- AuthResponse 已含 workspaces 字段 → login 响应直接拿到 user 的 workspace 角色,is_creator 检测零额外调用。
- ⚠️ verify-email-code endpoint 当前 workflow-api 不存在(AuthController 只有 register/login/logout/refresh-token/me/profile/change-password/sessions)→ 仍是 dragonflow team M1 必须新增项(staffing 已列)。
- ⚠️ Google Console 必须把 https://api-coucou.sentino.jp/... 加入 authorized redirect URIs(IT/dragonflow team Day-0 项)。
5.1 上游 workflow-api auth 真实契约(read source 对齐)¶
@RequestMapping("/api/auth") + @RequestMapping("/api/auth/oauth2"),实测签名:
| workflow-api endpoint | 方法 | Request body | 备注 |
|---|---|---|---|
/api/auth/register |
POST | {username, email, password, fullName, invitationCode?} |
invitationCode 可空(公开注册) |
/api/auth/login |
POST | {email, password, rememberMe?} |
返回 AuthResponse |
/api/auth/logout |
POST | — | |
/api/auth/refresh-token |
POST | {refreshToken} (Map) |
|
/api/auth/me |
GET | — (JWT) | 当前 user |
/api/auth/profile |
PUT | UpdateUserRequest |
改 avatar/displayName |
/api/auth/change-password |
POST | ChangePasswordRequest |
|
/api/auth/registration-config |
GET | — | {invitationCodeRequired, googleOAuthEnabled} |
/api/auth/validate-invitation-code |
POST | {code} (Map) |
launch 免费版不用 |
/api/auth/sessions |
GET / {id} DELETE |
— | 软件终端管理(PRD P1-07) |
/api/auth/verify-email-code |
POST | ⚠️ 待 dragonflow team 新增 | 验证码激活 |
/api/auth/oauth2/authorize/{provider} |
GET | ?link_token=? |
302 到 Google;redirect_uri 由 X-Forwarded-Host 决定 |
/api/auth/oauth2/callback/{provider} |
GET+POST | ?code&state&error |
302 回 {baseUrl}/oauth/callback?token=... |
/api/auth/oauth2/bind |
POST | OAuth2BindRequest |
binding_token 换正式 token |
/api/auth/oauth2/accounts |
GET / {provider} DELETE |
— | 已绑账号管理 |
AuthResponse 结构(login / refresh / oauth bound 返回):
{
"accessToken": "...", "refreshToken": "...",
"tokenType": "Bearer", "expiresIn": 3600,
"user": {"id", "username", "email", ...},
"workspaces": [{...role...}], // ← is_creator 检测数据源
"entryWorkspaceId": "..."
}
5.2 coucou-server auth 转发层设计 (~14 endpoint)¶
路径前缀 /api/coucou/auth/*,全部转发到 workflow-api /api/auth/*。
5.2.1 转发模式分类¶
| 模式 | endpoint | 行为 |
|---|---|---|
| 透传 | refresh-token / logout / registration-config / change-password / sessions / me | 改 host + 透 body + 回传响应(含 Set-Cookie / JWT),不签新 JWT |
| 透传 + 业务 hook | register / verify-email-code / login | 转发成功后额外跑 CouCou 业务(见 2.3) |
| 代理 + Header 注入 | oauth2/authorize / oauth2/callback | 注入 X-Forwarded-Host: api-coucou.sentino.jp 让回调落 coucou 域 |
5.2.2 endpoint 清单¶
| 方法 | coucou-server | → workflow-api | 模式 | hook |
|---|---|---|---|---|
| POST | /api/coucou/auth/register |
/api/auth/register |
透传+hook | 建 profile(pending)+ 触发验证码邮件(CouCou 文案) |
| POST | /api/coucou/auth/verify-email-code |
/api/auth/verify-email-code ⚠️ |
透传+hook | 激活 → is_fan=true |
| POST | /api/coucou/auth/login |
/api/auth/login |
透传+hook | 从 AuthResponse.workspaces 检测 OWNER/ADMIN → is_creator;upsert profile |
| POST | /api/coucou/auth/refresh-token |
/api/auth/refresh-token |
透传 | — |
| POST | /api/coucou/auth/logout |
/api/auth/logout |
透传 | — |
| GET | /api/coucou/auth/me |
/api/auth/me |
透传 | (可选 merge profile) |
| PUT | /api/coucou/auth/profile |
/api/auth/profile |
透传+hook | 同步 displayName/avatar 到 user_profiles |
| POST | /api/coucou/auth/change-password |
/api/auth/change-password |
透传 | — |
| GET | /api/coucou/auth/registration-config |
/api/auth/registration-config |
透传 | — |
| GET | /api/coucou/auth/sessions |
/api/auth/sessions |
透传 | 软件终端列表(PRD P1-07) |
| DELETE | /api/coucou/auth/sessions/:id |
/api/auth/sessions/:id |
透传 | 登出会话 |
| GET | /api/coucou/auth/oauth2/authorize/google |
/api/auth/oauth2/authorize/google |
代理+Header | 注入 X-Forwarded-Host |
| GET+POST | /api/coucou/auth/oauth2/callback/google |
/api/auth/oauth2/callback/google |
代理+Header | callback 落 coucou 域后 hook 建 profile |
| POST | /api/coucou/auth/oauth2/bind |
/api/auth/oauth2/bind |
透传+hook | bound 后建 profile |
5.2.3 业务 hook 逻辑(转发成功后执行)¶
register 成功:
└─ upsert coucou.user_profiles (dragonflow_user_id, status=pending, is_fan=false)
└─ (验证码邮件由 workflow-api 发,CouCou 文案由 dragonflow team 改造)
verify-email-code 成功:
└─ update user_profiles set is_fan=true where dragonflow_user_id=?
login / oauth callback bound 成功:
└─ 从 AuthResponse.workspaces 检测有无 OWNER/ADMIN 角色
└─ upsert user_profiles (is_creator = hasOwnerOrAdmin, coucou_display_name, avatar_url)
└─ 不阻塞登录响应(hook 失败仅 log,不影响 JWT 返回)
profile 更新成功:
└─ sync coucou_display_name / avatar_url → user_profiles
hook 失败策略:hook 是 CouCou 侧增量,不得阻塞 auth 主流程。hook 异常仅记 log + 告警,auth 响应照常返回(profile 可后续 login 时补建——upsert 幂等)。
5.2.4 OAuth 回调接管流程(关键设计)¶
1. H5 点 "Google 登录" → GET api-coucou.sentino.jp/api/coucou/auth/oauth2/authorize/google
2. coucou-server 代理转发到 workflow-api,注入 X-Forwarded-Host: api-coucou.sentino.jp
3. workflow-api resolveBaseUrl() 读到该 header
→ redirect_uri = https://api-coucou.sentino.jp/api/coucou/auth/oauth2/callback/google
→ 302 到 Google 授权页
4. 用户授权 → Google 302 回 api-coucou.../callback/google?code=...&state=...
5. coucou-server 代理转发 callback 到 workflow-api(带 code/state + X-Forwarded-Host)
6. workflow-api handleOAuth2Callback:
- bound=true → 302 {coucou域}/oauth/callback?token=...&refresh=...
- bound=false → 302 {coucou域}/oauth/callback?binding_token=...&provider=google&email=...
7. coucou-server 在 callback 转发成功后跑 hook(bound 时建 profile)
8. H5 /oauth/callback 页解析 query 拿 token / 或走 bind 流程
⚠️ Google Console 配置(Day-0 阻塞,IT + dragonflow team):
- authorized redirect URI 加 https://api-coucou.sentino.jp/api/coucou/auth/oauth2/callback/google
- workflow-api 的 Google OAuth client redirect 白名单同步加 coucou 域(如 OAuth2Service 校验 redirect_uri)
⚠️ 待验证:workflow-api 的 OAuth2Service.buildAuthorizationUrl 传给 Google 的 redirect_uri 必须与 Google Console 白名单 + callback 实际落地域三者一致。X-Forwarded-Host 方案成立的前提是 OAuth2Service 不二次硬编码 redirect_uri。Day-0 跟 dragonflow team 确认这一条。
5.3 转发层通用约束¶
- 不签新 JWT:workflow-api 是唯一 JWT issuer,coucou-server 透传 token,业务 endpoint 本地验签(共享 HMAC secret)
- Header 透传:
Authorization/Set-Cookie/X-Trace-ID全链路透传 - X-Forwarded-* 注入:所有 auth 代理请求注入
X-Forwarded-Host: api-coucou.sentino.jp+X-Forwarded-Proto: https - 错误透传:workflow-api 的 4xx/5xx + body 原样回传(前端 error handling 不变)
- 超时 + 降级:auth 转发设独立超时(如 5s);workflow-api 不可用时返回明确 503(不静默吞)
- rate limit:auth endpoint 单独限流(防注册/登录穷举),独立于业务 endpoint
5.4 待确认 (Day-0)¶
| # | 问题 | owner |
|---|---|---|
| 1 | workflow-api OAuth2Service 是否信任 X-Forwarded-Host 决定 redirect_uri(不二次硬编码) | dragonflow team |
| 2 | Google Console redirect URI 加 api-coucou 域 | IT |
| 3 | verify-email-code endpoint M1 d3 前 dev ready |
dragonflow team |
| 4 | JWT 共享 HMAC secret 提供 | dragonflow team |
| 5 | login 的 AuthResponse.workspaces 角色字段名 / 枚举值(OWNER/ADMIN 检测用) | dragonflow team |
6. 接口契约纪律(全局)¶
- OpenAPI spec finalize 6.13 EOD(M1 d2,陈玉祥 own)—— 前端拿 spec 独立开发不阻塞;launch 版含 auth 转发组 +10 endpoint
- Postman 集合 M1 d3 EOD ready —— 端到端测试基础
- CORS 全域允许(coucou-server 配
setAllowedOriginPatterns("*"),与 workflow-api 一致) - JWT 共享 secret:workflow-api 唯一 issuer,coucou-server 本地验签(共享 HMAC secret),不签新 JWT
- API_KEY 共享:CouCou workspace 专属,coucou-server 用此调 api.sentino.jp
- trace_id 透传:
X-Trace-IDheader 全链路透传 - X-Forwarded-* 注入:auth 代理请求注入
X-Forwarded-Host: api-coucou.sentino.jp+X-Forwarded-Proto: https(OAuth 回调落 coucou 域,§5.2.4) - Rate limit 双层:H5 → coucou-server(user-level)+ coucou-server → api.sentino.jp(workspace-level);auth endpoint 单独限流防穷举
7. M1 — identity(user_profiles + capability flag)¶
owner 陈玉祥 · M1 (6.12-6.14) · auth 转发层见 §5,本节是 auth 之上的 CouCou 身份业务层:profile schema + capability 判定 + me merge。M1 EOD 验收 = 注册登录通 +
GET /me返回 capability flag。
7.1 coucou.user_profiles schema¶
dragonflow user 表是 fan/creator 本体(uid = single source of truth,§1 数据边界);coucou-server 不复制 user,只建一张关联表存 CouCou capability。
CREATE TABLE coucou.user_profiles (
dragonflow_user_id varchar(64) PRIMARY KEY, -- = workflow-api user.id,外键语义(跨库不建真 FK)
is_fan boolean NOT NULL DEFAULT false, -- 邮箱验证码激活后 true
is_creator boolean NOT NULL DEFAULT false, -- workspace 有 OWNER/ADMIN 角色
status varchar(20) NOT NULL DEFAULT 'pending', -- pending(注册未激活)/active/suspended
coucou_display_name varchar(100), -- CouCou 内昵称(可与 dragonflow fullName 不同)
avatar_url varchar(500),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
is_fan/is_creator是派生 capability 的物化缓存:is_fan 来自验证码激活 hook,is_creator 来自 login 时AuthResponse.workspaces角色检测(§5.2.3)。upsert 幂等——任何一次 login 都会补建/刷新,profile 丢失可自愈。- 不存 email/password/JWT——那些是 workflow-api 的事。
7.2 capability 判定规则¶
| flag | 何时置 true | 数据源 | 影响 H5 |
|---|---|---|---|
is_fan |
verify-email-code 成功 hook | coucou 自己写 | 能订阅 / 聊天 |
is_creator |
login / oauth bound hook 检测到 workspace OWNER/ADMIN | AuthResponse.workspaces[].role |
Creator 卡翻转、能进 Studio Publish |
status |
register→pending,verify→active | coucou 自己写 | pending 限制功能 |
is_creator 判定(§5.4 Day-0 #5 待确认角色枚举值):
isCreator = workspaces.any(w => w.role in ('OWNER','ADMIN'))
zero 额外调用——login 的 AuthResponse 已带 workspaces(§5.1)。
7.3 GET /api/coucou/auth/me(转发 + merge profile)¶
| 转发 | GET workflow-api /api/auth/me(透传 JWT)拿 dragonflow user |
| merge | 用 user.id 查 coucou.user_profiles,合并 capability flag |
| profile 缺失 | 即时 upsert 补建(status=active 兜底,is_fan/is_creator 按当次可得信息)后返回——不 404 |
Response DTO:
{
"user": { // 来自 workflow-api /me 原样透传
"id": "u_xxx", "username": "...", "email": "...", "fullName": "..."
},
"coucou": { // coucou.user_profiles merge
"is_fan": true,
"is_creator": false,
"status": "active",
"display_name": "小桃",
"avatar_url": "https://.../a.png"
}
}
H5 一次请求拿到 user + capability,Home/Creator 卡渲染不再二次调用。
7.4 M1 identity endpoint 清单¶
| 方法 | endpoint | 说明 | 备注 |
|---|---|---|---|
| GET | /api/coucou/auth/me |
转发 /me + merge profile(§7.3) | 本节核心 |
| PUT | /api/coucou/auth/profile |
转发 /profile + sync display_name/avatar 到 profiles(§5.2.3 hook) | 复用 auth 转发 |
注册/登录/验证码/OAuth 全部在 §5 auth 转发层;M1 identity 业务只新增 me 的 merge 逻辑 + user_profiles 表 + capability hook,工时已含在 §5.0 的「3 业务 hook ~0.5d」。
7.5 边界 case¶
- profile 与 dragonflow 不一致:以 dragonflow user 为准(id/email),coucou 只补 capability;display_name 冲突时 CouCou 优先 coucou_display_name,空则回落 fullName。
- 未激活(status=pending)调业务:业务 endpoint 中间件校验
is_fan,pending 用户调订阅/聊天返回 403account_not_activated(前端引导去验证邮箱)。 - is_creator 时效:workspace 角色变化(Mia 被授 ADMIN)→ 下次 login hook 刷新;sprint 内不做实时同步(够用)。
8. M2 — agent 元数据 + 角色列表 + plan 读取¶
后端 owner 陈玉祥(M2 endpoint 巩固)· 前端 owner 伍天力(Home / 角色列表 overlay / Player Detail)· 陈玉祥 0.5d 协作角色列表 overlay。本节是 agent 的「读」契约(H5 展示);agent 的「写/发布」在 §3 + M4(郑国敏)。schema
coucou_agents见 §3.2。
8.1 三个读取场景¶
| 场景 | endpoint | 数据 | 前端 |
|---|---|---|---|
| Home「My Characters」 | GET /api/coucou/agents |
当前 user 已订阅的 agent(join subscriptions) | Home 卡片 |
| 角色列表 overlay(发现/全部) | GET /api/coucou/agents?scope=all |
所有 published agent | overlay 浏览 |
| Player Detail | GET /api/coucou/agents/:id |
单 agent 详情 + 当前 user 订阅态 | Detail 页 |
| Plan Sheet | GET /api/coucou/agents/:id/plans |
该 agent 的 plan 列表 | Subscribe 弹层(Subscribe 按钮 stub) |
8.2 coucou.coucou_subscriptions schema(M1 建表,sprint 内 admin grant 写)¶
订阅关系独立成表(不存 profile jsonb),Stripe 字段 M1 预留、M4/7.15 才写。
CREATE TABLE coucou.coucou_subscriptions (
id bigserial PRIMARY KEY,
dragonflow_user_id varchar(64) NOT NULL, -- fan
agent_id varchar(64) NOT NULL, -- → coucou_agents.agent_id
tier_slot smallint NOT NULL, -- 1/2/3,对应 agent 的 plan slot
status varchar(20) NOT NULL DEFAULT 'active', -- active/canceled/expired/past_due
source varchar(20) NOT NULL DEFAULT 'grant', -- grant(admin) / stripe(7.15)
-- Stripe 预留(sprint 内全空,7.15 webhook 回写)--
stripe_subscription_id varchar(80),
stripe_customer_id varchar(80),
current_period_end timestamptz,
-- --
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (dragonflow_user_id, agent_id) -- 一个 fan 对一个 agent 单一有效订阅
);
CREATE INDEX idx_sub_user ON coucou.coucou_subscriptions (dragonflow_user_id, status);
CREATE INDEX idx_sub_agent ON coucou.coucou_subscriptions (agent_id, status);
- sprint 内订阅由
POST /api/coucou/admin/grant-subscription(§A.1)写入source='grant';Subscribe 按钮 stub「✨ 即将上线」。 - 7.15 Stripe webhook 按
stripe_subscription_idupsert,source='stripe',status 由 webhook 事件驱动(§M5 状态机)。 - slot1 强制 free(§3)→ fan 注册激活后可被 admin grant slot1,或后续自助订阅 free slot。
8.3 GET /api/coucou/agents(My Characters / 全部)¶
Query:?scope=subscribed|all(默认 subscribed)
subscribed:
SELECT a.* FROM coucou.coucou_agents a
JOIN coucou.coucou_subscriptions s
ON s.agent_id = a.agent_id
AND s.dragonflow_user_id = :uid AND s.status = 'active'
WHERE a.status = 'published'
ORDER BY s.updated_at DESC;
all:WHERE a.status='published' ORDER BY a.published_at DESC(overlay 浏览)。
Response(item 结构,两 scope 共用):
{
"agents": [{
"agent_id": "agt_xxx",
"name": "桃子",
"avatar_url": "https://.../t.png",
"description": "...",
"by_creator_name": "Mia",
"category": "idol",
"tags": ["治愈","唱见"],
"body_color": "#FFD2E0", "accent_color": "#FF6FA5",
"subscribed": true, // 当前 user 是否已订阅(all scope 下区分卡片态)
"tier_slot": 1 // 已订阅时的档位,未订阅 null
}]
}
8.4 GET /api/coucou/agents/:id(Player Detail)¶
单 agent 全量 + 当前 user 订阅态 + plan 概要(详情进 Plan Sheet 拉 /plans):
{
"agent_id": "agt_xxx", "name": "...", "avatar_url": "...", "description": "...",
"by_creator_name": "Mia", "category": "idol", "tags": [...],
"body_color": "#...", "accent_color": "#...",
"subscription": { "subscribed": true, "tier_slot": 1, "status": "active" },
"plan_summary": { "free_slot": 1, "paid_slots": [2,3], "currency": "USD" }
}
- agent 不存在 / status≠published → 404
agent_not_found(草稿态对 fan 不可见)。
8.5 GET /api/coucou/agents/:id/plans(Plan Sheet)¶
返回该 agent 三档 plan(schema coucou_agent_plans 详见 §M3)。sprint 内 monthly only,USD。
{
"agent_id": "agt_xxx",
"currency": "USD",
"plans": [
{"tier_slot":1,"tier_name":"Free","price":0,"interval":"month","is_free":true,"benefits":["..."],"current":true},
{"tier_slot":2,"tier_name":"Companion","price":9.99,"interval":"month","is_free":false,"benefits":["..."],"current":false},
{"tier_slot":3,"tier_name":"Inner Circle","price":29.99,"interval":"month","is_free":false,"benefits":["..."],"current":false}
]
}
current= 当前 user 当前订阅的 slot(高亮)。price=0slot 必为 slot1(§3 强制 free)。- sprint 内 plan 的
stripe_price_id留空,Subscribe 按钮 stub。
8.6 admin / seed endpoint(M1,陈玉祥 seed 5 agent / 15 plan)¶
| 方法 | endpoint | 用途 |
|---|---|---|
| POST | /api/coucou/admin/agents |
seed agent metadata(含 status='published') |
| POST | /api/coucou/admin/agents/:id/plans |
批量配 3 plan(slot1 free) |
| POST | /api/coucou/admin/grant-subscription |
demo 账号预订阅 {user_id, agent_id, tier_slot} → 写 subscriptions source='grant' |
seed 数据来自产品
[产] demo agent persona 草稿(M1 ready)(5 idol agent)+[产] 15 plan 数字 6.13 EOD 定(5 agent × 3 tier)。
8.7 M2 边界 case¶
- scope=all 含已订阅:item
subscribed=true区分卡片态,不从 all 列表剔除(overlay 显示「已拥有」标)。 - agent published 但无 plan(seed 半成品):
/plans返回空数组 + 200,前端 Plan Sheet 显示「敬请期待」,不报错。 - 同一 fan 重复 grant:UNIQUE(user,agent) → upsert 改 tier_slot,不插重复行。
- 草稿 agent:status≠published 一律对 fan 404;admin/creator 自己的草稿走 §M4 creator endpoint(带 owner 校验)。
后续 module(增量追加)¶
- [x] M1 identity: user_profiles schema + capability flag + me merge(§7)
- [x] M2 agent: metadata + 角色列表 + plan 读取契约 + subscriptions schema(§8);publish 写流程见 §3 + M4
- [ ] M3 plan: per-agent 三档 + Stripe 映射(schema ready, endpoint stub)
- [ ] M4 chat: 转发 + history merge broadcast
- [ ] M5 subscription: 状态机(sprint stub, 7.15 接 Stripe)
- [ ] M6 broadcast: B 主力 + A 框架 + notification fanout
- [ ] M7 creator-ops: operation / dashboard / notes / controls
相关概念¶
- coucou-19day-sprint — sprint plan(接口契约总览 / 排期 / 范围)
- coucou-19day-sprint-staffing — 人力分配(auth 转发 +2-2.5d 落陈玉祥)
- 2026-06-11-architecture-business-readiness — v2 架构(§03 auth 直连论证,本文档 §5.0 推翻)
- dragonflow-platform — workflow-api 是 Sentino IdP