@seasonkoh/webaz 0.1.23 → 0.1.25

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 (187) hide show
  1. package/README.md +2 -0
  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 +198 -83
  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 +173 -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 +10 -2
  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-engine.js +109 -0
  35. package/dist/layer2-business/L2-9-contribution/identity-claim-fact-precondition.js +22 -0
  36. package/dist/layer2-business/L2-9-contribution/identity-claim-proof-verifier.js +97 -0
  37. package/dist/layer2-business/L2-9-contribution/identity-claim-read.js +59 -0
  38. package/dist/layer2-business/L2-9-contribution/task-proposal-store.js +129 -0
  39. package/dist/layer2-business/L2-notes/note-photo-storage.js +4 -2
  40. package/dist/layer3-trust/L3-1-dispute-engine/dispute-engine.js +17 -15
  41. package/dist/layer3-trust/L3-1-dispute-engine/evidence-storage.js +11 -8
  42. package/dist/layer4-economics/L4-3-reputation/reputation-engine.js +9 -8
  43. package/dist/layer4-economics/L4-4-skill-market/skill-engine.js +11 -8
  44. package/dist/layer4-economics/L4-4-skill-market/skill-listing-engine.js +22 -16
  45. package/dist/pwa/acp-feed.js +13 -1
  46. package/dist/pwa/contract-fingerprint.js +2 -0
  47. package/dist/pwa/endpoint-actions.js +5 -1
  48. package/dist/pwa/goal-index.js +8 -8
  49. package/dist/pwa/human-presence.js +62 -0
  50. package/dist/pwa/public/app.js +575 -68
  51. package/dist/pwa/public/i18n.js +29 -20
  52. package/dist/pwa/public/index.html +1 -0
  53. package/dist/pwa/public/openapi.json +2 -2
  54. package/dist/pwa/rate-limit.js +22 -0
  55. package/dist/pwa/routes/account-deletion.js +15 -13
  56. package/dist/pwa/routes/addresses.js +10 -9
  57. package/dist/pwa/routes/admin-admins.js +13 -14
  58. package/dist/pwa/routes/admin-analytics.js +109 -69
  59. package/dist/pwa/routes/admin-catalog.js +13 -11
  60. package/dist/pwa/routes/admin-editor-picks.js +15 -10
  61. package/dist/pwa/routes/admin-events.js +5 -3
  62. package/dist/pwa/routes/admin-health.js +2 -1
  63. package/dist/pwa/routes/admin-moderation.js +26 -29
  64. package/dist/pwa/routes/admin-ops.js +22 -21
  65. package/dist/pwa/routes/admin-protocol-params.js +16 -19
  66. package/dist/pwa/routes/admin-reports.js +23 -21
  67. package/dist/pwa/routes/admin-tokenomics.js +26 -25
  68. package/dist/pwa/routes/admin-users-lifecycle.js +37 -40
  69. package/dist/pwa/routes/admin-users-query.js +54 -53
  70. package/dist/pwa/routes/admin-verifier-flow.js +82 -41
  71. package/dist/pwa/routes/admin-verifier-whitelist.js +55 -27
  72. package/dist/pwa/routes/admin-wallet-ops.js +7 -5
  73. package/dist/pwa/routes/agent-buy.js +46 -22
  74. package/dist/pwa/routes/agent-governance.js +52 -56
  75. package/dist/pwa/routes/ai.js +7 -5
  76. package/dist/pwa/routes/analytics.js +43 -41
  77. package/dist/pwa/routes/anchors.js +19 -20
  78. package/dist/pwa/routes/announcements.js +13 -13
  79. package/dist/pwa/routes/arbitrator.js +97 -31
  80. package/dist/pwa/routes/auction.js +153 -114
  81. package/dist/pwa/routes/auth-login.js +6 -4
  82. package/dist/pwa/routes/auth-read.js +11 -9
  83. package/dist/pwa/routes/auth-register.js +35 -20
  84. package/dist/pwa/routes/auth-sessions.js +12 -11
  85. package/dist/pwa/routes/blocklist.js +16 -15
  86. package/dist/pwa/routes/build-feedback.js +10 -9
  87. package/dist/pwa/routes/build-reputation.js +6 -2
  88. package/dist/pwa/routes/build-tasks.js +45 -13
  89. package/dist/pwa/routes/buyer-feeds.js +27 -25
  90. package/dist/pwa/routes/cart.js +16 -15
  91. package/dist/pwa/routes/charity.js +212 -150
  92. package/dist/pwa/routes/chat.js +42 -43
  93. package/dist/pwa/routes/checkin-tasks.js +10 -9
  94. package/dist/pwa/routes/checkout-helpers.js +12 -10
  95. package/dist/pwa/routes/claim-initiators.js +34 -14
  96. package/dist/pwa/routes/claim-verify.js +86 -53
  97. package/dist/pwa/routes/claim-voting.js +43 -18
  98. package/dist/pwa/routes/contribution-identity.js +147 -0
  99. package/dist/pwa/routes/contribution-score.js +19 -0
  100. package/dist/pwa/routes/coupons.js +19 -16
  101. package/dist/pwa/routes/dashboards.js +18 -16
  102. package/dist/pwa/routes/dispute-cases.js +25 -24
  103. package/dist/pwa/routes/disputes-read.js +45 -51
  104. package/dist/pwa/routes/disputes-write.js +124 -61
  105. package/dist/pwa/routes/evidence.js +9 -9
  106. package/dist/pwa/routes/external-anchors.js +13 -12
  107. package/dist/pwa/routes/feedback.js +29 -33
  108. package/dist/pwa/routes/flash-sales.js +18 -16
  109. package/dist/pwa/routes/follows.js +25 -24
  110. package/dist/pwa/routes/governance-auto-deactivate.js +21 -9
  111. package/dist/pwa/routes/governance-onboarding.js +70 -59
  112. package/dist/pwa/routes/group-buys.js +22 -22
  113. package/dist/pwa/routes/growth.js +33 -30
  114. package/dist/pwa/routes/import-product.js +12 -10
  115. package/dist/pwa/routes/kyc.js +9 -8
  116. package/dist/pwa/routes/leaderboard.js +20 -18
  117. package/dist/pwa/routes/listings.js +23 -22
  118. package/dist/pwa/routes/logistics.js +10 -8
  119. package/dist/pwa/routes/manifests.js +27 -27
  120. package/dist/pwa/routes/me-data.js +23 -21
  121. package/dist/pwa/routes/notifications.js +7 -6
  122. package/dist/pwa/routes/offers.js +30 -12
  123. package/dist/pwa/routes/orders-action.js +33 -17
  124. package/dist/pwa/routes/orders-create.js +75 -20
  125. package/dist/pwa/routes/orders-read.js +21 -20
  126. package/dist/pwa/routes/p2p-products.js +30 -18
  127. package/dist/pwa/routes/payments-governance.js +61 -56
  128. package/dist/pwa/routes/peers.js +9 -8
  129. package/dist/pwa/routes/pin-receipts.js +13 -13
  130. package/dist/pwa/routes/products-aliases.js +12 -10
  131. package/dist/pwa/routes/products-claims.js +36 -17
  132. package/dist/pwa/routes/products-create.js +53 -38
  133. package/dist/pwa/routes/products-crud.js +17 -16
  134. package/dist/pwa/routes/products-links.js +49 -26
  135. package/dist/pwa/routes/products-list.js +6 -4
  136. package/dist/pwa/routes/products-meta.js +40 -39
  137. package/dist/pwa/routes/products-update.js +19 -5
  138. package/dist/pwa/routes/profile-credentials.js +14 -16
  139. package/dist/pwa/routes/profile-identity.js +14 -13
  140. package/dist/pwa/routes/profile-location.js +7 -6
  141. package/dist/pwa/routes/profile-placement.js +19 -17
  142. package/dist/pwa/routes/profile-prefs.js +11 -11
  143. package/dist/pwa/routes/promoter.js +55 -49
  144. package/dist/pwa/routes/public-build-tasks.js +19 -0
  145. package/dist/pwa/routes/public-utils.js +108 -46
  146. package/dist/pwa/routes/push.js +16 -15
  147. package/dist/pwa/routes/ratings.js +30 -30
  148. package/dist/pwa/routes/recover-key.js +13 -12
  149. package/dist/pwa/routes/referral.js +37 -32
  150. package/dist/pwa/routes/reputation.js +3 -2
  151. package/dist/pwa/routes/returns.js +76 -73
  152. package/dist/pwa/routes/reviews.js +41 -18
  153. package/dist/pwa/routes/rewards-apply.js +16 -15
  154. package/dist/pwa/routes/rewards-auto-downgrade.js +9 -7
  155. package/dist/pwa/routes/rewards-escrow-expire.js +7 -5
  156. package/dist/pwa/routes/rfqs.js +163 -85
  157. package/dist/pwa/routes/search.js +16 -14
  158. package/dist/pwa/routes/secondhand.js +25 -22
  159. package/dist/pwa/routes/seller-quota.js +24 -26
  160. package/dist/pwa/routes/share-redirects.js +59 -55
  161. package/dist/pwa/routes/shareables-interactions.js +34 -35
  162. package/dist/pwa/routes/shareables.js +55 -51
  163. package/dist/pwa/routes/shop-referral.js +57 -0
  164. package/dist/pwa/routes/shops.js +20 -18
  165. package/dist/pwa/routes/signaling.js +10 -9
  166. package/dist/pwa/routes/skill-market.js +16 -16
  167. package/dist/pwa/routes/skills.js +15 -14
  168. package/dist/pwa/routes/snf.js +14 -13
  169. package/dist/pwa/routes/tags.js +10 -9
  170. package/dist/pwa/routes/task-proposals.js +45 -0
  171. package/dist/pwa/routes/trial.js +69 -51
  172. package/dist/pwa/routes/trusted-kpi.js +20 -18
  173. package/dist/pwa/routes/url-claim.js +67 -28
  174. package/dist/pwa/routes/users-public.js +62 -60
  175. package/dist/pwa/routes/variants.js +12 -13
  176. package/dist/pwa/routes/verifier-user.js +61 -21
  177. package/dist/pwa/routes/verify-tasks.js +49 -25
  178. package/dist/pwa/routes/waitlist.js +16 -15
  179. package/dist/pwa/routes/wallet-read.js +74 -36
  180. package/dist/pwa/routes/wallet-write.js +12 -9
  181. package/dist/pwa/routes/webauthn.js +25 -26
  182. package/dist/pwa/routes/webhooks.js +26 -26
  183. package/dist/pwa/routes/welcome.js +45 -50
  184. package/dist/pwa/routes/wishlist-qa.js +29 -32
  185. package/dist/pwa/server.js +237 -81
  186. package/dist/version.js +1 -1
  187. package/package.json +47 -2
