lili 3f35acb4d3 feat(community): U3 R-SOC 评论/关注/排行ZSET/弹幕WS
社区社交后端四子能力(game-module-community),克隆通知底座/feed互动幂等范式:

- 评论 Comments:CRUD + 举报转 compliance。删除按 userId 归属强校验(防越权删他人评论),
  举报落本端举报态幂等 + 事务提交后 afterCommit 接缝(compliance 无受理 -api,留桩 OPEN ITEM)。
- 关注 Follow:active 软开关幂等切换(不删行,范本=feed FeedInteractLog),不能关注自己,
  并发双插由 DB 唯一键 DuplicateKeyException CAS 收敛;粉丝数/关注数 countActive。
- 排行 Ranking:Redis ZSET + ZSET↔DB 一致性窗口。DB=真相、ZSET=缓存索引:
  写穿透双写(DB 原子累加 + ZSET incrementScore)、ZSET 缺失/启动从 DB 回填重建、最终一致;
  维度白名单 RankDimensionEnum(game_hot/creator_hot)。
- 弹幕 Danmaku:WebSocket 单机最小实(LocalWebSocketMessageSender),自维护房间 gameId→sessionId
  注册表做房间维度广播(框架仅按用户维度推送),断连懒清理;多实例需切 sender-type=redis(MQ future-state 未部署)。

契约/库表:
- Flyway V23.0.0(U3 独占 V23.x):game_community_comment/follow/rank/danmaku 四表 +
  幂等唯一键(follow uk(user_id,target_user_id,deleted,tenant_id)、rank uk(dimension,target_id,...)),
  同步 huijing-server/db/migration + contracts/db-schemas。
- community.yaml additive 加 8 个 app 端点 + 5 个 schema(既有冻结)。
- 错误码段 1-107-004 评论 / 005 关注 / 006 排行 / 007 弹幕。

验证:mvn -pl game-module-community/game-module-community-server -am test -o 全绿
(57 tests:新增 U3 35 + 既有 22;CommentService 9 / FollowService 8 / RankService 8 / Danmaku 10)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 11:01:24 -07:00

578 lines
32 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

