hyapp-server/docs/CP关系服务端开发文档.md
2026-06-08 14:12:23 +08:00

9.9 KiB
Raw Blame History

CP关系服务端开发文档

本文定义当前 CP、兄弟、姐妹关系的服务端边界。关系事实归 user-service,送礼扣费归 wallet-service,房间送礼事实归 room-serviceCP 相关 IM 由 notice-service 消费 user_outbox 后补偿投递。

1. 关系规则

类型 编码 说明
CP cp CP 关系
兄弟 brother 兄弟关系
姐妹 sister 姐妹关系

规则:

  • 每个用户同时只能有一个 active 关系,不按类型拆数量,也不需要后台配置数量。
  • A 和 B 同时只能存在一种 active 关系。
  • A 可以给 B 发送 cpbrothersister 多种 CP 礼物,形成多条 pending 申请。
  • B 接受其中一个申请后,同一对用户其他 pending 申请会被置为 blocked,不能再接受。
  • 申请默认 24 小时过期,当前不提供后台配置入口。
  • 不提供佩戴 CP 功能;资料卡如果要展示关系,直接展示用户当前 active 关系。

2. 服务归属

服务 负责内容
wallet-service 礼物配置、扣费、热度值、礼物 icon 和动画快照
room-service 送礼命令、房间热度、房间礼物 IM、RoomGiftSent outbox
user-service CP 申请、关系、亲密值、等级、等级奖励幂等、user_outbox
gateway-service App HTTP 接口、鉴权、request_id 透传
notice-service 申请、同意、拒绝、关系成立的 C2C/房间/区域 IM
cron-service 定时触发 user-service 过期申请处理

边界:

  • room-service 不保存 CP 关系状态。
  • user-service 不直连 wallet-serviceroom-servicenotice-service 数据库。
  • CP 申请和亲密值变化只能消费 RoomGiftSent MQ 事实,不能扫 room_outbox
  • 下游服务只能消费 owner service 发布到 RocketMQ 的 outbox。

3. CP礼物配置

礼物大类使用 gift_type_code=cp。CP 子类型使用发送时快照字段 cp_relation_type,值为 cpbrothersister

配置 说明
礼物类型 gift_type_code=cp,礼物面板展示为 CP 礼物 Tab
CP 子类型 cp_relation_type=cp/brother/sister,用于 CP Tab 二级分类
礼物 icon 来自绑定资源的 asset_url,发送事件保存快照
礼物价格和热度 继续由 wallet-service 结算,客户端不能传价格或热度

cp_relation_type 写入礼物 presentation_json

{
  "cp_relation_type": "cp",
  "cp_relation_label": "CP礼物"
}

4. RoomGiftSent字段

api/proto/events/room/v1/events.protoRoomGiftSent 必须带以下送礼快照:

字段 类型 说明
gift_type_code string CP 礼物为 cp
cp_relation_type string cpbrothersister;非 CP 礼物为空
gift_name string 礼物名称
gift_icon_url string 礼物 icon
gift_animation_url string 礼物动画,可为空

5. 送礼触发申请

sequenceDiagram
  participant F as Flutter
  participant G as gateway-service
  participant R as room-service
  participant W as wallet-service
  participant U as user-service
  participant N as notice-service
  participant IM as Tencent IM

  F->>G: POST /api/v1/rooms/gift/send
  G->>R: SendGift
  R->>W: DebitGift
  W-->>R: heat_value + gift_type_code + cp_relation_type + gift snapshot
  R->>R: update heat/rank/command log
  R-->>F: SendGiftResponse
  R-->>U: RoomGiftSent MQ
  U->>U: create or refresh CP application
  U-->>N: UserCPApplicationCreated MQ
  N->>IM: A -> B C2C cp_application_created
  N->>IM: room cp_application_created, recipient_user_id=B

处理规则:

  • 只有 gift_type_code=cpcp_relation_type 有效时创建申请。
  • 同一个 RoomGiftSent.event_id 只能消费一次。
  • A 给 B 发送同一类型 CP 礼物时,如果已有 pending 申请,刷新礼物快照和 expires_at_ms
  • A 给 B 发送不同类型 CP 礼物时,可以各有一条 pending 申请。
  • 如果任一方已经有 active 关系,申请可以存在,但接受时会被拒绝;已有关系双方互送礼物会增加亲密值。

6. 同意和拒绝

sequenceDiagram
  participant F as Flutter B
  participant G as gateway-service
  participant U as user-service
  participant N as notice-service
  participant IM as Tencent IM

  F->>G: POST /api/v1/cp/applications/{id}/accept
  G->>U: AcceptCPApplication
  U->>U: lock pair + lock both users + create relationship
  U-->>N: UserCPApplicationAccepted MQ
  U-->>N: UserCPRelationshipCreated MQ
  N->>IM: B -> A C2C cp_application_accepted
  N->>IM: room cp_application_accepted, recipient_user_id=A
  N->>IM: region cp_relationship_created

