@seasonkoh/webaz 0.1.24 → 0.1.26

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 (195) hide show
  1. package/README.md +5 -1
  2. package/dist/layer0-foundation/L0-1-database/db-backends/pg-backend.js +51 -0
  3. package/dist/layer0-foundation/L0-1-database/db-backends/sql-dialect-datetime.js +437 -0
  4. package/dist/layer0-foundation/L0-1-database/db-backends/sql-placeholders.js +98 -0
  5. package/dist/layer0-foundation/L0-1-database/db.js +65 -0
  6. package/dist/layer0-foundation/L0-2-state-machine/order-chain.js +13 -11
  7. package/dist/layer0-foundation/L0-2-state-machine/transitions.js +1 -1
  8. package/dist/layer0-foundation/L0-5-manifest/manifest.js +13 -11
  9. package/dist/layer1-agent/L1-1-mcp-server/server.js +288 -208
  10. package/dist/layer1-agent/L1-2-external-anchor/anchor-engine.js +14 -12
  11. package/dist/layer2-business/L2-6-notifications/notification-engine.js +8 -5
  12. package/dist/layer2-business/L2-7-snf/snf-engine.js +16 -14
  13. package/dist/layer2-business/L2-8-feedback/build-feedback-engine.js +18 -10
  14. package/dist/layer2-business/L2-9-contribution/build-reputation-engine.js +37 -23
  15. package/dist/layer2-business/L2-9-contribution/build-task-agent-metadata-store.js +182 -0
  16. package/dist/layer2-business/L2-9-contribution/build-task-participation.js +47 -0
  17. package/dist/layer2-business/L2-9-contribution/build-task-read.js +222 -0
  18. package/dist/layer2-business/L2-9-contribution/build-tasks-engine.js +11 -3
  19. package/dist/layer2-business/L2-9-contribution/canonical-contribution-target.js +16 -0
  20. package/dist/layer2-business/L2-9-contribution/contribution-display-envelope.js +40 -0
  21. package/dist/layer2-business/L2-9-contribution/contribution-score-contract.js +36 -0
  22. package/dist/layer2-business/L2-9-contribution/contribution-score-evidence.js +61 -0
  23. package/dist/layer2-business/L2-9-contribution/github-credential/canonical.js +60 -0
  24. package/dist/layer2-business/L2-9-contribution/github-credential/github-credential.schema.js +140 -0
  25. package/dist/layer2-business/L2-9-contribution/github-credential/github-fetch-adapter.js +437 -0
  26. package/dist/layer2-business/L2-9-contribution/github-credential/self-consistency.js +38 -0
  27. package/dist/layer2-business/L2-9-contribution/github-credential/verifier.js +231 -0
  28. package/dist/layer2-business/L2-9-contribution/github-credential-ingestion-engine.js +145 -0
  29. package/dist/layer2-business/L2-9-contribution/github-credential-store.js +115 -0
  30. package/dist/layer2-business/L2-9-contribution/identity-binding-engine.js +134 -0
  31. package/dist/layer2-business/L2-9-contribution/identity-binding-store.js +101 -0
  32. package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-engine.js +126 -0
  33. package/dist/layer2-business/L2-9-contribution/identity-claim-challenge-store.js +30 -0
  34. package/dist/layer2-business/L2-9-contribution/identity-claim-discovery.js +55 -0
  35. package/dist/layer2-business/L2-9-contribution/identity-claim-engine.js +109 -0
  36. package/dist/layer2-business/L2-9-contribution/identity-claim-fact-precondition.js +22 -0
  37. package/dist/layer2-business/L2-9-contribution/identity-claim-proof-verifier.js +97 -0
  38. package/dist/layer2-business/L2-9-contribution/identity-claim-read.js +59 -0
  39. package/dist/layer2-business/L2-9-contribution/task-proposal-ai-store.js +99 -0
  40. package/dist/layer2-business/L2-9-contribution/task-proposal-draft.js +191 -0
  41. package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +129 -0
  42. package/dist/layer2-business/L2-notes/note-photo-storage.js +4 -2
  43. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +17 -15
  44. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +11 -8
  45. package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +9 -8
  46. package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +11 -8
  47. package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +22 -16
  48. package/dist/pwa/acp-feed.js +13 -1
  49. package/dist/pwa/admin-bearer-auth.js +21 -0
  50. package/dist/pwa/contract-fingerprint.js +2 -0
  51. package/dist/pwa/email-delivery.js +127 -0
  52. package/dist/pwa/endpoint-actions.js +5 -1
  53. package/dist/pwa/goal-index.js +8 -8
  54. package/dist/pwa/human-presence.js +62 -0
  55. package/dist/pwa/public/app.js +1485 -283
  56. package/dist/pwa/public/i18n.js +297 -59
  57. package/dist/pwa/public/index.html +1 -0
  58. package/dist/pwa/public/openapi.json +5 -5
  59. package/dist/pwa/public/whitepaper/en/index.html +153 -0
  60. package/dist/pwa/public/whitepaper/zh-CN/index.html +153 -0
  61. package/dist/pwa/rate-limit.js +22 -0
  62. package/dist/pwa/routes/account-deletion.js +15 -13
  63. package/dist/pwa/routes/addresses.js +10 -9
  64. package/dist/pwa/routes/admin-admins.js +13 -14
  65. package/dist/pwa/routes/admin-analytics.js +109 -69
  66. package/dist/pwa/routes/admin-atomic.js +10 -4
  67. package/dist/pwa/routes/admin-catalog.js +13 -11
  68. package/dist/pwa/routes/admin-editor-picks.js +15 -10
  69. package/dist/pwa/routes/admin-events.js +5 -3
  70. package/dist/pwa/routes/admin-health.js +2 -1
  71. package/dist/pwa/routes/admin-moderation.js +50 -29
  72. package/dist/pwa/routes/admin-ops.js +35 -23
  73. package/dist/pwa/routes/admin-protocol-params.js +16 -19
  74. package/dist/pwa/routes/admin-reports.js +23 -21
  75. package/dist/pwa/routes/admin-tokenomics.js +26 -25
  76. package/dist/pwa/routes/admin-users-lifecycle.js +37 -40
  77. package/dist/pwa/routes/admin-users-query.js +65 -53
  78. package/dist/pwa/routes/admin-verifier-flow.js +82 -41
  79. package/dist/pwa/routes/admin-verifier-whitelist.js +55 -27
  80. package/dist/pwa/routes/admin-wallet-ops.js +32 -7
  81. package/dist/pwa/routes/agent-buy.js +46 -22
  82. package/dist/pwa/routes/agent-governance.js +52 -56
  83. package/dist/pwa/routes/ai.js +7 -5
  84. package/dist/pwa/routes/analytics.js +43 -41
  85. package/dist/pwa/routes/anchors.js +19 -20
  86. package/dist/pwa/routes/announcements.js +13 -13
  87. package/dist/pwa/routes/arbitrator.js +97 -31
  88. package/dist/pwa/routes/auction.js +157 -116
  89. package/dist/pwa/routes/auth-login.js +6 -4
  90. package/dist/pwa/routes/auth-read.js +21 -10
  91. package/dist/pwa/routes/auth-register.js +111 -26
  92. package/dist/pwa/routes/auth-sessions.js +12 -11
  93. package/dist/pwa/routes/blocklist.js +16 -15
  94. package/dist/pwa/routes/build-feedback.js +10 -9
  95. package/dist/pwa/routes/build-reputation.js +6 -2
  96. package/dist/pwa/routes/build-tasks.js +45 -13
  97. package/dist/pwa/routes/buyer-feeds.js +27 -25
  98. package/dist/pwa/routes/cart.js +16 -15
  99. package/dist/pwa/routes/charity.js +212 -150
  100. package/dist/pwa/routes/chat.js +42 -43
  101. package/dist/pwa/routes/checkin-tasks.js +10 -9
  102. package/dist/pwa/routes/checkout-helpers.js +12 -10
  103. package/dist/pwa/routes/claim-initiators.js +34 -14
  104. package/dist/pwa/routes/claim-verify.js +86 -53
  105. package/dist/pwa/routes/claim-voting.js +43 -18
  106. package/dist/pwa/routes/contribution-identity.js +164 -0
  107. package/dist/pwa/routes/contribution-score.js +19 -0
  108. package/dist/pwa/routes/coupons.js +19 -16
  109. package/dist/pwa/routes/dashboards.js +18 -16
  110. package/dist/pwa/routes/dispute-cases.js +25 -24
  111. package/dist/pwa/routes/disputes-read.js +45 -51
  112. package/dist/pwa/routes/disputes-write.js +124 -61
  113. package/dist/pwa/routes/evidence.js +9 -9
  114. package/dist/pwa/routes/external-anchors.js +13 -12
  115. package/dist/pwa/routes/feedback.js +29 -33
  116. package/dist/pwa/routes/flash-sales.js +18 -16
  117. package/dist/pwa/routes/follows.js +25 -24
  118. package/dist/pwa/routes/governance-auto-deactivate.js +21 -9
  119. package/dist/pwa/routes/governance-onboarding.js +70 -59
  120. package/dist/pwa/routes/group-buys.js +22 -22
  121. package/dist/pwa/routes/growth.js +34 -31
  122. package/dist/pwa/routes/import-product.js +12 -10
  123. package/dist/pwa/routes/kyc.js +9 -8
  124. package/dist/pwa/routes/leaderboard.js +20 -18
  125. package/dist/pwa/routes/listings.js +23 -22
  126. package/dist/pwa/routes/logistics.js +10 -8
  127. package/dist/pwa/routes/manifests.js +27 -27
  128. package/dist/pwa/routes/me-data.js +23 -21
  129. package/dist/pwa/routes/notifications.js +7 -6
  130. package/dist/pwa/routes/offers.js +30 -12
  131. package/dist/pwa/routes/orders-action.js +51 -29
  132. package/dist/pwa/routes/orders-create.js +75 -20
  133. package/dist/pwa/routes/orders-read.js +21 -20
  134. package/dist/pwa/routes/p2p-products.js +30 -18
  135. package/dist/pwa/routes/payments-governance.js +61 -56
  136. package/dist/pwa/routes/peers.js +9 -8
  137. package/dist/pwa/routes/pin-receipts.js +13 -13
  138. package/dist/pwa/routes/products-aliases.js +12 -10
  139. package/dist/pwa/routes/products-claims.js +36 -17
  140. package/dist/pwa/routes/products-create.js +53 -38
  141. package/dist/pwa/routes/products-crud.js +17 -16
  142. package/dist/pwa/routes/products-links.js +49 -26
  143. package/dist/pwa/routes/products-list.js +6 -4
  144. package/dist/pwa/routes/products-meta.js +40 -39
  145. package/dist/pwa/routes/products-update.js +19 -5
  146. package/dist/pwa/routes/profile-credentials.js +20 -19
  147. package/dist/pwa/routes/profile-identity.js +14 -13
  148. package/dist/pwa/routes/profile-location.js +7 -6
  149. package/dist/pwa/routes/profile-placement.js +20 -19
  150. package/dist/pwa/routes/profile-prefs.js +11 -11
  151. package/dist/pwa/routes/promoter.js +58 -66
  152. package/dist/pwa/routes/public-build-tasks.js +19 -0
  153. package/dist/pwa/routes/public-utils.js +108 -46
  154. package/dist/pwa/routes/push.js +16 -15
  155. package/dist/pwa/routes/ratings.js +92 -32
  156. package/dist/pwa/routes/recover-key.js +66 -26
  157. package/dist/pwa/routes/referral.js +37 -52
  158. package/dist/pwa/routes/reputation.js +3 -2
  159. package/dist/pwa/routes/returns.js +76 -73
  160. package/dist/pwa/routes/reviews.js +41 -18
  161. package/dist/pwa/routes/rewards-apply.js +16 -15
  162. package/dist/pwa/routes/rewards-auto-downgrade.js +9 -7
  163. package/dist/pwa/routes/rewards-escrow-expire.js +7 -5
  164. package/dist/pwa/routes/rfqs.js +163 -85
  165. package/dist/pwa/routes/search.js +16 -14
  166. package/dist/pwa/routes/secondhand.js +25 -22
  167. package/dist/pwa/routes/seller-quota.js +24 -26
  168. package/dist/pwa/routes/share-redirects.js +60 -55
  169. package/dist/pwa/routes/shareables-interactions.js +34 -35
  170. package/dist/pwa/routes/shareables.js +55 -51
  171. package/dist/pwa/routes/shop-referral.js +58 -0
  172. package/dist/pwa/routes/shops.js +25 -20
  173. package/dist/pwa/routes/signaling.js +10 -9
  174. package/dist/pwa/routes/skill-market.js +16 -16
  175. package/dist/pwa/routes/skills.js +15 -14
  176. package/dist/pwa/routes/snf.js +14 -13
  177. package/dist/pwa/routes/tags.js +10 -9
  178. package/dist/pwa/routes/task-proposals.js +121 -0
  179. package/dist/pwa/routes/trial.js +72 -52
  180. package/dist/pwa/routes/trusted-kpi.js +20 -18
  181. package/dist/pwa/routes/url-claim.js +67 -28
  182. package/dist/pwa/routes/users-public.js +62 -70
  183. package/dist/pwa/routes/variants.js +12 -13
  184. package/dist/pwa/routes/verifier-user.js +61 -21
  185. package/dist/pwa/routes/verify-tasks.js +49 -25
  186. package/dist/pwa/routes/waitlist.js +16 -15
  187. package/dist/pwa/routes/wallet-read.js +75 -37
  188. package/dist/pwa/routes/wallet-write.js +12 -9
  189. package/dist/pwa/routes/webauthn.js +25 -26
  190. package/dist/pwa/routes/webhooks.js +26 -26
  191. package/dist/pwa/routes/welcome.js +45 -50
  192. package/dist/pwa/routes/wishlist-qa.js +29 -32
  193. package/dist/pwa/server.js +304 -90
  194. package/dist/version.js +1 -1
  195. package/package.json +76 -3