openapi: 3.0.3
# 契约 #8 API | 模块:community(通知底座:站内信 + 创作者等级 + 新人奖励触发)| owner:WS5,全员 review
# 设计依据:docs/agent-specs/2026-06-11-Wave4-community-biz-execution.md §4.3/§4.4(D-B 通知底座最小集,覆盖 5/5 P0)。
# 端:/app-api(产品端 game-studio:我的站内信中心 / 未读角标 / 我的等级 / 我的奖励,用户 Token + Mapper 强制 getLoginUserId() 归属谓词)
# /rpc-api(CommunityNotifyApi 四方法,供上游 aigc/project/trade 同进程 @Primary 调用,非对外业务路由)
# /admin-api(staging only:四类投递 mock-trigger,上游未接通前独立验收)
# 前缀由 huijing 框架按 controller.app/admin 包名自动添加;本文件 path 直接写含前缀的完整对外路径。
# 错误码段(落 game 业务段 1-107-***-***,模块归属决定,contracts/README.md §四 已在册):
# 1-107-001-*** = 站内信(消息不存在/非本人消息/已读状态非法…)
# 1-107-002-*** = 创作者等级(等级记录不存在/计数累加异常…)
# 1-107-003-*** = 新人奖励(里程碑重复触发被幂等拦截…)
# 响应统一 Huijing CommonResult 信封:{ code, data, msg };code=0 成功。
# 触达机制(评审版 §3.3 定型):aigc 生成完成 / compliance·project 审核出结果 / trade 收益变动后,
# 各自 service 在本地事务【提交后】同进程调用 community 暴露的 CommunityNotifyApi(复用 @Primary 本地实现)写站内信;
# 不新建 MQ、不动 events.schema.json。CommunityNotifyApiImpl 入口注入系统身份 LoginUser(id=0) + finally clearContext。
# 幂等:站内信以 (userId,type,bizRef) 去重(重复投递不重复落库);里程碑以 (creator_user_id,milestone) 唯一键幂等。
# 归属隔离(R-medium PII 红线):客户侧端点必须在 Mapper 查询里强制以登录态 user_id 作谓词 enforce(可信边界,非 @DataPermission),
# 前端不够;用户只见自己站内信/等级/奖励,不得读他人 PII。
# 边界 OUT:SOC/GRW 域(关系图谱/排行/成就/扇出/邮件,全 P1/P2)仅留接口桩、本波不实现;
# reward 真实记账/到账(记入钱包/流量包)留 M4,本波只写自有 reward 触发记录表。
info:
title: 绘境AI community 模块 API
version: 1.0.0
description: >-
通知底座最小集:站内消息中心(系统/公告/审核/收益四类聚合 + 未读角标 + 标记已读)+
创作者等级(published_count 计数权威源 = project 发布成功同进程 notify 累加,新人里程碑触发)+
新人奖励触发记录(里程碑幂等触发,真实发放留 M4)。
上游 aigc/project/trade 经 CommunityNotifyApi(@Primary 本地实现)在本地事务提交后同进程投递站内信。
覆盖产品功能 P-NTF-01/02/03/05(通知触达)+ P-INC-01(创作者成长激励计数)。
前端据此 vite-plugin-mock 自动生成 mock。
servers:
- url: http://localhost:48080
description: 本地(Swagger/Knife4j http://localhost:48080/doc.html)
paths:
# ===========================================================================
# app 端(产品端 game-studio):我的站内信中心 / 未读角标 / 我的等级 / 我的奖励
# 全部用户 Token,Mapper 强制 getLoginUserId() 归属谓词,用户只见自己数据。
# ===========================================================================
/app-api/community/messages:
get:
tags: [app-community]
summary: 站内信分页列表(P-NTF-01)
description: >-
我的消息中心列表,按已读态 + 创建时间倒序。
归属隔离:Mapper 强制 user_id = getLoginUserId() 谓词(可信边界),用户只见自己消息。
parameters:
- { name: type, in: query, required: false, schema: { type: integer, enum: [1, 2, 3, 4] }, description: '消息类型:1系统 2公告 3审核 4收益(可选,不传查全部)' }
- { name: readStatus, in: query, required: false, schema: { type: integer, enum: [0, 1] }, description: '已读状态:0未读 1已读(可选)' }
- { name: cursor, in: query, required: false, schema: { type: integer, format: int64 }, description: '游标分页:上一页末条 id(可选,首页不传)' }
- { name: size, in: query, required: false, schema: { type: integer, default: 20 }, description: '每页条数(默认 20)' }
responses:
'200':
description: 站内信分页结果(CommonResult 信封)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data:
type: object
properties:
list: { type: array, items: { $ref: '#/components/schemas/CommunityMessageRespVO' } }
nextCursor: { type: integer, format: int64, nullable: true, description: '下一页游标,null 表示无更多' }
msg: { type: string, example: '' }
/app-api/community/messages/{id}/read:
post:
tags: [app-community]
summary: 标记单条已读(P-NTF-01)
description: >-
标记单条站内信已读,落 read_status=1 + read_time。
归属隔离:service 校验该消息 user_id == getLoginUserId(),非本人消息拒绝(1-107-001-***)。
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: '消息 ID' }
responses:
'200':
description: 标记结果(CommonResult<Boolean>)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { type: boolean, example: true }
msg: { type: string, example: '' }
/app-api/community/messages/unread-count:
get:
tags: [app-community]
summary: 未读数角标(P-NTF-01)
description: 未读消息数角标,按 type 分组计数 + 总未读数。归属隔离同上。
responses:
'200':
description: 未读计数(CommonResult)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data:
type: object
properties:
total: { type: integer, description: '总未读数' }
byType:
type: object
description: '按四类分组未读数(key=type 1/2/3/4,value=count)'
additionalProperties: { type: integer }
msg: { type: string, example: '' }
/app-api/community/level:
get:
tags: [app-community]
summary: 当前创作者等级 + published_count + 下一里程碑进度(P-INC-01)
description: >-
当前登录创作者的等级记录:level + published_count + 距下一新人里程碑进度(如 2/3)。
published_count 权威源 = project 发布成功后同进程 notify 原子累加。
归属隔离:creator_user_id = getLoginUserId()。无记录时返回初始化默认(level=1/count=0)。
responses:
'200':
description: 创作者等级(CommonResult)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { $ref: '#/components/schemas/CommunityLevelRespVO' }
msg: { type: string, example: '' }
/app-api/community/rewards:
get:
tags: [app-community]
summary: 我的奖励触发记录列表(P-INC-01)
description: >-
我的新人奖励触发记录列表,status 触发态(0已触发待发放)/发放态(1已发放,M4 回写)均可见。
归属隔离:creator_user_id = getLoginUserId()。
responses:
'200':
description: 奖励触发记录列表(CommonResult)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { type: array, items: { $ref: '#/components/schemas/CommunityRewardRespVO' } }
msg: { type: string, example: '' }
# ===========================================================================
# app 端(U3 R-SOC 社区社交):评论 / 关注 / 排行 / 弹幕(additive,2026-06-18 plan002 U3)
# 错误码段:评论 1-107-004 / 关注 1-107-005 / 排行 1-107-006 / 弹幕 1-107-007。
# 写端点(发评论/删评论/举报/关注/取关/发弹幕)需用户 Token,归属/发起方一律 getLoginUserId()(非前端入参)。
# 公开读:评论列表/计数、排行 TopN、弹幕回放无需登录。举报转 compliance:落本端举报态 + 事务提交后接缝(compliance 受理 -api 待提供,OPEN ITEM)。
# 弹幕实时接收走 WebSocket(type="danmaku",本契约仅描述 HTTP;WS 路径见后端 huijing.websocket.path),HTTP 发送为兜底入口。
# ===========================================================================
/app-api/community/comments:
post:
tags: [app-community]
summary: 发表评论(R-SOC)
description: 需登录;作者 = 当前登录用户(token 解析,非前端入参)。内容非空。
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [targetType, targetId, content]
properties:
targetType: { type: integer, enum: [1], default: 1, description: '评论目标类型:1游戏' }
targetId: { type: integer, format: int64, description: '评论目标 ID(gameId)' }
content: { type: string, maxLength: 1024, description: '评论正文' }
parentId: { type: integer, format: int64, default: 0, description: '父评论 ID(0=一级,MVP 不展开树)' }
responses:
'200':
description: 新评论 ID(CommonResult<Long>)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { type: integer, format: int64, example: 1 }
msg: { type: string, example: '' }
get:
tags: [app-community]
summary: 评论分页列表(R-SOC)
description: 公开读;按 target_type+target_id 维度,id 倒序。
parameters:
- { name: targetType, in: query, required: false, schema: { type: integer, enum: [1], default: 1 }, description: '目标类型:1游戏' }
- { name: targetId, in: query, required: true, schema: { type: integer, format: int64 }, description: '目标 ID(gameId)' }
- { name: pageNo, in: query, required: false, schema: { type: integer, default: 1 }, description: '页码,从 1 开始' }
- { name: pageSize, in: query, required: false, schema: { type: integer, default: 10 }, description: '每页条数(最大 200)' }
responses:
'200':
description: 评论分页结果(CommonResult)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data:
type: object
properties:
list: { type: array, items: { $ref: '#/components/schemas/CommunityCommentRespVO' } }
total: { type: integer, format: int64, example: 100 }
msg: { type: string, example: '' }
/app-api/community/comments/{id}:
delete:
tags: [app-community]
summary: 删除评论(R-SOC)
description: 需登录;先校验归属当前登录用户,防越权删他人评论(数据边界红线)。
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: '评论 ID' }
responses:
'200':
description: CommonResult<Boolean>
content: { application/json: { schema: { $ref: '#/components/schemas/CommonResultBoolean' } } }
/app-api/community/comments/count:
get:
tags: [app-community]
summary: 评论数(R-SOC)
description: 某目标的评论总数(公开计数)。
parameters:
- { name: targetType, in: query, required: false, schema: { type: integer, enum: [1], default: 1 } }
- { name: targetId, in: query, required: true, schema: { type: integer, format: int64 }, description: '目标 ID(gameId)' }
responses:
'200':
description: CommonResult<Long>
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { type: integer, format: int64, example: 42 }
msg: { type: string, example: '' }
/app-api/community/comments/{id}/report:
post:
tags: [app-community]
summary: 举报评论(R-SOC)
description: >-
需登录;理由必填;落本端举报态(report_status=1)幂等 + 事务提交后转 compliance 受理。
OPEN ITEM:compliance 当前无举报受理 -api,本波仅留接缝桩,接通后经 -api 调用 + 幂等 + 补偿。
parameters:
- { name: id, in: path, required: true, schema: { type: integer, format: int64 }, description: '被举报评论 ID' }
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [reason]
properties:
reason: { type: string, maxLength: 200, description: '举报理由(必填)' }
responses:
'200':
description: CommonResult<Boolean>
content: { application/json: { schema: { $ref: '#/components/schemas/CommonResultBoolean' } } }
/app-api/community/follow/{targetUserId}:
post:
tags: [app-community]
summary: 关注(R-SOC)
description: 需登录;发起方 = 当前登录用户;幂等(重复关注同态不写);不能关注自己;并发由 DB 唯一键 CAS 收敛。
parameters:
- { name: targetUserId, in: path, required: true, schema: { type: integer, format: int64 }, description: '被关注方用户 ID' }
responses:
'200':
description: 关注结果(CommonResult)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { $ref: '#/components/schemas/CommunityFollowResultRespVO' }
msg: { type: string, example: '' }
delete:
tags: [app-community]
summary: 取关(R-SOC)
description: 需登录;幂等(反激活 active=0,不删行)。
parameters:
- { name: targetUserId, in: path, required: true, schema: { type: integer, format: int64 }, description: '被关注方用户 ID' }
responses:
'200':
description: 取关结果(CommonResult)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { $ref: '#/components/schemas/CommunityFollowResultRespVO' }
msg: { type: string, example: '' }
/app-api/community/follow/stat/{userId}:
get:
tags: [app-community]
summary: 关注统计(R-SOC)
description: 某用户粉丝数/关注数 + 当前登录者是否已关注 ta(未登录则 followedByMe=false)。
parameters:
- { name: userId, in: path, required: true, schema: { type: integer, format: int64 }, description: '查询的用户 ID' }
responses:
'200':
description: 关注统计(CommonResult)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { $ref: '#/components/schemas/CommunityFollowStatRespVO' }
msg: { type: string, example: '' }
/app-api/community/rank:
get:
tags: [app-community]
summary: 排行榜单 TopN(R-SOC)
description: >-
公开读;维度须为已定义维度(game_hot 游戏热度 / creator_hot 创作者热度)。
优先读 Redis ZSET(快路径),ZSET 缺失自动从 DB 权威源回填重建(读路径不空窗,最终一致)。
parameters:
- { name: dimension, in: query, required: true, schema: { type: string, enum: [game_hot, creator_hot] }, description: '排行维度' }
- { name: topN, in: query, required: false, schema: { type: integer, default: 10 }, description: '取前 N 名' }
responses:
'200':
description: 榜单(CommonResult)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { type: array, items: { $ref: '#/components/schemas/CommunityRankItemRespVO' } }
msg: { type: string, example: '' }
/app-api/community/danmaku:
post:
tags: [app-community]
summary: 发送弹幕(R-SOC)
description: >-
需登录;落库(可回放)+ 触发 WebSocket 房间(gameId)广播给在线观众;发送者 = 当前登录用户。
实时接收走 WebSocket(type="danmaku");本 HTTP 发送为不开 WS 客户端的兜底入口。
单实例边界:多实例需切 huijing.websocket.sender-type=redis(MQ future-state 未部署)。
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [gameId, content]
properties:
gameId: { type: integer, format: int64, description: '游戏 ID(弹幕房间)' }
content: { type: string, maxLength: 255, description: '弹幕正文' }
playTime: { type: integer, default: 0, description: '播放进度(秒,MVP 可为 0)' }
responses:
'200':
description: 已落库弹幕(CommonResult,同时是 WS 广播载荷)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { $ref: '#/components/schemas/CommunityDanmakuRespVO' }
msg: { type: string, example: '' }
/app-api/community/danmaku/recent:
get:
tags: [app-community]
summary: 最近弹幕回放(R-SOC)
description: 公开读;某游戏最近 N 条弹幕(进房补帧),按时间正序。
parameters:
- { name: gameId, in: query, required: true, schema: { type: integer, format: int64 }, description: '游戏 ID(房间)' }
- { name: limit, in: query, required: false, schema: { type: integer, default: 50 }, description: '取近 N 条' }
responses:
'200':
description: 弹幕列表(CommonResult)
content:
application/json:
schema:
type: object
properties:
code: { type: integer, example: 0 }
data: { type: array, items: { $ref: '#/components/schemas/CommunityDanmakuRespVO' } }
msg: { type: string, example: '' }
# ===========================================================================
# RPC 端(/rpc-api/community/*):CommunityNotifyApi 四方法
# 供上游 aigc/project/trade 同进程 @Primary 调用,非对外业务路由(@FeignClient(name="community-server"))。
# 实现入口注入系统身份 LoginUser(id=0) + finally clearContext;统一 CommonResult 信封。
# ===========================================================================
/rpc-api/community/notify/generate-done:
post:
tags: [rpc-community]
summary: '[RPC] P-NTF-02 生成完成通知'
description: >-
aigc 在 DifyCallbackServiceImpl.handleCallback 提交后调;触发判定 = 提交后回查任务为 SUCCEEDED 终态且 versionId 已回填
(非 result==TRUE);三实参均取自回查的 AigcTaskDO(creatorUserId/gameId/versionId)。落 type=1 系统消息。
parameters:
- { name: creatorUserId, in: query, required: true, schema: { type: integer, format: int64 }, description: '创作者用户 ID(取自回查任务)' }
- { name: gameId, in: query, required: true, schema: { type: integer, format: int64 }, description: '游戏 ID(取自回查任务)' }
- { name: versionId, in: query, required: true, schema: { type: integer, format: int64 }, description: '版本 ID(取自回查任务)' }
responses:
'200':
description: CommonResult<Boolean>
content: { application/json: { schema: { $ref: '#/components/schemas/CommonResultBoolean' } } }
/rpc-api/community/notify/review-result:
post:
tags: [rpc-community]
summary: '[RPC] P-NTF-03 审核结果通知'
description: >-
project 在 reviewProject 提交后调;decision 三态(1通过/2拒绝/3下架,对齐 ReviewDecisionEnum)——
本波仅对 1/2 发通知(caller 侧以 decision∈{1,2} 守门),reject(2) 须带 reason。落 type=3 审核消息。
parameters:
- { name: creatorUserId, in: query, required: true, schema: { type: integer, format: int64 }, description: '创作者用户 ID' }
- { name: gameId, in: query, required: true, schema: { type: integer, format: int64 }, description: '游戏 ID' }
- { name: decision, in: query, required: true, schema: { type: integer, enum: [1, 2] }, description: '审核决定:1通过 2拒绝(3下架本波不发)' }
- { name: reason, in: query, required: false, schema: { type: string }, description: '拒绝原因(decision=2 必带)' }
responses:
'200':
description: CommonResult<Boolean>
content: { application/json: { schema: { $ref: '#/components/schemas/CommonResultBoolean' } } }
/rpc-api/community/notify/income-changed:
post:
tags: [rpc-community]
summary: '[RPC] P-NTF-05 收益变动通知'
description: >-
trade 在 SettlementServiceImpl.settle 入账成功后调(仅 firstRecord 首次真实入账才通知,幂等复跑不重发);
netAmount=创作者实得净额(分)。落 type=4 收益消息。
parameters:
- { name: creatorUserId, in: query, required: true, schema: { type: integer, format: int64 }, description: '创作者用户 ID' }
- { name: source, in: query, required: true, schema: { type: integer }, description: '收益来源(对齐 trade 来源枚举)' }
- { name: sourceRef, in: query, required: true, schema: { type: string }, description: '来源业务键(settleId 等)' }
- { name: netAmount, in: query, required: true, schema: { type: integer, format: int64 }, description: '创作者实得净额(分)' }
responses:
'200':
description: CommonResult<Boolean>
content: { application/json: { schema: { $ref: '#/components/schemas/CommonResultBoolean' } } }
/rpc-api/community/notify/project-published:
post:
tags: [rpc-community]
summary: '[RPC] P-INC-01 发布计数(方案a)'
description: >-
project 发布成功后同进程 notify(与 notifyReviewResult 合并在同一 caller,isApprove 时多调一次);
累加 published_count 并触发新人里程碑(幂等)。落 type=1 系统消息(里程碑达成时)。
parameters:
- { name: creatorUserId, in: query, required: true, schema: { type: integer, format: int64 }, description: '创作者用户 ID' }
- { name: gameId, in: query, required: true, schema: { type: integer, format: int64 }, description: '游戏 ID' }
responses:
'200':
description: CommonResult<Boolean>
content: { application/json: { schema: { $ref: '#/components/schemas/CommonResultBoolean' } } }
# ===========================================================================
# admin 端(staging only):四类投递 mock-trigger
# 上游未接通前独立验收四类投递,仅 dev/staging 生效(@ConditionalOnProperty(name="community.mock-trigger.enabled",
# havingValue="true") 与现有灰度开关风格一致;或 @Profile({"dev","staging"}),二选一钉死),生产关闭。
# ===========================================================================
/admin-api/community/notifications/{type}/mock-trigger:
post:
tags: [admin-community]
summary: '[staging only] 四类投递 mock-trigger'
description: >-
上游未接通前独立验收四类站内信投递,仅 dev/staging 生效,生产关闭。
实查全 game-module 无 @Profile 先例(现有差异化手段是 @ConditionalOnProperty 灰度开关),建设时二选一钉死。
parameters:
- { name: type, in: path, required: true, schema: { type: integer, enum: [1, 2, 3, 4] }, description: '投递类型:1系统 2公告 3审核 4收益' }
requestBody:
required: false
content:
application/json:
schema:
type: object
properties:
userId: { type: integer, format: int64, description: '目标收件人 user_id(mock 指定)' }
title: { type: string, description: '消息标题' }
content: { type: string, description: '消息正文' }
bizRef: { type: string, description: '业务关联键' }
responses:
'200':
description: CommonResult<Boolean>
content: { application/json: { schema: { $ref: '#/components/schemas/CommonResultBoolean' } } }
components:
schemas:
CommonResultBoolean:
type: object
description: Huijing 统一响应信封(Boolean 数据)
properties:
code: { type: integer, example: 0 }
data: { type: boolean, example: true }
msg: { type: string, example: '' }
CommunityMessageRespVO:
type: object
description: 站内消息(我的消息中心列表项)
properties:
id: { type: integer, format: int64, description: '消息 ID' }
type: { type: integer, description: '消息类型:1系统 2公告 3审核 4收益' }
bizRef: { type: string, description: '业务关联键(供前端跳转)' }
title: { type: string, description: '消息标题' }
content: { type: string, description: '消息正文(审核拒绝含原因)' }
readStatus: { type: integer, description: '已读状态:0未读 1已读' }
readTime: { type: string, format: date-time, nullable: true, description: '已读时间' }
createTime: { type: string, format: date-time, description: '创建时间' }
CommunityLevelRespVO:
type: object
description: 创作者等级 + 计数 + 下一里程碑进度
properties:
level: { type: integer, description: '创作者等级:1小白(MVP)' }
publishedCount: { type: integer, description: '已发布作品数(计数权威源)' }
lastMilestone: { type: string, description: '末次已触发里程碑键(如 publish_3)' }
nextMilestone: { type: string, nullable: true, description: '下一里程碑键(null 表示已达顶)' }
nextMilestoneTarget: { type: integer, nullable: true, description: '下一里程碑目标作品数(如 3)' }
CommunityRewardRespVO:
type: object
description: 新人奖励触发记录
properties:
id: { type: integer, format: int64, description: '奖励触发记录 ID' }
milestone: { type: string, description: '里程碑键(如 publish_3)' }
rewardType: { type: integer, description: '奖励类型:1流量包 2现金' }
rewardAmount: { type: integer, format: int64, description: '奖励额度(流量包=曝光量;现金=分)' }
status: { type: integer, description: '发放状态:0已触发待发放(本波终态) 1已发放(M4)' }
grantedRef: { type: string, description: 'M4 发放回执(本波空)' }
createTime: { type: string, format: date-time, description: '触发时间' }
# ====================== U3 R-SOC 社区社交(additive) ======================
CommunityCommentRespVO:
type: object
description: 社区评论(列表项)
properties:
id: { type: integer, format: int64, description: '评论 ID' }
targetType: { type: integer, description: '评论目标类型:1游戏' }
targetId: { type: integer, format: int64, description: '评论目标 ID(gameId)' }
userId: { type: integer, format: int64, description: '评论作者用户 ID' }
content: { type: string, description: '评论正文' }
parentId: { type: integer, format: int64, description: '父评论 ID(0=一级评论)' }
reportStatus: { type: integer, description: '举报态:0正常 1被举报' }
createTime: { type: string, format: date-time, description: '创建时间' }
CommunityFollowResultRespVO:
type: object
description: 关注/取关结果(回显切换后关注态 + 被关注方最新粉丝数)
properties:
targetUserId: { type: integer, format: int64, description: '被关注方用户 ID' }
following: { type: boolean, description: '切换后是否已关注:true=已关注 false=已取关' }
followerCount: { type: integer, format: int64, description: '被关注方最新粉丝数' }
CommunityFollowStatRespVO:
type: object
description: 关注统计(粉丝数 + 关注数 + 当前登录者是否已关注 ta)
properties:
userId: { type: integer, format: int64, description: '查询的用户 ID' }
followerCount: { type: integer, format: int64, description: '粉丝数(被多少 active 用户关注)' }
followingCount: { type: integer, format: int64, description: '关注数(active 关注了多少人)' }
followedByMe: { type: boolean, description: '当前登录者是否已关注 ta(未登录则恒 false)' }
CommunityRankItemRespVO:
type: object
description: 排行榜单项
properties:
rank: { type: integer, description: '名次(从 1 开始)' }
targetId: { type: integer, format: int64, description: '排行对象 ID(game_hot=gameId / creator_hot=creatorUserId)' }
score: { type: integer, format: int64, description: '排行分值' }
CommunityDanmakuRespVO:
type: object
description: 弹幕(回放列表项 + WebSocket 广播载荷,type="danmaku" 的 content)
properties:
id: { type: integer, format: int64, description: '弹幕 ID' }
gameId: { type: integer, format: int64, description: '游戏 ID(房间)' }
userId: { type: integer, format: int64, description: '发送者用户 ID' }
content: { type: string, description: '弹幕正文' }
playTime: { type: integer, description: '播放进度(秒)' }
createTime: { type: string, format: date-time, description: '发送时间' }