@seasonkoh/webaz 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/LICENSE +48 -0
  2. package/README.md +156 -20
  3. package/dist/layer0-foundation/L0-1-database/schema.js +5 -4
  4. package/dist/layer0-foundation/L0-2-state-machine/engine.js +228 -7
  5. package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +156 -0
  6. package/dist/layer0-foundation/L0-2-state-machine/transitions.js +53 -12
  7. package/dist/layer0-foundation/L0-5-manifest/manifest.js +14 -1
  8. package/dist/layer1-agent/L1-1-mcp-server/auth.js +1 -1
  9. package/dist/layer1-agent/L1-1-mcp-server/server.js +3691 -714
  10. package/dist/layer1-agent/L1-2-external-anchor/anchor-engine.js +324 -0
  11. package/dist/layer1-agent/L1-2-identity/agent-passport.js +100 -0
  12. package/dist/layer2-business/L2-6-notifications/notification-engine.js +72 -5
  13. package/dist/layer2-business/L2-7-snf/snf-engine.js +287 -0
  14. package/dist/layer2-business/L2-anchor-registry/anchor-registry.js +396 -0
  15. package/dist/layer2-business/L2-notes/note-photo-storage.js +133 -0
  16. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +6 -6
  17. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +246 -0
  18. package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +95 -1
  19. package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +31 -2
  20. package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +358 -0
  21. package/dist/pwa/public/app.js +31947 -0
  22. package/dist/pwa/public/i18n.js +5751 -0
  23. package/dist/pwa/public/icon.svg +11 -0
  24. package/dist/pwa/public/index.html +21 -0
  25. package/dist/pwa/public/manifest.json +48 -0
  26. package/dist/pwa/public/openapi.json +5946 -0
  27. package/dist/pwa/public/style.css +535 -0
  28. package/dist/pwa/public/sw.js +63 -0
  29. package/dist/pwa/public/vendor/jsQR.js +10102 -0
  30. package/dist/pwa/public/webaz-logo.png +0 -0
  31. package/dist/pwa/routes/account-deletion.js +53 -0
  32. package/dist/pwa/routes/addresses.js +105 -0
  33. package/dist/pwa/routes/admin-admins.js +151 -0
  34. package/dist/pwa/routes/admin-analytics.js +253 -0
  35. package/dist/pwa/routes/admin-atomic.js +21 -0
  36. package/dist/pwa/routes/admin-catalog.js +64 -0
  37. package/dist/pwa/routes/admin-editor-picks.js +45 -0
  38. package/dist/pwa/routes/admin-events.js +60 -0
  39. package/dist/pwa/routes/admin-health.js +66 -0
  40. package/dist/pwa/routes/admin-moderation.js +120 -0
  41. package/dist/pwa/routes/admin-ops.js +179 -0
  42. package/dist/pwa/routes/admin-protocol-params.js +79 -0
  43. package/dist/pwa/routes/admin-reports.js +154 -0
  44. package/dist/pwa/routes/admin-tokenomics.js +113 -0
  45. package/dist/pwa/routes/admin-users-lifecycle.js +237 -0
  46. package/dist/pwa/routes/admin-users-query.js +390 -0
  47. package/dist/pwa/routes/admin-verifier-flow.js +126 -0
  48. package/dist/pwa/routes/admin-verifier-whitelist.js +111 -0
  49. package/dist/pwa/routes/admin-wallet-ops.js +66 -0
  50. package/dist/pwa/routes/agent-buy.js +215 -0
  51. package/dist/pwa/routes/agent-governance.js +341 -0
  52. package/dist/pwa/routes/agent-reputation.js +34 -0
  53. package/dist/pwa/routes/ai.js +101 -0
  54. package/dist/pwa/routes/analytics.js +272 -0
  55. package/dist/pwa/routes/anchors.js +169 -0
  56. package/dist/pwa/routes/announcements.js +110 -0
  57. package/dist/pwa/routes/arbitrator.js +117 -0
  58. package/dist/pwa/routes/auction.js +436 -0
  59. package/dist/pwa/routes/auth-login.js +40 -0
  60. package/dist/pwa/routes/auth-read.js +66 -0
  61. package/dist/pwa/routes/auth-register.js +138 -0
  62. package/dist/pwa/routes/auth-sessions.js +62 -0
  63. package/dist/pwa/routes/blocklist.js +60 -0
  64. package/dist/pwa/routes/buyer-feeds.js +224 -0
  65. package/dist/pwa/routes/cart.js +155 -0
  66. package/dist/pwa/routes/charity.js +816 -0
  67. package/dist/pwa/routes/chat.js +318 -0
  68. package/dist/pwa/routes/checkin-tasks.js +122 -0
  69. package/dist/pwa/routes/checkout-helpers.js +85 -0
  70. package/dist/pwa/routes/claim-initiators.js +88 -0
  71. package/dist/pwa/routes/claim-verify.js +615 -0
  72. package/dist/pwa/routes/claim-voting.js +114 -0
  73. package/dist/pwa/routes/claim-withdrawals.js +20 -0
  74. package/dist/pwa/routes/coupons.js +165 -0
  75. package/dist/pwa/routes/dashboards.js +99 -0
  76. package/dist/pwa/routes/dispute-cases.js +267 -0
  77. package/dist/pwa/routes/disputes-read.js +358 -0
  78. package/dist/pwa/routes/disputes-write.js +475 -0
  79. package/dist/pwa/routes/evidence.js +86 -0
  80. package/dist/pwa/routes/external-anchors.js +107 -0
  81. package/dist/pwa/routes/feedback.js +270 -0
  82. package/dist/pwa/routes/flash-sales.js +130 -0
  83. package/dist/pwa/routes/follows.js +103 -0
  84. package/dist/pwa/routes/group-buys.js +208 -0
  85. package/dist/pwa/routes/growth.js +199 -0
  86. package/dist/pwa/routes/import-product.js +153 -0
  87. package/dist/pwa/routes/kyc.js +40 -0
  88. package/dist/pwa/routes/leaderboard.js +149 -0
  89. package/dist/pwa/routes/listings.js +281 -0
  90. package/dist/pwa/routes/logistics.js +35 -0
  91. package/dist/pwa/routes/manifests.js +126 -0
  92. package/dist/pwa/routes/me-data.js +101 -0
  93. package/dist/pwa/routes/notifications.js +48 -0
  94. package/dist/pwa/routes/offers.js +96 -0
  95. package/dist/pwa/routes/orders-action.js +285 -0
  96. package/dist/pwa/routes/orders-create.js +339 -0
  97. package/dist/pwa/routes/orders-read.js +180 -0
  98. package/dist/pwa/routes/p2p-products.js +178 -0
  99. package/dist/pwa/routes/payments-governance.js +311 -0
  100. package/dist/pwa/routes/peers.js +34 -0
  101. package/dist/pwa/routes/pin-receipts.js +39 -0
  102. package/dist/pwa/routes/products-aliases.js +119 -0
  103. package/dist/pwa/routes/products-claims.js +60 -0
  104. package/dist/pwa/routes/products-create.js +206 -0
  105. package/dist/pwa/routes/products-crud.js +73 -0
  106. package/dist/pwa/routes/products-links.js +129 -0
  107. package/dist/pwa/routes/products-list.js +424 -0
  108. package/dist/pwa/routes/products-meta.js +155 -0
  109. package/dist/pwa/routes/products-update.js +125 -0
  110. package/dist/pwa/routes/profile-credentials.js +105 -0
  111. package/dist/pwa/routes/profile-identity.js +174 -0
  112. package/dist/pwa/routes/profile-location.js +35 -0
  113. package/dist/pwa/routes/profile-placement.js +70 -0
  114. package/dist/pwa/routes/profile-prefs.js +93 -0
  115. package/dist/pwa/routes/promoter.js +208 -0
  116. package/dist/pwa/routes/public-utils.js +170 -0
  117. package/dist/pwa/routes/push.js +54 -0
  118. package/dist/pwa/routes/ratings.js +220 -0
  119. package/dist/pwa/routes/recover-key.js +100 -0
  120. package/dist/pwa/routes/referral.js +58 -0
  121. package/dist/pwa/routes/reputation.js +34 -0
  122. package/dist/pwa/routes/returns.js +493 -0
  123. package/dist/pwa/routes/reviews.js +81 -0
  124. package/dist/pwa/routes/rfqs.js +443 -0
  125. package/dist/pwa/routes/search.js +172 -0
  126. package/dist/pwa/routes/secondhand.js +278 -0
  127. package/dist/pwa/routes/seller-quota.js +225 -0
  128. package/dist/pwa/routes/share-redirects.js +164 -0
  129. package/dist/pwa/routes/shareables-interactions.js +212 -0
  130. package/dist/pwa/routes/shareables.js +470 -0
  131. package/dist/pwa/routes/shops.js +98 -0
  132. package/dist/pwa/routes/signaling.js +43 -0
  133. package/dist/pwa/routes/skill-market.js +173 -0
  134. package/dist/pwa/routes/skills.js +174 -0
  135. package/dist/pwa/routes/snf.js +126 -0
  136. package/dist/pwa/routes/tags.js +47 -0
  137. package/dist/pwa/routes/trial.js +333 -0
  138. package/dist/pwa/routes/trusted-kpi.js +87 -0
  139. package/dist/pwa/routes/url-claim.js +113 -0
  140. package/dist/pwa/routes/users-public.js +317 -0
  141. package/dist/pwa/routes/variants.js +156 -0
  142. package/dist/pwa/routes/verifier-user.js +107 -0
  143. package/dist/pwa/routes/verify-tasks.js +120 -0
  144. package/dist/pwa/routes/waitlist.js +65 -0
  145. package/dist/pwa/routes/wallet-read.js +218 -0
  146. package/dist/pwa/routes/wallet-write.js +273 -0
  147. package/dist/pwa/routes/webauthn.js +188 -0
  148. package/dist/pwa/routes/webhooks.js +162 -0
  149. package/dist/pwa/routes/welcome.js +226 -0
  150. package/dist/pwa/routes/wishlist-qa.js +135 -0
  151. package/dist/pwa/security/ssrf.js +110 -0
  152. package/dist/pwa/server.js +9679 -698
  153. package/package.json +11 -4
