{
  "has_more": true,
  "issues": [
    {
      "assignee_id": "34d7c53d-bd70-45a8-bbbb-77dbb1da16b5",
      "assignee_type": "agent",
      "created_at": "2026-06-02T03:29:39Z",
      "creator_id": "2e7bc302-5016-48b6-a4b9-728e720ec622",
      "creator_type": "agent",
      "description": "## User request\n\n测试连通性",
      "due_date": null,
      "id": "e2645f7d-c3ed-426d-a290-538c64e12f61",
      "identifier": "FET-134",
      "labels": [],
      "metadata": {
        "decision": "PR_238 merged; main is half-fixed (8 sites still on || 7). PR_237 must rebase+merge to complete P0. FET-145 stays done (semantic dep satisfied by PR_238 constants.js). FET-144 stays blocked on DB access.",
        "pipeline_status": "PR_237_CONFLICTING_REBASE_NEEDED",
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/237"
      },
      "number": 134,
      "parent_issue_id": null,
      "position": -23,
      "priority": "none",
      "project_id": "51ec32b5-848a-496c-a573-1006cb2ec058",
      "start_date": null,
      "status": "done",
      "title": "测试连通性",
      "updated_at": "2026-06-04T05:39:24Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "34d7c53d-bd70-45a8-bbbb-77dbb1da16b5",
      "assignee_type": "agent",
      "created_at": "2026-06-02T01:26:09Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "请回复：MiniMax 直连成功，不再使用 OpenRouter",
      "due_date": null,
      "id": "3b4b7884-a064-4d4e-a94f-0249d9f3e28a",
      "identifier": "FET-133",
      "labels": [],
      "metadata": {},
      "number": 133,
      "parent_issue_id": null,
      "position": -23,
      "priority": "none",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "FET-203 最终验证 MiniMax 直连",
      "updated_at": "2026-06-03T06:06:16Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "64b26c5e-1823-477c-9c0f-c5c01d599365",
      "assignee_type": "agent",
      "created_at": "2026-06-02T01:17:28Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "请回复：MiniMax-M3 测试成功，当前时间是 {现在的UTC时间}",
      "due_date": null,
      "id": "5f09ee73-80eb-47b0-bf3a-850f4ddd22bf",
      "identifier": "FET-131",
      "labels": [],
      "metadata": {},
      "number": 131,
      "parent_issue_id": null,
      "position": -22,
      "priority": "none",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "FET-200 测试 MiniMax-M3 直连 API",
      "updated_at": "2026-06-02T01:18:16Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "34d7c53d-bd70-45a8-bbbb-77dbb1da16b5",
      "assignee_type": "agent",
      "created_at": "2026-06-01T15:03:58Z",
      "creator_id": "d1e4fe91-fb56-4c47-95d0-818d5f22b5bd",
      "creator_type": "agent",
      "description": "## 任务描述\n\n在 frontend/src/views/partner/ 目录下创建 TestComponent.vue 组件。\n\n## 参考文档\n- SPEC_FET107.md - 技术规格说明\n- API_FET107.md - 组件接口说明\n\n## 实现要求\n\n创建文件: `frontend/src/views/partner/TestComponent.vue`\n\n**重要**: 需求描述中的代码包含中文引号和格式问题,需要修正为:\n\n```vue\n\u003ctemplate\u003e\n  \u003cdiv class=\"test-component\"\u003e\n    \u003ch1\u003eOpus 4.8 测试成功\u003c/h1\u003e\n    \u003cp\u003e当前时间: {{ currentTime }}\u003c/p\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript setup\u003e\nimport { ref } from 'vue'\n\nconst currentTime = ref(new Date().toLocaleString())\n\u003c/script\u003e\n\n\u003cstyle scoped\u003e\n.test-component {\n  padding: 20px;\n  text-align: center;\n}\n\u003c/style\u003e\n```\n\n## 注意事项\n1. 所有引号使用英文引号 `\"`\n2. class 属性需要加引号\n3. import 语句中 'vue' 需要加引号\n4. 代码格式要符合项目规范\n\n## 完成标准\n1. 文件创建成功\n2. 代码格式正确\n3. 语法无错误\n4. 通过 typecheck 和 lint 检查\n\n## 工作流程\n1. 创建组件文件\n2. 运行 `cd frontend \u0026\u0026 npm run typecheck` 验证类型\n3. 运行 `cd frontend \u0026\u0026 npm run lint` 验证代码风格\n4. 创建 PR\n5. 请求代码评审",
      "due_date": null,
      "id": "bc3958a1-3e8c-4011-986b-3699f699a35c",
      "identifier": "FET-109",
      "labels": [],
      "metadata": {
        "pipeline_status": "merged",
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/230"
      },
      "number": 109,
      "parent_issue_id": "87a52a25-b2a7-4ee5-a5d9-4f53b1327c1c",
      "position": -3,
      "priority": "high",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "[FET-107] 创建 TestComponent.vue 组件",
      "updated_at": "2026-06-01T16:07:34Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "34d7c53d-bd70-45a8-bbbb-77dbb1da16b5",
      "assignee_type": "agent",
      "created_at": "2026-06-01T13:55:39Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "请回答：1+1等于几？这是一个测试任务，用于验证 Hermes runtime 配置了 Yunyi provider 后能否正常工作。",
      "due_date": null,
      "id": "efa39d27-477d-41d4-ab6c-8ca8edf3cac7",
      "identifier": "FET-108",
      "labels": [],
      "metadata": {},
      "number": 108,
      "parent_issue_id": null,
      "position": -3,
      "priority": "none",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "[测试] 验证 Hermes + Yunyi 配置",
      "updated_at": "2026-06-01T15:02:12Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "3748c2e4-22ff-4b85-8d23-d3994570f65e",
      "assignee_type": "agent",
      "created_at": "2026-05-30T06:36:59Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "请用中文回复：今天是星期几？1+1等于几？请简短回答。",
      "due_date": null,
      "id": "a2c889d2-2094-4eba-8917-7f0d69558830",
      "identifier": "FET-99",
      "labels": [],
      "metadata": {},
      "number": 99,
      "parent_issue_id": null,
      "position": -3,
      "priority": "none",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "测试 MiniMax-OpenCode Agent",
      "updated_at": "2026-05-30T14:04:41Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "d1e4fe91-fb56-4c47-95d0-818d5f22b5bd",
      "assignee_type": "agent",
      "created_at": "2026-06-05T14:47:13Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "请简单回复：你使用的是哪个模型和 provider？今天日期是？",
      "due_date": null,
      "id": "0e0d1a46-ebed-48b7-8959-1fec3cb995b5",
      "identifier": "FET-150",
      "labels": [],
      "metadata": {},
      "number": 150,
      "parent_issue_id": null,
      "position": -2,
      "priority": "none",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "[测试] 验证架构师兼项目经理 Yunyi 配置",
      "updated_at": "2026-06-05T16:04:33Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "34d7c53d-bd70-45a8-bbbb-77dbb1da16b5",
      "assignee_type": "agent",
      "created_at": "2026-06-03T16:03:30Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "## 问题描述\n\n**影响范围**：到付订单在合作人端和管理员端显示的总金额与用户端不一致\n\n**根本原因**：后端 `order.total_usd` 不包含到付冻结金额，前端显示逻辑不一致\n\n## 技术细节\n\n### 后端代码问题 (backend/app/services/order_service.py)\n\n**Line 344**：\n```python\nnew_order = Order(\n    ...\n    total_usd=total_item_usd,  # ❌ 只包含商品+运费，不包含冻结金额\n    ...\n)\n```\n\n**Line 173-175**：\n```python\ntotal_item_usd = (total_cny / rate).quantize(...)  # 商品+运费\n```\n\n**Line 209-211**：\n```python\ngrand_total_usd = (total_item_usd + service_fee_usd).quantize(...)  # 包含服务费\n```\n\n**Line 298**：\n```python\ntotal_required = grand_total_usd + frozen_usd  # 用户实际支付的金额\n```\n\n**问题**：`order.total_usd` 存的是 `total_item_usd`，不是 `total_required`\n\n### 前端显示逻辑\n\n1. **用户端** (frontend/src/views/orders/OrderDetailPage.vue Line 659)：\n```javascript\norder.total_usd + order.service_fee_usd + totalFrozenDepositUsd  // ✅ 正确\n```\n\n2. **合作人端** (frontend/src/views/partner/OrderDetailPage.vue Line 138)：\n```javascript\norder.total_usd + order.service_fee_usd  // ❌ 缺少冻结金额\n```\n\n3. **管理员端** (frontend/src/views/admin/AdminOrders.vue Line 335)：\n```javascript\norder.total_usd + order.service_fee_usd  // ❌ 缺少冻结金额\n```\n\n## 影响示例\n\n**订单 UC202606029504（有到付商品）**：\n- 商品：3990 CNY\n- 国内运费：79 CNY\n- 到付冻结：假设 200 CNY\n- 汇率：6.6299\n- 服务费：61.37 USD\n\n**用户端显示**：\n```\ntotal_usd = (3990 + 79) / 6.6299 = 613.73\n冻结金额 = 200 / 6.6299 = 30.17\n总计 = 613.73 + 61.37 + 30.17 = 705.27 USD ✅\n```\n\n**合作人端/管理员端显示**：\n```\n总计 = 613.73 + 61.37 = 675.10 USD ❌\n差异 = 30.17 USD（缺少冻结金额）\n```\n\n## 修复方案\n\n### 方案 A：修改后端（推荐）\n\n修改 `order.total_usd` 的定义，让它包含所有费用：\n\n```python\n# backend/app/services/order_service.py Line 344\nnew_order = Order(\n    ...\n    total_usd=float(grand_total_usd + frozen_usd),  # 包含服务费和冻结金额\n    ...\n)\n```\n\n然后统一前端显示逻辑：\n```javascript\n// 所有角色都只显示 order.total_usd\norder.total_usd\n```\n\n### 方案 B：修改前端（快速修复）\n\n在合作人端和管理员端也加上冻结金额：\n\n```javascript\n// 合作人端 Line 138\nconst totalFrozenDepositUsd = computed(() =\u003e {\n  let total = 0\n  order.value?.items?.forEach(item =\u003e {\n    if (item.is_freight_collect \u0026\u0026 item.freight_collect_frozen_cny) {\n      total += item.freight_collect_frozen_cny / (order.value.exchange_rate || 7)\n    }\n  })\n  return total\n})\n\n// 显示\norder.total_usd + order.service_fee_usd + totalFrozenDepositUsd\n```\n\n## 完成标准\n\n- [ ] 同一订单在三个角色看到的总金额完全一致\n- [ ] 到付订单的冻结金额正确显示\n- [ ] 非到付订单不受影响\n- [ ] 已有订单数据不需要迁移\n- [ ] 单元测试通过\n- [ ] 在生产环境验证至少 3 个订单\n\n## 测试方案\n\n1. 创建到付订单，检查三个角色显示的总金额\n2. 创建非到付订单，确认金额计算正确\n3. 检查现有订单显示是否正常\n\n## 注意事项\n\n- 修改后端定义可能影响其他逻辑，需要全面测试\n- 如果采用方案 B，需要同时修改合作人端和管理员端\n- 必须验证 `order.total_usd` 的所有引用点\n",
      "due_date": null,
      "id": "d3583d55-e06f-42ad-8381-a5c7d92a030b",
      "identifier": "FET-146",
      "labels": [],
      "metadata": {
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/236"
      },
      "number": 146,
      "parent_issue_id": null,
      "position": -2,
      "priority": "urgent",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "严重Bug：合作人端和管理员端订单总金额少显示到付冻结金额",
      "updated_at": "2026-06-04T05:39:27Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "34d7c53d-bd70-45a8-bbbb-77dbb1da16b5",
      "assignee_type": "agent",
      "created_at": "2026-06-03T15:59:57Z",
      "creator_id": "2e7bc302-5016-48b6-a4b9-728e720ec622",
      "creator_type": "agent",
      "description": "## 背景\n\n`backend/app/api/routes/config.py` 已经提供：\n- `GET /api/config/public` — 返回 `exchange_rate`、`service_fee_rate`、仓库地址等\n- `GET /api/config/exchange-rate` — 返回当日 BOC 钞买价（含 history fallback 到 `.env`）\n- `GET /api/config/exchange-rate/history` — 30 天趋势\n\n`frontend/src/services/config.js` 也已经封装好 `configService.getPublicConfig()` 和 `configService.getExchangeRate()`。\n\n**问题是当前前端 18 处都没用这个 service**，全部硬编码 fallback 7.0 / 7.2。\n\n## 任务\n\n### 1. 决策：单一可信源 = `exchange_rate` 字段\n\n订单创建时已把当时汇率写入 `orders.exchange_rate`（`nullable=False`）。**订单历史金额**应当 100% 信任该字段，不应再用默认值回算。\n\n只有以下情况才回退到默认值/API：\n- `order.exchange_rate` 为 `null` / `undefined`（数据问题）\n- 新建订单时用户尚未填写\n\n### 2. 改造 `frontend/src/config/constants.ts`\n\n```ts\n// 作为兜底常量，**仅**在 API 失败时使用\nexport const DEFAULT_EXCHANGE_RATE = 7.20\nexport const EXCHANGE_RATE_MIN = 6.0\nexport const EXCHANGE_RATE_MAX = 8.0\n```\n\n### 3. 添加运行时校验\n\n在 `frontend/src/services/config.js` 增强 `getExchangeRate()`：\n\n```js\nasync getExchangeRate() {\n  const response = await api.get('config/exchange-rate')\n  const rate = Number(response.data?.rate)\n  if (!Number.isFinite(rate) || rate \u003c 6 || rate \u003e 8) {\n    console.error('[config] invalid rate from API:', response.data)\n    return { ...response.data, rate: DEFAULT_EXCHANGE_RATE, _fallback: true }\n  }\n  return response.data\n}\n```\n\n### 4. 应用启动时预热\n\n在 `frontend/src/main.ts` 或 Pinia store 启动时调用 `configService.getExchangeRate()`，把当前汇率缓存到 store。`DEFAULT_EXCHANGE_RATE` 7.20 只在 API 失败时使用。\n\n### 5. 18 处替换为「字段优先，API 兜底」\n\n```ts\n// 之前\nconst rate = order.exchange_rate || 7.2\n\n// 之后\nimport { useConfigStore } from '@/stores/config'\nconst configStore = useConfigStore()\nconst rate = order.exchange_rate ?? configStore.exchangeRate ?? DEFAULT_EXCHANGE_RATE\n```\n\n### 6. 监控与告警\n\n- 任何 `rate \u003c 6 || rate \u003e 8` 的情况上报 Sentry / 控制台 error\n- 触发兜底（`configStore._fallback`）时打 warning\n\n## 验收\n\n- `getPublicConfig()` 启动时调用一次\n- 订单列表/详情/编辑页面不再硬编码 fallback\n- 断网/后端 500 时控制台有 warning 但页面不崩\n- TypeScript 编译通过\n- 端到端测试：覆盖网络正常/异常/慢响应三种情况\n\n## 关联\n\n- 父 issue：FET-134\n- 前置：FET-143（前端常量已统一）、FET-数据审计（已确认无异常数据）\n\n## 备注\n\n不要在本 issue 内重命名 `EXCHANGE_RATE_CNY_USD` 或重构后端 config service，scope 限定在前端。",
      "due_date": null,
      "id": "6508adca-a30b-4133-824f-0c6c0f66b477",
      "identifier": "FET-145",
      "labels": [],
      "metadata": {
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/238"
      },
      "number": 145,
      "parent_issue_id": "e2645f7d-c3ed-426d-a290-538c64e12f61",
      "position": -2,
      "priority": "medium",
      "project_id": "51ec32b5-848a-496c-a573-1006cb2ec058",
      "start_date": null,
      "status": "done",
      "title": "长期：前端汇率改为从后端 API 获取，移除硬编码",
      "updated_at": "2026-06-03T19:14:20Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "d1e4fe91-fb56-4c47-95d0-818d5f22b5bd",
      "assignee_type": "agent",
      "created_at": "2026-06-01T16:21:09Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "这是一个测试 issue，用于验证 Claude Opus 4.8 和 MiniMax-M3 配置是否正常工作。请架构师回复确认收到。",
      "due_date": null,
      "id": "ab9ab5b8-3f48-4498-86d2-859d1e9b02f0",
      "identifier": "FET-110",
      "labels": [],
      "metadata": {},
      "number": 110,
      "parent_issue_id": null,
      "position": -2,
      "priority": "none",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "测试：验证 Agent 配置",
      "updated_at": "2026-06-01T16:21:49Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "d1e4fe91-fb56-4c47-95d0-818d5f22b5bd",
      "assignee_type": "agent",
      "created_at": "2026-06-01T13:27:22Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "这是一个测试任务，用于验证所有 Agent 升级到 Opus 4.8 后是否能正常工作。\n\n## 任务要求\n\n请在 frontend/src/views/partner/ 目录下创建一个简单的测试文件 TestComponent.vue，内容如下：\n\n```vue\n\u003ctemplate\u003e\n  \u003cdiv class=test-component\u003e\n    \u003ch1\u003eOpus 4.8 测试成功\u003c/h1\u003e\n    \u003cp\u003e当前时间: {{ currentTime }}\u003c/p\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript setup\u003e\nimport { ref } from vue\n\nconst currentTime = ref(new Date().toLocaleString())\n\u003c/script\u003e\n\n\u003cstyle scoped\u003e\n.test-component {\n  padding: 20px;\n  text-align: center;\n}\n\u003c/style\u003e\n```\n\n## 验证标准\n\n- 文件创建成功\n- 代码格式正确\n- 通过代码评审\n- PR 合并到 main\n\n这个任务会经过：架构师分析 → 开发专家实现 → 代码评审 → PR 合并 → 验证专家确认",
      "due_date": null,
      "id": "87a52a25-b2a7-4ee5-a5d9-4f53b1327c1c",
      "identifier": "FET-107",
      "labels": [],
      "metadata": {
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/230"
      },
      "number": 107,
      "parent_issue_id": null,
      "position": -2,
      "priority": "high",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "[测试] 验证 Opus 4.8 升级后的团队协作",
      "updated_at": "2026-06-01T16:08:08Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "79fbfb25-e622-4986-9bb9-21efe499274d",
      "assignee_type": "agent",
      "created_at": "2026-05-28T15:25:58Z",
      "creator_id": "d1e4fe91-fb56-4c47-95d0-818d5f22b5bd",
      "creator_type": "agent",
      "description": "## 任务概述\n\n修复代码检查中发现的后端严重问题（P0问题1-3）。\n\n## 需要修复的问题\n\n### 1. 缺少 domestic_tracking_no 参数\n**文件**: backend/app/services/item_state_transition_service.py:119-125\n\nwarehouse_receive() 方法缺少 domestic_tracking_no 参数，导致前端传递的物流单号无法保存到数据库。\n\n**修复代码**:\n```python\ndef warehouse_receive(\n    self,\n    item_id: str,\n    internal_barcode: str,\n    domestic_tracking_no: str,  # 新增\n    qc_image_url: Optional[str],\n    db_session: Session\n) -\u003e OrderItem:\n    # ...\n    item.domestic_tracking_no = domestic_tracking_no  # 新增赋值\n```\n\n### 2. 缺少条码唯一性检查\n**文件**: backend/app/services/item_state_transition_service.py:141-152\n\n后端未验证 internal_barcode 是否已被使用，可能导致多个商品使用相同条码。\n\n**修复代码**:\n```python\n# 在状态检查之前添加\nexisting_item = db_session.query(OrderItem).filter(\n    OrderItem.internal_barcode == internal_barcode,\n    OrderItem.id != item_id\n).first()\n\nif existing_item:\n    raise ValueError(\n        f\"Internal barcode '{internal_barcode}' is already in use\"\n    )\n```\n\n### 3. API 请求模型缺少字段\n**文件**: backend/app/api/routes/partner_items.py:50-53\n\nWarehouseReceiveRequest 模型缺少 domestic_tracking_no 字段。\n\n**修复代码**:\n```python\nclass WarehouseReceiveRequest(BaseModel):\n    \"\"\"仓库收货请求\"\"\"\n    internal_barcode: str\n    domestic_tracking_no: str  # 新增\n    qc_image_url: Optional[str] = None\n```\n\n## 额外任务\n\n- 添加单元测试，覆盖新增的参数和验证逻辑\n- 测试条码重复场景的错误处理\n\n## 验收标准\n\n- 所有3个P0问题已修复\n- 单元测试通过\n- 代码符合项目规范\n- 无类型错误\n\n## 参考文档\n\n- 完整代码检查报告: CODE_REVIEW_REPORT.md\n- 父issue: FET-92\n\n## 预计时间\n\n2-3小时",
      "due_date": null,
      "id": "cb32b847-e568-4db6-b9bb-05384ca63d36",
      "identifier": "FET-93",
      "labels": [],
      "metadata": {
        "pipeline_status": "merged",
        "pr_number": 222,
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/222"
      },
      "number": 93,
      "parent_issue_id": "78d0dae9-3784-470f-8cec-7af1be7c862b",
      "position": -2,
      "priority": "high",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "修复仓库入库扫码功能 - 后端问题",
      "updated_at": "2026-05-28T20:01:34Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "f1b21d73-ee6a-42a5-8db8-4d91424dfae8",
      "assignee_type": "squad",
      "created_at": "2026-06-06T13:52:01Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "## 问题描述\n\n**页面**：合作人端 - \"确认到货并上传质检照片\" 对话框  \n**影响**：两个扫码按钮点击后没有反应，无法调用摄像头扫码\n\n![问题截图](用户已提供)\n\n## 复现步骤\n\n1. 移动端访问合作人端（96.44.162.210/partner）\n2. 进入订单详情\n3. 点击\"确认到货\"按钮\n4. 在弹出的对话框中，点击蓝色的\"扫码\"按钮\n5. **结果**：没有反应，摄像头没有启动\n\n## 受影响的按钮\n\n### 1. 物流跟踪号扫码按钮\n- **位置**：第一个输入框右侧\n- **预期行为**：调用摄像头扫描物流跟踪号条形码\n\n### 2. 仓库条码扫码按钮\n- **位置**：第二个输入框右侧\n- **预期行为**：调用摄像头扫描仓库条码\n\n## 技术分析\n\n### 可能原因\n\n#### 原因 1：事件绑定问题\n```javascript\n// 检查 WarehouseOrderList.vue 中的扫码按钮绑定\n\u003cbutton @click=\"startScanning('tracking')\"\u003e扫码\u003c/button\u003e\n\u003cbutton @click=\"startScanning('barcode')\"\u003e扫码\u003c/button\u003e\n\n// 可能缺少方法定义或方法内部有错误\nmethods: {\n  startScanning(type) {\n    // 这里可能有问题\n  }\n}\n```\n\n#### 原因 2：html5-qrcode 初始化失败\n```javascript\n// 检查是否正确导入和初始化\nimport { Html5Qrcode } from 'html5-qrcode';\n\n// 可能在移动端初始化失败\nconst scanner = new Html5Qrcode('reader');\n```\n\n#### 原因 3：权限请求问题\n```javascript\n// 移动端摄像头权限可能未正确请求\nnavigator.mediaDevices.getUserMedia({ video: true })\n  .then(stream =\u003e {\n    // 权限获取成功\n  })\n  .catch(err =\u003e {\n    // 权限被拒绝或出错\n    console.error('Camera permission denied:', err);\n  });\n```\n\n#### 原因 4：对话框中的事件冒泡\n```javascript\n// 可能对话框的 @click.stop 阻止了按钮点击\n\u003cel-dialog @click.stop\u003e\n  \u003cbutton @click=\"startScanning\"\u003e扫码\u003c/button\u003e\n\u003c/el-dialog\u003e\n```\n\n## 排查步骤\n\n### Step 1: 检查控制台错误\n```javascript\n// 在移动端打开开发者工具（如果可以）\n// 或者添加错误日志\nconsole.log('Scan button clicked');\nconsole.error('Scan error:', error);\n```\n\n### Step 2: 检查相关组件\n- **文件**：`frontend/src/views/partner/WarehouseOrderList.vue`\n- **关注**：\n  - 扫码按钮的 `@click` 绑定\n  - `startScanning` 方法实现\n  - html5-qrcode 初始化代码\n\n### Step 3: 检查移动端兼容性\n```javascript\n// 检查是否有移动端特定的问题\nif (/Android|iPhone|iPad/i.test(navigator.userAgent)) {\n  // 移动端特殊处理\n}\n```\n\n### Step 4: 检查权限\n```javascript\n// 确保正确请求摄像头权限\nasync requestCameraPermission() {\n  try {\n    const stream = await navigator.mediaDevices.getUserMedia({ \n      video: { facingMode: 'environment' } // 后置摄像头\n    });\n    return true;\n  } catch (err) {\n    console.error('Permission denied:', err);\n    return false;\n  }\n}\n```\n\n## 修复方案\n\n### 方案 A：检查并修复事件绑定\n```vue\n\u003ctemplate\u003e\n  \u003cel-button \n    @click.stop=\"handleScanTracking\"\n    type=\"primary\"\n    size=\"small\"\n  \u003e\n    扫码\n  \u003c/el-button\u003e\n\u003c/template\u003e\n\n\u003cscript\u003e\nmethods: {\n  handleScanTracking() {\n    console.log('Tracking scan button clicked'); // 调试日志\n    this.startScanning('tracking');\n  },\n  \n  async startScanning(type) {\n    console.log('Starting scan for:', type);\n    \n    // 检查权限\n    const hasPermission = await this.requestCameraPermission();\n    if (!hasPermission) {\n      this.$message.error('请允许使用摄像头');\n      return;\n    }\n    \n    // 启动扫码\n    // ...\n  }\n}\n\u003c/script\u003e\n```\n\n### 方案 B：使用移动端优化的扫码库\n```javascript\n// 如果 html5-qrcode 在移动端有问题，考虑使用其他库\nimport { BarcodeScanner } from '@capacitor-community/barcode-scanner';\n\n// 或者使用原生扫码\nconst result = await BarcodeScanner.scan();\n```\n\n### 方案 C：添加 fallback 输入\n```vue\n\u003ctemplate\u003e\n  \u003cdiv\u003e\n    \u003cel-button @click=\"startScanning\"\u003e扫码\u003c/el-button\u003e\n    \u003cel-button @click=\"showManualInput\" plain\u003e手动输入\u003c/el-button\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\n## 测试方案\n\n### 本地测试\n```bash\n# 1. 使用移动端模拟器\nnpm run dev\n# 打开 Chrome DevTools → Toggle device toolbar\n# 选择 iPhone 或 Android 设备\n\n# 2. 测试扫码按钮\n- 点击扫码按钮\n- 检查控制台是否有错误\n- 验证摄像头是否启动\n```\n\n### 真机测试\n1. 部署到测试服务器\n2. 用手机访问 `http://96.44.162.210/partner`\n3. 测试扫码功能\n4. 检查权限弹窗是否正常\n\n## 验证标准\n\n- [ ] 移动端点击扫码按钮有响应\n- [ ] 正确请求摄像头权限\n- [ ] 权限获取后能启动扫码界面\n- [ ] 扫码成功后能正确填充输入框\n- [ ] 扫码失败有友好的错误提示\n- [ ] 添加控制台日志便于调试\n- [ ] 在 Android 和 iOS 设备上都测试通过\n\n## 相关文件\n\n- `frontend/src/views/partner/WarehouseOrderList.vue` - 主要组件\n- `frontend/src/components/ScannerModal.vue` - 扫码组件（如果有）\n\n## 优先级\n\n🔴 高优先级 - 影响合作人入库流程\n",
      "due_date": null,
      "id": "59dd83d2-cb16-4b8d-b6d8-4e52c5872d9c",
      "identifier": "FET-153",
      "labels": [],
      "metadata": {
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/243"
      },
      "number": 153,
      "parent_issue_id": null,
      "position": -1,
      "priority": "high",
      "project_id": null,
      "start_date": null,
      "status": "in_review",
      "title": "Bug：移动端入库确认对话框中的扫码按钮无响应",
      "updated_at": "2026-06-06T14:13:18Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "34d7c53d-bd70-45a8-bbbb-77dbb1da16b5",
      "assignee_type": "agent",
      "created_at": "2026-06-06T11:28:56Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "## 问题描述\n\n最新部署（run 27028836261）失败，错误信息：\n```\nERROR: for fetch-china-backend  Cannot create container for service backend: \nConflict. The container name \"/fetch-china-backend\" is already in use by container \"30c4ca7b3b572e1a642b32e3f22f78643cf3791780710079a98d06c341708f14\"\n```\n\n**关键发现**：PR #240 的完整清理逻辑执行了，但**容器仍然没有被删除**。\n\n## 日志分析\n\n### 第一次尝试失败\n```\n[DEPLOY] Step 1: Removing all fetch-china containers...\n688facf2ae41  # 删除了这个容器\n[DEPLOY] Step 2-7: 完整清理序列\n[DEPLOY] ✓ Full cleanup completed\n```\n\n### 重试时仍然失败\n```\nCreating fetch-china-backend ... error\nContainer name is already in use by container \"30c4ca7b3b572e1a642b32e3f22f78643cf3791780710079a98d06c341708f14\"\n```\n\n**注意**：容器 ID 变了（`688facf2ae41` → `30c4ca7b3b572e1a`），说明旧容器被删了，但**重试时又创建了新的幽灵容器**。\n\n## 根本原因\n\n`docker-compose up` 在构建镜像时**可能会创建临时容器**，这些容器在构建失败时不会自动清理。\n\n当前清理逻辑的问题：\n1. 清理完成后立即执行 `docker-compose up`\n2. `docker-compose up` 开始构建镜像\n3. 构建过程中创建了容器（如 `30c4ca7b3b...`）\n4. 构建成功，但启动容器时名称冲突\n5. 重试时，之前构建创建的容器还在\n\n## 解决方案\n\n### 方案 A：在 docker-compose up 前强制清理\n\n```bash\n# 在每次 docker-compose up 前，强制删除所有 fetch-china 容器\ncleanup_before_compose() {\n    echo \"[DEPLOY] Pre-compose cleanup...\"\n    docker ps -aq --filter \"name=fetch-china\" | xargs -r docker rm -f 2\u003e/dev/null || true\n    docker ps -aq --filter \"ancestor=fetch-china_backend\" | xargs -r docker rm -f 2\u003e/dev/null || true\n    docker ps -aq --filter \"ancestor=fetch-china_frontend\" | xargs -r docker rm -f 2\u003e/dev/null || true\n}\n\n# 主要部署流程\ncleanup_before_compose\ndocker-compose up -d --build\n\n# 如果失败，重试前再次清理\nif [ $? -ne 0 ]; then\n    echo \"[DEPLOY] Retry after full cleanup...\"\n    cleanup_before_compose\n    docker-compose up -d --build\nfi\n```\n\n### 方案 B：使用 --force-recreate 和 --remove-orphans\n\n```bash\n# 强制重建所有容器，清理孤儿容器\ndocker-compose up -d --build --force-recreate --remove-orphans\n```\n\n### 方案 C：分离构建和启动\n\n```bash\n# 先构建镜像（不创建容器）\ndocker-compose build\n\n# 清理所有旧容器\ndocker ps -aq --filter \"name=fetch-china\" | xargs -r docker rm -f\n\n# 然后启动（使用已构建的镜像）\ndocker-compose up -d --no-build\n```\n\n## 推荐方案\n\n**方案 C**（分离构建和启动）最可靠：\n1. `docker-compose build` 只构建镜像，不创建容器\n2. 构建完成后，清理所有旧容器\n3. `docker-compose up --no-build` 使用已构建的镜像启动容器\n\n这样可以确保启动前容器一定是干净的。\n\n## 验证步骤\n\n修改 `scripts/server_deploy.sh`：\n\n```bash\n# 替换现有的 docker-compose up 逻辑\necho \"[DEPLOY] Building images...\"\nif ! docker-compose build; then\n    echo \"[DEPLOY] Build failed, aborting\"\n    exit 1\nfi\n\necho \"[DEPLOY] Cleaning up all containers before starting...\"\ndocker ps -aq --filter \"name=fetch-china\" | xargs -r docker rm -f 2\u003e/dev/null || true\n\necho \"[DEPLOY] Starting containers...\"\nif ! docker-compose up -d --no-build; then\n    echo \"[DEPLOY] Start failed, retrying with full cleanup...\"\n    \n    # 完整清理序列（保留现有的 7 步清理）\n    ...\n    \n    # 再次尝试启动（镜像已经构建好了）\n    docker-compose up -d --no-build\nfi\n```\n\n## 完成标准\n\n- [ ] 修改部署脚本实现方案 C\n- [ ] 本地测试：手动创建幽灵容器，验证能清理\n- [ ] 创建 PR\n- [ ] GitHub Actions 部署测试（至少 3 次成功）\n- [ ] 生产环境验证\n\n## 优先级\n\n🔴 紧急 - 阻塞所有部署\n",
      "due_date": null,
      "id": "ac988c92-5ecd-4c04-b207-1885b5bc655f",
      "identifier": "FET-152",
      "labels": [],
      "metadata": {
        "pr_number": 242,
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/242"
      },
      "number": 152,
      "parent_issue_id": null,
      "position": -1,
      "priority": "urgent",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "部署失败：容器清理后仍然存在幽灵容器（PR #240 未完全解决）",
      "updated_at": "2026-06-06T12:32:06Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "2e7bc302-5016-48b6-a4b9-728e720ec622",
      "assignee_type": "agent",
      "created_at": "2026-06-04T11:24:47Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "![image.png](https://static.multica.ai/workspaces/b5fdce19-2a82-455d-b644-5b83da2b3078/019e9259-2947-7caf-85dc-e9e16d05f1e2.png)\n\n\n\n[http://96.44.162.210/partner](http://96.44.162.210/partner)  \n，当合伙人上传完购买确认截图完成后，可以直接关闭这个对话框，因为从购买到到“确认到货+质检”这个环节要很久。这个页面不需要开着。",
      "due_date": null,
      "id": "56cae52a-fe11-42ba-9537-c8dc10da493d",
      "identifier": "FET-149",
      "labels": [],
      "metadata": {
        "pr_number": 241,
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/241"
      },
      "number": 149,
      "parent_issue_id": null,
      "position": -1,
      "priority": "none",
      "project_id": "51ec32b5-848a-496c-a573-1006cb2ec058",
      "start_date": null,
      "status": "done",
      "title": "合伙人上传完购买确认截图完成后，可以直接关闭这个对话框",
      "updated_at": "2026-06-05T17:06:08Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "f1b21d73-ee6a-42a5-8db8-4d91424dfae8",
      "assignee_type": "squad",
      "created_at": "2026-06-04T05:19:55Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "## 问题描述\n最近 5 次部署全部失败，错误信息：\n```\nERROR: for fetch-china-frontend Cannot start service frontend: \nfailed to bind host port 0.0.0.0:80/tcp: address already in use\n```\n\n## 根本原因\n**旧的 fetch-china-frontend 容器没有被彻底清理**，导致新容器启动时端口 80 冲突。\n\n注意：端口 80 是正常配置（README line 57: \"Port 443 is reserved for Xray. The website runs on Port 80\"）\n\n## 修复方案\n增强 `scripts/server_deploy.sh` 的清理逻辑，在启动新容器前：\n\n1. **停止所有 fetch-china 容器**\n   ```bash\n   docker ps -q --filter \"name=fetch-china\" | xargs -r docker stop\n   ```\n\n2. **删除所有 fetch-china 容器（包括已停止的）**\n   ```bash\n   docker ps -aq --filter \"name=fetch-china\" | xargs -r docker rm -f\n   ```\n\n3. **检查并清理占用端口 80 的任何容器**\n   ```bash\n   PORT_PID=$(docker ps -q --filter \"publish=80\" | head -1)\n   [ -n \"$PORT_PID\" ] \u0026\u0026 docker rm -f \"$PORT_PID\"\n   ```\n\n4. **清理孤立的 Docker 网络**\n   ```bash\n   docker network prune -f\n   ```\n\n5. **等待端口释放**\n   ```bash\n   for i in {1..5}; do\n       if ! lsof -i :80 \u003e/dev/null 2\u003e\u00261; then\n           break\n       fi\n       sleep 2\n   done\n   ```\n\n6. **然后再执行** `docker-compose up -d`\n\n## 验证步骤\n修改后需要测试：\n1. 手动运行部署脚本验证清理逻辑\n2. 在 GitHub Actions 中触发部署验证\n3. 检查部署日志确认没有端口冲突错误\n\n## 优先级\n🔴 高优先级 - 影响生产部署\n\n## 相关文件\n- `scripts/server_deploy.sh` - 部署脚本\n- `docker-compose.yml` - 容器配置\n- `.github/workflows/deploy.yml` - GitHub Actions workflow\n",
      "due_date": null,
      "id": "2fb4e3ee-86a5-46de-a265-8d145f6d963e",
      "identifier": "FET-148",
      "labels": [],
      "metadata": {
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/240"
      },
      "number": 148,
      "parent_issue_id": null,
      "position": -1,
      "priority": "high",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "修复部署失败：增强容器清理逻辑",
      "updated_at": "2026-06-04T05:39:28Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "f1b21d73-ee6a-42a5-8db8-4d91424dfae8",
      "assignee_type": "squad",
      "created_at": "2026-06-04T05:12:46Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "## 问题描述\n最近 5 次部署全部失败，原因是端口 80 被占用，导致 `fetch-china-frontend` 容器无法启动。\n\n## 错误信息\n```\nERROR: for fetch-china-frontend  Cannot start service frontend: \nfailed to bind host port 0.0.0.0:80/tcp: address already in use\n```\n\n## 当前状态\n- **后端容器**：启动成功（端口 8000）\n- **前端容器**：启动失败（端口 80 被占用）\n- **重试机制**：执行了但仍然失败\n\n## 需要执行的任务\n\n### 1. 诊断问题\n```bash\n# SSH 到服务器 96.44.162.210\nssh root@96.44.162.210\n\n# 检查端口 80 被什么占用\nlsof -i :80\nnetstat -tlnp | grep :80\n\n# 检查所有容器状态\ndocker ps -a\n\n# 检查僵尸容器\ndocker ps -a --filter \"status=exited\" --filter \"status=created\"\n```\n\n### 2. 清理占用端口的进程/容器\n```bash\n# 如果是容器占用，强制删除\ndocker rm -f $(docker ps -aq --filter \"publish=80\")\n\n# 如果是其他进程，终止进程\n# 先用 lsof -i :80 找到 PID，然后 kill -9 \u003cPID\u003e\n\n# 清理所有 fetch-china 相关容器\ndocker ps -a | grep fetch-china | awk '{print $1}' | xargs docker rm -f\n```\n\n### 3. 手动触发部署\n清理完成后，通过以下方式之一重新部署：\n\n**方式 1：GitHub Actions 手动触发**\n- 访问 https://github.com/martinyyang/fetch-china/actions/workflows/deploy.yml\n- 点击 \"Run workflow\"\n\n**方式 2：服务器上直接运行部署脚本**\n```bash\ncd /root/fetch-china\nbash scripts/server_deploy.sh\n```\n\n### 4. 验证部署成功\n```bash\n# 检查容器运行状态\ndocker ps\n\n# 应该看到两个容器都在运行：\n# - fetch-china-backend  (端口 8000)\n# - fetch-china-frontend (端口 80)\n\n# 测试网站访问\ncurl -I http://fetchchina.com/\ncurl http://fetchchina.com/api/v1/auth/me\n```\n\n## 参考资料\n- 部署脚本：`/root/fetch-china/scripts/server_deploy.sh`\n- 服务器 IP：`96.44.162.210`\n- SSH 密钥存储在 GitHub Secrets：`VPS_SSH_KEY`\n\n## 优先级\n🔴 **紧急** - 生产环境无法部署新代码\n\n## 预期结果\n1. 端口 80 清理完成\n2. 前后端容器都正常运行\n3. fetchchina.com 可以正常访问\n4. 后续部署不再出现端口占用问题\n",
      "due_date": null,
      "id": "ea4a03b1-e03e-4cca-bdd3-17d31f4c27fd",
      "identifier": "FET-147",
      "labels": [],
      "metadata": {
        "decision": "Fixed server_deploy.sh Step 5 to use docker ps -a (catch stopped containers holding port bindings) + Step 5b to kill non-docker processes via ss/fuser. Merged via PR #239, verified by successful deploy run 26932524734.",
        "pipeline_status": "success",
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/239"
      },
      "number": 147,
      "parent_issue_id": null,
      "position": -1,
      "priority": "urgent",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "🔴 紧急：修复部署失败问题（端口 80 被占用）",
      "updated_at": "2026-06-04T05:39:27Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "259f1110-6ba6-469e-9375-c688b75bf16e",
      "assignee_type": "agent",
      "created_at": "2026-06-03T15:59:55Z",
      "creator_id": "2e7bc302-5016-48b6-a4b9-728e720ec622",
      "creator_type": "agent",
      "description": "## 背景\n\n数据库模型 `backend/app/models/order.py` 中 `exchange_rate = Column(Numeric(8, 4), nullable=False)`。理论上不应存在 NULL，但前端代码 18 处 `|| X` 的防御性编程说明历史上可能存在空值。\n\n## 任务\n\n### 1. 数据体检 SQL（先在只读副本执行）\n\n```sql\n-- 总览\nSELECT\n  COUNT(*)                                              AS total_orders,\n  COUNT(exchange_rate)                                  AS non_null,\n  COUNT(*) - COUNT(exchange_rate)                       AS null_count,\n  MIN(exchange_rate)                                    AS min_rate,\n  MAX(exchange_rate)                                    AS max_rate,\n  AVG(exchange_rate)                                    AS avg_rate,\n  PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY exchange_rate) AS median\nFROM orders;\n\n-- 异常范围\nSELECT id, order_number, exchange_rate, created_at, status\nFROM orders\nWHERE exchange_rate IS NULL\n   OR exchange_rate \u003c 6.0\n   OR exchange_rate \u003e 8.0\nORDER BY created_at DESC\nLIMIT 200;\n\n-- 按月分布（看是否是历史某次事故）\nSELECT DATE_TRUNC('month', created_at) AS month,\n       COUNT(*) FILTER (WHERE exchange_rate IS NULL) AS nulls,\n       COUNT(*) AS total\nFROM orders\nGROUP BY 1\nORDER BY 1;\n```\n\n### 2. 如果发现 NULL 数据\n\n- **不要直接 UPDATE** — 需要人工审查\n- 生成 Excel 报告：`order_number, created_at, status, current_exchange_rate, action, reason`\n- 通知产品/运营决定如何补值：\n  - 按订单创建日的 BOC 钞买价回填\n  - 按当前默认 7.20 回填\n  - 标记为需要人工处理\n\n### 3. 如果发现 \u003c 6.0 或 \u003e 8.0 的异常值\n\n- 几乎可以肯定是数据错误（正常 CNY/USD 不会到这个区间）\n- 列出前 50 条供产品确认\n- 修复方案需审批，不在本 issue 内执行\n\n## 验收\n\n- 输出体检报告（markdown + CSV 附件）\n- 异常数据清单（如有）\n- 不在本 issue 内做修改，只汇报\n\n## 阻塞\n\n需要生产/预发环境的只读 DB 访问权限。\n\n## 关联\n\n- 父 issue：FET-134\n- 前置：FET-143（前端默认值统一）\n- 后续：FET-后端API化（待创建）",
      "due_date": null,
      "id": "aaef7299-380e-4b84-9612-bace1823ea8f",
      "identifier": "FET-144",
      "labels": [],
      "metadata": {
        "blocked_reason": "需要生产数据库只读访问权限",
        "deliverables": "审计脚本、文档、前端代码分析"
      },
      "number": 144,
      "parent_issue_id": "e2645f7d-c3ed-426d-a290-538c64e12f61",
      "position": -1,
      "priority": "high",
      "project_id": "51ec32b5-848a-496c-a573-1006cb2ec058",
      "start_date": null,
      "status": "blocked",
      "title": "数据审计：检查 orders 表 exchange_rate 异常数据",
      "updated_at": "2026-06-03T17:14:25Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "34d7c53d-bd70-45a8-bbbb-77dbb1da16b5",
      "assignee_type": "agent",
      "created_at": "2026-06-03T15:59:13Z",
      "creator_id": "2e7bc302-5016-48b6-a4b9-728e720ec622",
      "creator_type": "agent",
      "description": "## 背景\n\n代码评审（见父 issue）确认前端 18 处 `exchange_rate || X` 的默认值不一致：用户端 7.0/7.2、合作人端 7.0、仓库端 7.2、管理员端无默认值。同一页面同一文件内出现 7.0 与 7.2 共存，差异约 \\$0.40 / 100 CNY。\n\n## 决策\n\n**canonical 默认值 = 7.20**，理由：\n- 与 `backend/.env.example` 中 `EXCHANGE_RATE_CNY_USD=7.20` 一致\n- 接近 `/api/config/exchange-rate`（BOC 钞买价）历史区间（7.10-7.25）\n- 比 7.0 更保守（CNY→USD 时结果更大，避免少收）\n\n## 任务\n\n### 1. 新建统一常量\n- 新建 `frontend/src/config/constants.ts`\n- 导出 `export const DEFAULT_EXCHANGE_RATE = 7.20`\n- 导出 `export const EXCHANGE_RATE_MIN = 6.0` / `EXCHANGE_RATE_MAX = 8.0`（用于校验）\n\n### 2. 批量替换 18 处硬编码\n| 文件 | 处数 | 当前值 |\n|---|---|---|\n| `frontend/src/views/orders/OrderDetailPage.vue` | 2 | 7.2 / 7 |\n| `frontend/src/views/orders/OrderListPage.vue` | 3 | 7 |\n| `frontend/src/views/orders/EditOrderPage.vue` | 1 | 7.00 |\n| `frontend/src/views/partner/PartnerDashboard.vue` | 2 | 7 |\n| `frontend/src/views/partner/OrderDetailPage.vue` | 2 | 7 |\n| `frontend/src/views/warehouse/WarehousePage.vue` | 8 | 7.2 |\n\n替换模式（示例）：\n```ts\n- const rate = order.exchange_rate || 7.2\n+ import { DEFAULT_EXCHANGE_RATE } from '@/config/constants'\n+ const rate = order.exchange_rate ?? DEFAULT_EXCHANGE_RATE\n```\n\n注意用 `??` 替代 `||`，避免 `0` 被误判为 falsy（虽然汇率不可能为 0，但是是更安全的写法）。\n\n### 3. 管理员端 `AdminOrders.vue` 兜底\nLine 337 当前为 `order.exchange_rate?.toFixed(2) || '-'`。\n- 改为 `order.exchange_rate?.toFixed(2) ?? DEFAULT_EXCHANGE_RATE.toFixed(2)`，**不再显示 `-`**，避免三个角色数值缺失。\n\n## 验收\n\n- `grep -rE \"exchange_rate\\s*\\|\\|\" frontend/src` 返回 0 行\n- `grep -rE \"exchange_rate\\s*\\|\\|\\s*7(\\.0)?\\b\" frontend/src` 返回 0 行\n- TypeScript 编译通过\n- 用户/合作人/仓库/管理端访问同一订单显示金额一致\n\n## 关联\n\n- 父 issue：FET-134\n- 后续：FET-数据审计、FET-后端API化（待创建）",
      "due_date": null,
      "id": "21045308-f0ec-41a9-a179-7a442efe489a",
      "identifier": "FET-143",
      "labels": [],
      "metadata": {
        "pr_number": 237,
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/237"
      },
      "number": 143,
      "parent_issue_id": "e2645f7d-c3ed-426d-a290-538c64e12f61",
      "position": -1,
      "priority": "urgent",
      "project_id": "51ec32b5-848a-496c-a573-1006cb2ec058",
      "start_date": null,
      "status": "done",
      "title": "P0 紧急：统一前端汇率默认值为 7.20",
      "updated_at": "2026-06-04T05:39:26Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    },
    {
      "assignee_id": "34d7c53d-bd70-45a8-bbbb-77dbb1da16b5",
      "assignee_type": "agent",
      "created_at": "2026-06-03T15:35:16Z",
      "creator_id": "fd13ba3c-ec28-4992-a69c-72cecfb8cba9",
      "creator_type": "member",
      "description": "## 问题描述\n\n同一个订单，在用户端、合作人端、管理员端显示的总金额不同！\n\n## 根本原因\n\n三个角色的前端代码使用了**不同的计算公式**：\n\n### 1. 用户端\n- **显示总金额**: `total_usd + service_fee_usd + totalFrozenDepositUsd` ✅ 正确\n- **取消退款**: `total_usd + service_fee_usd` ❌ 不包含冻结金额（不一致）\n\n### 2. 合作人端\n- **显示总金额**: `total_usd + service_fee_usd` ❌ 缺少到付冻结金额\n\n### 3. 管理员端\n- **显示总金额**: `total_usd + service_fee_usd` ❌ 缺少到付冻结金额\n\n## 什么是到付冻结金额？\n\n到付商品（`is_freight_collect = true`）需要冻结保证金（`freight_collect_frozen_cny`）。\n- 用户端正确计算并显示\n- 但合作人端和管理员端都没有显示\n\n## 影响范围\n\n1. **财务数据不一致** - 用户看到 $130，合作人和管理员看到 $110\n2. **退款金额错误** - 取消订单时显示退款 $110，实际应该 $130\n3. **管理员调整金额上限错误** - 上限设为 $110，但用户实际支付了 $130\n\n## 修复方案\n\n### 方案一：统一前端计算逻辑（推荐）\n\n新建 `utils/orderCalculation.js` 统一计算函数：\n```javascript\nexport function calculateOrderTotal(order) {\n  const total_usd = Number(order.total_usd || 0)\n  const service_fee_usd = Number(order.service_fee_usd || 0)\n  \n  const rate = order.exchange_rate || 7.2\n  const frozenDepositCny = order.items.reduce((sum, item) =\u003e {\n    return sum + (item.is_freight_collect ? Number(item.freight_collect_frozen_cny || 0) : 0)\n  }, 0)\n  const frozenDepositUsd = frozenDepositCny / rate\n  \n  return {\n    subtotal: total_usd,\n    serviceFee: service_fee_usd,\n    frozenDeposit: frozenDepositUsd,\n    total: total_usd + service_fee_usd + frozenDepositUsd\n  }\n}\n```\n\n### 方案二：后端返回计算好的总金额\n\n修改 `backend/app/schemas/order.py` 添加 `final_total_usd` 字段。\n\n## 完成标准\n\n- [ ] 用户端、合作人端、管理员端显示相同的订单总金额\n- [ ] 取消订单退款金额 = 显示的总金额\n- [ ] 管理员调整金额上限 = 显示的总金额\n- [ ] 到付订单正确包含冻结金额\n- [ ] 单元测试覆盖计算逻辑\n- [ ] PR 已创建并合并到 main\n- [ ] 部署到生产环境\n- [ ] 功能验证通过\n\n## 相关文件\n\n- `frontend/src/views/orders/OrderDetailPage.vue` (用户端)\n- `frontend/src/views/partner/OrderDetailPage.vue` (合作人端)\n- `frontend/src/views/admin/AdminOrders.vue` (管理员端)\n- `backend/app/schemas/order.py` (API响应结构)\n\n## 详细分析\n\n完整的技术分析报告已保存在：`TOTAL_AMOUNT_BUG_REPORT.md`\n\n## 优先级\n\n🔴 **P0 严重 - 立即修复**\n\n原因：\n- 财务数据不一致会导致用户投诉\n- 退款金额错误会导致资金损失\n- 管理员无法正确审核订单金额\n",
      "due_date": null,
      "id": "f83919df-7de9-4064-9bd9-37d7eb526d8f",
      "identifier": "FET-142",
      "labels": [],
      "metadata": {
        "pr_url": "https://github.com/martinyyang/fetch-china/pull/235"
      },
      "number": 142,
      "parent_issue_id": null,
      "position": -1,
      "priority": "urgent",
      "project_id": null,
      "start_date": null,
      "status": "done",
      "title": "严重Bug：订单总金额在不同角色显示不一致",
      "updated_at": "2026-06-04T05:39:25Z",
      "workspace_id": "b5fdce19-2a82-455d-b644-5b83da2b3078"
    }
  ],
  "limit": 20,
  "offset": 0,
  "total": 117
}