@@ -1,5 +1,7 @@
1
+ import { dbOne } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  export function registerAiRoutes(app, deps) {
2
- const { db, auth, anthropic } = deps;
3
+ // db 已走 RFC-016 异步 seam(dbOne),不再直接用 deps.db
4
+ const { auth, anthropic } = deps;
3
5
  // G-2: AI 价格建议
4
6
  app.post('/api/ai/price-suggestion', async (req, res) => {
5
7
  const user = auth(req, res);
@@ -11,17 +13,17 @@ export function registerAiRoutes(app, deps) {
11
13
  if (!title)
12
14
  return void res.status(400).json({ error: '请提供 title' });
13
15
  // 类目历史价位
14
- const stats = db.prepare(`
16
+ const stats = (await dbOne(`
15
17
  SELECT COUNT(*) as cnt, COALESCE(AVG(price), 0) as avg, COALESCE(MIN(price), 0) as min, COALESCE(MAX(price), 0) as max,
16
18
  COALESCE((SELECT price FROM products WHERE status='active' AND category = ? ORDER BY price LIMIT 1 OFFSET CAST((SELECT COUNT(*) FROM products WHERE status='active' AND category = ?) / 2 AS INTEGER)), 0) as median
17
19
  FROM products WHERE status = 'active' AND category = ?
18
- `).get(category || '', category || '', category || '');
20
+ `, [category || '', category || '', category || '']));
19
21
  // 近 30 天成交均价(更可信)
20
- const recentAvg = db.prepare(`
22
+ const recentAvg = (await dbOne(`
21
23
  SELECT COALESCE(AVG(total_amount), 0) as avg FROM orders o
22
24
  JOIN products p ON p.id = o.product_id
23
25
  WHERE p.category = ? AND o.status = 'completed' AND o.created_at > datetime('now', '-30 days')
24
- `).get(category || '').avg;
26
+ `, [category || ''])).avg;
25
27
  try {
26
28
  const message = await anthropic.messages.create({
27
29
  model: 'claude-haiku-4-5-20251001',
@@ -1,3 +1,4 @@
1
+ import { dbOne, dbAll } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  function median(arr) {
2
3
  if (arr.length === 0)
3
4
  return null;
@@ -6,9 +7,10 @@ function median(arr) {
6
7
  return s.length % 2 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
7
8
  }
8
9
  export function registerAnalyticsRoutes(app, deps) {
9
- const { db, auth } = deps;
10
+ // db 已全量走 RFC-016 异步 seam(dbOne/dbAll),不再直接用 deps.db
11
+ const { auth } = deps;
10
12
  // 物流绩效卡 (Wave B-4)
11
- app.get('/api/logistics/me/performance', (req, res) => {
13
+ app.get('/api/logistics/me/performance', async (req, res) => {
12
14
  const user = auth(req, res);
13
15
  if (!user)
14
16
  return;
@@ -16,22 +18,22 @@ export function registerAnalyticsRoutes(app, deps) {
16
18
  return void res.status(403).json({ error: '仅物流角色可访问' });
17
19
  }
18
20
  const windowDays = Math.max(7, Math.min(365, Number(req.query.window) || 30));
19
- const orders = db.prepare(`
21
+ const orders = await dbAll(`
20
22
  SELECT id, status, created_at, updated_at,
21
23
  pickup_deadline, delivery_deadline
22
24
  FROM orders
23
25
  WHERE logistics_id = ? AND created_at > datetime('now', '-' || ? || ' days')
24
- `).all(user.id, windowDays);
26
+ `, [user.id, windowDays]);
25
27
  const orderIds = orders.map(o => o.id);
26
28
  let history = [];
27
29
  if (orderIds.length > 0) {
28
30
  const placeholders = orderIds.map(() => '?').join(',');
29
- history = db.prepare(`
31
+ history = await dbAll(`
30
32
  SELECT order_id, from_status, to_status, created_at
31
33
  FROM order_state_history
32
34
  WHERE order_id IN (${placeholders})
33
35
  ORDER BY created_at ASC
34
- `).all(...orderIds);
36
+ `, orderIds);
35
37
  }
36
38
  const histByOrder = new Map();
37
39
  for (const h of history) {
@@ -78,24 +80,24 @@ export function registerAnalyticsRoutes(app, deps) {
78
80
  }
79
81
  }
80
82
  }
81
- const disputes = db.prepare(`
83
+ const disputes = (await dbOne(`
82
84
  SELECT COUNT(*) as n FROM disputes d
83
85
  JOIN orders o ON o.id = d.order_id
84
86
  WHERE o.logistics_id = ? AND d.created_at > datetime('now', '-' || ? || ' days')
85
- `).get(user.id, windowDays).n;
87
+ `, [user.id, windowDays])).n;
86
88
  // 败诉两路:auto-fault 判物流 + 仲裁裁定物流为被告且退款
87
- const autoFaultLost = db.prepare(`
89
+ const autoFaultLost = (await dbOne(`
88
90
  SELECT COUNT(*) as n FROM orders
89
91
  WHERE logistics_id = ? AND status = 'fault_logistics'
90
92
  AND updated_at > datetime('now', '-' || ? || ' days')
91
- `).get(user.id, windowDays).n;
92
- const arbitratedLost = db.prepare(`
93
+ `, [user.id, windowDays])).n;
94
+ const arbitratedLost = (await dbOne(`
93
95
  SELECT COUNT(*) as n FROM disputes d
94
96
  JOIN orders o ON o.id = d.order_id
95
97
  WHERE o.logistics_id = ? AND d.defendant_id = ?
96
98
  AND d.ruling_type IN ('refund_buyer','partial_refund')
97
99
  AND d.created_at > datetime('now', '-' || ? || ' days')
98
- `).get(user.id, user.id, windowDays).n;
100
+ `, [user.id, user.id, windowDays])).n;
99
101
  const disputeLoss = autoFaultLost + arbitratedLost;
100
102
  const pickupTotalEvaluated = pickupOnTime + pickupOverdue;
101
103
  const deliveryTotalEvaluated = deliveryOnTime + deliveryOverdue;
@@ -125,14 +127,14 @@ export function registerAnalyticsRoutes(app, deps) {
125
127
  });
126
128
  });
127
129
  // 卖家销售分析 (Wave C-5)
128
- app.get('/api/sellers/me/analytics', (req, res) => {
130
+ app.get('/api/sellers/me/analytics', async (req, res) => {
129
131
  const user = auth(req, res);
130
132
  if (!user)
131
133
  return;
132
134
  if (user.role !== 'seller')
133
135
  return void res.status(403).json({ error: '仅卖家可访问' });
134
136
  const windowDays = Math.max(7, Math.min(365, Number(req.query.window) || 30));
135
- const ordersAgg = db.prepare(`
137
+ const ordersAgg = (await dbOne(`
136
138
  SELECT
137
139
  COUNT(*) as total_orders,
138
140
  SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_orders,
@@ -141,8 +143,8 @@ export function registerAnalyticsRoutes(app, deps) {
141
143
  COALESCE(SUM(CASE WHEN status = 'completed' THEN total_amount ELSE 0 END), 0) as gmv,
142
144
  COALESCE(AVG(CASE WHEN status = 'completed' THEN total_amount END), 0) as aov
143
145
  FROM orders WHERE seller_id = ? AND created_at > datetime('now', '-' || ? || ' days')
144
- `).get(user.id, windowDays);
145
- const topProducts = db.prepare(`
146
+ `, [user.id, windowDays]));
147
+ const topProducts = await dbAll(`
146
148
  SELECT p.id, p.title, p.price, COUNT(o.id) as sales,
147
149
  COALESCE(SUM(o.total_amount), 0) as revenue
148
150
  FROM products p
@@ -153,27 +155,27 @@ export function registerAnalyticsRoutes(app, deps) {
153
155
  GROUP BY p.id
154
156
  HAVING sales > 0
155
157
  ORDER BY sales DESC LIMIT 10
156
- `).all(windowDays, user.id);
157
- const buyerStats = db.prepare(`
158
+ `, [windowDays, user.id]);
159
+ const buyerStats = (await dbOne(`
158
160
  SELECT
159
161
  COUNT(DISTINCT buyer_id) as unique_buyers,
160
162
  COUNT(*) as orders_count
161
163
  FROM orders WHERE seller_id = ? AND status = 'completed'
162
164
  AND created_at > datetime('now', '-' || ? || ' days')
163
- `).get(user.id, windowDays);
164
- const repeatBuyers = db.prepare(`
165
+ `, [user.id, windowDays]));
166
+ const repeatBuyers = (await dbOne(`
165
167
  SELECT COUNT(*) as n FROM (
166
168
  SELECT buyer_id FROM orders WHERE seller_id = ? AND status = 'completed'
167
169
  AND created_at > datetime('now', '-' || ? || ' days')
168
170
  GROUP BY buyer_id HAVING COUNT(*) > 1
169
171
  )
170
- `).get(user.id, windowDays).n;
171
- const wishlistAdds = db.prepare(`
172
+ `, [user.id, windowDays])).n;
173
+ const wishlistAdds = (await dbOne(`
172
174
  SELECT COUNT(*) as n FROM user_wishlist w
173
175
  JOIN products p ON p.id = w.product_id
174
176
  WHERE p.seller_id = ? AND w.created_at > datetime('now', '-' || ? || ' days')
175
- `).get(user.id, windowDays).n;
176
- const dailyTrend = db.prepare(`
177
+ `, [user.id, windowDays])).n;
178
+ const dailyTrend = await dbAll(`
177
179
  SELECT DATE(created_at) as date,
178
180
  COUNT(*) as orders,
179
181
  COALESCE(SUM(CASE WHEN status = 'completed' THEN total_amount ELSE 0 END), 0) as gmv
@@ -181,30 +183,30 @@ export function registerAnalyticsRoutes(app, deps) {
181
183
  WHERE seller_id = ? AND created_at > datetime('now', '-' || ? || ' days')
182
184
  GROUP BY DATE(created_at)
183
185
  ORDER BY date ASC
184
- `).all(user.id, Math.min(windowDays, 30));
185
- const ratingsAgg = db.prepare(`
186
+ `, [user.id, Math.min(windowDays, 30)]);
187
+ const ratingsAgg = await dbOne(`
186
188
  SELECT COUNT(*) as cnt, COALESCE(AVG(stars), 0) as avg_stars
187
189
  FROM order_ratings WHERE seller_id = ?
188
190
  AND created_at > datetime('now', '-' || ? || ' days')
189
- `).get(user.id, windowDays);
190
- const refundsCount = db.prepare(`
191
+ `, [user.id, windowDays]);
192
+ const refundsCount = (await dbOne(`
191
193
  SELECT COUNT(*) as n FROM return_requests
192
194
  WHERE seller_id = ? AND status = 'refunded'
193
195
  AND created_at > datetime('now', '-' || ? || ' days')
194
- `).get(user.id, windowDays).n;
196
+ `, [user.id, windowDays])).n;
195
197
  // S1: 平均备货时长(paid → shipped 中位 hours)
196
- const handlingRow = db.prepare(`
198
+ const handlingRow = (await dbOne(`
197
199
  SELECT COALESCE(AVG((julianday(h_ship.created_at) - julianday(h_paid.created_at)) * 24), 0) as avg_handling_hours,
198
200
  COUNT(*) as sample_n
199
201
  FROM orders o
200
202
  JOIN order_state_history h_paid ON h_paid.order_id = o.id AND h_paid.to_status = 'paid'
201
203
  JOIN order_state_history h_ship ON h_ship.order_id = o.id AND h_ship.to_status = 'shipped'
202
204
  WHERE o.seller_id = ? AND o.created_at > datetime('now', '-' || ? || ' days')
203
- `).get(user.id, windowDays);
205
+ `, [user.id, windowDays]));
204
206
  const completedN = Number(ordersAgg.completed_orders) || 0;
205
207
  const returnRate = completedN > 0 ? refundsCount / completedN : 0;
206
208
  // S1: 上一窗口对比
207
- const prevAgg = db.prepare(`
209
+ const prevAgg = (await dbOne(`
208
210
  SELECT
209
211
  COUNT(*) as total_orders,
210
212
  SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_orders,
@@ -212,7 +214,7 @@ export function registerAnalyticsRoutes(app, deps) {
212
214
  FROM orders WHERE seller_id = ?
213
215
  AND created_at > datetime('now', '-' || ? || ' days')
214
216
  AND created_at <= datetime('now', '-' || ? || ' days')
215
- `).get(user.id, windowDays * 2, windowDays);
217
+ `, [user.id, windowDays * 2, windowDays]));
216
218
  res.json({
217
219
  window_days: windowDays,
218
220
  orders: ordersAgg,
@@ -247,19 +249,19 @@ export function registerAnalyticsRoutes(app, deps) {
247
249
  });
248
250
  });
249
251
  // 卖家退货仪表盘
250
- app.get('/api/sellers/me/return-stats', (req, res) => {
252
+ app.get('/api/sellers/me/return-stats', async (req, res) => {
251
253
  const user = auth(req, res);
252
254
  if (!user)
253
255
  return;
254
- const totalReturns = db.prepare(`SELECT COUNT(*) as n FROM return_requests WHERE seller_id = ?`).get(user.id).n;
255
- const refunded = db.prepare(`SELECT COUNT(*) as n FROM return_requests WHERE seller_id = ? AND status = 'refunded'`).get(user.id).n;
256
- const rejected = db.prepare(`SELECT COUNT(*) as n FROM return_requests WHERE seller_id = ? AND status = 'rejected'`).get(user.id).n;
257
- const pending = db.prepare(`SELECT COUNT(*) as n FROM return_requests WHERE seller_id = ? AND status = 'pending'`).get(user.id).n;
258
- const totalOrders = db.prepare(`SELECT COUNT(*) as n FROM orders WHERE seller_id = ? AND status IN ('delivered','completed','refunded')`).get(user.id).n;
259
- const reasonBreakdown = db.prepare(`
256
+ const totalReturns = (await dbOne(`SELECT COUNT(*) as n FROM return_requests WHERE seller_id = ?`, [user.id])).n;
257
+ const refunded = (await dbOne(`SELECT COUNT(*) as n FROM return_requests WHERE seller_id = ? AND status = 'refunded'`, [user.id])).n;
258
+ const rejected = (await dbOne(`SELECT COUNT(*) as n FROM return_requests WHERE seller_id = ? AND status = 'rejected'`, [user.id])).n;
259
+ const pending = (await dbOne(`SELECT COUNT(*) as n FROM return_requests WHERE seller_id = ? AND status = 'pending'`, [user.id])).n;
260
+ const totalOrders = (await dbOne(`SELECT COUNT(*) as n FROM orders WHERE seller_id = ? AND status IN ('delivered','completed','refunded')`, [user.id])).n;
261
+ const reasonBreakdown = await dbAll(`
260
262
  SELECT reason, COUNT(*) as cnt FROM return_requests
261
263
  WHERE seller_id = ? GROUP BY reason ORDER BY cnt DESC
262
- `).all(user.id);
264
+ `, [user.id]);
263
265
  const returnRate = totalOrders > 0 ? (refunded / totalOrders) : 0;
264
266
  res.json({
265
267
  total_returns: totalReturns,
@@ -1,4 +1,5 @@
1
1
  import { generateAnchor, lookupAnchor, retireAnchor, userReferralVolume, computeTierLetter, userAnchorQuotaStats, TIER_THRESHOLDS, ANCHOR_HANDLE_MAX_FOR_USE, } from '../../layer2-business/L2-anchor-registry/anchor-registry.js';
2
+ import { dbOne, dbAll, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
2
3
  export function registerAnchorsRoutes(app, deps) {
3
4
  const { db, auth, rateLimitOk } = deps;
4
5
  // POST /api/anchor/generate
@@ -25,7 +26,7 @@ export function registerAnchorsRoutes(app, deps) {
25
26
  res.json({ ok: true, anchor: r.anchor, tier_letter: r.tier_letter });
26
27
  });
27
28
  // GET /api/anchor/:code/lookup — 公开(无需 auth)
28
- app.get('/api/anchor/:code/lookup', (req, res) => {
29
+ app.get('/api/anchor/:code/lookup', async (req, res) => {
29
30
  if (!rateLimitOk(req.ip || 'anon', 60, 60_000))
30
31
  return void res.status(429).json({ error: 'too_many_lookups' });
31
32
  const r = lookupAnchor(db, String(req.params.code || ''));
@@ -38,15 +39,15 @@ export function registerAnchorsRoutes(app, deps) {
38
39
  return void res.status(404).json({ found: false, hint: 'reclaimable' });
39
40
  }
40
41
  // 2026-05-24 富化响应:附 owner 详情 + 商品推荐指数
41
- const owner = db.prepare(`
42
+ const owner = await dbOne(`
42
43
  SELECT u.name, u.handle, u.region, u.created_at, u.bio,
43
44
  (SELECT COUNT(*) FROM follows WHERE followee_id = u.id) as follower_count,
44
45
  (SELECT COALESCE(SUM(s.like_count), 0) FROM shareables s WHERE s.owner_id = u.id AND s.status = 'active') as total_likes_received
45
46
  FROM users u WHERE u.id = ? AND u.id != 'sys_protocol'
46
- `).get(r.owner_id);
47
+ `, [r.owner_id]);
47
48
  let product = null;
48
49
  if (r.target_kind === 'product') {
49
- product = db.prepare(`
50
+ product = (await dbOne(`
50
51
  SELECT p.id, p.title, p.price, p.category, p.images, p.completion_count, p.total_likes,
51
52
  (SELECT COUNT(DISTINCT buyer_id) FROM order_ratings rt WHERE rt.product_id = p.id AND rt.stars >= 4) as recommend_count,
52
53
  (SELECT ROUND(AVG(stars), 2) FROM order_ratings rt WHERE rt.product_id = p.id) as avg_rating,
@@ -54,12 +55,12 @@ export function registerAnchorsRoutes(app, deps) {
54
55
  u.handle as seller_handle, u.name as seller_name
55
56
  FROM products p LEFT JOIN users u ON u.id = p.seller_id
56
57
  WHERE p.id = ? AND p.status = 'active'
57
- `).get(r.target_id);
58
+ `, [r.target_id])) ?? null;
58
59
  }
59
60
  else if (r.target_kind === 'shareable') {
60
- const sh = db.prepare(`SELECT related_product_id FROM shareables WHERE id = ?`).get(r.target_id);
61
+ const sh = await dbOne(`SELECT related_product_id FROM shareables WHERE id = ?`, [r.target_id]);
61
62
  if (sh?.related_product_id) {
62
- product = db.prepare(`
63
+ product = (await dbOne(`
63
64
  SELECT p.id, p.title, p.price, p.category, p.images, p.completion_count, p.total_likes,
64
65
  (SELECT COUNT(DISTINCT buyer_id) FROM order_ratings rt WHERE rt.product_id = p.id AND rt.stars >= 4) as recommend_count,
65
66
  (SELECT ROUND(AVG(stars), 2) FROM order_ratings rt WHERE rt.product_id = p.id) as avg_rating,
@@ -67,7 +68,7 @@ export function registerAnchorsRoutes(app, deps) {
67
68
  u.handle as seller_handle, u.name as seller_name
68
69
  FROM products p LEFT JOIN users u ON u.id = p.seller_id
69
70
  WHERE p.id = ? AND p.status = 'active'
70
- `).get(sh.related_product_id);
71
+ `, [sh.related_product_id])) ?? null;
71
72
  }
72
73
  }
73
74
  res.json({
@@ -81,7 +82,7 @@ export function registerAnchorsRoutes(app, deps) {
81
82
  });
82
83
  });
83
84
  // POST /api/anchor/:code/touch — 写 attribution(first-touch + 30d)
84
- app.post('/api/anchor/:code/touch', (req, res) => {
85
+ app.post('/api/anchor/:code/touch', async (req, res) => {
85
86
  const user = auth(req, res);
86
87
  if (!user)
87
88
  return;
@@ -93,27 +94,25 @@ export function registerAnchorsRoutes(app, deps) {
93
94
  let attributedProducts = 0;
94
95
  const expiresAt = new Date(Date.now() + 30 * 86400_000).toISOString().slice(0, 19).replace('T', ' ');
95
96
  if (r.target_kind === 'product') {
96
- const existing = db.prepare(`SELECT 1 FROM product_share_attribution WHERE product_id = ? AND recipient_id = ?`).get(r.target_id, user.id);
97
+ const existing = await dbOne(`SELECT 1 FROM product_share_attribution WHERE product_id = ? AND recipient_id = ?`, [r.target_id, user.id]);
97
98
  if (!existing) {
98
- db.prepare(`INSERT INTO product_share_attribution (product_id, recipient_id, sharer_id, shareable_id, expires_at) VALUES (?,?,?,NULL,?)`)
99
- .run(r.target_id, user.id, r.owner_id, expiresAt);
99
+ await dbRun(`INSERT INTO product_share_attribution (product_id, recipient_id, sharer_id, shareable_id, expires_at) VALUES (?,?,?,NULL,?)`, [r.target_id, user.id, r.owner_id, expiresAt]);
100
100
  attributedProducts = 1;
101
101
  }
102
102
  }
103
103
  else if (r.target_kind === 'shareable') {
104
- const s = db.prepare(`SELECT id, related_product_id FROM shareables WHERE id = ?`).get(r.target_id);
104
+ const s = await dbOne(`SELECT id, related_product_id FROM shareables WHERE id = ?`, [r.target_id]);
105
105
  if (s?.related_product_id) {
106
- const existing = db.prepare(`SELECT 1 FROM product_share_attribution WHERE product_id = ? AND recipient_id = ?`).get(s.related_product_id, user.id);
106
+ const existing = await dbOne(`SELECT 1 FROM product_share_attribution WHERE product_id = ? AND recipient_id = ?`, [s.related_product_id, user.id]);
107
107
  if (!existing) {
108
- db.prepare(`INSERT INTO product_share_attribution (product_id, recipient_id, sharer_id, shareable_id, expires_at) VALUES (?,?,?,?,?)`)
109
- .run(s.related_product_id, user.id, r.owner_id, s.id, expiresAt);
108
+ await dbRun(`INSERT INTO product_share_attribution (product_id, recipient_id, sharer_id, shareable_id, expires_at) VALUES (?,?,?,?,?)`, [s.related_product_id, user.id, r.owner_id, s.id, expiresAt]);
110
109
  attributedProducts = 1;
111
110
  }
112
111
  }
113
112
  }
114
113
  else if (r.target_kind === 'user') {
115
114
  // 限 LIMIT 50 防 DoS
116
- const ownerProducts = db.prepare(`SELECT id FROM products WHERE seller_id = ? AND status = 'active' ORDER BY last_sold_at DESC NULLS LAST LIMIT 50`).all(r.owner_id);
115
+ const ownerProducts = await dbAll(`SELECT id FROM products WHERE seller_id = ? AND status = 'active' ORDER BY last_sold_at DESC NULLS LAST LIMIT 50`, [r.owner_id]);
117
116
  db.transaction(() => {
118
117
  for (const p of ownerProducts) {
119
118
  const existing = db.prepare(`SELECT 1 FROM product_share_attribution WHERE product_id = ? AND recipient_id = ?`).get(p.id, user.id);
@@ -146,14 +145,14 @@ export function registerAnchorsRoutes(app, deps) {
146
145
  }
147
146
  res.json({ ok: true });
148
147
  });
149
- app.get('/api/anchor/me', (req, res) => {
148
+ app.get('/api/anchor/me', async (req, res) => {
150
149
  const user = auth(req, res);
151
150
  if (!user)
152
151
  return;
153
- const rows = db.prepare(`
152
+ const rows = await dbAll(`
154
153
  SELECT anchor, prefix, middle, tier_letter, target_kind, target_id, status, retired_at, hits, last_hit_at, created_at
155
154
  FROM anchor_registry WHERE owner_id = ? ORDER BY created_at DESC LIMIT 100
156
- `).all(user.id);
155
+ `, [user.id]);
157
156
  const vol = userReferralVolume(db, user.id);
158
157
  const tier = computeTierLetter(vol);
159
158
  const quota = userAnchorQuotaStats(db, user.id);
@@ -1,6 +1,8 @@
1
+ import { dbOne, dbAll, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
1
2
  export function registerAnnouncementsRoutes(app, deps) {
2
- const { db, generateId, auth, safeRoles, requireProtocolAdmin, isRootAdmin, getAdminScope, logAdminAction } = deps;
3
- app.post('/api/admin/announcements', (req, res) => {
3
+ // db 已走 RFC-016 异步 seam(dbOne/dbAll/dbRun),不再直接用 deps.db
4
+ const { generateId, auth, safeRoles, requireProtocolAdmin, isRootAdmin, getAdminScope, logAdminAction } = deps;
5
+ app.post('/api/admin/announcements', async (req, res) => {
4
6
  const admin = requireProtocolAdmin(req, res);
5
7
  if (!admin)
6
8
  return;
@@ -26,16 +28,15 @@ export function registerAnnouncementsRoutes(app, deps) {
26
28
  }
27
29
  }
28
30
  const id = generateId('ann');
29
- db.prepare(`INSERT INTO announcements (id, author_id, title, body, target_roles, target_regions, severity, starts_at, expires_at) VALUES (?,?,?,?,?,?,?,?,?)`)
30
- .run(id, admin.id, title.trim(), body.trim(), rolesJson, regionsJson, severity || 'info', starts_at || null, expires_at || null);
31
+ await dbRun(`INSERT INTO announcements (id, author_id, title, body, target_roles, target_regions, severity, starts_at, expires_at) VALUES (?,?,?,?,?,?,?,?,?)`, [id, admin.id, title.trim(), body.trim(), rolesJson, regionsJson, severity || 'info', starts_at || null, expires_at || null]);
31
32
  logAdminAction(admin.id, 'create_announcement', 'announcement', id, { title, severity: severity || 'info' });
32
33
  res.json({ success: true, id });
33
34
  });
34
- app.patch('/api/admin/announcements/:id', (req, res) => {
35
+ app.patch('/api/admin/announcements/:id', async (req, res) => {
35
36
  const admin = requireProtocolAdmin(req, res);
36
37
  if (!admin)
37
38
  return;
38
- const ann = db.prepare('SELECT id, author_id FROM announcements WHERE id = ?').get(req.params.id);
39
+ const ann = await dbOne('SELECT id, author_id FROM announcements WHERE id = ?', [req.params.id]);
39
40
  if (!ann)
40
41
  return void res.status(404).json({ error: '公告不存在' });
41
42
  if (!isRootAdmin(admin) && ann.author_id !== admin.id)
@@ -54,17 +55,17 @@ export function registerAnnouncementsRoutes(app, deps) {
54
55
  if (sets.length === 0)
55
56
  return void res.status(400).json({ error: '无可更新字段' });
56
57
  args.push(req.params.id);
57
- db.prepare(`UPDATE announcements SET ${sets.join(', ')} WHERE id = ?`).run(...args);
58
+ await dbRun(`UPDATE announcements SET ${sets.join(', ')} WHERE id = ?`, args);
58
59
  res.json({ success: true });
59
60
  });
60
61
  // 列出对当前用户可见的活跃公告(按角色 + 区域过滤)
61
- app.get('/api/announcements/active', (req, res) => {
62
+ app.get('/api/announcements/active', async (req, res) => {
62
63
  const user = auth(req, res);
63
64
  if (!user)
64
65
  return;
65
66
  const userRoles = safeRoles(user);
66
67
  const userRegion = user.region || 'global';
67
- const rows = db.prepare(`
68
+ const rows = await dbAll(`
68
69
  SELECT a.id, a.title, a.body, a.severity, a.created_at, a.target_roles, a.target_regions,
69
70
  (SELECT 1 FROM announcement_reads WHERE user_id = ? AND announcement_id = a.id) as is_read
70
71
  FROM announcements a
@@ -72,7 +73,7 @@ export function registerAnnouncementsRoutes(app, deps) {
72
73
  AND (a.starts_at IS NULL OR a.starts_at <= datetime('now'))
73
74
  AND (a.expires_at IS NULL OR a.expires_at >= datetime('now'))
74
75
  ORDER BY a.created_at DESC LIMIT 50
75
- `).all(user.id);
76
+ `, [user.id]);
76
77
  // JS 端 filter 角色 / 区域(避免 JSON LIKE 在 SQLite 中麻烦)
77
78
  const filtered = rows.filter(a => {
78
79
  if (a.target_roles) {
@@ -96,13 +97,12 @@ export function registerAnnouncementsRoutes(app, deps) {
96
97
  }).map(a => ({ ...a, target_roles: undefined, target_regions: undefined, is_read: !!a.is_read }));
97
98
  res.json({ items: filtered });
98
99
  });
99
- app.post('/api/announcements/:id/read', (req, res) => {
100
+ app.post('/api/announcements/:id/read', async (req, res) => {
100
101
  const user = auth(req, res);
101
102
  if (!user)
102
103
  return;
103
104
  try {
104
- db.prepare(`INSERT OR IGNORE INTO announcement_reads (user_id, announcement_id) VALUES (?,?)`)
105
- .run(user.id, req.params.id);
105
+ await dbRun(`INSERT OR IGNORE INTO announcement_reads (user_id, announcement_id) VALUES (?,?)`, [user.id, req.params.id]);
106
106
  }
107
107
  catch { }
108
108
  res.json({ success: true });