[{"data":1,"prerenderedAt":38},["ShallowReactive",2],{"doc:\u002Fcontracts\u002Foauth\u002F08-creator-applications":3},{"title":4,"route":5,"toc":6,"segments":33,"source":37},"08 — 创作者申请（Creator-Role Application）","\u002Fcontracts\u002Foauth\u002F08-creator-applications",[7,10,13,17,20,23,25,28,31],{"id":8,"text":8,"depth":9},"模型",2,{"id":11,"text":12,"depth":9},"用户端点-bearer-用户-jwt","用户端点（Bearer \u002F 用户 JWT）",{"id":14,"text":15,"depth":16},"post-api-v1-creator-applications-提交申请","POST \u002Fapi\u002Fv1\u002Fcreator\u002Fapplications — 提交申请",3,{"id":18,"text":19,"depth":16},"get-api-v1-creator-applications-me-查我的申请","GET \u002Fapi\u002Fv1\u002Fcreator\u002Fapplications\u002Fme — 查我的申请",{"id":21,"text":22,"depth":9},"管理员端点-admin-jwt-不在下游接入范围","管理员端点（Admin JWT，不在下游接入范围）",{"id":24,"text":24,"depth":9},"申请对象",{"id":26,"text":27,"depth":9},"下游耦合点-重命名-重构时务必同步本节","下游耦合点（重命名 \u002F 重构时务必同步本节）",{"id":29,"text":30,"depth":9},"已知限制-可接受","已知限制（可接受）",{"id":32,"text":32,"depth":9},"变更摘要",[34],{"type":35,"html":36},"html","\u003Ch1 id=\"08-创作者申请-creator-role-application\" tabindex=\"-1\">08 — 创作者申请（Creator-Role Application）\u003C\u002Fh1>\n\u003Cp>返回 \u003Ca href=\"\u002Fcontracts\u002Foauth\">README\u003C\u002Fa>\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>本节是\u003Cstrong>跨服务契约\u003C\u002Fstrong>：下游（kungal 论坛 \u002F moyu 补丁站）按此调用 OAuth 的创作者申请队列。\n角色由 OAuth 独占授予（身份契约：只有 OAuth 能发角色），\u003Cstrong>资格门槛由下游自治\u003C\u002Fstrong>。\n设计背景与取舍见 infra 内部设计文档《创作者角色设计》（\u003Ccode>docs\u002Fauth\u002F01-creator-role-design.md\u003C\u002Fcode>，\n仅 infra 仓 \u002F 文档门户可见，本契约文件对下游自包含）。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"模型\" tabindex=\"-1\">模型\u003C\u002Fh2>\n\u003Cp>\u003Ccode>creator\u003C\u002Fcode>（创作者）是 OAuth 的一个\u003Cstrong>附加角色\u003C\u002Fstrong>——可信发布者档位，赋予「galgame 直接发布\n（含无 VNDB ID）」等发布信任能力。授予走 \u003Cstrong>申请 → 管理员审核 → 通过 \u002F 拒绝（可重申）\u003C\u002Fstrong>：\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>下游(论坛\u002F补丁站): 按【自己的】门槛判定用户是否可申请(查 wiki \u002Fuser\u002F:id\u002Fstats + 自有数据)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>        → 合格则带【用户 JWT】调 OAuth POST \u002Fcreator\u002Fapplications，附 evidence(满足了哪条)\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>OAuth: creator_applications 中央队列 → 管理员审核 → approve 授予 creator 角色 \u002F decline 附理由\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>用户: GET \u002Fcreator\u002Fapplications\u002Fme 查自己的申请状态\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cul>\n\u003Cli>\u003Cstrong>资格判定在下游\u003C\u002Fstrong>（软门槛，会变、各站不同）；\u003Cstrong>授予在 OAuth + 管理员\u003C\u002Fstrong>（硬门槛）。下游门槛写松最坏只是塞满审核队列，绝不能绕过人工审核拿到角色。\u003C\u002Fli>\n\u003Cli>\u003Ccode>evidence\u003C\u002Fcode> 是下游随申请带的「满足了哪条」提示，供管理员参考，\u003Cstrong>不作授权依据\u003C\u002Fstrong>。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"用户端点-bearer-用户-jwt\" tabindex=\"-1\">用户端点（Bearer \u002F 用户 JWT）\u003C\u002Fh2>\n\u003Cp>挂 \u003Ccode>middleware.Auth\u003C\u002Fcode>，需登录用户的 access token。\u003C\u002Fp>\n\u003Ch3 id=\"post-api-v1-creator-applications-提交申请\" tabindex=\"-1\">POST \u002Fapi\u002Fv1\u002Fcreator\u002Fapplications — 提交申请\u003C\u002Fh3>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>\u002F\u002F 请求体\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"source\": \"forum\",        \u002F\u002F 必填，oneof: \"forum\" | \"moyu\"（申请来源站点）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"message\": \"我的简介…\",    \u002F\u002F 可选，max 1000，申请人附言（给管理员看）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"evidence\": {              \u002F\u002F 可选，jsonb，下游提供的「满足了哪条门槛」提示\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    \"pr_merged\": 7,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>    \"galgame_created\": 12\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  }\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>成功\u003C\u002Fstrong>：返回创建的申请对象（见下「申请对象」），\u003Ccode>status\u003C\u002Fcode> = \u003Ccode>pending\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>失败\u003C\u002Fstrong>（HTTP 400 + 业务码，\u003Ccode>message\u003C\u002Fcode> 为可直接展示给用户的中文）：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>code\u003C\u002Fth>\n\u003Cth>message\u003C\u002Fth>\n\u003Cth>含义\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>17001\u003C\u002Ftd>\n\u003Ctd>你已经是创作者了\u003C\u002Ftd>\n\u003Ctd>用户已持有 \u003Ccode>creator\u003C\u002Fcode> 角色，无需申请\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>17002\u003C\u002Ftd>\n\u003Ctd>已有一份待审核的创作者申请\u003C\u002Ftd>\n\u003Ctd>已有 pending 申请（同时只允许一个）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>17003\u003C\u002Ftd>\n\u003Ctd>申请被拒绝后需等待冷却期才能重新申请\u003C\u002Ftd>\n\u003Ctd>上一次被拒后未过冷却期（\u003Cstrong>1 天\u003C\u002Fstrong>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cblockquote>\n\u003Cp>申请前 OAuth 不校验门槛（门槛在下游）；这三个守卫只防重复 \u002F 滥用申请。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch3 id=\"get-api-v1-creator-applications-me-查我的申请\" tabindex=\"-1\">GET \u002Fapi\u002Fv1\u002Fcreator\u002Fapplications\u002Fme — 查我的申请\u003C\u002Fh3>\n\u003Cp>返回该用户\u003Cstrong>最近一条\u003C\u002Fstrong>申请对象。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>⚠️ \u003Cstrong>从未申请过的用户\u003C\u002Fstrong>：服务返回 nil → 响应\u003Cstrong>省略 \u003Ccode>data\u003C\u002Fcode> 字段\u003C\u002Fstrong>（不是 \u003Ccode>data: null\u003C\u002Fcode>，\n是整个 key 不存在；envelope 的 \u003Ccode>data\u003C\u002Fcode> 带 \u003Ccode>omitempty\u003C\u002Fcode>）。下游必须把「\u003Ccode>data\u003C\u002Fcode> 缺省」当作\n**「从未申请」**处理，\u003Cstrong>不要\u003C\u002Fstrong>当错误。曾导致论坛首次访问用户 500（forum \u003Ccode>3f4a61b5\u003C\u002Fcode> 修复前）。\u003C\u002Fp>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>\u002F\u002F 从未申请：\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>{ \"code\": 0, \"message\": \"成功\" }            \u002F\u002F 注意：没有 data\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>\u002F\u002F 申请过：\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>{ \"code\": 0, \"message\": \"成功\", \"data\": { \u002F* 申请对象 *\u002F } }\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"管理员端点-admin-jwt-不在下游接入范围\" tabindex=\"-1\">管理员端点（Admin JWT，不在下游接入范围）\u003C\u002Fh2>\n\u003Cp>挂 admin 组（\u003Ccode>role=admin\u003C\u002Fcode>）。列在此仅为契约完整：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>端点\u003C\u002Fth>\n\u003Cth>作用\u003C\u002Fth>\n\u003Cth>响应\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fapi\u002Fv1\u002Fadmin\u002Fcreator\u002Fapplications?status=&amp;page=&amp;limit=\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>审核队列（\u003Ccode>status\u003C\u002Fcode> 缺省 \u003Ccode>pending\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ items: [申请对象 + user], total }\u003C\u002Fcode>（每条多带申请人简介 \u003Ccode>user\u003C\u002Fcode>，见下「管理员列表额外字段」）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fapi\u002Fv1\u002Fadmin\u002Fcreator\u002Fapplications\u002F:id\u002Fapprove\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>通过 → 授予 \u003Ccode>creator\u003C\u002Fcode> 角色 + 标记 approved\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ id, status: &quot;approved&quot; }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fapi\u002Fv1\u002Fadmin\u002Fcreator\u002Fapplications\u002F:id\u002Fdecline\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>拒绝（body \u003Ccode>{ reason?: max 500 }\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>{ id, status: &quot;declined&quot; }\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>审核为状态机原子转移；对已处理的申请重复审核 → \u003Ccode>17005\u003C\u002Fcode>「该申请已被处理」；id 不存在 → \u003Ccode>17004\u003C\u002Fcode>「申请不存在」。\u003C\u002Fp>\n\u003Ch2 id=\"申请对象\" tabindex=\"-1\">申请对象\u003C\u002Fh2>\n\u003Cpre class=\"shiki shiki-themes github-light github-dark\" style=\"background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8\" tabindex=\"0\">\u003Ccode class=\"language-text\">\u003Cspan class=\"line\">\u003Cspan>{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"id\": 12,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"user_id\": 1007,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"source\": \"forum\",                 \u002F\u002F \"forum\" | \"moyu\" | …\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"status\": \"pending\",               \u002F\u002F 状态枚举，见下\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"evidence\": { \"pr_merged\": 7 },    \u002F\u002F omitempty：无则缺省\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"message\": \"我的简介…\",             \u002F\u002F 申请人附言（无则 \"\"）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"reviewer_id\": 2,                  \u002F\u002F omitempty：审核后才有\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"reviewed_at\": \"2026-06-18T...\",   \u002F\u002F omitempty：审核后才有\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"decline_reason\": \"\",              \u002F\u002F 拒绝时填，可直接展示给申请人\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"created_at\": \"2026-06-18T...\",\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>  \"updated_at\": \"2026-06-18T...\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan>}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>状态枚举 \u003Ccode>status\u003C\u002Fcode>\u003C\u002Fstrong>：\u003Ccode>pending\u003C\u002Fcode>（待审核）| \u003Ccode>approved\u003C\u002Fcode>（已通过）| \u003Ccode>declined\u003C\u002Fcode>（已拒绝）。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>管理员列表额外字段（仅 \u003Ccode>GET \u002Fadmin\u002Fcreator\u002Fapplications\u003C\u002Fcode>）\u003C\u002Fstrong>：每个 item 在上述申请对象字段之外再带一个 \u003Ccode>user\u003C\u002Fcode> —— 申请人的 \u003Ccode>UserBrief\u003C\u002Fcode>（\u003Ccode>id\u003C\u002Fcode> \u002F \u003Ccode>name\u003C\u002Fcode> \u002F \u003Ccode>avatar\u003C\u002Fcode> \u002F \u003Ccode>avatar_image_hash\u003C\u002Fcode> \u002F \u003Ccode>status\u003C\u002Fcode> \u002F \u003Ccode>roles\u003C\u002Fcode> \u002F …），由 OAuth \u003Cstrong>服务端注入\u003C\u002Fstrong>，管理端 UI 直接展示，\u003Cstrong>无需\u003C\u002Fstrong>再调 S2S 专用的 \u003Ccode>GET \u002Fusers\u002Fbatch\u003C\u002Fcode>（那是 client Basic 鉴权的服务间端点，浏览器调会 401）。用户行已不存在时 \u003Ccode>user\u003C\u002Fcode> 为 \u003Ccode>null\u003C\u002Fcode>。\u003Ccode>GET \u002Fcreator\u002Fapplications\u002Fme\u003C\u002Fcode>（用户查自己）\u003Cstrong>不带\u003C\u002Fstrong> \u003Ccode>user\u003C\u002Fcode>。\u003C\u002Fp>\n\u003Ch2 id=\"下游耦合点-重命名-重构时务必同步本节\" tabindex=\"-1\">下游耦合点（重命名 \u002F 重构时务必同步本节）\u003C\u002Fh2>\n\u003Cp>下游已硬编码以下事实，改动需在同一 PR 更新本契约并跑 \u003Ccode>docs:sync\u003C\u002Fcode>：\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>角色字符串恰为 \u003Ccode>creator\u003C\u002Fcode>\u003C\u002Fstrong>——下游徽章 \u002F 直发判定按 \u003Ccode>slices.Contains(roles, &quot;creator&quot;)\u003C\u002Fcode>，重命名会静默失效。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>状态枚举值\u003C\u002Fstrong> \u003Ccode>pending\u003C\u002Fcode> \u002F \u003Ccode>approved\u003C\u002Fcode> \u002F \u003Ccode>declined\u003C\u002Fcode>（字符串，非数字）。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode>GET \u002Fcreator\u002Fapplications\u002Fme\u003C\u002Fcode> 在「从未申请」时省略 \u003Ccode>data\u003C\u002Fcode> 字段\u003C\u002Fstrong>（非 \u003Ccode>data: null\u003C\u002Fcode>）——下游须按「缺省 = 从未申请」处理。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>申请 \u003Ccode>source\u003C\u002Fcode> 为 \u003Ccode>oneof=forum moyu\u003C\u002Fcode>\u003C\u002Fstrong>（新增下游站点需在此扩展）。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>业务错误码 17001 \u002F 17002 \u002F 17003 的 \u003Ccode>message\u003C\u002Fcode> 是可直接展示的中文\u003C\u002Fstrong>——下游 verbatim 透传给用户。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"已知限制-可接受\" tabindex=\"-1\">已知限制（可接受）\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>生效有延迟\u003C\u002Fstrong>：approve \u002F decline \u003Cstrong>不向下游推事件\u003C\u002Fstrong>。下游在下一次 \u003Ccode>\u002Fusers\u002Fbatch\u003C\u002Fcode> 缓存刷新（约 10 分钟 TTL）或用户轮询 \u003Ccode>\u002Fcreator\u002Fapplications\u002Fme\u003C\u002Fcode> 时才反映新授予的角色。当前可接受；若将来需即时反馈，可加 webhook \u002F role 版本号让下游在 approve 时失效缓存。\u003C\u002Fli>\n\u003Cli>角色生效本身经 token 刷新后才进 JWT 的 \u003Ccode>roles\u003C\u002Fcode> claim，符合既有 OAuth 模型。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"变更摘要\" tabindex=\"-1\">变更摘要\u003C\u002Fh2>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>2026-06-18 新增\u003C\u002Fstrong>：创作者申请契约纳入 OAuth 文档族并同步下游镜像。端点 \u003Ccode>POST \u002Fcreator\u002Fapplications\u003C\u002Fcode> + \u003Ccode>GET \u002Fcreator\u002Fapplications\u002Fme\u003C\u002Fcode>（用户）、\u003Ccode>\u002Fadmin\u002Fcreator\u002Fapplications*\u003C\u002Fcode>（管理员）；错误码 17001-17005。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n","kun-galgame-infra\u002Fdocs\u002Fintegration\u002Foauth\u002F08-creator-applications.md",1783514297217]