处理规则:

  • 接受和拒绝都只能由申请目标用户 B 操作。
  • pending 申请过期后不能接受gateway envelope 返回 CONFLICT;内部 gRPC 语义是 FailedPrecondition。
  • 接受时同时校验 A 和 B 是否已经有任意 active 关系;任一方已有关系就返回 CONFLICT
  • 同意成功后写 active 关系,关闭同一对用户其他 pending 申请。
  • 同意或拒绝都向 A 发送 C2C IM 和房间 IM。
  • 同意成功后额外发送区域 IM区域取申请来源房间的 visible_region_id

7. 亲密值和升级

任意礼物发送成功后,如果发送方和收礼方已经有 active 关系,则按本次礼物的 heat_value 增加亲密值。

规则:

  • 普通礼物按 100% 热度值增加亲密值。
  • 幸运礼物、超级幸运礼物直接使用钱包回执里的最终 heat_value,不重复计算比例。
  • 多目标送礼时按每个目标的账务回执分别增加对应关系亲密值。
  • 等级范围 1 到 5当前使用 user-service 默认等级规则,不提供后台配置入口。
  • 每个等级可绑定奖励资源组,升级时调用 wallet-service 给双方发放资源。
  • 奖励发放幂等键为 cp_level_reward:{relationship_id}:{level}:{user_id}

8. App HTTP接口

所有接口走 gateway-service,响应使用 {code,message,request_id,data}

方法 地址 说明
GET /api/v1/cp/applications 查询我的申请
POST /api/v1/cp/applications/{application_id}/accept 同意申请
POST /api/v1/cp/applications/{application_id}/reject 拒绝申请
GET /api/v1/cp/relationships 查询我的 active 关系

申请列表参数:

参数 类型 必填 说明
direction string incomingoutgoing,默认 incoming
status string pendingacceptedrejectedexpiredblocked
page int32 默认 1
page_size int32 默认 20

关系列表参数:

参数 类型 必填 说明
relation_type string cpbrothersister;不传返回全部
page int32 默认 1
page_size int32 默认 20

拒绝请求体:

字段 类型 必填 说明
reason string 拒绝原因,只进入 IM/outbox payload

9. 数据表

表放在 user-service 库,所有表带 app_code,时间使用 UTC epoch milliseconds。

说明
user_cp_relation_configs 历史兼容表;当前固定 max_count_per_user=1,不提供后台配置
user_cp_level_rules 默认等级阈值和奖励资源组
user_cp_gift_event_consumption RoomGiftSent 消费幂等
user_cp_applications CP 关系申请
user_cp_relationships 双人关系事实
user_cp_level_reward_grants 等级奖励发放幂等
user_outbox CP 事件事实

关键唯一约束:

  • active 关系按用户对唯一:(app_code, user_a_id, user_b_id, status='active')
  • active 关系按单用户唯一:任一用户已有 active 关系就不能再接受新关系。
  • pending 申请按用户对和类型唯一:(app_code, requester_user_id, target_user_id, relation_type, status='pending')

10. 相关IM

IM 通道 发送时机
cp_application_created C2C A 送 CP 礼物触发申请后A 发给 B
cp_application_created 房间群 A 送 CP 礼物触发申请后发到来源房间payload 带 recipient_user_id=B
cp_application_accepted C2C B 同意后B 发给 A
cp_application_accepted 房间群 B 同意后发到来源房间payload 带 recipient_user_id=A
cp_application_rejected C2C B 拒绝后B 发给 A
cp_application_rejected 房间群 B 拒绝后发到来源房间payload 带 recipient_user_id=A
cp_relationship_created 区域群 关系建立后发送区域播报

C2C 使用腾讯云 IM From_Account 表达真实操作人:申请为 A处理结果为 B。区域 IM 群 ID 使用 hy_<app_code>_bc_r_<region_id>,本地测试可通过 group_id_prefix 加前缀。

11. 错误码

场景 code 说明
申请不存在 NOT_FOUND application_id 不存在或不属于当前用户
申请已过期 CONFLICT 不能再同意;内部 gRPC 语义是 FailedPrecondition
已有关系 CONFLICT A/B 任一方已经有 active 关系
无权限处理 PERMISSION_DENIED 不是申请目标用户
幂等冲突 CONFLICT command_id 或事件消费 payload 不一致

12. 验证项

  • CP 礼物发送成功后,RoomGiftSentgift_type_code=cpcp_relation_type、礼物 icon 快照。
  • A 给 B 连发 cpbrothersister 三种礼物,会生成三条 pending 申请。
  • B 接受其中一条后,其他两条不能再接受。
  • A/B 建立关系后A 或 B 都不能再和第三人建立 active 关系。
  • pending 申请超过默认 24 小时后不能接受。
  • active 关系双方互送任意礼物,亲密值按 heat_value 增加。
  • 同意、拒绝、关系成立的 IM 都写入 notice_delivery_events,失败可重试。