🏠 home › projects › coucou-backend-api-design
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-apistudio.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-serverapi-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)。

数据边界

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 阶段全部成立:

  1. 统一入口:H5 只认 api-coucou.sentino.jp 一个 backend,不暴露 studio.sentino.jp
  2. 隐藏 dragonflow:C 端浏览器不可见 Studio 域名(launch 阶段品牌 + 安全收益真实)
  3. 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 转发层通用约束

  1. 不签新 JWT:workflow-api 是唯一 JWT issuer,coucou-server 透传 token,业务 endpoint 本地验签(共享 HMAC secret)
  2. Header 透传Authorization / Set-Cookie / X-Trace-ID 全链路透传
  3. X-Forwarded-* 注入:所有 auth 代理请求注入 X-Forwarded-Host: api-coucou.sentino.jp + X-Forwarded-Proto: https
  4. 错误透传:workflow-api 的 4xx/5xx + body 原样回传(前端 error handling 不变)
  5. 超时 + 降级:auth 转发设独立超时(如 5s);workflow-api 不可用时返回明确 503(不静默吞)
  6. 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. 接口契约纪律(全局)

  1. OpenAPI spec finalize 6.13 EOD(M1 d2,陈玉祥 own)—— 前端拿 spec 独立开发不阻塞;launch 版含 auth 转发组 +10 endpoint
  2. Postman 集合 M1 d3 EOD ready —— 端到端测试基础
  3. CORS 全域允许(coucou-server 配 setAllowedOriginPatterns("*"),与 workflow-api 一致)
  4. JWT 共享 secret:workflow-api 唯一 issuer,coucou-server 本地验签(共享 HMAC secret),不签新 JWT
  5. API_KEY 共享:CouCou workspace 专属,coucou-server 用此调 api.sentino.jp
  6. trace_id 透传X-Trace-ID header 全链路透传
  7. X-Forwarded-* 注入:auth 代理请求注入 X-Forwarded-Host: api-coucou.sentino.jp + X-Forwarded-Proto: https(OAuth 回调落 coucou 域,§5.2.4)
  8. 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()
);

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


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);

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;

allWHERE 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" }
}

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}
  ]
}

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


后续 module(增量追加)


相关概念