diff --git a/11.md b/11.md
deleted file mode 100644
index bd8a6464..00000000
--- a/11.md
+++ /dev/null
@@ -1,75 +0,0 @@
-2701835328032037898 2209880145223 海底捞第五人格皮肤 0.01 测试连接
-{
- "item_id": 1038123159888,
- "seller_id": 2209880145223,
- "MsgTypeDes": "订单已付款",
- "biz_order_id": "2701835328032037898",
- "order_status": 2,
- "_agisoTradeDetail": {
- "sku": "6227949975509|类型:海底捞第五人格皮肤",
- "item": {
- "price": 1,
- "title": "测试链接,勿拍",
- "item_id": 1038123159888,
- "pic_url": "https://gw.alicdn.com/bao/uploaded/i1/2209880145223/O1CN0178ZkcG1oSBmmmr1V8_!!4611686018427387207-53-xy_item.heic"
- },
- "payment": 1,
- "end_time": 0,
- "pay_time": 1776144458000,
- "post_fee": 0,
- "isEticket": false,
- "ship_time": 0,
- "buy_amount": 1,
- "buyer_nick": "梦里梦",
- "isRecharge": false,
- "create_time": 1776144455000,
- "seller_nick": "大锤号商",
- "biz_order_id": "2701835328032037898",
- "order_status": 2,
- "coupon_eticket_info": [],
- "encryption_buyer_id": "enV7xJs5DRbJBzIH8uUUcg=="
- }
-}
-
-
-SKU: 6227949975509
-ItemId: 1038123159888
-
-
-
-----------------------------
-
-(现货秒发)海底捞第五人格联动限定兑换码cdk拉拉队员皮肤+
-SKU: 1038123159888
-ItemId: 1038123159888
-
-{
- "item_id": 1038123159888,
- "seller_id": 2209880145223,
- "MsgTypeDes": "交易成功",
- "biz_order_id": "4502265554049011443",
- "order_status": 4,
- "_agisoTradeDetail": {
- "item": {
- "price": 4000,
- "title": "(现货秒发)海底捞第五人格联动限定兑换码cdk拉拉队员皮肤+",
- "item_id": 1038123159888,
- "pic_url": "https://gw.alicdn.com/bao/uploaded/i2/2209880145223/O1CN01xGf3721oSBmd1W0DI_!!4611686018427387207-53-xy_item.heic"
- },
- "payment": 4000,
- "end_time": 1776143786000,
- "pay_time": 1775279772000,
- "post_fee": 0,
- "isEticket": false,
- "ship_time": 1775279779000,
- "buy_amount": 1,
- "buyer_nick": "b***5",
- "isRecharge": false,
- "create_time": 1775279753000,
- "seller_nick": "大锤号商",
- "biz_order_id": "4502265554049011443",
- "order_status": 4,
- "coupon_eticket_info": [],
- "encryption_buyer_id": "6iabfI7bB2cCa4y8JR2VQw=="
- }
-}
\ No newline at end of file
diff --git a/apps/backend/data/khhao-sync-state.json b/apps/backend/data/khhao-sync-state.json
index cdcf4ee9..019628ea 100644
--- a/apps/backend/data/khhao-sync-state.json
+++ b/apps/backend/data/khhao-sync-state.json
@@ -1,13 +1,13 @@
{
"syncFromCreatedAt": "2026-05-02T10:53:28.550Z",
- "lastRunStartedAt": "2026-05-04T01:42:53.462Z",
- "lastRunFinishedAt": "2026-05-04T01:42:54.077Z",
+ "lastRunStartedAt": "2026-05-04T13:32:11.217Z",
+ "lastRunFinishedAt": "2026-05-04T13:32:11.824Z",
"lastRunStatus": "success",
"lastErrorMessage": "",
"fetchedCount": 40,
"syncedCount": 0,
"ignoredCount": 40,
- "lastOrderCreatedAt": "2026-05-04T01:42:50.000Z",
+ "lastOrderCreatedAt": "2026-05-04T13:32:06.000Z",
"watchMode": "normal",
"watchOrders": []
}
diff --git a/apps/backend/data/order-fulfillment-bindings.json b/apps/backend/data/order-fulfillment-bindings.json
index 7e909b15..6fffc22f 100644
--- a/apps/backend/data/order-fulfillment-bindings.json
+++ b/apps/backend/data/order-fulfillment-bindings.json
@@ -1,31 +1,4 @@
[
- {
- "provider": "khhao",
- "platform": "kuaishou",
- "shopId": "4269276762",
- "shopName": "稚嫩游戏交易店",
- "khhaoShopId": "10",
- "skuCode": "套装-浪漫天命",
- "skuName": "套装-浪漫天命",
- "profileKey": "kuaishou_ct_assisted",
- "enabled": true,
- "priority": 100,
- "config": {
- "kuaishouShop": {
- "shopId": "4269276762",
- "shopName": "稚嫩游戏交易店",
- "khhaoShopId": "10"
- }
- },
- "match": {
- "externalSkuCode": "830",
- "externalItemId": "830",
- "externalSkuName": "测试1",
- "config": {
- "resolvedSkuName": "套装-浪漫天命"
- }
- }
- },
{
"provider": "agiso",
"platform": "xianyu",
diff --git a/docs/91卡券/1.说明.md b/docs/91卡券/1.说明.md
new file mode 100644
index 00000000..9b1a3f58
--- /dev/null
+++ b/docs/91卡券/1.说明.md
@@ -0,0 +1,6 @@
+# 本文档仅做示例说明,接口路径、签名规则、参数字段、入参格式等,可自定义。
+
+#
+# 注意,本业务对接非代码开发级别的对接,内部集成了模块人工参数配置实现的。部分特殊场景有所限制,主要涉及特殊的算法不支持、部分参数不支持传递等。
+
+# 可以先编写好接口文档提供给客服,有问题客服会联系你哈
diff --git a/docs/91卡券/2.签名规则示例.md b/docs/91卡券/2.签名规则示例.md
new file mode 100644
index 00000000..44b7f8d2
--- /dev/null
+++ b/docs/91卡券/2.签名规则示例.md
@@ -0,0 +1,45 @@
+# 签名计算规则示例:
+
+# 以下仅做示例说明,实际的签名规则可自行设计。
+
+### 简要描述:
+- 除sign字段外,所有参数按照字段名的ascii码从小到大排序后,使用QueryString的格式(即key1=value1&key2=value2…)拼接成字符串后,再在前后追加上 商户密钥 的值,然后转换成32位大写的MD5字符串。
+#### 注意事项:如空值参数是否参与签名需要在提供的文档中进行说明哈
+
+**假设 商户密钥 值为:** rste57w8rsubsnxsb384ur3u9kn5fzhr0a091b3aa4324435aab703142518a8f7
+
+**假设请求参数为:**
+```csharp
+{
+ "userId":"1001",
+ "orderNo":"2023061917481700001",
+ "productNo":"test01",
+ "buyNum":1,
+ "attach":"{\"account\":\"13888888888\"}",
+ "maxAmount":""
+ "timestamp":1687168097,
+ "callbackUrl":"",
+ "version":"1.0",
+ "sign":"EF387ED4D401A275498A9FCC6C22116B"
+}
+```
+**验签步骤:**
+1. 转换成字符串如下:
+```
+attach={"account":"13888888888"}&buyNum=1&callbackUrl=&maxAmount=&orderNo=2023061917481700001&productNo=test01×tamp=1687168097&userId=1001&version=1.0
+```
+
+2. 前后追加上 商户密钥 的MD5源串如下:
+```
+rste57w8rsubsnxsb384ur3u9kn5fzhr0a091b3aa4324435aab703142518a8f7attach={"account":"13888888888"}&buyNum=1&callbackUrl=&maxAmount=&orderNo=2023061917481700001&productNo=test01×tamp=1687168097&userId=1001&version=1.0rste57w8rsubsnxsb384ur3u9kn5fzhr0a091b3aa4324435aab703142518a8f7
+```
+
+3. MD5后转成32位大写的签名值如下:
+```
+EF387ED4D401A275498A9FCC6C22116B
+```
+
+
+
+
+
diff --git a/docs/91卡券/3.异步卡密下单.md b/docs/91卡券/3.异步卡密下单.md
new file mode 100644
index 00000000..fcc68668
--- /dev/null
+++ b/docs/91卡券/3.异步卡密下单.md
@@ -0,0 +1,154 @@
+# 91卡券异步卡密下单接口
+
+## 1. 接口说明
+
+本文档为当前项目对接 `91卡券` 的正式技术文档,用于提交客服审核。
+
+本项目对接场景说明:
+
+- `91卡券` 负责售卖与自动发货;
+- 我方系统负责接收订单、创建任务、生成领取链接;
+- 该领取链接会在后续查询订单接口中,作为最终卡密内容返回给 `91卡券`;
+- `91卡券` 再将该链接自动发给买家。
+
+注意:
+
+- 本接口为**异步卡密下单接口**;
+- 首次下单成功受理后,返回 `orderStatus = 10`;
+- **不会**在该接口首次响应中直接返回最终领取链接;
+- 最终链接请通过“查询订单接口”获取。
+
+## 2. 请求方向
+
+- `91卡券平台 -> 接入方系统`
+
+## 3. 请求 URL
+
+请按以下地址配置:
+
+```text
+POST https://221329.cc.cd/api/v1/open/91/orders/create
+```
+
+## 4. 请求方式
+
+- `POST`
+- `Content-Type: application/json;charset=utf-8`
+
+## 5. 签名规则
+
+签名规则采用 签名规则示例 中的约定:
+
+- 除 `sign` 外,所有参数按字段名 ASCII 升序排序;
+- 使用 `key=value&key=value` 方式拼接;
+- 前后拼接商户密钥;
+- 取 `MD5`,输出 32 位大写字符串;
+- 空值参数参与签名。
+
+## 6. 请求参数
+
+| 参数名 | 必填 | 类型 | 说明 |
+| --- | --- | --- | --- |
+| `orderNo` | 是 | string | 商家订单号,唯一,用于幂等处理。 |
+| `productNo` | 是 | string | 接入方商品编号。建议与我方内部履约 SKU 一一对应。 |
+| `buyNum` | 是 | int | 购买数量。 |
+| `maxAmount` | 否 | string | 商家可接受最大成本金额。值为整单金额,非单价。若传值,则我方按该金额校验,超出时返回失败。 |
+| `callbackUrl` | 否 | string | 由 `91卡券` 提供的回调地址。当前项目可接收但不依赖该字段完成主流程。 |
+| `timestamp` | 是 | long | 10 位秒级 Unix 时间戳,用于请求时效校验。 |
+| `version` | 是 | string | 固定传 `1.0`。 |
+| `sign` | 是 | string | 签名。 |
+
+## 7. 请求示例
+
+```json
+{
+ "orderNo": "P91KS202605040001",
+ "productNo": "KS-CLOUD-SKU-001",
+ "buyNum": 1,
+ "maxAmount": "0.0000",
+ "callbackUrl": "https://cb.example.com/notify/91/order",
+ "timestamp": 1777867200,
+ "version": "1.0",
+ "sign": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+}
+```
+
+## 8. MD5 源串示例
+
+假设:
+
+- `userId = 1001`
+- 商户密钥为:`your_secret_key`
+
+则源串示例如下:
+
+```text
+your_secret_keybuyNum=1&callbackUrl=https://cb.example.com/notify/91/order&maxAmount=0.0000&orderNo=P91KS202605040001&productNo=KS-CLOUD-SKU-001×tamp=1777867200&userId=1001&version=1.0your_secret_key
+```
+
+## 9. 业务处理规则
+
+我方系统收到请求后,按以下规则处理:
+
+1. 校验签名与时间戳;
+2. 按 `orderNo` 做幂等;
+3. 校验 `productNo` 是否已映射到当前项目的快手履约商品;
+4. 若传入 `maxAmount`,则校验成本是否超限;
+5. 创建内部订单;
+6. 创建快手 Cloud 履约任务;
+7. 生成当前项目领取链接,例如:`https://221329.cc.cd/#/claim/{token}`;
+8. 首次响应返回处理中状态,由 `91卡券` 后续调用查询订单接口获取最终卡密内容。
+
+## 10. 响应参数
+
+| 参数名 | 类型 | 必须返回 | 说明 |
+| --- | --- | --- | --- |
+| `orderNo` | string | 必须返回 | 商家订单号,来源 `91卡券` 下单请求。 |
+| `outTradeNo` | string | 成功时必须 | 我方系统内部订单号。 |
+| `orderStatus` | int | 成功时必须 | 订单状态。`10`:处理中;`30`:失败。
注意:当前接口为异步商品下单接口,首次响应**不会返回 `20`**。 |
+| `orderCost` | decimal(14,4) | 成功时可返回 | 订单总成本,单位:元。若当前阶段无法确认,可返回 `0.0000`。 |
+| `cards` | string | 非必须 | 当前阶段建议返回空字符串。最终卡密内容请在查询订单接口中返回。 |
+| `failCode` | int | 失败时可返回 | 失败代码。 |
+| `failReason` | string | 失败时可返回 | 失败原因。 |
+
+## 11. 响应示例
+
+### 11.1 受理成功
+
+```json
+{
+ "code": 200,
+ "message": "接口调用成功",
+ "data": {
+ "orderNo": "P91KS202605040001",
+ "outTradeNo": "OS202605040001",
+ "orderStatus": 10,
+ "orderCost": 0.0000,
+ "cards": ""
+ }
+}
+```
+
+### 11.2 下单失败
+
+```json
+{
+ "code": 200,
+ "message": "接口调用成功",
+ "data": {
+ "orderNo": "P91KS202605040001",
+ "outTradeNo": "",
+ "orderStatus": 30,
+ "failCode": 1220,
+ "failReason": "订单成本超出可接受范围"
+ }
+}
+```
+
+## 12. 对接说明
+
+- `productNo` 请按我方提供的商品编号配置;
+- 本接口成功受理后,不代表最终卡密已可交付;
+- 最终交付内容为我方系统生成的领取链接,链接域名固定为 `221329.cc.cd`;
+- 该链接将在“查询订单接口”中,通过 `cards` 加密串返回;
+- 建议 `91卡券` 将此商品配置为:`异步卡密商品`。
diff --git a/docs/91卡券/4.查询订单接口.md b/docs/91卡券/4.查询订单接口.md
new file mode 100644
index 00000000..87d755d6
--- /dev/null
+++ b/docs/91卡券/4.查询订单接口.md
@@ -0,0 +1,203 @@
+# 91卡券查询订单接口
+
+## 1. 接口说明
+
+本文档为当前项目对接 `91卡券` 的正式技术文档,用于提交客服审核。
+
+本接口用于在异步卡密下单后,由 `91卡券` 主动查询订单状态与最终卡密内容。
+
+当前项目的最终交付物不是传统卡号密码,而是:
+
+- 我方系统生成的**领取链接**;
+- 该链接将作为最终卡密内容,通过 `cards` 字段返回给 `91卡券`;
+- `91卡券` 再使用自动发货能力,将该领取链接展示给买家。
+
+## 2. 请求方向
+
+- `91卡券平台 -> 接入方系统`
+
+## 3. 请求 URL
+
+请按以下地址配置:
+
+```text
+POST https://221329.cc.cd/api/v1/open/91/orders/query
+```
+
+## 4. 请求方式
+
+- `POST`
+- `Content-Type: application/json;charset=utf-8`
+
+## 5. 签名规则
+
+签名规则采用 2.签名规则示例 中的约定:
+
+- 除 `sign` 外,所有参数按字段名 ASCII 升序排序;
+- 使用 `key=value&key=value` 方式拼接;
+- 前后拼接商户密钥;
+- 取 `MD5`,输出 32 位大写字符串;
+- 空值参数参与签名。
+
+## 6. 请求参数
+
+| 参数名 | 必填 | 类型 | 说明 |
+| --- | --- | --- | --- |
+| `orderNo` | 是 | string | 商家订单号。 |
+| `timestamp` | 是 | long | 10 位秒级 Unix 时间戳,用于请求时效校验。 |
+| `version` | 是 | string | 固定传 `1.0`。 |
+| `sign` | 是 | string | 签名。 |
+
+## 7. 请求示例
+
+```json
+{
+ "orderNo": "P91KS202605040001",
+ "timestamp": 1777867260,
+ "version": "1.0",
+ "sign": "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
+}
+```
+
+## 8. MD5 源串示例
+
+假设:
+
+- `userId = 1001`
+- 商户密钥为:`your_secret_key`
+
+则源串示例如下:
+
+```text
+your_secret_keyorderNo=P91KS202605040001×tamp=1777867260&userId=1001&version=1.0your_secret_key
+```
+
+## 9. 业务处理规则
+
+我方系统收到查询请求后,按以下规则返回:
+
+1. 校验签名与时间戳;
+2. 根据 `orderNo` 查询内部订单;
+3. 若订单存在但领取链接尚未准备完成,返回 `orderStatus = 10`;
+4. 若领取链接已生成,可交付,返回 `orderStatus = 20`;
+5. 若订单无法履约,返回 `orderStatus = 30`;
+6. 当返回 `20` 时,通过 `cards` 返回最终卡密内容。
+
+## 10. 响应参数
+
+| 参数名 | 类型 | 必须返回 | 说明 |
+| --- | --- | --- | --- |
+| `orderNo` | string | 必须返回 | 商家订单号,来源 `91卡券` 下单请求。 |
+| `outTradeNo` | string | 必须返回 | 我方系统内部订单号。 |
+| `orderStatus` | int | 必须返回 | 订单状态。`10`:处理中;`20`:成功;`30`:失败。 |
+| `failCode` | int | 失败时建议返回 | 失败代码。可以不返回该字段,但不要返回 `null`。 |
+| `failReason` | string | 失败时建议返回 | 失败原因。 |
+| `orderCost` | decimal(14,4) | 成功时建议返回 | 订单总成本,单位:元。 |
+| `cards` | string | `orderStatus = 20` 时必须 | 卡密数据加密串。当前项目通过该字段返回领取链接。 |
+| `cards[0].cardNo` | string | 成功时必须 | 最终交付的领取链接,格式为 `https://221329.cc.cd/#/claim/{token}`。 |
+| `cards[0].cardPwd` | string | 可选 | 当前场景固定为空字符串。 |
+| `cards[0].expireTime` | string | 可选 | 领取链接过期时间,支持 `yyyy-MM-dd HH:mm:ss` 或 10 位秒级 Unix 时间戳。 |
+| `cards[0].jumpLink` | string | 可选 | 与 `cardNo` 保持一致,用于兼容链接展示场景。 |
+
+## 11. cards 原始结构约定
+
+由于已确认允许将链接作为最终卡密内容展示给买家,当前项目约定:
+
+- `cardNo`:直接放领取链接;
+- `cardPwd`:留空;
+- `expireTime`:放领取链接过期时间;
+- `jumpLink`:与 `cardNo` 一致。
+
+原始结构示例如下:
+
+```json
+[
+ {
+ "cardNo": "https://221329.cc.cd/#/claim/abc123xyz",
+ "cardPwd": "",
+ "expireTime": "2026-05-05 12:00:00",
+ "jumpLink": "https://221329.cc.cd/#/claim/abc123xyz"
+ }
+]
+```
+
+若 `buyNum > 1`,则 `cards` 原始结构中返回多个卡项,每个卡项对应一个独立领取链接。
+
+## 12. 响应示例
+
+### 12.1 处理中
+
+```json
+{
+ "code": 200,
+ "message": "接口调用成功",
+ "data": {
+ "orderNo": "P91KS202605040001",
+ "outTradeNo": "OS202605040001",
+ "orderStatus": 10,
+ "failCode": 0,
+ "failReason": "",
+ "orderCost": 0.0000,
+ "cards": ""
+ }
+}
+```
+
+### 12.2 查询成功
+
+```json
+{
+ "code": 200,
+ "message": "接口调用成功",
+ "data": {
+ "orderNo": "P91KS202605040001",
+ "outTradeNo": "OS202605040001",
+ "orderStatus": 20,
+ "failCode": 0,
+ "failReason": "",
+ "orderCost": 0.0000,
+ "cards": "加密后的cards字符串"
+ }
+}
+```
+
+`cards` 解密前原始结构示例:
+
+```json
+[
+ {
+ "cardNo": "https://221329.cc.cd/#/claim/abc123xyz",
+ "cardPwd": "",
+ "expireTime": "2026-05-05 12:00:00",
+ "jumpLink": "https://221329.cc.cd/#/claim/abc123xyz"
+ }
+]
+```
+
+### 12.3 查询失败
+
+```json
+{
+ "code": 200,
+ "message": "接口调用成功",
+ "data": {
+ "orderNo": "P91KS202605040001",
+ "outTradeNo": "OS202605040001",
+ "orderStatus": 30,
+ "failCode": 1204,
+ "failReason": "商品未配置或订单无法履约"
+ }
+}
+```
+
+## 13. 对接说明
+
+- 本接口是 `91卡券` 自动发货的关键接口;
+- 当返回 `orderStatus = 20` 时,表示我方已准备好最终交付内容;
+- 最终交付内容是领取链接,不是传统卡号密码;
+- 领取链接域名固定为 `221329.cc.cd`;
+- 买家收到链接后,会进入我方系统领取页完成后续快手核销与履约流程;
+- 建议 `91卡券` 侧确认:
+ - `cards.cardNo` 可直接展示完整链接;
+ - `jumpLink` 可按链接字段兼容展示;
+ - 查询频率按异步商品标准轮询配置。
diff --git a/docs/91卡券/5.当前项目对接配置.md b/docs/91卡券/5.当前项目对接配置.md
new file mode 100644
index 00000000..3345bed8
--- /dev/null
+++ b/docs/91卡券/5.当前项目对接配置.md
@@ -0,0 +1,315 @@
+# 91卡券接入当前项目接口配置
+
+## 1. 对接目标
+
+- `91卡券` 作为外部售卖与自动发货通道。
+- 当前项目负责:
+ - 接收 `91卡券` 下单请求;
+ - 在系统内生成订单与快手履约任务;
+ - 生成当前项目自己的领取链接;
+ - 由 `91卡券` 通过自动发货,将该领取链接展示给买家。
+- 已确认:**允许将链接作为最终卡密内容展示给买家**。
+
+## 2. 对接结论
+
+本次对接不走 `Agiso/咸鱼` 的站内消息链路,而是走:
+
+1. `91卡券` 调用我方异步下单接口;
+2. 我方创建内部订单,并生成领取链接;
+3. `91卡券` 调用我方查询订单接口;
+4. 我方在查询结果的 `cards` 中返回领取链接;
+5. `91卡券` 自动发货给买家。
+
+## 3. 接口地址建议
+
+基础前缀建议:
+
+```text
+https://你的域名/api/v1/open/91
+```
+
+接口列表:
+
+- 异步卡密下单:`POST /api/v1/open/91/orders/create`
+- 查询订单接口:`POST /api/v1/open/91/orders/query`
+
+## 4. 签名配置
+
+签名规则采用当前文档里的示例规则:
+
+- 除 `sign` 外,所有参数按字段名 ASCII 升序排序;
+- 使用 `key=value&key=value` 方式拼接;
+- 前后拼接商户密钥;
+- 取 `MD5`,输出 32 位大写字符串。
+
+建议固定配置:
+
+- `signType`: `MD5`
+- `charset`: `UTF-8`
+- `timestamp`: 10 位秒级 Unix 时间戳
+- `version`: `1.0`
+- 空值参数是否参与签名:`参与`
+
+## 5. 业务字段映射
+
+### 5.1 下单请求 -> 当前项目
+
+| 91字段 | 当前项目用途 | 说明 |
+| --- | --- | --- |
+| `orderNo` | 外部订单号 / 幂等键 | 建议映射为内部 `platformOrderId` |
+| `productNo` | 商品映射 | 映射到内部 SKU 或履约绑定规则 |
+| `buyNum` | 购买数量 | 生成对应数量的履约任务 |
+| `maxAmount` | 成本上限校验 | 可选,超限返回失败 |
+| `callbackUrl` | 备用回调地址 | 先保留,不作为主流程依赖 |
+| `timestamp` | 防重放 | 校验请求时效 |
+| `version` | 版本 | 固定 `1.0` |
+| `sign` | 验签 | 必填 |
+
+### 5.2 当前项目内部建议映射
+
+建议新增一个独立来源:
+
+- `provider = '91kaquan'`
+- `platform = 'kuaishou'`
+
+商品绑定建议:
+
+- `productNo` 对应当前项目内部 `skuCode`
+- 再由现有快手履约配置,匹配到 `kuaishou_ct_assisted` 履约链路
+
+## 6. 异步下单接口配置
+
+### 6.1 请求方向
+
+- `91卡券 -> 当前项目`
+
+### 6.2 请求地址
+
+```text
+POST /api/v1/open/91/orders/create
+Content-Type: application/json;charset=utf-8
+```
+
+### 6.3 请求参数
+
+| 参数名 | 必填 | 类型 | 说明 |
+| --- | --- | --- | --- |
+| `orderNo` | 是 | string | 91 商家订单号,唯一 |
+| `productNo` | 是 | string | 我方商品编号 |
+| `buyNum` | 是 | int | 购买数量 |
+| `maxAmount` | 否 | string | 可接受最大成本金额 |
+| `callbackUrl` | 否 | string | 91 提供的回调地址 |
+| `timestamp` | 是 | long | 10 位秒级时间戳 |
+| `version` | 是 | string | 固定 `1.0` |
+| `sign` | 是 | string | 签名 |
+
+### 6.4 请求示例
+
+```json
+{
+ "orderNo": "P91KS202605040001",
+ "productNo": "KS-CLOUD-SKU-001",
+ "buyNum": 1,
+ "maxAmount": "0.0000",
+ "callbackUrl": "https://cb.example.com/notify/91/order",
+ "timestamp": 1777867200,
+ "version": "1.0",
+ "sign": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+}
+```
+
+### 6.5 下单处理规则
+
+- 验签失败:直接返回失败。
+- `orderNo` 已存在:按幂等处理,返回已有订单状态。
+- `productNo` 未配置:返回失败。
+- 成本超限:返回失败,错误码可用 `1220`。
+- 下单成功后:
+ - 创建内部订单;
+ - 创建快手履约任务;
+ - 生成领取链接;
+ - 异步商品首次响应返回 `10`,表示处理中。
+
+### 6.6 响应字段约定
+
+| 参数名 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `orderNo` | string | 是 | 原样返回 |
+| `outTradeNo` | string | 是 | 我方内部订单号 |
+| `orderStatus` | int | 是 | 异步商品固定返回 `10` 或 `30` |
+| `orderCost` | decimal | 否 | 成功受理时可返回 |
+| `cards` | string | 否 | 异步下单阶段建议为空 |
+
+### 6.7 响应示例
+
+```json
+{
+ "code": 200,
+ "message": "接口调用成功",
+ "data": {
+ "orderNo": "P91KS202605040001",
+ "outTradeNo": "OS202605040001",
+ "orderStatus": 10,
+ "orderCost": 0.0000,
+ "cards": ""
+ }
+}
+```
+
+## 7. 查询订单接口配置
+
+### 7.1 请求方向
+
+- `91卡券 -> 当前项目`
+
+### 7.2 请求地址
+
+```text
+POST /api/v1/open/91/orders/query
+Content-Type: application/json;charset=utf-8
+```
+
+### 7.3 请求参数
+
+| 参数名 | 必填 | 类型 | 说明 |
+| --- | --- | --- | --- |
+| `orderNo` | 是 | string | 91 商家订单号 |
+| `timestamp` | 是 | long | 10 位秒级时间戳 |
+| `version` | 是 | string | 固定 `1.0` |
+| `sign` | 是 | string | 签名 |
+
+### 7.4 查询处理规则
+
+- 找不到订单:返回失败。
+- 订单已创建但领取链接未准备好:返回 `orderStatus = 10`。
+- 领取链接已生成,可交付:返回 `orderStatus = 20`。
+- 订单无法履约:返回 `orderStatus = 30`,并带失败原因。
+
+### 7.5 响应字段约定
+
+| 参数名 | 类型 | 必填 | 说明 |
+| --- | --- | --- | --- |
+| `orderNo` | string | 是 | 原样返回 |
+| `outTradeNo` | string | 是 | 我方内部订单号 |
+| `orderStatus` | int | 是 | `10`处理中,`20`成功,`30`失败 |
+| `failCode` | int | 否 | 失败时返回 |
+| `failReason` | string | 否 | 失败时返回 |
+| `orderCost` | decimal | 否 | 成功时返回 |
+| `cards` | string | 成功时必须 | 卡密加密串 |
+
+## 8. cards 字段配置
+
+### 8.1 推荐方案
+
+既然已确认允许将链接作为最终卡密内容展示给买家,建议:
+
+- `cardNo` 直接放领取链接;
+- `cardPwd` 留空;
+- `expireTime` 可放领取链接过期时间;
+- `jumpLink` 可与 `cardNo` 保持一致,作为兼容字段。
+
+这样做的好处是:
+
+- `91卡券` 自动发货可直接展示链接;
+- 复杂文案、快手操作说明、核销码校验、角色确认,全部放在我方领取页完成;
+- 降低 91 展示层格式差异带来的风险。
+
+### 8.2 单卡结构建议
+
+```json
+[
+ {
+ "cardNo": "https://你的域名/claim/abc123xyz",
+ "cardPwd": "",
+ "expireTime": "2026-05-05 12:00:00",
+ "jumpLink": "https://你的域名/claim/abc123xyz"
+ }
+]
+```
+
+### 8.3 多数量建议
+
+如果 `buyNum > 1`:
+
+- 每个数量生成一个独立领取链接;
+- `cards` 中放多个卡项;
+- 每个 `cardNo` 对应一个独立链接。
+
+## 9. 查询成功响应示例
+
+```json
+{
+ "code": 200,
+ "message": "接口调用成功",
+ "data": {
+ "orderNo": "P91KS202605040001",
+ "outTradeNo": "OS202605040001",
+ "orderStatus": 20,
+ "failCode": 0,
+ "failReason": "",
+ "orderCost": 0.0000,
+ "cards": "加密后的cards字符串"
+ }
+}
+```
+
+`cards` 解密前原始结构建议为:
+
+```json
+[
+ {
+ "cardNo": "https://你的域名/claim/abc123xyz",
+ "cardPwd": "",
+ "expireTime": "2026-05-05 12:00:00",
+ "jumpLink": "https://你的域名/claim/abc123xyz"
+ }
+]
+```
+
+## 10. 状态流转建议
+
+### 10.1 91侧状态
+
+- `10`: 已下单,当前项目正在生成领取链接
+- `20`: 已可交付,91 可自动发货给买家
+- `30`: 无法履约
+
+### 10.2 当前项目侧状态建议
+
+- 创建订单成功:进入待履约
+- 已生成 `claimUrl`:视为可交付
+- 查询接口检测到 `claimUrl` 已存在:返回 `20`
+- 配置缺失、商品未匹配、履约初始化失败:返回 `30`
+
+## 11. 商品配置建议
+
+建议在你方给 91 的商品配置中约定:
+
+- `productNo`:对应当前项目内部的快手履约 SKU
+- 商品名称:明确标注“自动发链接”
+- 发货类型:异步卡密
+- 买家收到内容:领取链接
+
+## 12. 对接方需确认的固定项
+
+发给 `91卡券` 客服/对接方时,建议一次性确认以下配置:
+
+- 异步卡密下单 URL
+- 查询订单 URL
+- `userId`
+- 商户密钥
+- 签名算法是否按本文档固定
+- `cards` 中 `cardNo` 直接展示链接是否按预期显示
+- `jumpLink` 是否会同步展示或仅作兼容字段
+- 查询频率与超时要求
+
+## 13. 最终建议
+
+这次对接的最稳方案是:
+
+- `91` 负责卖货与自动发货;
+- 当前项目负责生成 `claimUrl`;
+- `claimUrl` 作为最终卡密内容,通过 `cards.cardNo` 返回;
+- 所有快手专属引导和后续交互,都留在当前项目领取页中完成。
+
+这样对当前项目改动最小,也最符合现在快手 Cloud 履约链路的设计。
diff --git a/前后端现状分析.md b/前后端现状分析.md
index 08c7fb87..3daa854e 100644
--- a/前后端现状分析.md
+++ b/前后端现状分析.md
@@ -2,25 +2,30 @@
## 当前结论
-整体架构方向没有跑偏,后端主干仍然是 `routes -> services -> repositories`,前端也已经逐步从“巨石页面”往“薄页面 + composable + 子组件”推进。
+整体架构方向没有跑偏。
-现在最需要继续优化的,不是全项目平均整理,而是两类高风险区域:
+- 后端主干仍然是 `routes -> services -> repositories`
+- 前端后台管理也已经从“单页巨石”逐步转成“薄页面 + composable + 子组件 + 分领域 service”
-1. 已经明显长成巨石、后续改动容易连锁的后端运行时模块
-2. 已经完成第一轮拆分、但还需要收口和补测试的前端后台配置模块
+和最初相比,当前最重要的变化有两点:
+
+1. 平台配置这条前后端竖线,第一阶段拆分已经基本完成
+2. 真正还需要优先处理的,已经转向后端运行时巨石模块
+
+所以现在不适合再把精力平均分散到全项目,而应该把已经拆开的区域收口,把还没拆开的大文件优先解决。
## 已完成
-### 平台配置页前端拆分
+### 1. 平台配置页前端拆分
这部分已经不再是最初判断里的“一个页面承载四套完整平台流”。
- `8e1a4be` 拆分前端后台管理 API 客户端
- `apps/frontend/src/services/admin.ts` 已改成聚合入口
- - 后台 API 已按领域拆到 `services/admin/` 目录
+ - 后台 API 已按领域拆到 `apps/frontend/src/services/admin/`
- `dff7da7` 拆分平台配置页脚本逻辑
- - `AdminPlatformShopsView.vue` 的平台状态和行为已经拆到多个 composable
+ - `apps/frontend/src/views/admin/AdminPlatformShopsView.vue` 的平台状态和行为已经拆到 composable
- 已有:
- `useAdminAgisoPlatform.ts`
- `useAdminKhhaoPlatform.ts`
@@ -30,9 +35,7 @@
- `4650c3e` 抽离平台配置页共享样式
- 共享样式已统一收口到 `apps/frontend/src/styles/admin-platform-shops.css`
-- `60c2d91` 拆分平台配置页 Agiso 与 khhao 区块
-- `254a031` 拆分平台配置页快手核销区块
-- `8f6a72f` 拆分平台配置页履约调试区块
+- `60c2d91`、`254a031`、`8f6a72f` 完成平台区块拆分
- 四个平台区块已经分别下沉为独立组件:
- `AdminPlatformAgisoSection.vue`
- `AdminPlatformKhhaoSection.vue`
@@ -43,134 +46,120 @@
- 新增 `AdminResultCard.vue`
- `khhao` / `快手核销` / `cloudtentacles` 的重复结果卡片已统一
-### 现阶段对平台配置页的重新判断
+### 2. `session-proof.js` 已完成第一轮拆分
-`apps/frontend/src/views/admin/AdminPlatformShopsView.vue` 仍然不算小,但问题性质已经变了:
+这条线已经不是“下一步待开始”,而是已经落地完成。
-- 现在主要问题不再是“大模板混杂”
-- 现在的主要问题是:
- - 父页面 `script setup` 仍然偏胖
- - 几个子组件之间还有少量可复用片段
- - 前端缺少测试防线
+当前 `apps/backend/src/services/session/` 下已经拆出:
-也就是说,这条线的第一阶段“拆分大页面”已经基本完成,后面不应该继续无限抽象,而应该适时收口。
+- `session-proof-mode.js`
+- `session-proof-paths.js`
+- `session-proof-result-writer.js`
+- `session-proof-beijing-time.js`
+- `session-proof-renderer.js`
+- `session-proof-html.js`
+- `session-proof-constants.js`
+
+`apps/backend/src/services/session/session-proof.js` 现在已经收敛成门面层,文件体量约 `145` 行,风险比之前明显下降。
+
+### 3. 后台平台配置服务已完成按平台拆分
+
+这条线也已经不是最初的 `admin-platform-config-service.js` 巨石形态了。
+
+最近这波提交已经完成:
+
+- `d629bc9` 整理后台平台配置服务目录结构
+- `d11879f` 下沉后台履约规则校验编排逻辑
+- `8c9d487` 下沉后台云触手会话结果组装逻辑
+- `dad646b` 抽离后台平台配置写入归一化逻辑
+- `60b84b2` 下沉后台 khhao 平台辅助组装逻辑
+- `3a7c311` 按平台拆分后台平台配置服务
+
+当前 `apps/backend/src/services/admin/platform-config/` 已形成按职责拆分的目录:
+
+- `service.js`
+ - 只保留统一导出入口
+- `agiso-service.js`
+- `khhao-service.js`
+- `kuaishou-eticket-service.js`
+- `cloudtentacles-service.js`
+- `fulfillment-bindings-service.js`
+- `kuaishou-cloud-fulfillment-service.js`
+- 配套 helper / mapper / validation / test 文件
+
+其中 `service.js` 已经从原来的 600+ 行缩到 53 行,平台配置后端竖线的第一阶段拆分已经完成。
## 已过时的判断
-下面这些结论需要更新,不再按最初版本理解:
+下面这些判断不应该再按旧版本理解。
-### `AdminPlatformShopsView.vue` 是前端第一优先级
+### 1. `AdminPlatformShopsView.vue` 是前端第一优先级
-这个判断已经部分过时。
+这个判断已经过时。
原因:
-- 该页面的四套平台模板已经完成拆分
-- 平台行为已经移动到 composable
-- 剩余问题主要是“收口”和“测试”,不是继续大拆
+- 四个平台模板已经拆开
+- 主要行为已经下沉到 composable
+- 剩余问题更多是收口、少量复用和测试,而不是继续大拆页面
新判断:
- 这条线仍可继续小步优化
-- 但优先级已经低于后端的 `session.js`、`session-proof.js`、`admin-platform-config-service.js`
+- 但优先级已经明显低于后端 `session.js`、`claim-session-service.js`、`admin-write-service.js`
-### 继续深挖前端平台配置页
+### 2. `admin-platform-config-service.js` 是后端当前主战场
-这个方向不应该再作为主战场。
+这个判断也已经过时。
-剩余可做项有,但应该控制强度:
+原因:
-- 可以继续抽 `section-title-row` 或 `meta-card` 这一层公共片段
-- 但不能为了统一而统一
-- 再往下更应该转向后端高风险链路
+- 这条线已经迁到 `apps/backend/src/services/admin/platform-config/`
+- 统一入口 `service.js` 已经很薄
+- 大部分公共逻辑也已经拆到 `context.js`、`writes.js`、`validation.js`、`mappers.js`、`fulfillment.js` 等模块
+
+新判断:
+
+- 平台配置后端已经从“继续大拆”阶段,进入“适度收口 + 补测试 + 控制 cloudtentacles 继续膨胀”阶段
+
+### 3. 下一刀应该从 `session-proof.js` 开始
+
+这个判断也已过时。
+
+原因:
+
+- `session-proof.js` 第一轮拆分已经完成
+- 现在更该处理的是仍然明显偏大的运行时主线文件
## 仍然优先拆分 / 优先优化
-### 1. `apps/backend/src/services/session/session.js`
+### 1. `apps/backend/src/services/admin/admin-write-service.js`
-后端第一优先级,判断不变。
+这是现在最胖、最值得优先处理的大文件之一,当前约 `1710` 行。
-它同时承担:
+它更像“后台万能动作总线”,跨域过多:
-- Playwright 浏览器生命周期
-- 会话内存状态
-- 二维码抓取
-- 登录态判断
-- 角色信息同步
-- 兑换执行
-- 截图产物
-- 自动关闭
+- 库存
+- claim
+- webhook
+- cloudtentacles
+- 快手核销
+- 腾讯 session
+- task 手工动作
-而且仍然是 `@ts-nocheck`。这类文件每继续增长一次,后面改动成本都会上升。
+建议方向:
-建议目标:
+- `admin-inventory-write`
+- `admin-task-actions`
+- `admin-claim-actions`
+- `admin-webhook-actions`
+- `admin-manual-redeem-actions`
-- `browser-runtime`
-- `session-store`
-- `session-presentation`
-- `session-redeem-orchestrator`
-- `session-persistence`
+如果继续放大,这类文件会成为后续所有后台动作改动的冲突中心。
-### 2. `apps/backend/src/services/session/session-proof.js`
+### 2. `apps/backend/src/services/claim/claim-session-service.js`
-这是下一步最合适的切入点。
-
-当前职责混在一起:
-
-- 证明模式判定
-- 产物路径拼接
-- 结果页 DOM 注入
-- 北京时间来源抓取
-- 截图拼接
-- 结果 JSON 落盘
-
-这类文件比 `session.js` 更小,适合作为后端拆分的第一刀,风险更低,能先把拆分节奏跑顺。
-
-建议拆分方向:
-
-- `proof-mode`
- - 处理 `resolveRedeemProofMode`
- - 处理 `shouldCaptureBeijingTimeProof`
-
-- `proof-paths`
- - 处理 `buildArtifactPaths`
-
-- `proof-result-writer`
- - 处理 `writeRedeemResultFile`
-
-- `proof-beijing-time`
- - 处理北京时间页面抓取与截图
-
-- `proof-renderer`
- - 处理结果弹层、合成图、截图表现逻辑
-
-先保留 `session-proof.js` 作为门面导出层,第一阶段不要改公开接口。
-
-### 3. `apps/backend/src/services/admin/admin-platform-config-service.js`
-
-这条线依然偏胖,而且和前端平台配置页是一整条竖线。
-
-虽然前端页面已经拆开,但后端这条 service 仍然同时承载:
-
-- 平台配置读写
-- 登录测试
-- 订单查询
-- SKU 调试
-- 虚拟号调试
-- 履约绑定
-
-建议后续按平台拆为:
-
-- `agiso-admin-service`
-- `khhao-admin-service`
-- `kuaishou-eticket-admin-service`
-- `cloudtentacles-admin-service`
-
-同时把“配置读写”和“调试接口”分层处理。
-
-### 4. `apps/backend/src/services/claim/claim-session-service.js`
-
-仍然是后续重点。
+当前约 `1255` 行,仍然是后端运行时第二优先级。
问题主要在:
@@ -184,29 +173,50 @@
- `claim-task-sync`
- `claim-fulfillment-finalizer`
-### 5. `apps/backend/src/services/admin/admin-write-service.js`
+这条线和下单、兑换、任务状态强相关,越晚拆越难动。
-依然偏胖,但优先级略低于上面三项。
+### 3. `apps/backend/src/services/session/session.js`
-它更像“后台万能动作总线”,跨域过多:
+当前约 `566` 行,虽然比前两项小,但仍然是高风险运行时核心。
-- 库存
-- claim
-- webhook
-- cloudtentacles
-- 快手核销
-- 腾讯 session
+它同时承担:
-建议后续至少拆成:
+- Playwright 浏览器生命周期
+- 会话内存状态
+- 二维码抓取
+- 登录态判断
+- 角色信息同步
+- 兑换执行
+- 自动关闭
-- `admin-inventory-write`
-- `admin-task-actions`
-- `admin-webhook-actions`
-- `admin-manual-redeem-actions`
+好消息是:`session-proof.js`、`session-qq.js`、`session-wx.js`、`session-redeem.js`、`session-page.js`、`session-state.js` 等子模块已经存在,说明这条线并不是没拆过。
-### 6. `apps/backend/src/services/webhook/webhook-service.js`
+当前更合适的目标不是“从零拆分”,而是继续把 `session.js` 剩余的编排职责下沉,最终把它收敛成真正的 orchestrator。
-仍然适合改成流水线。
+### 4. 平台配置竖线的收口项
+
+虽然平台配置的大拆分已经完成,但这条线还有一些第二优先级问题:
+
+- `apps/backend/src/services/admin/platform-config/cloudtentacles-service.js`
+ - 当前约 `250` 行
+ - 已经比原来小很多,但仍同时承载登录、会话校验、商品目录、虚拟号调试、完整 flow 调试
+ - 如果后面 cloudtentacles 功能继续增加,可以进一步拆成:
+ - `cloudtentacles-auth-service`
+ - `cloudtentacles-catalog-service`
+ - `cloudtentacles-virtual-number-service`
+
+- `apps/frontend/src/views/admin/AdminPlatformShopsView.vue`
+ - 当前约 `402` 行
+ - 已经不是巨石模板问题,剩下主要是 overview 卡片和页面级加载编排还在父层
+
+- `apps/frontend/src/services/admin/platform-config.ts`
+ - 当前约 `373` 行
+ - 还可以进一步按平台拆成更细的 `services/admin/platform-config/` 子文件
+ - 但这属于可做项,不是最高优先级
+
+### 5. `apps/backend/src/services/webhook/webhook-service.js`
+
+这条线仍然适合改成流水线。
建议拆成:
@@ -217,7 +227,9 @@
- `webhook-process`
- `webhook-replay`
-### 7. `apps/backend/src/config/runtime.js`
+它不是眼下最大的文件,但属于典型“流程很多、分支很多、回归成本高”的区域。
+
+### 6. `apps/backend/src/config/runtime.js`
仍需要“配置声明化”优化。
@@ -235,25 +247,26 @@
## 质量防线现状
-这个判断仍然成立,而且重要性上升了。
+这个判断仍然成立,而且现在重要性更高。
当前风险:
- 后端高风险模块仍然存在 `@ts-nocheck`
-- 前端后台配置页最近已经连续多次重构
-- 目前主要依靠 `typecheck + build` 防回归
+- 平台配置页和后台平台配置服务最近连续经历了多轮重构
+- 当前主要还是依赖 `typecheck + test + build` 防回归
这能挡住:
- 类型问题
-- 组件引用问题
-- 构建问题
+- 引用错误
+- 明显的构建错误
+- 一部分纯函数退化
-但挡不住:
+但仍挡不住:
-- 交互退化
-- 条件渲染错误
-- 任务状态流转异常
+- 页面条件渲染退化
+- 管理台复杂交互异常
+- 任务状态流转串线
- 外部平台调试链路断裂
建议至少补两层最小防线:
@@ -263,8 +276,8 @@
- 关键按钮存在性 / 条件渲染测试
2. 后端
- - `session-proof.js` 纯函数单测
- - 快手 cloud / claim / task 状态流转的集成 smoke 测试
+ - `claim-session-service.js` 关键状态流转测试
+ - `admin-write-service.js` 高风险动作 smoke 测试
## 暂时不用急着拆
@@ -272,9 +285,9 @@
- `TencentBrowserView.vue`
- `session-qq.js`
-- 对应 `wx` 变体
+- `session-wx.js`
-这些区域当前模式反而比较健康,后续可以作为其它模块拆分时的参照。
+这些区域当前模式相对健康,仍然可以作为其它模块拆分时的参考。
## 下一步计划
@@ -285,55 +298,58 @@
3. 抽离平台配置页共享样式
4. 拆分 Agiso / khhao / 快手核销 / cloudtentacles 四个平台区块
5. 抽离结果信息卡片
+6. 拆分 `session-proof.js`
+7. 重构后台 `platform-config` 目录并完成按平台拆分
-### 下一阶段
+### 下一阶段建议
-#### Phase 1:从 `session-proof.js` 开始
+#### Phase 1:先处理 `admin-write-service.js`
目标:
-- 先拆一个比 `session.js` 更小、更收敛的后端高风险文件
-- 建立后端拆分模板
-- 不改公开接口,不改产物格式,不改调用方
+- 优先拆掉当前最大的后台动作总线
+- 先按职责切片,不改路由公开接口
+- 把高频变更区域从一个文件拆成多个动作模块
建议步骤:
-1. 抽纯函数
- - proof mode
- - proof path
- - result writer
+1. 先按动作域分段
+ - inventory
+ - task
+ - claim
+ - webhook
+ - redeem
-2. 抽副作用模块
- - 北京时间截图采集
- - 证明图合成
+2. 把纯组装和副作用编排分开
-3. 保留门面文件
- - `session-proof.js` 暂时继续导出原接口
+3. 保留 `admin-write-service.js` 作为门面导出层
-4. 每个小阶段都跑校验并单独提交
-
-#### Phase 2:再处理 `session.js`
+#### Phase 2:再处理 `claim-session-service.js`
前提:
-- `session-proof.js` 拆分完成
-- 对这条运行时链路已经熟悉
+- `admin-write-service.js` 的动作边界更清楚
+- claim / task / inventory 之间的依赖关系已经更容易梳理
-#### Phase 3:回到平台配置后端竖线
+#### Phase 3:继续收口 `session.js`
-重点转向:
+重点:
-- `admin-platform-config-service.js`
-- 相关 route
-- 相关调试接口
+- 不是重写
+- 而是继续把剩余运行时编排下沉到已有 session 子模块
+
+#### Phase 4:补平台配置页与后台动作的最小 smoke 测试
+
+这一步不一定最先做,但应该尽早插入,避免后续继续重构时缺乏防线。
## 当前建议
-前端平台配置页这条线可以暂时收口,不建议再继续大规模抽模板。
+平台配置这条前后端竖线现在可以先收口,不建议继续把它当主战场。
-下一步最合适的是:
+更合适的下一步是:
-1. 开始拆 `apps/backend/src/services/session/session-proof.js`
-2. 同时补一层最小测试防线
+1. 开始拆 `apps/backend/src/services/admin/admin-write-service.js`
+2. 同步梳理 `claim-session-service.js` 的职责边界
+3. 尽快补一层最小 smoke 测试
-如果继续执行,下一刀就从 `session-proof.js` 开始。
+如果继续执行,下一刀最值得从 `admin-write-service.js` 开始。