[{"data":1,"prerenderedAt":56},["ShallowReactive",2],{"doc:\u002Fcontracts\u002Foauth\u002F06-moemoepoint":3},{"title":4,"route":5,"toc":6,"segments":51,"source":55},"06 — 萌萌点（moemoepoint）统一货币（精简版）","\u002Fcontracts\u002Foauth\u002F06-moemoepoint",[7,11,14,18,21,24,27,30,33,36,39,42,45,48],{"id":8,"text":9,"depth":10},"0-决策与定位","0. 决策与定位",2,{"id":12,"text":13,"depth":10},"1-数据模型","1. 数据模型",{"id":15,"text":16,"depth":17},"1-1-余额-users-moemoepoint-可变列-保留","1.1 余额：`users.moemoepoint`（可变列，保留）",3,{"id":19,"text":20,"depth":17},"1-2-审计日志-moemoepoint_log-只追加-不改不删","1.2 审计日志：`moemoepoint_log`（只追加，不改不删）",{"id":22,"text":23,"depth":10},"2-reason-一张小而稳的通用枚举-oauth-拥有","2. reason（一张小而稳的通用枚举，OAuth 拥有）",{"id":25,"text":26,"depth":10},"3-服务到服务-api","3. 服务到服务 API",{"id":28,"text":29,"depth":17},"3-1-post-users-id-moemoepoint","3.1 POST \u002Fusers\u002F:id\u002Fmoemoepoint",{"id":31,"text":32,"depth":17},"3-2-读取","3.2 读取",{"id":34,"text":35,"depth":10},"4-幂等-唯一需要严谨的点","4. 幂等（唯一需要严谨的点）",{"id":37,"text":38,"depth":10},"5-管理端","5. 管理端",{"id":40,"text":41,"depth":10},"6-迁移-一次性","6. 迁移（一次性）",{"id":43,"text":44,"depth":10},"7-下游接入","7. 下游接入",{"id":46,"text":47,"depth":10},"8-错误码-16xxx","8. 错误码（16xxx）",{"id":49,"text":50,"depth":10},"9-刻意没做的-将来需要时再升级","9. 刻意没做的（将来需要时再升级）",[52],{"type":53,"html":54},"html","\u003Ch1 id=\"06-萌萌点-moemoepoint-统一货币-精简版\" tabindex=\"-1\">06 — 萌萌点（moemoepoint）统一货币（精简版）\u003C\u002Fh1>\n\u003Cp>返回 \u003Ca href=\"\u002Fcontracts\u002Foauth\">README\u003C\u002Fa>\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>状态：已实现\u003C\u002Fstrong>。\u003Ccode>moemoepoint_log\u003C\u002Fcode> 表 + s2s 端点 \u003Ccode>POST\u002FGET \u002Fusers\u002F:id\u002Fmoemoepoint\u003C\u002Fcode>（\u003Ccode>Adjust\u003C\u002Fcode> 幂等 \u002F \u003Ccode>GetBalance\u003C\u002Fcode>）+ 用户自助 \u003Ccode>GET \u002Fauth\u002Fme\u002Fmoemoepoint\u002Flog\u003C\u002Fcode> 均已上线。实现见 \u003Ccode>internal\u002Fplatform\u002Fauth\u002Fhandler\u002Fmoemoepoint_handler.go\u003C\u002Fcode>，路由注册见 \u003Ccode>cmd\u002Foauth\u002Fmain.go\u003C\u002Fcode>。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"0-决策与定位\" tabindex=\"-1\">0. 决策与定位\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>moemoepoint 全站统一\u003C\u002Fstrong>：一个用户在 kungal \u002F moyu \u002F 未来所有接入站点\u003Cstrong>共享一个余额\u003C\u002Fstrong>，\u003Cstrong>单一真源在 OAuth\u003C\u002Fstrong>（共享身份库）。\u003C\u002Fli>\n\u003Cli>本设计\u003Cstrong>刻意精简\u003C\u002Fstrong>：萌萌点是软性 karma（非货币、低频写入、出错最坏只是&quot;数字不对&quot;，不涉资损）。所以只保留三个真正有价值的属性 —— \u003Cstrong>幂等、审计、单源\u003C\u002Fstrong> —— 砍掉金融账本级的严谨度和多团队治理（详见 §9 与早期完整版的差异）。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"1-数据模型\" tabindex=\"-1\">1. 数据模型\u003C\u002Fh2>\n\u003Ch3 id=\"1-1-余额-users-moemoepoint-可变列-保留\" tabindex=\"-1\">1.1 余额：\u003Ccode>users.moemoepoint\u003C\u002Fcode>（可变列，保留）\u003C\u002Fh3>\n\u003Cp>仍是一个普通可变整型列，作为\u003Cstrong>当前余额\u003C\u002Fstrong>。每次调整在\u003Cstrong>同一事务\u003C\u002Fstrong>里 \u003Ccode>moemoepoint += delta\u003C\u002Fcode>。不把它变成&quot;日志的派生值&quot;——对 karma 来说没必要。\u003C\u002Fp>\n\u003Ch3 id=\"1-2-审计日志-moemoepoint_log-只追加-不改不删\" tabindex=\"-1\">1.2 审计日志：\u003Ccode>moemoepoint_log\u003C\u002Fcode>（只追加，不改不删）\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-sql\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">CREATE\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> TABLE\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> moemoepoint_log\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> (\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  id              \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">BIGSERIAL\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> PRIMARY KEY\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  user_id         \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">INTEGER\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">     NOT NULL\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  delta           \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">INTEGER\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">     NOT NULL\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,              \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 有符号，非 0\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  reason          \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">VARCHAR\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">40\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">NOT NULL\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,              \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 见 §2 小枚举\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  source_app      \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">VARCHAR\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">32\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">NOT NULL\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,              \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 来源站点（服务端从认证 client 推导）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  ref             \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">VARCHAR\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">80\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">),                       \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 触发实体，自由格式如 \"galgame:1207\"（可空）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  actor_user_id   \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">INTEGER\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">     NOT NULL\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> DEFAULT\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\"> 0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,    \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 谁导致：0=系统 \u002F 管理员 id\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  idempotency_key \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">VARCHAR\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">128\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">) \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">NOT NULL\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,             \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 防重放，全局唯一\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  note            \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">VARCHAR\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">(\u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">255\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">),                       \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 备注（管理员操作填）\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">  created_at      \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">TIMESTAMPTZ\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> NOT NULL\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> DEFAULT\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> now\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">()\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">CREATE\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> UNIQUE INDEX\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\"> idx_mp_log_idem\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> ON\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> moemoepoint_log (idempotency_key);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">CREATE\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> INDEX\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">        idx_mp_log_user\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> ON\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> moemoepoint_log (user_id, id \u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">DESC\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">);\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\">CREATE\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> INDEX\u003C\u002Fspan>\u003Cspan style=\"color:#6F42C1;--shiki-dark:#B392F0\">        idx_mp_log_reason\u003C\u002Fspan>\u003Cspan style=\"color:#D73A49;--shiki-dark:#F97583\"> ON\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> moemoepoint_log (reason);  \u003C\u002Fspan>\u003Cspan style=\"color:#6A737D;--shiki-dark:#6A737D\">-- 分类查看\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cul>\n\u003Cli>日志\u003Cstrong>只追加\u003C\u002Fstrong>：发错了写一条反向记录（\u003Ccode>delta\u003C\u002Fcode> 取负），不改旧行。\u003C\u002Fli>\n\u003Cli>&quot;分类查看&quot; = 按 \u003Ccode>reason\u003C\u002Fcode>（或 \u003Ccode>source_app\u003C\u002Fcode>）过滤 \u002F 聚合。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"2-reason-一张小而稳的通用枚举-oauth-拥有\" tabindex=\"-1\">2. reason（一张小而稳的通用枚举，OAuth 拥有）\u003C\u002Fh2>\n\u003Cp>扁平、通用、少。具体业务细节靠 \u003Ccode>source_app\u003C\u002Fcode> + \u003Ccode>ref\u003C\u002Fcode> 区分，\u003Cstrong>不为每个站点维护各自的枚举\u003C\u002Fstrong>（那是被砍掉的治理层）。\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>reason\u003C\u002Fth>\n\u003Cth>方向\u003C\u002Fth>\n\u003Cth>说明\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>admin_grant\u003C\u002Fcode> \u002F \u003Ccode>admin_deduct\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>±\u003C\u002Ftd>\n\u003Ctd>管理员发放 \u002F 扣除\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>migration\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>+\u003C\u002Ftd>\n\u003Ctd>迁移起始值（§6）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>register_gift\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>+\u003C\u002Ftd>\n\u003Ctd>注册欢迎礼（OAuth 注册成功时一次性发放，note=「鲲给予你的第一份礼物」）；OAuth 内部，s2s 不可用\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>content_approved\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>+\u003C\u002Ftd>\n\u003Ctd>产出被采纳（Wiki 投稿通过、补丁发布…，用 source_app+ref 区分）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>content_removed\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>−\u003C\u002Ftd>\n\u003Ctd>上述产出被删 \u002F 撤回时回收（与发放同 \u003Ccode>ref\u003C\u002Fcode>）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>daily_checkin\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>+\u003C\u002Ftd>\n\u003Ctd>每日签到\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>liked\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>+\u003C\u002Ftd>\n\u003Ctd>内容被点赞\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>约定：\u003Ccode>delta\u003C\u002Fcode> 禁止为 0；可回收的产出，回收用相同 \u003Ccode>ref\u003C\u002Fcode> 对账。新增一种来源 = 往这张表加一行（你一个人控制全部 client，一次小改即可，无需治理协调）。\u003C\u002Fp>\n\u003Ch2 id=\"3-服务到服务-api\" tabindex=\"-1\">3. 服务到服务 API\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>鉴权\u003C\u002Fstrong>：与 \u003Ca href=\"\u002Fcontracts\u002Foauth\u002F03-cross-service\">\u003Ccode>\u002Fusers\u002Fbatch\u003C\u002Fcode>\u003C\u002Fa> 相同 —— \u003Cstrong>OAuth Client Basic Auth\u003C\u002Fstrong>。\u003Ccode>source_app\u003C\u002Fcode> 服务端从认证 client 推导，不信任请求体自报。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>铸币白名单（POST 专属，2026-06-13 新增）\u003C\u002Fstrong>：\u003Cstrong>写入\u003C\u002Fstrong>（\u003Ccode>POST\u003C\u002Fcode> 调整余额）额外要求该 client \u003Ccode>oauth_clients.moemoepoint_awarder = true\u003C\u002Fcode>。萌萌点是\u003Cstrong>全生态共享的单一钱包\u003C\u002Fstrong>，只有合法发放方（论坛 \u002F 补丁）在白名单内；其它任何已注册 client 默认 \u003Cstrong>fail-closed\u003C\u002Fstrong>（\u003Ccode>awarder=false\u003C\u002Fcode>），POST 返回 \u003Ccode>403 \u002F 16005\u003C\u002Fcode>。\u003Cstrong>读取\u003C\u002Fstrong>（\u003Ccode>GET\u003C\u002Fcode> 余额 \u002F 流水）不受影响，对任意已注册 client 开放。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>\u003Cstrong>为什么要白名单\u003C\u002Fstrong>：一个定位不同的站点（例如成人向资源站 letmoe）可能\u003Cstrong>读取\u003C\u002Fstrong>用户余额来\u003Cstrong>一次性 1:1 初始化自己的本地积分\u003C\u002Fstrong>——这没问题；但它\u003Cstrong>绝不能往共享钱包铸币\u003C\u002Fstrong>，否则会把自己的 provenance 戳进每个用户的全生态流水。&quot;读取做种&quot;放行，&quot;铸币&quot;按 \u003Cstrong>parent Site.Domain\u003C\u002Fstrong> 显式授权（\u003Ccode>cmd\u002Fmigrate\u003C\u002Fcode> 按 \u003Ccode>www.kungal.com\u003C\u002Fcode> \u002F \u003Ccode>www.moyu.moe\u003C\u002Fcode> 幂等回填，不硬编码 per-env \u003Ccode>client_id\u003C\u002Fcode>）。新增一个合法发放方 = 往该域名列表加一行；新增一个只读站点 = 什么都不用做（保持 fail-closed）。\u003C\u002Fp>\n\u003C\u002Fblockquote>\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>\u002Fusers\u002F:id\u002Fmoemoepoint\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>POST\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>调整\u003C\u002Fstrong>余额（发放 \u002F 扣除），幂等\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>\u002Fusers\u002F:id\u002Fmoemoepoint\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>GET\u003C\u002Ftd>\n\u003Ctd>读当前余额\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>\u002Fusers\u002F:id\u002Fmoemoepoint\u002Flog\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>GET\u003C\u002Ftd>\n\u003Ctd>分页拉流水（可选；用户&quot;积分明细&quot; \u002F 排查）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Ch3 id=\"3-1-post-users-id-moemoepoint\" tabindex=\"-1\">3.1 POST \u002Fusers\u002F:id\u002Fmoemoepoint\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-json\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">{\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"delta\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">3\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"reason\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"content_approved\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"ref\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"galgame:1207\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"actor_user_id\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"idempotency_key\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"moyu:wiki_approved:1207\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">,\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">  \"note\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"\"\u003C\u002Fspan>\u003C\u002Fspan>\n\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">}\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\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>delta\u003C\u002Ftd>\n\u003Ctd>是\u003C\u002Ftd>\n\u003Ctd>有符号整数，非 0，且 |delta| ≤ 1,000,000（防呆上限）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>reason\u003C\u002Ftd>\n\u003Ctd>是\u003C\u002Ftd>\n\u003Ctd>§2 枚举之一。\u003Cstrong>s2s 不可用\u003C\u002Fstrong> \u003Ccode>admin_grant\u003C\u002Fcode> \u002F \u003Ccode>admin_deduct\u003C\u002Fcode> \u002F \u003Ccode>migration\u003C\u002Fcode> \u002F \u003Ccode>register_gift\u003C\u002Fcode>（OAuth 保留）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>ref\u003C\u002Ftd>\n\u003Ctd>否\u003C\u002Ftd>\n\u003Ctd>触发实体（建议填，用于对账）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>actor_user_id\u003C\u002Ftd>\n\u003Ctd>否\u003C\u002Ftd>\n\u003Ctd>默认 0（系统）；管理员操作填管理员 id\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>idempotency_key\u003C\u002Ftd>\n\u003Ctd>是\u003C\u002Ftd>\n\u003Ctd>全局唯一，\u003Cstrong>调用方生成稳定键\u003C\u002Fstrong>（见 §4）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>note\u003C\u002Ftd>\n\u003Ctd>否\u003C\u002Ftd>\n\u003Ctd>备注\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>\u003Cstrong>成功响应\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-json\">\u003Cspan class=\"line\">\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">{ \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"code\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">0\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"message\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#032F62;--shiki-dark:#9ECBFF\">\"成功\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"data\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: { \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"user_id\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">1207\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"balance\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">42\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">, \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">\"applied\"\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\">: \u003C\u002Fspan>\u003Cspan style=\"color:#005CC5;--shiki-dark:#79B8FF\">true\u003C\u002Fspan>\u003Cspan style=\"color:#24292E;--shiki-dark:#E1E4E8\"> } }\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>applied=false\u003C\u002Fcode> 表示幂等键命中、未重复执行。\u003C\u002Fp>\n\u003Cp>\u003Cstrong>错误\u003C\u002Fstrong>（HTTP 400 + 对应 code，除非另注）：\u003Ccode>16002\u003C\u002Fcode> delta 为 0 或超 ±1,000,000；\u003Ccode>16003\u003C\u002Fcode> reason 非法 \u002F 用了保留 reason；\u003Ccode>16004\u003C\u002Fcode> 幂等键已存在但请求体不一致；\u003Ccode>403\u002F16005\u003C\u002Fcode> client 不在铸币白名单（\u003Ccode>moemoepoint_awarder=false\u003C\u002Fcode>）；\u003Ccode>404\u002F10005\u003C\u002Fcode> 用户不存在；\u003Ccode>401\u003C\u002Fcode> Basic Auth 失败。\u003C\u002Fp>\n\u003Cblockquote>\n\u003Cp>余额\u003Cstrong>允许为负\u003C\u002Fstrong>（精简取舍：不做非负约束，保证回收\u002F反转永不被挡）。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch3 id=\"3-2-读取\" tabindex=\"-1\">3.2 读取\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Ccode>GET \u002Fusers\u002F:id\u002Fmoemoepoint\u003C\u002Fcode> → \u003Ccode>{ &quot;balance&quot;: 42 }\u003C\u002Fcode>。\u003C\u002Fli>\n\u003Cli>\u003Ccode>GET \u002Fusers\u002F:id\u002Fmoemoepoint\u002Flog?limit=20&amp;before_id=&amp;reason=\u003C\u002Fcode> → 分页流水（\u003Ccode>reason\u003C\u002Fcode> 可选过滤）。\u003Cstrong>s2s 返回精简视图\u003C\u002Fstrong>：\u003Ccode>{ id, delta, reason, source_app, ref, created_at }\u003C\u002Fcode>，\u003Cstrong>不含\u003C\u002Fstrong> \u003Ccode>note\u003C\u002Fcode> \u002F \u003Ccode>actor_user_id\u003C\u002Fcode>（这俩可能含管理处罚备注，下游可能渲染给终端用户，故不下发；管理端 \u003Ccode>\u002Fadmin\u002F...\u002Flog\u003C\u002Fcode> 返回完整视图）。\u003C\u002Fli>\n\u003Cli>也可在 \u003Ccode>\u002Fauth\u002Fme\u003C\u002Fcode> \u002F userinfo 里直接返回 OAuth 的实时余额（替掉现在的冻结快照）。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>自助流水\u003C\u002Fstrong>：\u003Ccode>GET \u002Fauth\u002Fme\u002Fmoemoepoint\u002Flog?limit=&amp;before_id=&amp;reason=\u003C\u002Fcode>（\u003Cstrong>用户 JWT\u003C\u002Fstrong>，\u003Ccode>Auth\u003C\u002Fcode> 鉴权，id 取自 token 非路径参——避免越权读他人）。返回与 s2s 同口径的\u003Cstrong>精简视图\u003C\u002Fstrong>（无 \u003Ccode>note\u003C\u002Fcode> \u002F \u003Ccode>actor_user_id\u003C\u002Fcode>）。OAuth web 端 \u003Ccode>\u002Fprofile\u003C\u002Fcode>「萌萌点记录」直接用它；下游站点若不想自己代理 s2s 端点，用户也可直连此端点查自己的流水。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"4-幂等-唯一需要严谨的点\" tabindex=\"-1\">4. 幂等（唯一需要严谨的点）\u003C\u002Fh2>\n\u003Cp>下游发放常由会重试 \u002F 重放的路径触发（典型：moyu cron「Wiki 消息 → +3」），没有幂等就会重复加分。\u003C\u002Fp>\n\u003Cul>\n\u003Cli>调用方为\u003Cstrong>每个业务事件\u003C\u002Fstrong>生成\u003Cstrong>稳定\u003C\u002Fstrong>键，推荐 \u003Ccode>&lt;app&gt;:&lt;event&gt;:&lt;事件唯一id&gt;\u003C\u002Fcode>，如 \u003Ccode>moyu:wiki_approved:1207\u003C\u002Fcode>、\u003Ccode>kungal:checkin:1207:2026-05-29\u003C\u002Fcode>。\u003C\u002Fli>\n\u003Cli>服务端：\u003Ccode>idempotency_key\u003C\u002Fcode> 唯一索引。已存在 → 不重复执行，回原结果（\u003Ccode>applied:false\u003C\u002Fcode>）。请求体不一致 → \u003Ccode>400\u002F16004\u003C\u002Fcode>。\u003C\u002Fli>\n\u003Cli>写入在单事务内对该用户行加锁（\u003Ccode>SELECT … FOR UPDATE\u003C\u002Fcode>）防并发竞态；唯一索引兜底。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"5-管理端\" tabindex=\"-1\">5. 管理端\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>发放 \u002F 扣除走\u003Cstrong>同一个 Adjust 入口\u003C\u002Fstrong>（\u003Ccode>reason=admin_grant\u003C\u002Fcode>\u002F\u003Ccode>admin_deduct\u003C\u002Fcode>、\u003Ccode>actor_user_id=管理员id\u003C\u002Fcode>、\u003Ccode>note\u003C\u002Fcode> 填理由、幂等键用表单 token）→ 自动进同一审计日志。\u003C\u002Fli>\n\u003Cli>OAuth admin（用户管理页）加：发放\u002F扣除弹窗 + 流水查看。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>不要\u003C\u002Fstrong>给管理员开&quot;直接编辑整型&quot;的口子（绕过日志）。\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"6-迁移-一次性\" tabindex=\"-1\">6. 迁移（一次性）\u003C\u002Fh2>\n\u003Col>\n\u003Cli>下游停止本地 \u003Ccode>moemoepoint\u003C\u002Fcode> 写入（或短期双写过渡）。\u003C\u002Fli>\n\u003Cli>每个用户取各站本地值之和作为统一起始余额。\u003Cstrong>已落地\u003C\u002Fstrong>：\u003Ccode>cmd\u002Fmigrate-users\u003C\u002Fcode> 在 ID 统一时已把 kungal + moyu 的本地值累加进 \u003Ccode>users.moemoepoint\u003C\u002Fcode>。\u003C\u002Fli>\n\u003Cli>写一条 \u003Ccode>reason=migration\u003C\u002Fcode> 日志（\u003Ccode>idempotency_key=oauth:migration:v1:&lt;userId&gt;\u003C\u002Fcode>，可重复跑）。\u003Cstrong>已落地\u003C\u002Fstrong>：\u003Ccode>go run .\u002Fcmd\u002Fmigrate-moemoepoint\u003C\u002Fcode>（支持 \u003Ccode>-dry-run\u003C\u002Fcode>）为每个有余额却无流水的用户回填一条 \u003Ccode>reason=migration\u003C\u002Fcode>、\u003Ccode>note=「从 鲲 Galgame 论坛 和 鲲 Galgame 补丁 继承」\u003C\u002Fcode> 的记录；delta = 当前余额 − 已有流水之和（取“未被解释的余额”，使账本严格对账），\u003Cstrong>不改 \u003Ccode>users.moemoepoint\u003C\u002Fcode>\u003C\u002Fstrong>（已是正确值），幂等可重跑。\u003C\u002Fli>\n\u003Cli>下游删本地写逻辑，改：发放\u002F扣除调 §3.1；显示余额回读 OAuth。\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cblockquote>\n\u003Cp>注册欢迎礼：新账号在 OAuth 注册成功时自动 +7（\u003Ccode>reason=register_gift\u003C\u002Fcode>，\u003Ccode>note=「鲲给予你的第一份礼物」\u003C\u002Fcode>，\u003Ccode>idempotency_key=oauth:register_gift:&lt;userId&gt;\u003C\u002Fcode>，best-effort 不阻塞注册）。这是 OAuth 内部一次性发放，与上面的 \u003Ccode>migration\u003C\u002Fcode> 互不影响（新用户的余额由 \u003Ccode>register_gift\u003C\u002Fcode> 这条流水解释，不会被 §6 回填）。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"7-下游接入\" tabindex=\"-1\">7. 下游接入\u003C\u002Fh2>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>现在\u003C\u002Fth>\n\u003Cth>改成\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>本地 \u003Ccode>UPDATE user SET moemoepoint = moemoepoint + N\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>调 \u003Ccode>POST \u002Fusers\u002F:id\u002Fmoemoepoint\u003C\u002Fcode>（Basic Auth + 稳定幂等键）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>cron 重放发放\u003C\u002Ftd>\n\u003Ctd>同上，幂等键用业务事件唯一 id → 重放安全\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>渲染余额读本地列\u003C\u002Ftd>\n\u003Ctd>读 OAuth 实时余额（\u003Ccode>\u002Fauth\u002Fme\u003C\u002Fcode> 或 §3.2）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cblockquote>\n\u003Cp>\u003Cstrong>可用性注意\u003C\u002Fstrong>：发放现在依赖 OAuth 可达。对\u003Cstrong>非关键\u003C\u002Fstrong>奖励（签到、点赞），调用失败应&quot;记录待补 + 不阻塞用户主流程&quot;，靠幂等键之后重试补发；不要让 OAuth 抖动卡住下游核心操作。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Cp>OAuth \u003Cstrong>不发布 SDK\u003C\u002Fstrong>，每个 consumer 自己写薄客户端（同 \u003Ccode>\u002Fusers\u002Fbatch\u003C\u002Fcode> 的 Basic Auth）。\u003C\u002Fp>\n\u003Ch2 id=\"8-错误码-16xxx\" tabindex=\"-1\">8. 错误码（16xxx）\u003C\u002Fh2>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>code\u003C\u002Fth>\n\u003Cth>常量\u003C\u002Fth>\n\u003Cth>含义\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>16002\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>ErrMoemoepointInvalidDelta\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>delta 为 0 或 |delta| &gt; 1,000,000\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>16003\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>ErrMoemoepointInvalidReason\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>reason 不在枚举内，或 s2s 用了保留 reason（admin_*\u002Fmigration）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>16004\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>ErrMoemoepointIdemConflict\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>idempotency_key 已存在但请求体不一致\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>16005\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>ErrMoemoepointNotAwarder\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>client 无铸币权限（\u003Ccode>moemoepoint_awarder=false\u003C\u002Fcode>，仅 POST 调整，HTTP 403）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cblockquote>\n\u003Cp>已实现状态：上述 4 个码 + \u003Ccode>moemoepoint_log\u003C\u002Fcode> 表 + s2s\u002Fadmin 端点 + 管理端 UI \u003Cstrong>均已落地\u003C\u002Fstrong>（待 oauth 后端重启生效）。铸币白名单（\u003Ccode>16005\u003C\u002Fcode> + \u003Ccode>moemoepoint_awarder\u003C\u002Fcode> 列 + \u003Ccode>cmd\u002Fmigrate\u003C\u002Fcode> 按域名回填论坛\u002F补丁）于 2026-06-13 落地。下游消费 + 数据合并迁移（§6\u002F§7）仍待各站对接。\n并发同键竞态：唯一索引兜底（不会重复加分），极少数并发同键会得到一次性 500，调用方重试即转为 \u003Ccode>applied:false\u003C\u002Fcode>。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"9-刻意没做的-将来需要时再升级\" tabindex=\"-1\">9. 刻意没做的（将来需要时再升级）\u003C\u002Fh2>\n\u003Cp>为对齐当前规模（~9 万用户的爱好社区、单人维护、karma 非货币），以下\u003Cstrong>故意省略\u003C\u002Fstrong>——等真有需求再加：\u003C\u002Fp>\n\u003Cdiv class=\"kun-table-wrap\">\u003Ctable>\u003Cthead>\n\u003Ctr>\n\u003Cth>砍掉的\u003C\u002Fth>\n\u003Cth>完整账本版才需要 \u002F 何时再加\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>余额 = \u003Ccode>SUM(delta)\u003C\u002Fcode> 派生 + \u003Ccode>balance_after\u003C\u002Fcode> 快照 + 定时对账巡检\u003C\u002Ftd>\n\u003Ctd>资损级系统 \u002F 需要逐行可证一致性时\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>两级 \u003Ccode>category\u003C\u002Fcode> 闭集 + \u003Ccode>reason\u003C\u002Fcode> 命名空间防伪 + per-app reason 清单模板\u003C\u002Ftd>\n\u003Ctd>出现\u003Cstrong>多个独立团队\u003C\u002Fstrong>各自定义大量积分玩法、需要解耦发版时\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>per-client category 白名单、单次\u002F单日累计上限\u003C\u002Ftd>\n\u003Ctd>萌萌点可兑换实物、出现真实刷分\u002F欺诈动机时\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\u003Cp>升级是可逆且渐进的：日志表已记 \u003Ccode>reason\u003C\u002Fcode>\u002F\u003Ccode>source_app\u003C\u002Fcode>\u002F\u003Ccode>ref\u003C\u002Fcode>，将来要分两级或加约束都能在现有数据上演进，不必现在预付复杂度。\u003C\u002Fp>\n","kun-galgame-infra\u002Fdocs\u002Fintegration\u002Foauth\u002F06-moemoepoint.md",1781708341787]