openapi: 3.0.3 # 契约 #1 API | 模块:compliance(game-module-compliance,Wave3 合规域「锁风门 Gate」)| owner:Wave3 compliance 子 agent 主笔,全员 review # 职责(架构 Doc B + D2 范围铁律):只做 Gate——发布前合规扫描(聚合风格/IP 原子→verdict)+ 内容分级 + 封禁台账。 # 【明确不做】审核状态机:审核状态机权威归 project(game_review_record),compliance 绝不重写(D2)。 # seam(R1):compliance 只交付 ComplianceGateApi.evaluate 供 project.submitPublish 同进程注入(block→admitted=false 不抛异常); # 不自改 submitPublish、不自登记 publish-blocked 错误码(那条文案由 project 侧 GateResultVO.code 携带)。 # 封禁(R3 状态分支):写 game_user_ban 台账=主操作恒执行;下架联动仅当游戏==PUBLISHED 时才调 project 状态机; # 本波 ProjectApi 暂无 reviewProject seam,下架联动标 TODO 留 Phase C 主 agent 收口(只写台账、不裸调不存在的方法)。 # 降权(R5):降权出流屏蔽 P1 本波不生效(不建 FeedDownweightApi 孤儿 seam、不调 feed)。 # IP(D5):ip 不独立建模块,仅在 compliance 内留 IpComplianceSeam 扩展点(默认 pass 桩),Wave4 接 ip 只换实现。 # U4 R-ACC(2026-06-18 扩):政策正文页(富文本 + 版本,app 只读最新 / admin 发布多版本)+ 申诉回路(app 本人提交/查询 + admin 处理状态机,可选关联封禁台账)。归属 compliance(已有 rating/ban/gate 合规语义),薄 CRUD。 # 端:/rpc-api(project.publish 同进程 Feign 注入)/ /admin-api(管理端 game-admin,RBAC 封禁+政策发布+申诉处理)/ /app-api(产品端只读分级+政策最新+本人申诉);前缀由 huijing 框架按 controller 包名/RpcConstants 自动添加 # 错误码段:compliance = 1-109-***-***(本模块独占,禁止与他模块重叠:100 project/101 aigc/102 runtime/103 feed/104 telemetry/106 trade/111 ad/112 studio) # 子段:001 锁风门 / 002 封禁 / 003 分级 / 004 政策正文(U4)/ 005 申诉(U4)。publish-blocked 不在本模块登记(R1:block→admitted=false 不抛异常)。 # 响应统一 Huijing CommonResult 信封:{ code, data, msg };code=0 成功 info: title: 绘境AI compliance 模块 API version: 1.0.0 description: >- 合规锁风门 Gate("做得出"安全闸门)。project.publish 发布前同步调 evaluate → 聚合风格/IP 合规原子产出 verdict (pass/review/block)+ 落锁风门台账 + 落内容分级;block 则 project 置 admitted=false(不抛异常)。 封禁台账供 admin 处置游戏/用户;下架联动按 project 状态分支(本波 TODO 留 Phase C)。MVP 合规原子全 mock 桩(默认 pass)。 servers: - url: http://localhost:48080 description: 本地(Swagger/Knife4j http://localhost:48080/doc.html) paths: # =========================================================================== # RPC 端 /rpc-api(project.publish 同进程注入,唯一 seam,R1) # =========================================================================== /rpc-api/compliance/evaluate: post: tags: [rpc-compliance] summary: 锁风门评估(project.publish 注入用,R1 唯一形态) description: >- project.submitPublish 发布前同步调;聚合风格/IP 合规原子产出 verdict = max(各原子)(block>review>pass), 落 game_compliance_gate_result 审计台账 + upsert game_content_rating 分级。 block 仍正常返回 verdict(不抛异常),由 project 侧据此置 admitted=false。 requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/ComplianceGateReqDTO' } responses: '200': description: 聚合裁决 content: application/json: schema: { $ref: '#/components/schemas/CommonResultGateVerdict' } # =========================================================================== # 管理端 /admin-api(封禁台账,RBAC,R3 状态分支) # =========================================================================== /admin-api/compliance/ban/create: post: tags: [admin-compliance] summary: 创建封禁(写台账=主操作恒执行;游戏下架联动按 project 状态分支) description: >- 落 game_user_ban(主操作恒执行,同目标生效中封禁查重 uk_target_active)。 R3 下架联动:targetType=游戏 且 game.status==PUBLISHED 时才调 project reviewProject(decision=3); 非 PUBLISHED 只记封禁、不调状态机、不抛异常。【本波】ProjectApi 暂无 reviewProject seam,下架联动 TODO 留 Phase C。 R5 降权出流屏蔽 P1 本波不生效(不调 feed)。 requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/BanCreateReqVO' } responses: '200': description: 新建封禁台账 ID content: application/json: schema: { $ref: '#/components/schemas/CommonResultLong' } /admin-api/compliance/ban/cancel: post: tags: [admin-compliance] summary: 解封(status 1→0) description: 按 banId 解封;记录不存在或已解封报 COMPLIANCE_BAN_NOT_EXISTS。 requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/BanCancelReqVO' } responses: '200': description: 是否成功 content: application/json: schema: { $ref: '#/components/schemas/CommonResultBoolean' } /admin-api/compliance/ban/page: get: tags: [admin-compliance] summary: 封禁台账分页(admin RBAC) parameters: - { name: pageNo, in: query, required: true, schema: { type: integer, default: 1 } } - { name: pageSize, in: query, required: true, schema: { type: integer, default: 10 } } - { name: targetType, in: query, required: false, schema: { type: integer }, description: '目标类型过滤:1 游戏/2 用户' } - { name: status, in: query, required: false, schema: { type: integer }, description: '状态过滤:1 生效/0 已解封' } responses: '200': description: 分页结果 content: application/json: schema: { $ref: '#/components/schemas/CommonResultPageUserBan' } # =========================================================================== # 产品端 /app-api(内容分级只读,用户/匿名 Token) # =========================================================================== /app-api/compliance/rating/get: get: tags: [app-compliance] summary: 取内容分级(只读) description: 按游戏+版本读分级(all/8+/12+/16+);无记录报 COMPLIANCE_RATING_NOT_FOUND。分级写入仅由 Gate evaluate + admin 复核产生。 parameters: - { name: gameId, in: query, required: true, schema: { type: integer, format: int64 } } - { name: versionId, in: query, required: false, schema: { type: integer, format: int64 }, description: '无版本传 0' } responses: '200': description: 内容分级 content: application/json: schema: { $ref: '#/components/schemas/CommonResultContentRating' } # =========================================================================== # U4 R-ACC 政策正文(app 只读取最新 / admin 发布富文本多版本) # 设计:政策正文 = 薄 CRUD(富文本 + 版本);app 端取某政策类型「最新已发布」一版(无登录依赖、匿名可读,仿 rating)。 # =========================================================================== /app-api/compliance/policy/latest: get: tags: [app-compliance] summary: 取某政策正文最新已发布版(只读,匿名可读) description: 按政策类型取「最新已发布(status=1)+最大 version」一版;无记录报 COMPLIANCE_POLICY_NOT_FOUND。 parameters: - { name: policyType, in: query, required: true, schema: { type: string }, description: '政策类型:user-agreement/privacy/community-guideline/minor-protection' } responses: '200': description: 政策正文 content: application/json: schema: { $ref: '#/components/schemas/CommonResultPolicyDocument' } /admin-api/compliance/policy/publish: post: tags: [admin-compliance] summary: 发布政策正文新版(admin RBAC) description: 新增一版政策正文(富文本 + version);version 须比同类型现有最大版本大,否则报 COMPLIANCE_POLICY_VERSION_CONFLICT。 requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/PolicyPublishReqVO' } responses: '200': description: 新政策正文记录 ID content: application/json: schema: { $ref: '#/components/schemas/CommonResultLong' } /admin-api/compliance/policy/page: get: tags: [admin-compliance] summary: 政策正文历史分页(admin RBAC) parameters: - { name: pageNo, in: query, required: true, schema: { type: integer, default: 1 } } - { name: pageSize, in: query, required: true, schema: { type: integer, default: 10 } } - { name: policyType, in: query, required: false, schema: { type: string }, description: '政策类型过滤' } responses: '200': description: 分页结果 content: application/json: schema: { $ref: '#/components/schemas/CommonResultPagePolicyDocument' } # =========================================================================== # U4 R-ACC 申诉(app 本人提交/查询 / admin 处理状态机) # 设计:申诉 = 薄 CRUD + 状态机(提交→admin 处理→结果);可选关联 game_user_ban 台账(banId)。 # 归属隔离:本人 userId 取自 token(getLoginUserId()),Mapper 强制 WHERE 谓词(可信边界,非前端入参)。 # 状态机 status:0 待处理 → 1 已通过 / 2 已驳回(admin 处理;重复提交守卫=同用户+同 banId 存在待处理则拒)。 # =========================================================================== /app-api/compliance/appeal/submit: post: tags: [app-compliance] summary: 提交申诉(本人 Token) description: 提交申诉;可选关联 banId。重复提交守卫=同用户同 banId 已有待处理申诉则报 COMPLIANCE_APPEAL_DUPLICATE。 requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/AppealSubmitReqVO' } responses: '200': description: 新申诉记录 ID content: application/json: schema: { $ref: '#/components/schemas/CommonResultLong' } /app-api/compliance/appeal/my-page: get: tags: [app-compliance] summary: 我的申诉分页(本人 Token,归属隔离) parameters: - { name: pageNo, in: query, required: true, schema: { type: integer, default: 1 } } - { name: pageSize, in: query, required: true, schema: { type: integer, default: 10 } } - { name: status, in: query, required: false, schema: { type: integer }, description: '状态过滤:0 待处理/1 已通过/2 已驳回' } responses: '200': description: 分页结果 content: application/json: schema: { $ref: '#/components/schemas/CommonResultPageAppeal' } /admin-api/compliance/appeal/handle: post: tags: [admin-compliance] summary: 处理申诉(admin RBAC,状态机流转) description: 待处理(0)→已通过(1)/已驳回(2);记录不存在或非待处理报 COMPLIANCE_APPEAL_CANNOT_HANDLE。 requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/AppealHandleReqVO' } responses: '200': description: 处理成功 content: application/json: schema: { $ref: '#/components/schemas/CommonResultBoolean' } /admin-api/compliance/appeal/page: get: tags: [admin-compliance] summary: 申诉台账分页(admin RBAC) parameters: - { name: pageNo, in: query, required: true, schema: { type: integer, default: 1 } } - { name: pageSize, in: query, required: true, schema: { type: integer, default: 10 } } - { name: status, in: query, required: false, schema: { type: integer }, description: '状态过滤:0 待处理/1 已通过/2 已驳回' } responses: '200': description: 分页结果 content: application/json: schema: { $ref: '#/components/schemas/CommonResultPageAppeal' } components: schemas: # ===== 锁风门 Gate(RPC seam,R1 唯一形态)===== ComplianceGateReqDTO: type: object required: [gameId, versionId] properties: gameId: { type: integer, format: int64, description: '游戏项目 ID(锁风门定位锚点)' } versionId: { type: integer, format: int64, description: '待发布版本 ID' } title: { type: string, description: '标题(风格原子参考,可空)' } summary: { type: string, description: '摘要(风格原子参考,可空)' } ageRating: { type: string, description: '适龄分级建议:all/8+/12+/16+' } promptHash: { type: string, description: '生成 Prompt 摘要(合规溯源,可空)' } GateVerdict: # = 草案 ComplianceGateRespDTO,R1 命名对齐 type: object required: [verdict] properties: verdict: { type: string, enum: [pass, review, block], description: '聚合裁决 = max(各原子),block>review>pass' } rating: { type: string, description: '本次评估落定的内容分级' } detail: { type: array, items: { $ref: '#/components/schemas/ComplianceGateItemDTO' } } ComplianceGateItemDTO: type: object properties: atom: { type: string, description: '原子标识:textContent/imageContent/ip-reserved' } verdict: { type: string, enum: [pass, review, block] } reason: { type: string, description: '裁决原因(pass 可空)' } # ===== 封禁(admin,R3 状态分支)===== BanCreateReqVO: type: object required: [targetType, targetId] properties: targetType: { type: integer, description: '封禁目标类型:1 游戏 / 2 用户' } targetId: { type: integer, format: int64, description: '封禁目标 ID(游戏=game_id / 用户=user_id)' } banType: { type: integer, description: '处置类型:1 封禁 / 2 降权(P1 本波不生效)' } reason: { type: string, description: '封禁/降权原因' } BanCancelReqVO: type: object required: [banId] properties: banId: { type: integer, format: int64, description: '封禁台账记录 ID' } UserBanRespVO: type: object properties: id: { type: integer, format: int64 } targetType: { type: integer, description: '1 游戏 / 2 用户' } targetId: { type: integer, format: int64 } banType: { type: integer, description: '1 封禁 / 2 降权' } status: { type: integer, description: '1 生效 / 0 已解封' } reason: { type: string } createTime: { type: string, format: date-time } # ===== 分级(app 只读)===== ContentRatingRespVO: type: object properties: rating: { type: string, description: 'all/8+/12+/16+' } ratedBy: { type: string, description: 'gate 锁风门自动 / admin 人工复核' } updateTime: { type: string, format: date-time } # ===== U4 政策正文(app 只读最新 / admin 发布多版本)===== PolicyPublishReqVO: type: object required: [policyType, version, title, content] properties: policyType: { type: string, description: '政策类型:user-agreement/privacy/community-guideline/minor-protection' } version: { type: integer, description: '版本号(须比同类型现有最大版本大)' } title: { type: string, description: '政策标题' } content: { type: string, description: '政策正文(富文本 HTML,可大文本)' } PolicyDocumentRespVO: type: object properties: id: { type: integer, format: int64 } policyType: { type: string, description: '政策类型' } version: { type: integer, description: '版本号' } title: { type: string } content: { type: string, description: '政策正文(富文本)' } status: { type: integer, description: '状态:1 已发布/0 草稿(本波只发布态)' } updateTime: { type: string, format: date-time } # ===== U4 申诉(app 本人提交/查询 / admin 处理状态机)===== AppealSubmitReqVO: type: object required: [appealType, reason] properties: appealType: { type: integer, description: '申诉类型:1 封禁申诉 / 2 审核申诉 / 3 其它' } banId: { type: integer, format: int64, description: '可选关联封禁台账 ID(封禁申诉时填)' } reason: { type: string, description: '申诉理由' } AppealHandleReqVO: type: object required: [appealId, decision] properties: appealId: { type: integer, format: int64, description: '申诉记录 ID' } decision: { type: integer, description: '处理决定:1 通过 / 2 驳回' } handleResult: { type: string, description: '处理结果说明(可空)' } AppealRespVO: type: object properties: id: { type: integer, format: int64 } appealType: { type: integer, description: '1 封禁申诉/2 审核申诉/3 其它' } banId: { type: integer, format: int64, description: '关联封禁台账 ID(可空)' } reason: { type: string } status: { type: integer, description: '0 待处理/1 已通过/2 已驳回' } handleResult: { type: string, description: '处理结果说明' } createTime: { type: string, format: date-time } updateTime: { type: string, format: date-time } # ===== CommonResult 信封 ===== CommonResultGateVerdict: type: object properties: code: { type: integer } data: { $ref: '#/components/schemas/GateVerdict' } msg: { type: string } CommonResultContentRating: type: object properties: code: { type: integer } data: { $ref: '#/components/schemas/ContentRatingRespVO' } msg: { type: string } CommonResultPageUserBan: type: object properties: code: { type: integer } data: type: object properties: list: { type: array, items: { $ref: '#/components/schemas/UserBanRespVO' } } total: { type: integer, format: int64 } msg: { type: string } CommonResultLong: type: object properties: code: { type: integer } data: { type: integer, format: int64 } msg: { type: string } CommonResultBoolean: type: object properties: code: { type: integer } data: { type: boolean } msg: { type: string } CommonResultPolicyDocument: type: object properties: code: { type: integer } data: { $ref: '#/components/schemas/PolicyDocumentRespVO' } msg: { type: string } CommonResultPagePolicyDocument: type: object properties: code: { type: integer } data: type: object properties: list: { type: array, items: { $ref: '#/components/schemas/PolicyDocumentRespVO' } } total: { type: integer, format: int64 } msg: { type: string } CommonResultPageAppeal: type: object properties: code: { type: integer } data: type: object properties: list: { type: array, items: { $ref: '#/components/schemas/AppealRespVO' } } total: { type: integer, format: int64 } msg: { type: string }