package/LICENSE ADDED
@@ -0,0 +1,48 @@
1
+ Business Source License 1.1
2
+
3
+
4
+ Parameters
5
+
6
+ Licensor: seasonsagents-art
7
+ Licensed Work: WebAZ
8
+ Copyright © 2026 seasonsagents-art and contributors
9
+ Additional Use Grant: You may make production use of the Licensed Work,
10
+ provided that your use does not include offering
11
+ the Licensed Work to third parties as a hosted or
12
+ managed service that is substantially similar to,
13
+ or competes with, the WebAZ commerce protocol
14
+ service operated by the Licensor.
15
+ Change Date: 2030-05-18
16
+ Change License: MIT
17
+
18
+ For information about alternative licensing arrangements for the
19
+ Licensed Work, please contact seasons.agents@gmail.com.
20
+ License text copyright © 2024 MariaDB plc, All Rights Reserved. “Business Source License” is a trademark of MariaDB plc.
21
+
22
+ Terms
23
+ The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute, and make non-production use of the Licensed Work. The Licensor may make an Additional Use Grant, above, permitting limited production use.
24
+
25
+ Effective on the Change Date, or the fourth anniversary of the first publicly available distribution of a specific version of the Licensed Work under this License, whichever comes first, the Licensor hereby grants you rights under the terms of the Change License, and the rights granted in the paragraph above terminate.
26
+
27
+ If your use of the Licensed Work does not comply with the requirements currently in effect as described in this License, you must purchase a commercial license from the Licensor, its affiliated entities, or authorized resellers, or you must refrain from using the Licensed Work.
28
+
29
+ All copies of the original and modified Licensed Work, and derivative works of the Licensed Work, are subject to this License. This License applies separately for each version of the Licensed Work and the Change Date may vary for each version of the Licensed Work released by Licensor.
30
+
31
+ You must conspicuously display this License on each original or modified copy of the Licensed Work. If you receive the Licensed Work in original or modified form from a third party, the terms and conditions set forth in this License apply to your use of that work.
32
+
33
+ Any use of the Licensed Work in violation of this License will automatically terminate your rights under this License for the current and all other versions of the Licensed Work.
34
+
35
+ This License does not grant you any right in any trademark or logo of Licensor or its affiliates (provided that you may use a trademark or logo of Licensor as expressly required by this License).TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE. MariaDB hereby grants you permission to use this License’s text to license your works, and to refer to it using the trademark “Business Source License”, as long as you comply with the Covenants of Licensor below.
36
+
37
+ Covenants of Licensor
38
+ In consideration of the right to use this License’s text and the “Business Source License” name and trademark, Licensor covenants to MariaDB, and to all other recipients of the licensed work to be provided by Licensor:
39
+
40
+ To specify as the Change License the GPL Version 2.0 or any later version, or a license that is compatible with GPL Version 2.0 or a later version, where “compatible” means that software provided under the Change License can be included in a program with software provided under GPL Version 2.0 or a later version. Licensor may specify additional Change Licenses without limitation.
41
+ To either: (a) specify an additional grant of rights to use that does not impose any additional restriction on the right granted in this License, as the Additional Use Grant; or (b) insert the text “None” to specify a Change Date. Not to modify this License in any other way.
42
+
43
+ Notice
44
+ The Business Source License (this document, or the “License”) is not an Open Source license. However, the Licensed Work will eventually be made available under an Open Source License, as stated in this License.
45
+
46
+ For more information on the use of the Business Source License for MariaDB products, please visit the MariaDB Business Source License FAQ. For more information on the use of the Business Source License generally, please visit the Adopting and Developing Business Source License FAQ.
47
+
48
+ Projects using BSL
package/README.md CHANGED
@@ -1,23 +1,86 @@
1
+ > **Code is Rule, Protocol is Trust.**
2
+ > **代码即规则,协议即信任。**
3
+ > — webaz
4
+
1
5
  # WebAZ
