From e9a613dbad9fa4595a4baf8ccc8c7b52282c0509 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Mon, 5 Oct 2026 10:26:24 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=8C=BA=E5=88=86=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=9C=AA=E5=86=B3=E7=8A=B6=E6=80=81=E5=B9=B6?= =?UTF-8?q?=E5=8F=8A=E6=97=B6=E5=85=B3=E9=97=AD=E7=A1=AE=E8=AE=A4=E6=A1=86?= =?UTF-8?q?=20(#29)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_upload.go | 2 +- app_upload_test.go | 4 ++-- docs/03-business-rules-and-glossary.md | 6 +++--- docs/06-troubleshooting.md | 6 +++--- frontend/src/views/ProductListView.vue | 9 +++++---- internal/store/product.go | 2 +- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/app_upload.go b/app_upload.go index c0944b2..0a2be1f 100644 --- a/app_upload.go +++ b/app_upload.go @@ -56,7 +56,7 @@ func (a *App) uploadOneVideo(ctx context.Context, client *erpgo.Client, id strin a.log.Error("商品 %s 视频上传失败:%s", p.ID, code) } pending := func(code string) { - if err := a.db.UpdateProductStatus(p.ID, "", "", store.UploadPending, code); err != nil { + if err := a.db.UpdateProductStatus(p.ID, "", "", store.UploadUnconfirmed, code); err != nil { a.log.Error("商品 %s 更新上传状态失败:%v", p.ID, err) } a.log.Warn("商品 %s 上传结果待确认:%s;再次操作只查询原任务", p.ID, code) diff --git a/app_upload_test.go b/app_upload_test.go index e297aeb..285c02e 100644 --- a/app_upload_test.go +++ b/app_upload_test.go @@ -72,7 +72,7 @@ func TestVideoUploadUsesERPGoAndPersistsOutcome(t *testing.T) { if err != nil { t.Fatal(err) } - want := store.UploadPending + want := store.UploadUnconfirmed if status == "succeeded" { want = store.UploadDone } @@ -249,7 +249,7 @@ func TestProcessingVideoDoesNotCreateUploadOperation(t *testing.T) { } a.uploadOneVideo(context.Background(), client, "internal-1", 1) p, _, err := db.GetProduct("internal-1") - if err != nil || p.UploadStatus != store.UploadPending || p.LastError != "VIDEO_ALREADY_PROCESSING" || putCount != 0 { + if err != nil || p.UploadStatus != store.UploadUnconfirmed || p.LastError != "VIDEO_ALREADY_PROCESSING" || putCount != 0 { t.Fatalf("unexpected upload state: %+v err=%v put=%d", p, err, putCount) } if _, found, err := db.GetUploadOperation("internal-1"); err != nil || found { diff --git a/docs/03-business-rules-and-glossary.md b/docs/03-business-rules-and-glossary.md index 31ee3f9..b91fc71 100644 --- a/docs/03-business-rules-and-glossary.md +++ b/docs/03-business-rules-and-glossary.md @@ -2,15 +2,15 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Business-Rules-and-Glossary wiki_url: https://git.ilapage.cn/chengma/cmsp/wiki/Business-Rules-and-Glossary.- -wiki_revision: 07e5b780520ce93c657ce4ee0504e279abfac89f -synchronized_at: 2026-10-05T02:08:23Z +wiki_revision: 5856776c55b67b5c0b6e10dd25ed8463c2e355bf +synchronized_at: 2026-10-05T02:24:26Z # 业务规则与术语 ## 指定商品视频上传规则(2026-09-30) -单商品显示远端已有视频的覆盖警告;批量发现远端已有视频则跳过。上传文件取 Shopee ID 子目录下排序首个 mp4,先完成时长、格式、像素与大小校验。写入前在 SQLite 持久化幂等键。HTTP 202 和 `processing`/`unknown` 均不代表成功;再次操作仅查询原键,`succeeded` 才更新本地视频与上传状态。ERPGo 预检返回 200 且 `video: []` 时才确认远端当前无视频;返回 502 / `HHH_UPSTREAM_ERROR` / `stage=check` 表示视频状态未知,显示“视频状态无法确认,上传未提交”,不创建上传操作、不发送 PUT,保留本地 MP4 与 requestId。批量路径的该商品上传状态为 `unconfirmed`,不记作上传失败。`tempVideoUrl` 或 `videoUploadIdStr` 非空表示远端仍有处理中标记:单商品预览停止,执行前再次检查;批量遇到该商品标为待处理,不创建新操作、不发送 PUT。只有普通已存在视频且没有处理中标记时,单商品才可在明确覆盖确认后继续。 +单商品显示远端已有视频的覆盖警告;批量发现远端已有视频则跳过。上传文件取 Shopee ID 子目录下排序首个 mp4,先完成时长、格式、像素与大小校验。写入前在 SQLite 持久化幂等键。HTTP 202 和 `processing`/`unknown` 均不代表成功;再次操作仅查询原键,`succeeded` 才更新本地视频与上传状态。ERPGo 预检返回 200 且 `video: []` 时才确认远端当前无视频;返回 502 / `HHH_UPSTREAM_ERROR` / `stage=check` 表示视频状态未知,显示“视频状态无法确认,上传未提交”,不创建上传操作、不发送 PUT,保留本地 MP4 与 requestId。批量路径的该商品上传状态为 `unconfirmed`,不记作上传失败。`tempVideoUrl` 或 `videoUploadIdStr` 非空表示远端仍有处理中标记:单商品预览停止,执行前再次检查;批量遇到该商品标为待处理,不创建新操作、不发送 PUT。只有普通已存在视频且没有处理中标记时,单商品才可在明确覆盖确认后继续。用户确认后弹窗立即关闭,上传按钮在调用期间继续显示运行中;弹窗关闭不代表远端成功。远端操作 processing/unknown 或操作查询异常保留原键,商品上传状态为 `unconfirmed`,列表显示“待确认”;只有本次操作返回 `succeeded` 才显示“已上传”。 ## 未选择店铺的同步范围(2026-09-29,#26) diff --git a/docs/06-troubleshooting.md b/docs/06-troubleshooting.md index f3dff74..5877bc6 100644 --- a/docs/06-troubleshooting.md +++ b/docs/06-troubleshooting.md @@ -2,15 +2,15 @@ generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Troubleshooting wiki_url: https://git.ilapage.cn/chengma/cmsp/wiki/Troubleshooting -wiki_revision: b73573e8b1db7c2179e7063dc4ab5c16e0fc40d3 -synchronized_at: 2026-10-05T02:08:24Z +wiki_revision: e3146f8c3a628e166bc54be6192930b3340cb4cd +synchronized_at: 2026-10-05T02:24:28Z # 故障排查 ## 视频上传经 erpgo(2026-09-30) -先核对参数设置中的 erpgo 地址和 API Key。视频目录使用 Shopee 商品 ID;需 mp4、10—60 秒、宽高不超过 1280、文件不超过 30 MB。操作不确定时 SQLite 会保留原幂等键;重复点击只查询原操作,不会重新提交。`succeeded` 是 erpgo 对本次货憨憨视频关联的回读结果,Shopee 页面仍需人工验收。预览若出现 502 / `HHH_UPSTREAM_ERROR` / `stage=check`,界面提示“视频状态无法确认,上传未提交”并保留 requestId;此时 ERPGo 未确认货憨憨 `video` 字段,cmsp 不会发送 PUT。不能把字段缺失当成 `video: []`,排除上游回读问题后再手动重试。若返回 `VIDEO_ALREADY_PROCESSING`,等待 ERPGo 核对处理中标记及原操作,不新建幂等键。cmsp 的 ERPGo 客户端直连配置的服务地址,避免本机环境代理截断 MP4 PUT;若仍有 `NETWORK_ERROR`,按请求时段核对网络、代理入口、ERPGo 应用日志和原键操作查询。`VIDEO_OPERATION_NOT_FOUND` 不自动证明视频未提交,不删除本地记录或换键重放。运维用另一操作成功上传的视频不自动认领为 cmsp 原操作成功。 +先核对参数设置中的 erpgo 地址和 API Key。视频目录使用 Shopee 商品 ID;需 mp4、10—60 秒、宽高不超过 1280、文件不超过 30 MB。操作不确定时 SQLite 会保留原幂等键;重复点击只查询原操作,不会重新提交。`succeeded` 是 erpgo 对本次货憨憨视频关联的回读结果,Shopee 页面仍需人工验收。预览若出现 502 / `HHH_UPSTREAM_ERROR` / `stage=check`,界面提示“视频状态无法确认,上传未提交”并保留 requestId;此时 ERPGo 未确认货憨憨 `video` 字段,cmsp 不会发送 PUT。不能把字段缺失当成 `video: []`,排除上游回读问题后再手动重试。若返回 `VIDEO_ALREADY_PROCESSING`,等待 ERPGo 核对处理中标记及原操作,不新建幂等键。cmsp 的 ERPGo 客户端直连配置的服务地址,避免本机环境代理截断 MP4 PUT;若仍有 `NETWORK_ERROR`,按请求时段核对网络、代理入口、ERPGo 应用日志和原键操作查询。`VIDEO_OPERATION_NOT_FOUND` 不自动证明视频未提交,不删除本地记录或换键重放。运维用另一操作成功上传的视频不自动认领为 cmsp 原操作成功。确认上传后弹窗应立即关闭,上传按钮继续转圈直到当前调用结束。列表“待确认”表示视频状态或本次操作结果尚未证实;即使货憨憨当前有视频,也要按原幂等键查询 ERPGo 操作状态。`processing/check/submitted` 不能改为已上传,尤其是商品此前已有其他操作的视频;等待 ERPGo 核对本次操作的素材归属,只有 `succeeded` 才更新本地完成状态。 ## 全店铺同步与部分成功(2026-09-29,#26) diff --git a/frontend/src/views/ProductListView.vue b/frontend/src/views/ProductListView.vue index 41bfd0d..bf3f026 100644 --- a/frontend/src/views/ProductListView.vue +++ b/frontend/src/views/ProductListView.vue @@ -85,7 +85,7 @@ const uploadStatusOptions = [ { label: '上传中', value: 'running' }, { label: '已上传', value: 'done' }, { label: '上传失败', value: 'failed' }, - { label: '视频状态未确认', value: 'unconfirmed' }, + { label: '待确认', value: 'unconfirmed' }, { label: '已有视频', value: 'existing' }, { label: '缺少视频', value: 'missing' }, { label: '视频不合规', value: 'invalid' }, @@ -379,7 +379,7 @@ const uploadText = { running: ['上传中', 'primary'], done: ['已上传', 'ok'], failed: ['失败', 'bad'], - unconfirmed: ['视频状态未确认', 'warn'], + unconfirmed: ['待确认', 'warn'], existing: ['已有视频', 'muted'], missing: ['缺少视频', 'warn'], invalid: ['视频不合规', 'bad'], @@ -677,8 +677,9 @@ async function uploadSelectedVideo() { ]), positiveText: '确认上传', negativeText: '取消', - onPositiveClick: async () => { - await run('上传数据', () => UploadVideos([...checkedIds.value])) + onPositiveClick: () => { + // 让确认框立即关闭;按钮与日志继续反映上传调用的执行状态。 + void run('上传数据', () => UploadVideos([...checkedIds.value])) }, }) } catch (err) { diff --git a/internal/store/product.go b/internal/store/product.go index 4cc76da..107e74d 100644 --- a/internal/store/product.go +++ b/internal/store/product.go @@ -35,7 +35,7 @@ const ( UploadRunning = "running" // 上传中 UploadDone = "done" // 已上传 UploadFailed = "failed" // 失败 - UploadUnconfirmed = "unconfirmed" // 远端当前视频状态无法确认,上传未提交 + UploadUnconfirmed = "unconfirmed" // 远端当前视频或本次上传操作结果待确认 UploadSkippedExisting = "existing" // 货憨憨已有视频,批量时跳过 UploadMissingVideo = "missing" // 子目录里没有 mp4 UploadInvalidVideo = "invalid" // 有文件但不符合货憨憨要求