@@ -1,16 +1,17 @@
1
1
  import { computeAgentPassport } from '../../layer1-agent/L1-2-identity/agent-passport.js';
2
+ import { dbOne, dbAll, dbRun } from '../../layer0-foundation/L0-1-database/db.js'; // RFC-016 异步 DB seam
2
3
  export function registerAgentGovernanceRoutes(app, deps) {
3
4
  const { db, generateId, auth, requireRootAdmin, invalidateAgentBlockedCache, requireHumanPresence, issueAgentStrike, custodianFingerprint, signPassport, issuerAddress } = deps;
4
5
  // /api/me/agents — 列出本账号所有 agent + declaration / strikes
5
- app.get('/api/me/agents', (req, res) => {
6
+ app.get('/api/me/agents', async (req, res) => {
6
7
  const user = auth(req, res);
7
8
  if (!user)
8
9
  return;
9
- const keys = db.prepare(`SELECT api_key FROM users WHERE id = ? UNION SELECT api_key FROM agent_reputation WHERE user_id = ?`).all(user.id, user.id);
10
- const items = keys.map(k => {
11
- const decl = db.prepare(`SELECT operator_name, operator_contact, purpose, declared_scope, attestations, repo_url, revoked_at FROM agent_declarations WHERE api_key = ?`).get(k.api_key);
10
+ const keys = await dbAll(`SELECT api_key FROM users WHERE id = ? UNION SELECT api_key FROM agent_reputation WHERE user_id = ?`, [user.id, user.id]);
11
+ const items = await Promise.all(keys.map(async (k) => {
12
+ const decl = await dbOne(`SELECT operator_name, operator_contact, purpose, declared_scope, attestations, repo_url, revoked_at FROM agent_declarations WHERE api_key = ?`, [k.api_key]);
12
13
  // P1 fix 4.4:附 signals JSON
13
- const rep = db.prepare(`SELECT trust_score, level, signals, last_calculated_at FROM agent_reputation WHERE api_key = ?`).get(k.api_key);
14
+ const rep = await dbOne(`SELECT trust_score, level, signals, last_calculated_at FROM agent_reputation WHERE api_key = ?`, [k.api_key]);
14
15
  if (rep && rep.signals) {
15
16
  try {
16
17
  rep.signals = JSON.parse(rep.signals);
@@ -19,9 +20,9 @@ export function registerAgentGovernanceRoutes(app, deps) {
19
20
  rep.signals = null;
20
21
  }
21
22
  }
22
- const calls30d = db.prepare(`SELECT COUNT(*) as n FROM agent_call_log WHERE api_key = ? AND created_at > datetime('now', '-30 days')`).get(k.api_key).n;
23
- const last = db.prepare(`SELECT endpoint, method, status_code, created_at FROM agent_call_log WHERE api_key = ? ORDER BY created_at DESC LIMIT 1`).get(k.api_key);
24
- const strikes = db.prepare(`SELECT severity, reason_code, issued_at, expires_at, appeal_status FROM agent_strikes WHERE api_key = ? ORDER BY issued_at DESC LIMIT 5`).all(k.api_key);
23
+ const calls30d = (await dbOne(`SELECT COUNT(*) as n FROM agent_call_log WHERE api_key = ? AND created_at > datetime('now', '-30 days')`, [k.api_key])).n;
24
+ const last = await dbOne(`SELECT endpoint, method, status_code, created_at FROM agent_call_log WHERE api_key = ? ORDER BY created_at DESC LIMIT 1`, [k.api_key]);
25
+ const strikes = await dbAll(`SELECT severity, reason_code, issued_at, expires_at, appeal_status FROM agent_strikes WHERE api_key = ? ORDER BY issued_at DESC LIMIT 5`, [k.api_key]);
25
26
  let passport = null;
26
27
  try {
27
28
  passport = computeAgentPassport(db, k.api_key, user.id, custodianFingerprint);
@@ -37,9 +38,9 @@ export function registerAgentGovernanceRoutes(app, deps) {
37
38
  recent_strikes: strikes,
38
39
  passport,
39
40
  };
40
- });
41
+ }));
41
42
  // Phase 2 监护人总览(只读/软绑定):真人态 + 旗下 agent 聚合 + 连带
42
- const hasPasskey = (db.prepare('SELECT COUNT(*) AS n FROM webauthn_credentials WHERE user_id = ?').get(user.id)?.n || 0) > 0;
43
+ const hasPasskey = ((await dbOne('SELECT COUNT(*) AS n FROM webauthn_credentials WHERE user_id = ?', [user.id]))?.n || 0) > 0;
43
44
  const pps = items.map(i => i.passport).filter(Boolean);
44
45
  const depthRank = { shallow: 0, medium: 1, deep: 2, profound: 3 };
45
46
  const deepest = pps.reduce((d, p) => (depthRank[p.engagement_depth] ?? 0) > (depthRank[d] ?? 0) ? p.engagement_depth : d, 'shallow');
@@ -65,7 +66,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
65
66
  const prefix = String(req.params.apiKeyPrefix || '').replace('...', '');
66
67
  if (prefix.length < 6)
67
68
  return void res.status(400).json({ error: 'apiKeyPrefix 太短' });
68
- const keys = db.prepare(`SELECT api_key FROM users WHERE id = ? UNION SELECT api_key FROM agent_reputation WHERE user_id = ?`).all(user.id, user.id);
69
+ const keys = await dbAll(`SELECT api_key FROM users WHERE id = ? UNION SELECT api_key FROM agent_reputation WHERE user_id = ?`, [user.id, user.id]);
69
70
  const match = keys.find(k => k.api_key.startsWith(prefix));
70
71
  if (!match)
71
72
  return void res.status(404).json({ error: '未找到该 agent(或不属于你)' });
@@ -135,32 +136,30 @@ export function registerAgentGovernanceRoutes(app, deps) {
135
136
  webaz_format, // 显式重复让消费者明确选哪个
136
137
  });
137
138
  });
138
- app.get('/api/me/agents/:apiKeyPrefix/log', (req, res) => {
139
+ app.get('/api/me/agents/:apiKeyPrefix/log', async (req, res) => {
139
140
  const user = auth(req, res);
140
141
  if (!user)
141
142
  return;
142
143
  const prefix = String(req.params.apiKeyPrefix || '').replace(/[^A-Za-z0-9_]/g, '').slice(0, 32);
143
144
  if (prefix.length < 8)
144
145
  return void res.status(400).json({ error: 'apiKeyPrefix 至少 8 字符' });
145
- const targetKey = db.prepare(`SELECT api_key FROM users WHERE id = ? AND api_key LIKE ? || '%'
146
- UNION SELECT api_key FROM agent_reputation WHERE user_id = ? AND api_key LIKE ? || '%'`)
147
- .get(user.id, prefix, user.id, prefix);
146
+ const targetKey = await dbOne(`SELECT api_key FROM users WHERE id = ? AND api_key LIKE ? || '%'
147
+ UNION SELECT api_key FROM agent_reputation WHERE user_id = ? AND api_key LIKE ? || '%'`, [user.id, prefix, user.id, prefix]);
148
148
  if (!targetKey)
149
149
  return void res.status(404).json({ error: '未找到匹配的 agent api_key(仅可查本人的)' });
150
150
  const limit = Math.min(500, Math.max(10, Number(req.query.limit) || 100));
151
- const rows = db.prepare(`SELECT endpoint, method, status_code, created_at FROM agent_call_log
152
- WHERE api_key = ? AND created_at > datetime('now', '-30 days') ORDER BY id DESC LIMIT ?`).all(targetKey.api_key, limit);
151
+ const rows = await dbAll(`SELECT endpoint, method, status_code, created_at FROM agent_call_log
152
+ WHERE api_key = ? AND created_at > datetime('now', '-30 days') ORDER BY id DESC LIMIT ?`, [targetKey.api_key, limit]);
153
153
  res.json({ items: rows });
154
154
  });
155
- app.post('/api/me/agents/declarations', (req, res) => {
155
+ app.post('/api/me/agents/declarations', async (req, res) => {
156
156
  const user = auth(req, res);
157
157
  if (!user)
158
158
  return;
159
159
  const b = req.body;
160
160
  const targetApiKey = b.api_key ? String(b.api_key) : user.api_key;
161
- const ownership = db.prepare(`SELECT id FROM users WHERE id = ? AND api_key = ?
162
- UNION SELECT user_id as id FROM agent_reputation WHERE user_id = ? AND api_key = ?`)
163
- .get(user.id, targetApiKey, user.id, targetApiKey);
161
+ const ownership = await dbOne(`SELECT id FROM users WHERE id = ? AND api_key = ?
162
+ UNION SELECT user_id as id FROM agent_reputation WHERE user_id = ? AND api_key = ?`, [user.id, targetApiKey, user.id, targetApiKey]);
164
163
  if (!ownership)
165
164
  return void res.status(403).json({ error: 'api_key 不属于本账号' });
166
165
  const operator_name = String(b.operator_name || '').trim().slice(0, 60);
@@ -185,7 +184,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
185
184
  const attestationsJson = b.attestations && typeof b.attestations === 'object' ? JSON.stringify(b.attestations).slice(0, 2000) : null;
186
185
  const repo_url = b.repo_url ? String(b.repo_url).slice(0, 200) : null;
187
186
  const homepage = b.homepage ? String(b.homepage).slice(0, 200) : null;
188
- db.prepare(`INSERT INTO agent_declarations (
187
+ await dbRun(`INSERT INTO agent_declarations (
189
188
  api_key, user_id, operator_name, operator_contact, purpose, declared_scope, attestations, repo_url, homepage
190
189
  ) VALUES (?,?,?,?,?,?,?,?,?)
191
190
  ON CONFLICT(api_key) DO UPDATE SET
@@ -197,36 +196,35 @@ export function registerAgentGovernanceRoutes(app, deps) {
197
196
  repo_url = excluded.repo_url,
198
197
  homepage = excluded.homepage,
199
198
  revoked_at = NULL,
200
- updated_at = datetime('now')`).run(targetApiKey, user.id, operator_name, operator_contact, purpose, scopeJson, attestationsJson, repo_url, homepage);
199
+ updated_at = datetime('now')`, [targetApiKey, user.id, operator_name, operator_contact, purpose, scopeJson, attestationsJson, repo_url, homepage]);
201
200
  invalidateAgentBlockedCache(targetApiKey);
202
201
  res.json({ ok: true });
203
202
  });
204
203
  // 用户撤销 agent(铁律 §4 human presence)
205
- app.post('/api/me/agents/:apiKeyPrefix/revoke', (req, res) => {
204
+ app.post('/api/me/agents/:apiKeyPrefix/revoke', async (req, res) => {
206
205
  const user = auth(req, res);
207
206
  if (!user)
208
207
  return;
209
208
  const prefix = String(req.params.apiKeyPrefix || '').replace(/[^A-Za-z0-9_]/g, '').slice(0, 32);
210
209
  if (prefix.length < 8)
211
210
  return void res.status(400).json({ error: 'apiKeyPrefix 至少 8 字符' });
212
- const targetKey = db.prepare(`SELECT api_key FROM users WHERE id = ? AND api_key LIKE ? || '%'
213
- UNION SELECT api_key FROM agent_reputation WHERE user_id = ? AND api_key LIKE ? || '%'`)
214
- .get(user.id, prefix, user.id, prefix);
211
+ const targetKey = await dbOne(`SELECT api_key FROM users WHERE id = ? AND api_key LIKE ? || '%'
212
+ UNION SELECT api_key FROM agent_reputation WHERE user_id = ? AND api_key LIKE ? || '%'`, [user.id, prefix, user.id, prefix]);
215
213
  if (!targetKey)
216
214
  return void res.status(404).json({ error: '未找到匹配的 agent api_key' });
217
215
  const hpCheck = requireHumanPresence(user.id, 'agent_revoke', (req.body || {}).webauthn_token, 'require_human_presence_for_agent_revoke', () => true);
218
216
  if (!hpCheck.ok)
219
217
  return void res.status(412).json({ error: hpCheck.reason, error_code: hpCheck.error_code });
220
218
  const reason = String((req.body || {}).reason || '').slice(0, 300);
221
- db.prepare(`INSERT INTO agent_revocations (target_kind, target_value, revoked_by, revoked_by_role, reason)
219
+ await dbRun(`INSERT INTO agent_revocations (target_kind, target_value, revoked_by, revoked_by_role, reason)
222
220
  VALUES ('api_key', ?, ?, 'self', ?)
223
- ON CONFLICT(target_kind, target_value, revoked_by) DO NOTHING`).run(targetKey.api_key, user.id, reason);
224
- db.prepare(`UPDATE agent_declarations SET revoked_at = datetime('now'), revoked_reason = ? WHERE api_key = ?`).run(reason, targetKey.api_key);
221
+ ON CONFLICT(target_kind, target_value, revoked_by) DO NOTHING`, [targetKey.api_key, user.id, reason]);
222
+ await dbRun(`UPDATE agent_declarations SET revoked_at = datetime('now'), revoked_reason = ? WHERE api_key = ?`, [reason, targetKey.api_key]);
225
223
  invalidateAgentBlockedCache(targetKey.api_key);
226
224
  res.json({ ok: true });
227
225
  });
228
226
  // 撤销同 operator 名下所有 agent(仅撤销本用户给 operator 旗下 agent 的 attestation)
229
- app.post('/api/me/agents/operators/:operator_name/revoke', (req, res) => {
227
+ app.post('/api/me/agents/operators/:operator_name/revoke', async (req, res) => {
230
228
  const user = auth(req, res);
231
229
  if (!user)
232
230
  return;
@@ -237,27 +235,26 @@ export function registerAgentGovernanceRoutes(app, deps) {
237
235
  if (!hpCheck.ok)
238
236
  return void res.status(412).json({ error: hpCheck.reason, error_code: hpCheck.error_code });
239
237
  const reason = String((req.body || {}).reason || '').slice(0, 300);
240
- const affected = db.prepare(`UPDATE agent_attestations SET revoked_at = datetime('now')
238
+ const affected = await dbRun(`UPDATE agent_attestations SET revoked_at = datetime('now')
241
239
  WHERE user_id = ? AND revoked_at IS NULL
242
- AND api_key IN (SELECT api_key FROM agent_declarations WHERE operator_name = ?)`)
243
- .run(user.id, opName);
244
- db.prepare(`INSERT INTO agent_revocations (target_kind, target_value, revoked_by, revoked_by_role, reason)
240
+ AND api_key IN (SELECT api_key FROM agent_declarations WHERE operator_name = ?)`, [user.id, opName]);
241
+ await dbRun(`INSERT INTO agent_revocations (target_kind, target_value, revoked_by, revoked_by_role, reason)
245
242
  VALUES ('operator_name', ?, ?, 'self', ?)
246
- ON CONFLICT(target_kind, target_value, revoked_by) DO NOTHING`).run(opName, user.id, reason);
247
- const keys = db.prepare(`SELECT api_key FROM agent_declarations WHERE operator_name = ?`).all(opName);
243
+ ON CONFLICT(target_kind, target_value, revoked_by) DO NOTHING`, [opName, user.id, reason]);
244
+ const keys = await dbAll(`SELECT api_key FROM agent_declarations WHERE operator_name = ?`, [opName]);
248
245
  for (const k of keys)
249
246
  invalidateAgentBlockedCache(k.api_key);
250
247
  res.json({ ok: true, attestations_revoked: affected.changes });
251
248
  });
252
249
  // P0 audit fix 4.2: 申诉 strike
253
- app.post('/api/me/agents/strikes/:strikeId/appeal', (req, res) => {
250
+ app.post('/api/me/agents/strikes/:strikeId/appeal', async (req, res) => {
254
251
  const user = auth(req, res);
255
252
  if (!user)
256
253
  return;
257
254
  const strikeId = Number(req.params.strikeId);
258
255
  if (!Number.isInteger(strikeId) || strikeId <= 0)
259
256
  return void res.status(400).json({ error: 'strikeId 必须是正整数' });
260
- const strike = db.prepare(`SELECT id, api_key, user_id, severity, issued_at, appeal_status FROM agent_strikes WHERE id = ?`).get(strikeId);
257
+ const strike = await dbOne(`SELECT id, api_key, user_id, severity, issued_at, appeal_status FROM agent_strikes WHERE id = ?`, [strikeId]);
261
258
  if (!strike)
262
259
  return void res.status(404).json({ error: 'strike 不存在' });
263
260
  if (strike.user_id !== user.id)
@@ -270,11 +267,11 @@ export function registerAgentGovernanceRoutes(app, deps) {
270
267
  const reason = String((req.body || {}).reason || '').trim().slice(0, 500);
271
268
  if (reason.length < 10)
272
269
  return void res.status(400).json({ error: '申诉理由 ≥10 字' });
273
- db.prepare(`UPDATE agent_strikes SET appeal_status = 'pending', appeal_reason = ? WHERE id = ?`).run(reason, strikeId);
270
+ await dbRun(`UPDATE agent_strikes SET appeal_status = 'pending', appeal_reason = ? WHERE id = ?`, [reason, strikeId]);
274
271
  res.json({ ok: true, message: '申诉已提交,等待 root admin 审核' });
275
272
  });
276
273
  // Admin: 审核 strike 申诉
277
- app.post('/api/admin/agent-strikes/:strikeId/decide', (req, res) => {
274
+ app.post('/api/admin/agent-strikes/:strikeId/decide', async (req, res) => {
278
275
  const user = requireRootAdmin(req, res);
279
276
  if (!user)
280
277
  return;
@@ -284,21 +281,20 @@ export function registerAgentGovernanceRoutes(app, deps) {
284
281
  const decision = String((req.body || {}).decision || '');
285
282
  if (!['approved', 'denied'].includes(decision))
286
283
  return void res.status(400).json({ error: 'decision 必须是 approved / denied' });
287
- const strike = db.prepare(`SELECT id, api_key, appeal_status FROM agent_strikes WHERE id = ?`).get(strikeId);
284
+ const strike = await dbOne(`SELECT id, api_key, appeal_status FROM agent_strikes WHERE id = ?`, [strikeId]);
288
285
  if (!strike)
289
286
  return void res.status(404).json({ error: 'strike 不存在' });
290
287
  if (strike.appeal_status !== 'pending')
291
288
  return void res.status(409).json({ error: `当前状态 ${strike.appeal_status} 不可裁决` });
292
- db.prepare(`UPDATE agent_strikes SET appeal_status = ?, appeal_decided_by = ?, appeal_decided_at = datetime('now') WHERE id = ?`)
293
- .run(decision, user.id, strikeId);
289
+ await dbRun(`UPDATE agent_strikes SET appeal_status = ?, appeal_decided_by = ?, appeal_decided_at = datetime('now') WHERE id = ?`, [decision, user.id, strikeId]);
294
290
  invalidateAgentBlockedCache(strike.api_key);
295
291
  // P1 fix 5.3: appeal approved → 恢复因 strike 自动停用的 skills
296
292
  if (decision === 'approved') {
297
293
  try {
298
- const uRow = db.prepare(`SELECT id FROM users WHERE api_key = ?`).get(strike.api_key);
294
+ const uRow = await dbOne(`SELECT id FROM users WHERE api_key = ?`, [strike.api_key]);
299
295
  if (uRow) {
300
- const r = db.prepare(`UPDATE skills SET active = 1, disabled_by_strike_at = NULL
301
- WHERE seller_id = ? AND disabled_by_strike_at IS NOT NULL AND active = 0`).run(uRow.id);
296
+ const r = await dbRun(`UPDATE skills SET active = 1, disabled_by_strike_at = NULL
297
+ WHERE seller_id = ? AND disabled_by_strike_at IS NOT NULL AND active = 0`, [uRow.id]);
302
298
  if (r.changes > 0)
303
299
  console.log(`[appeal approved→skill] restored ${r.changes} skills for ${uRow.id}`);
304
300
  }
@@ -310,17 +306,17 @@ export function registerAgentGovernanceRoutes(app, deps) {
310
306
  res.json({ ok: true, decision });
311
307
  });
312
308
  // Admin: 列出待审 strike 申诉
313
- app.get('/api/admin/agent-strikes/pending', (req, res) => {
309
+ app.get('/api/admin/agent-strikes/pending', async (req, res) => {
314
310
  const user = requireRootAdmin(req, res);
315
311
  if (!user)
316
312
  return;
317
- const rows = db.prepare(`SELECT s.id, s.api_key, s.user_id, u.handle, s.severity, s.reason_code, s.reason_detail, s.issued_at, s.appeal_reason
313
+ const rows = await dbAll(`SELECT s.id, s.api_key, s.user_id, u.handle, s.severity, s.reason_code, s.reason_detail, s.issued_at, s.appeal_reason
318
314
  FROM agent_strikes s JOIN users u ON u.id = s.user_id
319
- WHERE s.appeal_status = 'pending' ORDER BY s.id DESC LIMIT 100`).all();
315
+ WHERE s.appeal_status = 'pending' ORDER BY s.id DESC LIMIT 100`);
320
316
  res.json({ items: rows });
321
317
  });
322
318
  // P1 fix 4.3: admin 主动 issue strike
323
- app.post('/api/admin/agent-strikes/issue', (req, res) => {
319
+ app.post('/api/admin/agent-strikes/issue', async (req, res) => {
324
320
  const adminUser = requireRootAdmin(req, res);
325
321
  if (!adminUser)
326
322
  return;
@@ -328,7 +324,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
328
324
  const apiKey = String(b.api_key || '').trim();
329
325
  if (apiKey.length < 8)
330
326
  return void res.status(400).json({ error: 'api_key 必填' });
331
- const targetUser = db.prepare(`SELECT id, handle FROM users WHERE api_key = ?`).get(apiKey);
327
+ const targetUser = await dbOne(`SELECT id, handle FROM users WHERE api_key = ?`, [apiKey]);
332
328
  if (!targetUser)
333
329
  return void res.status(404).json({ error: '未找到该 api_key' });
334
330
  const reasonCode = String(b.reason_code || '').trim().slice(0, 40);
@@ -349,7 +345,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
349
345
  res.json({ ok: true, target_handle: targetUser.handle, ...result });
350
346
  });
351
347
  // bilateral attestation(用户批准某 agent 的 scope)
352
- app.post('/api/me/agents/attestations', (req, res) => {
348
+ app.post('/api/me/agents/attestations', async (req, res) => {
353
349
  const user = auth(req, res);
354
350
  if (!user)
355
351
  return;
@@ -357,7 +353,7 @@ export function registerAgentGovernanceRoutes(app, deps) {
357
353
  const apiKey = String(b.api_key || '');
358
354
  if (!apiKey)
359
355
  return void res.status(400).json({ error: 'api_key 必填' });
360
- const decl = db.prepare(`SELECT declared_scope, operator_name, purpose FROM agent_declarations WHERE api_key = ? AND revoked_at IS NULL`).get(apiKey);
356
+ const decl = await dbOne(`SELECT declared_scope, operator_name, purpose FROM agent_declarations WHERE api_key = ? AND revoked_at IS NULL`, [apiKey]);
361
357
  if (!decl)
362
358
  return void res.status(404).json({ error: '该 agent 未声明 / 已撤销,无法授权' });
363
359
  let approvedScopeJson;
@@ -373,14 +369,14 @@ export function registerAgentGovernanceRoutes(app, deps) {
373
369
  const spendCapPerOrder = b.spend_cap_per_order != null ? Math.max(0, Number(b.spend_cap_per_order)) : null;
374
370
  const spendCapDaily = b.spend_cap_daily != null ? Math.max(0, Number(b.spend_cap_daily)) : null;
375
371
  const id = generateId('aat');
376
- db.prepare(`INSERT INTO agent_attestations (id, api_key, user_id, approved_scope, spend_cap_per_order, spend_cap_daily)
372
+ await dbRun(`INSERT INTO agent_attestations (id, api_key, user_id, approved_scope, spend_cap_per_order, spend_cap_daily)
377
373
  VALUES (?,?,?,?,?,?)
378
374
  ON CONFLICT(api_key, user_id) DO UPDATE SET
379
375
  approved_scope = excluded.approved_scope,
380
376
  spend_cap_per_order = excluded.spend_cap_per_order,
381
377
  spend_cap_daily = excluded.spend_cap_daily,
382
378
  revoked_at = NULL,
383
- granted_at = datetime('now')`).run(id, apiKey, user.id, approvedScopeJson, spendCapPerOrder, spendCapDaily);
379
+ granted_at = datetime('now')`, [id, apiKey, user.id, approvedScopeJson, spendCapPerOrder, spendCapDaily]);
384
380
  res.json({ ok: true });
385
381
  });
386
382
  }
@@ -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,