2
6
 
7
+ [![npm](https://img.shields.io/npm/v/@seasonkoh/webaz.svg)](https://www.npmjs.com/package/@seasonkoh/webaz)
8
+ [![MCP Registry](https://img.shields.io/badge/MCP%20Registry-active-blue)](https://registry.modelcontextprotocol.io/v0/servers?search=webaz)
9
+ [![License: BUSL-1.1](https://img.shields.io/badge/License-BUSL--1.1-orange.svg)](LICENSE) [![Change Date: 2030-05-18](https://img.shields.io/badge/Change%20Date-2030--05--18-blue.svg)](NOTICE)
10
+
3
11
  让 AI Agent 成为去中心化商业协议的原生参与者。卖家零额外工作量接入新渠道,买家通过 Agent 自动购物,人类与 AI 在同一协议上平等参与。
4
12
 
13
+ > ⭐ **运营哲学与远景** → [`docs/VISION-AND-OPERATIONS.md`](docs/VISION-AND-OPERATIONS.md)
14
+ > COP(参与即建设,贡献即收益)· 团队"消失"在协议里 · 最终全 DAO
15
+
16
+ > **试一下**:`npx -y @seasonkoh/webaz`,或在 Claude Desktop 加入 MCP 配置(见下)。
17
+ > **PWA 演示**:[webaz.xyz](https://webaz.xyz)
18
+
19
+ ---
20
+
21
+ ## 📜 授权说明
22
+
23
+ 自 **2026-05-18** 起,本项目使用 **Business Source License 1.1 (BUSL-1.1)** 协议发布。
24
+
25
+ - ✅ 允许:内部使用、研究、二次开发、非竞争性商业使用
26
+ - ❌ 禁止:运营与 WebAZ 实质相似的去中心化商业协议托管服务
27
+ - ⏰ **2030-05-18** 之后,本协议自动转为 **MIT** 协议
28
+ - 📦 **2026-05-18 之前**发布的所有版本(含历史 git 提交)仍持有 **MIT** 授权,不可撤销
29
+
30
+ 详见 [`LICENSE`](LICENSE) 与 [`NOTICE`](NOTICE)。
31
+
5
32
  ---
6
33
 
7
34
  ## 核心特性
8
35
 
9
- - **Agent 原生**:通过 MCP 协议让 Claude 直接搜索商品、下单、确认收货
10
- - **人类 + Agent 双轨**:PWA 供人类操作,MCP Agent 调用,共用同一后端
11
- - **自动执法**:每笔交易有明确责任方,超时未履行自动判责,无需人工干预
12
- - **争议系统**:买卖双方举证,仲裁员裁定,败诉方缴纳 1% 仲裁费
13
- - **声誉体系**:5 级制(新手→传奇),影响质押折扣和搜索排名
14
- - **Skill 市场**:卖家发布 auto_accept / catalog_sync 等能力插件,Agent 订阅后自动享用
15
- - **货币单位**:WAZPhase 0 为模拟代币,Phase 2 接入链上稳定币)
36
+ ### 协议骨架
37
+ - **Agent 原生**:MCP 协议下,Claude 直接搜索、锁价、下单、举证、确认
38
+ - **人类 + Agent 双轨**:PWA 给人类,MCP 给 Agent,共用同一后端
39
+ - **协议级精准镜像**:q= 严格 exact match,"茶" 不会命中"茶具套装";买家从外部平台复制的标识必须字面相等才返回结果(见 [intent-driven-buy](docs/modules/intent-driven-buy.md))
40
+ - **商品 alias 系统**:卖家声明 kouling_token / short_url / title_substring,让用户从外部平台的任何复制形式都能精准命中同一 SKU(见 [product-aliases](docs/modules/product-aliases.md))
41
+ - **角色感知 API**:`/api/products?mode=pwa|agent|raw` 三种调用方分级;agent 模式返回 score_breakdown,raw 模式 HMAC 签名(见 [products-api](docs/modules/products-api.md))
42
+ - **Agent Reputation 体系**:每个 api_key 独立 trust_score4 layer band),raw mode 需 trust ≥ 30(见 [agent-reputation](docs/modules/agent-reputation.md))
43
+
44
+ ### 多元交易场景
45
+ - **🆕 多商家跟卖 (P1)**:listings 1:N offers,4 类目 stake 体系(standard 1.0× / general 1.5× / highvalue 2.0× / restricted 3.0× × base 50 WAZ)
46
+ - **🆕 加权排序 + urgency 路由 (P2)**:trending score 含完成度/信誉/分享/点赞/新鲜度/季节性;urgency 三档 (now/today/flex) 路由不同 ETA 硬限
47
+ - **🆕 RFQ 求购抢单 (P3)**:买家发求购,卖家限时报价;first_match 即时触发 / 提前结算 / 自然语言预填 / auto_bid Skill(见 [rfq-auction-chat](docs/modules/rfq-auction-chat.md))
48
+ - **🆕 加价拍卖 AUC**:English forward auction;反狙击 sniper_extend_min + max_extends cap;卖家担保金 5%
49
+ - **🆕 P2P 原生商店**:无外链商品的去中心化路径;卖家本地节点存详情,WebAZ 锚定 hash + HMAC 签名 + 客户端 sha256 校验
50
+ - **🆕 上下文绑定聊天**:order / RFQ / listing_qa 三类 chat;反诈正则检测微信号/电话/银行卡
51
+
52
+ ### 信任与合规
53
+ - **claim 验证 + 条件订单**:买家可对推荐理由发起验证,3 verifier 共识仲裁,4 路径结算(pass / fail / no_fault / timeout)+ outlier 处罚(见 [claim-verification](docs/modules/claim-verification.md))
54
+ - **地区合规分润**:china/us/eu/india = 2 levels;其他 3 levels;UI 按地区动态显示
55
+ - **零门槛上架**:卖家上架免质押,首单成交时从 escrow 自动锁 15% stake(trusted 卖家跳过)
56
+ - **争议系统**:双方举证 → 仲裁员裁定 → 败诉方缴 1% 仲裁费(含超时自动判)
57
+ - **链接认领验证**:卖家关联外部链接需通过众包验证码核验,防止他人冒用商品主权
58
+
59
+ ### 决策辅助
60
+ - **🆕 价格历史 + 成交量分布**:3 窗口(30d/90d/lifetime) × 6 统计;价位分布 / sparkline / category_avg / anomaly_flags;防底价倾销
61
+ - **结构化商品 + agent_summary**:规格 / 物流 / 售后字段拼成一句话决策摘要
62
+ - **下单前价格锁定**:`webaz_verify_price` 返回 `session_token`,避免决策与下单之间价格漂移
63
+
64
+ ### 社会化 / 公益
65
+ - **🆕 慈善许愿池**:双匿名(HMAC 派生 handle)+ 双签锚定(commit_hash)+ 反自圆梦封锁;许愿/圆梦/还愿/转捐 4 套荣誉细分(见 [charity](docs/modules/charity.md))
66
+ - **🆕 慈善基金**:还愿被谢绝自动转入;任何人可捐款(≤500 WAZ/愿,1 WAZ = 1 honor 上限 50/日);公开账本 + 慈善家排行
67
+ - **🆕 点赞 + 排行榜**:shareable_likes 防 Sybil;商品榜 (sales+推荐+点赞) + 创作者榜
68
+ - **Skill 市场**:catalog_sync / auto_accept / instant_ship / auto_bid 等插件,Agent 订阅自动享用
69
+ - **声誉 5 级**(新手 → 传奇):影响质押折扣 + 搜索排名
70
+
71
+ ### 链上 / 安全
72
+ - **链上托管**:USDC on Base Sepolia,充值地址按用户派生,自动扫归集 + 自动执行提现
73
+ - **WebAuthn / Passkey** 大额操作闸门
74
+ - **storage.persist()** 申请 + 持久化状态可视
75
+ - **自动执法**:状态机责任归因,超时自动判责(每 5 分钟 cron 扫)
16
76
 
17
77
  ---
18
78
 
19
79
  ## 快速开始
20
80
 
81
+ > **自部署?** → 看 [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md)(30 分钟从零到上线 · systemd + Nginx + Let's Encrypt + 监控 + 备份完整 cookbook)
82
+ > **环境变量?** → 复制 [`.env.example`](.env.example) 为 `.env` 填值
83
+
21
84
  ### 方式一:Claude MCP 接入(Agent 原生体验)
22
85
 
23
86
  **1. 添加到 Claude Desktop 配置**
@@ -66,15 +129,28 @@ npm run pwa
66
129
  |------|------|----------|
67
130
  | `webaz_info` | 获取协议概览和实时统计 | — |
68
131
  | `webaz_register` | 注册账号,获取 api_key | `name`, `role` |
69
- | `webaz_search` | 搜索商品(按声誉权重排序) | `query`, `category`, `max_price` |
70
- | `webaz_list_product` | 卖家上架商品 | `title`, `price`, `stock`, `api_key` |
71
- | `webaz_place_order` | 买家下单 | `product_id`, `shipping_address`, `api_key` |
132
+ | `webaz_search` | 搜索商品(结构化字段 + agent_summary + 声誉加权) | `query`, `category`, `max_price`, `min_return_days`, `max_handling_hours` |
133
+ | `webaz_verify_price` | 下单前锁价 10 分钟,拿到 `session_token` | `product_id`, `quantity`, `api_key` |
134
+ | `webaz_list_product` | 卖家上架商品(含品牌/规格/物流/售后字段) | `title`, `price`, `specs`, `handling_hours`, `return_days`, `api_key` |
135
+ | `webaz_place_order` | 买家下单(可传 `session_token` 防价格漂移) | `product_id`, `shipping_address`, `session_token`, `api_key` |
72
136
  | `webaz_update_order` | 更新订单状态(接单/发货/确认/争议) | `order_id`, `action`, `api_key` |
73
137
  | `webaz_get_status` | 查询订单/钱包/争议详情 | `order_id` / `wallet` / `dispute_id`, `api_key` |
74
- | `webaz_wallet` | 查看钱包余额 | `api_key` |
138
+ | `webaz_wallet` | 钱包余额 + 链上充值地址 + 提现 | `action`, `api_key` |
75
139
  | `webaz_notifications` | 查询未读通知 | `api_key` |
76
140
  | `webaz_dispute` | 争议操作(查看/举证/裁定) | `action`, `api_key` |
77
- | `webaz_skill` | Skill 市场(发布/订阅) | `action`, `api_key` |
141
+ | `webaz_skill` | Skill 市场(发布/订阅;含 auto_bid) | `action`, `api_key` |
142
+ | `webaz_profile` | 个人资料 + 多角色管理 | `action`, `api_key` |
143
+ | `webaz_mykey` | api_key 恢复(已注册用户重新获取密钥) | `name`, `recovery_code` |
144
+ | `webaz_rfq` | 🆕 RFQ 求购(发布/浏览/认领/详情) | `action`, `api_key` |
145
+ | `webaz_bid` | 🆕 RFQ 报价(创建/修改/撤销/中标) | `action`, `rfq_id`, `api_key` |
146
+ | `webaz_chat` | 🆕 上下文绑定聊天(order/rfq/listing_qa) | `action`, `context_id`, `api_key` |
147
+ | `webaz_auto_bid` | 🆕 卖家自动报价 Skill 配置 | `action`, `api_key` |
148
+ | `webaz_auction` | 🆕 加价拍卖(发起/浏览/出价/中标) | `action`, `api_key` |
149
+ | `webaz_like` | 🆕 分享点赞(toggle/status) | `action`, `shareable_id`, `api_key` |
150
+ | `webaz_leaderboard` | 🆕 排行榜(products / creators) | `kind`, `limit` |
151
+ | `webaz_p2p_product` | 🆕 P2P 原生商店(卖家节点 hash 锚定) | `action`, `api_key` |
152
+ | `webaz_price_history` | 🆕 商品历史成交价 + 价位分布 | `product_id` |
153
+ | `webaz_charity` | 🆕 慈善许愿池 + 还愿 + 基金 + 捐款(11 sub-action) | `action`, `api_key` |
78
154
 
79
155
  完整协议规范(状态机/经济模型/争议规则)可通过 MCP Resource 读取:
80
156
 
@@ -127,7 +203,8 @@ webaz://protocol/manifest
127
203
  | 协议费 | 2% |
128
204
  | 推荐人(如有) | 3% |
129
205
 
130
- 卖家上架需质押 15% 作为保证金(声誉越高折扣越大,最低 5%)。
206
+ 卖家上架**零质押**(M7.2.6 起)。首单成交时自动从订单 escrow 锁定 15% 作买家保护,
207
+ 信誉 ≥ trusted 的卖家直接跳过 stake 锁定(信任奖励)。
131
208
 
132
209
  ---
133
210
 
@@ -159,23 +236,82 @@ npm run test-manifest# 测试协议 Manifest
159
236
 
160
237
  ## 当前阶段
161
238
 
162
- **Phase 0 · 概念验证** ✅ 完成
163
- **Phase 1 · 功能完善** 完成
239
+ **Phase 0 · 概念验证** ✅
240
+ **Phase 1 · 功能完善 + 链上 testnet 闭环**
241
+ - 14 个 MCP 工具 / 全角色 PWA / 通知 / 争议 / 声誉 / Skill 市场 / Manifest
242
+ - USDC on Base Sepolia:派生充值地址、自动监听入账、热钱包扫归集、自动执行提现
243
+ - 链接认领验证(众包验证码 + 主权流转)
244
+ - Agent 决策三件套(结构化字段 / agent_summary / verify_price 锁价)
245
+ - MCP 工具调用遥测(默认开,可关)
164
246
 
165
- Phase 2 将把核心资金和状态上链,实现真正的去中心化。
247
+ **Phase 2 · 主网 + 真正去中心化**(下一步)
166
248
 
167
249
  ---
168
250
 
169
251
  ## 路线图
170
252
 
253
+ ### Phase 1 完成项
171
254
  - [x] 状态机 + 责任归因引擎
172
- - [x] MCP Server(11 个工具)
255
+ - [x] MCP Server(14+ 个工具)
173
256
  - [x] 通知系统(SSE 实时推送)
174
257
  - [x] 争议系统(举证 + 超时自动裁定 + 仲裁费)
175
- - [x] 声誉积分体系
258
+ - [x] 声誉积分体系(5 级)
176
259
  - [x] Skill 市场
177
260
  - [x] Protocol Manifest(机器可读协议规范)
178
261
  - [x] PWA 前端(全角色覆盖,人类 + Agent 双轨)
179
- - [ ] 链上集成(Base/Optimism)
180
- - [ ] IPFS 证据存储
262
+ - [x] 智能商品导入(贴链接自动提取)
263
+ - [x] 链接认领验证(卖家主权 + 众包核验)
264
+ - [x] 链上 USDC testnet 闭环(Base Sepolia)
265
+ - [x] 下单前价格锁定(verify_price + session_token)
266
+ - [x] 结构化商品规格 + agent_summary 决策摘要
267
+ - [x] 遥测看板(/api/admin/usage)
268
+
269
+ ### M1-M7 协议级里程碑(2026-05 完成)
270
+ - [x] **M1** 角色感知 API(mode=pwa/agent/raw)+ cursor 分页
271
+ - [x] **M2** 基础排序公平(jitter / seller cap / 新人 slot)
272
+ - [x] **M3** 反操纵层(click 去重 / 双轨同支审计 / 上架限速 / 注册 IP hash)
273
+ - [x] **M4** Agent Reputation 体系(trust_score + raw mode 门槛 + HMAC 签名)
274
+ - [x] **M5** 新人保护 + 阶梯新鲜度 + sort UI
275
+ - [x] **M6** 商品类型 + 库存稀缺 + 季节性 lifecycle
276
+ - [x] **M7.1** 智能下单 intent-driven UI 重做
277
+ - [x] **M7.1.5** q= exact match 协议契约
278
+ - [x] **M7.2** 商品 alias 系统(kouling_token / title_substring / short_url)
279
+ - [x] **M7.2.5/6/7** 卖家 funnel + 地区合规(2/3 级分润)+ L3 区域感知
280
+ - [x] **代码审计** H-1 / H-2 / M-1 / M-2 / M-5 / 3f254b8 完整批次
281
+ - [x] **M7.3** claim 验证任务系统(4 路径结算 + outlier 处罚)
282
+ - [x] **M7.4** 条件订单(has_pending_claim 暂缓自动判责)
283
+
284
+ ### P1-P3 多元交易场景(2026-05-18+)
285
+ - [x] **P1** 多商家跟卖(listings + 4 类目 stake)
286
+ - [x] **P2** 加权排序 + urgency 路由 + cold-start 衰减
287
+ - [x] **P3 RFQ** 求购抢单 + bid_stake + first_match + auto_bid Skill + 买家 NLP Agent
288
+ - [x] **CHAT** 上下文绑定聊天 + 反诈正则
289
+ - [x] **AUC** 加价拍卖(English forward + 反狙击)
290
+ - [x] **LIKE + Leaderboard** 分享点赞 + 商品/创作者双榜
291
+ - [x] **P2P 原生商店** 卖家节点 hash 锚定 + HMAC 签名 + 客户端 sha256
292
+ - [x] **Price History** 历史成交价 + 价位分布 + 异常预警
293
+
294
+ ### 慈善 + 社会化(2026-05-18 完成)
295
+ - [x] **慈善许愿池** 双匿名 + 双签锚定 + 4 张表 + 6 前端页 + 反自圆梦封锁
296
+ - [x] **还愿系统** 三态响应(accept / decline_to_fund / 7 天 auto-accept)
297
+ - [x] **慈善基金** 单例池 + 任何人捐款 + 慈善家排行 + 公开账本
298
+ - [x] **5 项荣誉细分**(repay/redirect/grace/donation_total/donation_honor)
299
+ - [x] **16 项审计修复**(4 处竞态 + 6 项业务/隐私 + 6 项工程)
300
+
301
+ ### UX 重构(2026-05-19 完成)
302
+ - [x] **导航重组** 角色化 tab bar(买家 5 / 卖家 5)
303
+ - [x] **#me 私人 hub** 三段式(通用 6 / 角色专区 / 账户)
304
+ - [x] **#discover 商务横条** 6 板块快捷入口(拍卖/求购/跟卖/P2P/排行/慈善)
305
+ - [x] **i18n 双语全覆盖** 2700+ 条目;服务端按 Accept-Language 返回
306
+
307
+ ### 进行中 / 下一步
308
+ - [ ] **跨模块审计收尾**(alias 限额 TOCTOU / settleOrder 原子化 / 杂项硬化)
309
+ - [ ] **慈善基金 v2**:自动拨款机制(紧急医疗/教育大额匹配)+ 捐物(specific event 定向)
310
+ - [ ] **导航 v2**:4 角色(logistics/verifier/arbitrator/admin)也加 #me 入口完整化
311
+
312
+ ### 长期
313
+ - [ ] 链上 USDC 主网(Base)
314
+ - [ ] IPFS 证据存储 + Pin Network(已搭骨架,留 P2)
315
+ - [ ] 评价系统(结构化 1-5 星,反哺声誉)
316
+ - [ ] 证据上传通道(争议附图)
181
317
  - [ ] 治理 DAO
@@ -6,6 +6,7 @@ import Database from 'better-sqlite3';
6
6
  import path from 'path';
7
7
  import os from 'os';
8
8
  import fs from 'fs';
9
+ import { randomBytes } from 'crypto';
9
10
  const DATA_DIR = path.join(os.homedir(), '.webaz');
10
11
  if (!fs.existsSync(DATA_DIR))
11
12
  fs.mkdirSync(DATA_DIR, { recursive: true });
@@ -181,9 +182,9 @@ export function initDatabase() {
181
182
  console.error('✅ L0-1 数据库初始化完成:', DB_PATH);
182
183
  return db;
183
184
  }
184
- // 生成唯一 ID 的工具函数
185
+ // 生成唯一 ID 的工具函数 — crypto-safe,128 bit 熵
186
+ // Why: 旧版用 Date.now() + Math.random() 4 字符,api_key 仅 4 字符随机 → 36⁴≈168 万种秒破;
187
+ // 且 api_key / user_id 共享时间戳前缀泄漏关联(QA agent 抓到)。改用 crypto.randomBytes 后两个 ID 互不相关。
185
188
  export function generateId(prefix) {
186
- const timestamp = Date.now().toString(36);
187
- const random = Math.random().toString(36).slice(2, 6);
188
- return `${prefix}_${timestamp}${random}`;
189
+ return `${prefix}_${randomBytes(16).toString('hex')}`;
189
190
  }
@@ -7,7 +7,8 @@
7
7
  * 3. getStatus() — 查询订单当前状态和责任方
8
8
  */
9
9
  import { generateId } from '../L0-1-database/schema.js';
10
- import { VALID_TRANSITIONS, CURRENT_RESPONSIBLE } from './transitions.js';
10
+ import { appendOrderEvent } from './order-chain.js';
11
+ import { VALID_TRANSITIONS, CURRENT_RESPONSIBLE, CURRENT_RESPONSIBLE_SELF_FULFILL } from './transitions.js';
11
12
  // ─── 核心函数 ────────────────────────────────────────────────
12
13
  /**
13
14
  * 执行状态转移
@@ -60,12 +61,28 @@ export function transition(db, orderId, toStatus, actorId, evidenceIds = [], not
60
61
  SET status = ?, updated_at = datetime('now')
61
62
  WHERE id = ?
62
63
  `).run(toStatus, orderId);
63
- // 记录状态历史(自举证的核心)
64
+ // 记录状态历史(人类可读层)
64
65
  db.prepare(`
65
66
  INSERT INTO order_state_history
66
67
  (id, order_id, from_status, to_status, actor_id, actor_role, evidence_ids, notes)
67
68
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
68
69
  `).run(historyId, orderId, fromStatus, toStatus, actorId, actor.role, JSON.stringify(evidenceIds), notes);
70
+ // 协议层:append-only 签名链事件(防篡改 + 防伪造)
71
+ try {
72
+ appendOrderEvent(db, {
73
+ orderId,
74
+ eventType: 'transition',
75
+ fromStatus,
76
+ toStatus,
77
+ actorId,
78
+ actorRole: actor.role,
79
+ extra: { evidence_ids: evidenceIds, notes: notes || null, history_id: historyId },
80
+ });
81
+ }
82
+ catch (e) {
83
+ // 签名链失败不应阻塞主流程(旧数据 / 老 actor 缺 api_key 等场景)
84
+ console.warn('[order-chain] appendEvent failed:', e.message);
85
+ }
69
86
  });
70
87
  execute();
71
88
  return { success: true, newStatus: toStatus, historyId };
@@ -78,9 +95,11 @@ export function checkTimeouts(db) {
78
95
  const now = new Date().toISOString();
79
96
  const details = [];
80
97
  // 找出所有进行中的订单
98
+ // M7.4:跳过 claim 验证进行中的订单(has_pending_claim=1)— auto-confirm / 判责暂缓
81
99
  const activeOrders = db.prepare(`
82
100
  SELECT * FROM orders
83
101
  WHERE status NOT IN ('completed', 'cancelled', 'fault_buyer', 'fault_seller', 'fault_logistics')
102
+ AND COALESCE(has_pending_claim, 0) = 0
84
103
  `).all();
85
104
  for (const order of activeOrders) {
86
105
  const transitionKey = findActiveDeadlineTransition(order, now);
@@ -95,10 +114,16 @@ export function checkTimeouts(db) {
95
114
  orderId: order.id,
96
115
  action: `${order.status} → ${autoFaultState}(超时自动判责)`
97
116
  });
98
- // 如果判责状态可以自动完成,继续执行
117
+ // 如果判责状态可以自动完成,继续执行 — 资金处置 + 状态转移在一个事务内
99
118
  const completionKey = `${autoFaultState}→completed`;
100
119
  if (VALID_TRANSITIONS[completionKey]) {
101
- transition(db, order.id, 'completed', systemUser.id, [], '系统自动执行处置');
120
+ try {
121
+ settleFault(db, order.id, autoFaultState);
122
+ transition(db, order.id, 'completed', systemUser.id, [], '系统自动执行处置');
123
+ }
124
+ catch (e) {
125
+ console.error(`[settleFault] order=${order.id} state=${autoFaultState}`, e);
126
+ }
102
127
  }
103
128
  }
104
129
  }
@@ -118,8 +143,11 @@ export function getOrderStatus(db, orderId) {
118
143
  WHERE h.order_id = ?
119
144
  ORDER BY h.created_at ASC
120
145
  `).all(orderId);
121
- const currentResponsible = CURRENT_RESPONSIBLE[order.status] ?? null;
122
- const activeDeadline = getActiveDeadline(order);
146
+ // QA 7 P1:self-fulfill 时 logistics_id 为 null,应该按 seller 表示责任方
147
+ const isSelfFulfill = !order.logistics_id;
148
+ const responsibleTable = isSelfFulfill ? CURRENT_RESPONSIBLE_SELF_FULFILL : CURRENT_RESPONSIBLE;
149
+ const currentResponsible = responsibleTable[order.status] ?? null;
150
+ const activeDeadline = getActiveDeadline(order, db);
123
151
  return {
124
152
  order,
125
153
  history,
@@ -128,6 +156,180 @@ export function getOrderStatus(db, orderId) {
128
156
  isOverdue: activeDeadline ? new Date() > new Date(activeDeadline.deadline) : false
129
157
  };
130
158
  }
159
+ // ─── 超时判责资金处置 ────────────────────────────────────────
160
+ // fault_*→completed 转移时由 checkTimeouts 调用,确保 escrow / stake / 库存 全部正确清算
161
+ // 设计原则:
162
+ // - fault_seller → buyer 全额退款 + stake 50/50 扣罚(与 dispute refund_buyer 对等)
163
+ // - fault_logistics→ buyer 全额退款 + seller stake 全额返还(卖家无责,物流坏账协议吸收)
164
+ // - fault_buyer → 仅库存回退(escrow 未锁,无资金动作)
165
+ // 不发放 commission / PV / 基金池入金 — 无真实成交
166
+ function settleFault(db, orderId, faultState) {
167
+ const sysUserId = 'sys_protocol';
168
+ // 幂等检查 + 资金处置全部包在 transaction 内(防未来迁 PG 时并发 race)
169
+ db.transaction(() => {
170
+ const order = db.prepare('SELECT * FROM orders WHERE id = ?').get(orderId);
171
+ if (!order)
172
+ return;
173
+ if (order.settled_fault_at)
174
+ return; // 幂等:已处置过
175
+ const total = Number(order.total_amount);
176
+ const buyerId = order.buyer_id;
177
+ const sellerId = order.seller_id;
178
+ const isSecondhand = order.source === 'secondhand';
179
+ // QA 轮 7 P0 修复 — 改 per-order stake
180
+ // 旧逻辑读 product.stake_amount + stake_locked_at(per-product 模型残留,已废弃)
181
+ // 新逻辑:stake = order.total_amount × 0.15,下单时已锁,fault 时必然 lock(除非该订单未到 paid)
182
+ const sellerStakeRate = 0.15;
183
+ const stakeAmount = isSecondhand ? 0 : Math.round(total * sellerStakeRate * 100) / 100;
184
+ // 订单到 paid 之后 stake 必然 lock(place_order 已扣,余额不足会 reject)
185
+ const stakeLocked = !isSecondhand && Number(order.total_amount) > 0 && order.status !== 'created';
186
+ // P0.1:RFQ 路径的 bid_stake_held — fault 时由各分支按规则处理
187
+ const bidStakeHeld = Number(order.bid_stake_held || 0);
188
+ if (faultState === 'fault_seller') {
189
+ // 1. buyer escrow 全额退回
190
+ db.prepare('UPDATE wallets SET escrowed = escrowed - ?, balance = balance + ? WHERE user_id = ?')
191
+ .run(total, total, buyerId);
192
+ // P0.1:bid_stake_held 没收 50/50(中标后弃单的额外惩罚)
193
+ if (bidStakeHeld > 0) {
194
+ db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(bidStakeHeld, sellerId);
195
+ const compToBuyer = Math.round(bidStakeHeld * 0.5 * 100) / 100;
196
+ const compToSys = Math.round((bidStakeHeld - compToBuyer) * 100) / 100;
197
+ if (compToBuyer > 0)
198
+ db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compToBuyer, buyerId);
199
+ if (compToSys > 0)
200
+ db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compToSys, sysUserId);
201
+ }
202
+ // 2. stake 50/50 扣罚(严格资金守恒:实扣多少就分多少,不允许协议印钱)
203
+ if (stakeAmount > 0) {
204
+ // 先确定实际可扣金额
205
+ let actualDeduct = 0;
206
+ if (stakeLocked) {
207
+ // 已锁 → stake 全额扣(必然可扣,因 stake 已 lock 不可挪用)
208
+ db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(stakeAmount, sellerId);
209
+ actualDeduct = stakeAmount;
210
+ }
211
+ else {
212
+ // 未锁 → 从 seller.balance 扣(不足则只扣到 0,差额不发放,不印钱)
213
+ const w = db.prepare('SELECT balance FROM wallets WHERE user_id = ?').get(sellerId);
214
+ actualDeduct = Math.min(stakeAmount, Number(w?.balance ?? 0));
215
+ if (actualDeduct > 0) {
216
+ db.prepare('UPDATE wallets SET balance = balance - ? WHERE user_id = ?').run(actualDeduct, sellerId);
217
+ }
218
+ }
219
+ // 按 actualDeduct 比例分配(buyer 50% + sys_protocol 50%),保证 入 = 出
220
+ const compensation = Math.round(actualDeduct * 0.5 * 100) / 100;
221
+ const protocolShare = Math.round((actualDeduct - compensation) * 100) / 100;
222
+ if (compensation > 0)
223
+ db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compensation, buyerId);
224
+ if (protocolShare > 0)
225
+ db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(protocolShare, sysUserId);
226
+ }
227
+ // 3. 库存回退(非二手)
228
+ if (!isSecondhand)
229
+ db.prepare('UPDATE products SET stock = stock + 1 WHERE id = ?').run(order.product_id);
230
+ // 4. 二手物品:状态恢复 available(保护卖家可重新发布)
231
+ if (isSecondhand) {
232
+ try {
233
+ db.prepare("UPDATE secondhand_items SET status = 'available', updated_at = datetime('now') WHERE id = ?").run(order.product_id);
234
+ }
235
+ catch { }
236
+ }
237
+ }
238
+ else if (faultState === 'fault_logistics') {
239
+ // Phase 1(2026-05-27):logistics_id 为 null 等价"self-fulfill seller"信号
240
+ // → seller 自选自负物流,超时未送达即 seller 违约,按 fault_seller 同样规则处置
241
+ // → 严格守"无责方零成本,谁责任谁承担"原则(详见 docs/LOGISTICS-PHASING.md)
242
+ // Phase 2 logistics 市场启用后,logistics_id 非空走原始 logistics-penalty 逻辑
243
+ const isSelfFulfill = !order.logistics_id;
244
+ if (isSelfFulfill) {
245
+ // 1. buyer escrow 全额退回
246
+ db.prepare('UPDATE wallets SET escrowed = escrowed - ?, balance = balance + ? WHERE user_id = ?')
247
+ .run(total, total, buyerId);
248
+ // 2. bid_stake_held 没收 50/50(同 fault_seller 逻辑)
249
+ if (bidStakeHeld > 0) {
250
+ db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(bidStakeHeld, sellerId);
251
+ const compToBuyer = Math.round(bidStakeHeld * 0.5 * 100) / 100;
252
+ const compToSys = Math.round((bidStakeHeld - compToBuyer) * 100) / 100;
253
+ if (compToBuyer > 0)
254
+ db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compToBuyer, buyerId);
255
+ if (compToSys > 0)
256
+ db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compToSys, sysUserId);
257
+ }
258
+ // 3. stake 50/50 扣罚(seller 违约:自选 self-fulfill 但没送达)
259
+ if (stakeAmount > 0) {
260
+ let actualDeduct = 0;
261
+ if (stakeLocked) {
262
+ db.prepare('UPDATE wallets SET staked = staked - ? WHERE user_id = ?').run(stakeAmount, sellerId);
263
+ actualDeduct = stakeAmount;
264
+ }
265
+ else {
266
+ const w = db.prepare('SELECT balance FROM wallets WHERE user_id = ?').get(sellerId);
267
+ actualDeduct = Math.min(stakeAmount, Number(w?.balance ?? 0));
268
+ if (actualDeduct > 0) {
269
+ db.prepare('UPDATE wallets SET balance = balance - ? WHERE user_id = ?').run(actualDeduct, sellerId);
270
+ }
271
+ }
272
+ const compensation = Math.round(actualDeduct * 0.5 * 100) / 100;
273
+ const protocolShare = Math.round((actualDeduct - compensation) * 100) / 100;
274
+ if (compensation > 0)
275
+ db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(compensation, buyerId);
276
+ if (protocolShare > 0)
277
+ db.prepare('UPDATE wallets SET balance = balance + ? WHERE user_id = ?').run(protocolShare, sysUserId);
278
+ }
279
+ // 4. 库存回退
280
+ if (!isSecondhand)
281
+ db.prepare('UPDATE products SET stock = stock + 1 WHERE id = ?').run(order.product_id);
282
+ if (isSecondhand) {
283
+ try {
284
+ db.prepare("UPDATE secondhand_items SET status = 'available', updated_at = datetime('now') WHERE id = ?").run(order.product_id);
285
+ }
286
+ catch { }
287
+ }
288
+ }
289
+ else {
290
+ // Phase 2 logistics 市场:真正的 logistics 接单后违约
291
+ // 1. buyer escrow 全额退回
292
+ db.prepare('UPDATE wallets SET escrowed = escrowed - ?, balance = balance + ? WHERE user_id = ?')
293
+ .run(total, total, buyerId);
294
+ // 2. seller 无责 → bid_stake_held / stake 全额返还
295
+ if (bidStakeHeld > 0) {
296
+ db.prepare('UPDATE wallets SET balance = balance + ?, staked = staked - ? WHERE user_id = ?').run(bidStakeHeld, bidStakeHeld, sellerId);
297
+ }
298
+ if (stakeAmount > 0 && stakeLocked) {
299
+ db.prepare('UPDATE wallets SET staked = staked - ?, balance = balance + ? WHERE user_id = ?')
300
+ .run(stakeAmount, stakeAmount, sellerId);
301
+ }
302
+ // 3. 库存回退
303
+ if (!isSecondhand)
304
+ db.prepare('UPDATE products SET stock = stock + 1 WHERE id = ?').run(order.product_id);
305
+ if (isSecondhand) {
306
+ try {
307
+ db.prepare("UPDATE secondhand_items SET status = 'available', updated_at = datetime('now') WHERE id = ?").run(order.product_id);
308
+ }
309
+ catch { }
310
+ }
311
+ // TODO(Phase 2):logistics 接入 stake/insurance/deposit 后,从 logistics 池扣给 seller 补货款
312
+ }
313
+ }
314
+ else if (faultState === 'fault_buyer') {
315
+ // created→fault_buyer:escrow 未锁,仅库存回退
316
+ if (!isSecondhand)
317
+ db.prepare('UPDATE products SET stock = stock + 1 WHERE id = ?').run(order.product_id);
318
+ if (isSecondhand) {
319
+ try {
320
+ db.prepare("UPDATE secondhand_items SET status = 'available', updated_at = datetime('now') WHERE id = ?").run(order.product_id);
321
+ }
322
+ catch { }
323
+ }
324
+ // P0.1:买家违约 → bid_stake_held 全额返还卖家(卖家未失责)
325
+ if (bidStakeHeld > 0) {
326
+ db.prepare('UPDATE wallets SET balance = balance + ?, staked = staked - ? WHERE user_id = ?').run(bidStakeHeld, bidStakeHeld, sellerId);
327
+ }
328
+ }
329
+ // 标记已结算(防止重复处置)
330
+ db.prepare("UPDATE orders SET settled_fault_at = datetime('now') WHERE id = ?").run(orderId);
331
+ })();
332
+ }
131
333
  // ─── 内部工具函数 ─────────────────────────────────────────────
132
334
  /** 找出当前订单超时的转移(如果有) */
133
335
  function findActiveDeadlineTransition(order, now) {
@@ -145,15 +347,34 @@ function findActiveDeadlineTransition(order, now) {
145
347
  return null;
146
348
  }
147
349
  /** 获取当前有效的截止时间 */
148
- function getActiveDeadline(order) {
350
+ function getActiveDeadline(order, db) {
351
+ // QA 轮 7 P1:旧表 picked_up 状态没 deadline → agent 不知道下一步多久前要做完
352
+ // 修:picked_up 状态视为"已揽收,等运输/投递",下一个 deadline 是 delivery_deadline
353
+ // QA 轮 7 P1(另一条):disputed 状态下没读 dispute_cases 的 arbitrate_deadline → agent 不知道仲裁还有多久
149
354
  const deadlineMap = {
150
355
  created: 'pay_deadline',
151
356
  paid: 'accept_deadline',
152
357
  accepted: 'ship_deadline',
153
358
  shipped: 'pickup_deadline',
359
+ picked_up: 'delivery_deadline',
154
360
  in_transit: 'delivery_deadline',
155
361
  delivered: 'confirm_deadline',
156
362
  };
363
+ // disputed 状态:从 `disputes` 表(active dispute, not the `dispute_cases` archive)查 deadline
364
+ // 优先返 arbitrate_deadline(仲裁总截止);如果还在 respond 窗口(被诉方未回应)返 respond_deadline
365
+ if (order.status === 'disputed' && db) {
366
+ try {
367
+ const dispute = db.prepare(`SELECT respond_deadline, arbitrate_deadline, ruling_type, status FROM disputes WHERE order_id = ? ORDER BY created_at DESC LIMIT 1`).get(order.id);
368
+ if (dispute && !dispute.ruling_type && dispute.status !== 'resolved' && dispute.status !== 'dismissed') {
369
+ if (dispute.arbitrate_deadline)
370
+ return { field: 'arbitrate_deadline', deadline: dispute.arbitrate_deadline };
371
+ if (dispute.respond_deadline)
372
+ return { field: 'respond_deadline', deadline: dispute.respond_deadline };
373
+ }
374
+ }
375
+ catch { }
376
+ return null;
377
+ }
157
378
  const field = deadlineMap[order.status];
158
379
  if (!field)
159
380
  return null;