d1149b6
0fe24f7
16e4f0f
origin/main
#17 实现的上传只支持一次一个商品,且强制 len(productIDs) == 1。 选定文件的方式是「取该商品 videos 表中第一个 status='downloaded' 且文件真实存在的记录」——依赖 videos 表。
len(productIDs) == 1
videos
status='downloaded'
这带来两个问题:
video_dir
批量勾选商品后一次点击完成上传。以磁盘为事实来源,不查 videos 表、 不关心视频来源。上传前用 ffprobe 按货憨憨的四条规则本地预检, 不合规的跳过而不是白传。
upload_status
local_path
「上传数据」对每个勾选的商品串行执行,任一分支结束后继续下一个商品:
1. 读远端状态:CheckShopProductVideo(product.ID) ← 只读 已有视频(Confirmed() 为真) └─ 勾选数 == 1 → 确认框红字警告,使用者确认后覆盖上传 └─ 勾选数 > 1 → upload_status = existing,跳过 2. 扫描磁盘:<video_dir>/<SafeDirName(itemId)>/*.mp4,按文件名升序 没有文件 → upload_status = missing,跳过 3. 本地预检(ffprobe,见下表) 不合规 → upload_status = invalid,last_error 写明违反哪一条,跳过 4. 上传:uploadFiles → batchUpdateShopProductVideo → 回读校验 成功 → upload_status = done,补写 videos 记录 失败 → upload_status = failed,last_error 记中文原因
逐个串行,不要用接口的数组批量能力。 接口支持一次传多个商品, 但那样某个商品失败时无法定位是哪个,错误也没法精确写回。 素材上传本来也是一个文件一次。
单商品失败不是全局停止门:网络错误、货憨憨返回失败、回读不通过, 都只影响当前商品,记录后继续下一个。这一点与淘宝登录失效的全局停止门不同。
batchUpdateShopProductVideo 是覆盖语义,一个商品只挂一个视频, 传新地址即替换旧的,且不可恢复。
batchUpdateShopProductVideo
判断依据用实时读取的远端状态,不用本地 video_diagnosis—— 后者只在「下载数据」时刷新,可能是几天前的。多一次只读请求, 批量二三十个的代价可以忽略。
video_diagnosis
30 * 1024 * 1024
os.Stat
10.0 <= duration <= 60.0
format_name
mp4
width
height
时长必须严格判定:本地已有的 49365131605_1.mp4 是 9.985011 秒, 差 0.015 秒卡在门槛下,任何取整都会把它误判为合规。
49365131605_1.mp4
internal/downloader 现有的 ProbeResult 只有 Duration / Size / FormatName,需要扩展出 Width / Height。ffprobe 调用命令相应调整, 保持现有的「ffprobe 找不到就报明确中文错误,不静默跳过」行为。
internal/downloader
ProbeResult
Duration
Size
FormatName
Width
Height
预检失败的 last_error 必须写明违反了哪一条,例如 「视频时长 9.99 秒,不满足 10—60 秒要求」,不能只写「视频不合规」。
last_error
Go 侧常量沿用现有英文风格(pending / running / done / failed):
pending
running
done
failed
UploadSkippedExisting
existing
UploadMissingVideo
missing
UploadInvalidVideo
invalid
前端「上传状态」列与筛选下拉框补上这三项。 前端按状态值分支,不得按中文文本分支(AGENTS.md)。
video_status = 'none' 曾经因为被断点逻辑永久跳过而造成过问题(见 #15)。 本工单不得重蹈覆辙:
video_status = 'none'
界面上也不得因为这些状态禁用勾选框。
批量时不能逐个弹窗,改为一次汇总。内容至少包含:
将处理 17 个商品 可上传 12 个,共 38.4 MB 缺少视频 3 个 视频不合规 2 个(时长或像素不符合货憨憨要求) 已有视频 0 个,将跳过 无法查询货憨憨剩余容量(接口未确认)。图片空间已用约 98.7%, 剩余约 26 GB,请自行确认后再继续。
前三类的统计由扫盘和本地预检得出,不需要联网; 「已有视频」需要读远端,在确认框阶段不做(否则弹框前要等 N 次请求), 放到实际执行时逐个读。确认框里对这一项写「执行时逐个检查,已有视频的会跳过」。
不实现、不猜测、不调用任何容量查询接口。 确认框保留上面那行静态提示,除此之外不做任何容量相关的判断或分支。 这是负责人的明确决定,不是遗漏。
手工放进目录的视频在 videos 表里没有记录。上传成功后按 local_path 写入或更新一条:
product_id
file_size
remote_url
status = uploaded
source_item
ReplaceVideos
批量可能耗时较久,需要进度反馈。复用现有的 logx 运行日志即可, 每个商品处理完输出一行结果。不要新建一套任务队列或进度事件—— internal/task 的 Runner 是为取视频设计的(串行 CDP + 并行下载), 上传是纯串行 HTTP,套上去反而复杂。上传期间禁用「下载数据」「下载视频」按钮。
internal/task
预计修改文件:
internal/downloader/downloader.go
downloader_test.go
internal/store/product.go
product_test.go
internal/store/video.go
video_test.go
app.go
frontend/src/views/ProductListView.vue
不新增第三方依赖。internal/ 下不得 import wails。不新增 migration。
internal/
<video_dir>/<SafeDirName(itemId)>/*.mp4
upload_status = missing
upload_status = invalid
upload_status = existing
go vet ./... go build ./... go test ./internal/... -v go test ./... cd frontend && npx vite build
全部用 httptest 假服务器,绝对不许连真实货憨憨。
httptest
单元测试需覆盖:
30*1024*1024
真机验证(负责人执行):
55066525387
git revert
videoUrl
提交:c6bafd9 feat: 批量上传视频,以磁盘为准并按货憨憨规则本地预检 (#19) 已推送到 origin/main。
c6bafd9
改动文件(8 个)
_test.go
FindFirstMP4
UploadValidationError
UpsertUploadedVideo
findUploadVideo
validateUploadVideo
Wails 绑定变更:GetUploadPreview(productIDs []string) (UploadPreview, error) 返回值由单商品详情改为批量汇总。UploadVideos 签名未变。 下次运行前需重启 run_dev.bat 重新生成绑定。
GetUploadPreview(productIDs []string) (UploadPreview, error)
UploadVideos
run_dev.bat
验证结果
go vet ./... 通过 go build ./... 通过 go test ./... 全部通过 npx vite build 通过(仅原有 chunk 体积警告)
11 项硬性验收逐条复核,全部通过:实施全程未向 www.huohanhan.com 发出请求; 选文件只依据磁盘扫描(findUploadVideo 不查 videos 表); 时长判定为 Duration < 10.0 || > 60.0 直接比较,math.Round 仅用于错误文案; existing 分支在任何写请求之前 return;三个新状态全项目只写不读、 不存在按 upload_status 排除商品的逻辑;补写记录用 UpsertUploadedVideo 而非 ReplaceVideos;Go 侧无任何容量逻辑;前端无绕过绑定的写法且按状态值分支; 未新增 migration、依赖、未 import wails;未修改 internal/huohanhan/、 internal/task/、internal/store/store.go;未改动 cmsp.db、payloads/、 config.yaml。
Duration < 10.0 || > 60.0
math.Round
internal/huohanhan/
internal/task/
internal/store/store.go
cmsp.db
payloads/
config.yaml
预检边界测试(Test上传视频本地预检边界)
Test上传视频本地预检边界
9.985 秒不合规 ✓ 10 秒合规 ✓ 60 秒合规 ✓ 60.01 秒不合规 ✓ 30MB 合规 ✓ 多 1 字节不合规 ✓ 1281×1280 不合规 ✓ 1280×1281 不合规 ✓ matroska 容器不合规 ✓
对当前库内 4 个商品的预期行为
40483130206
49365131605
47117207389
视频/
47117207389/
最后一个需要注意:该视频是改子目录规则之前下载的,文件名为 淘宝-1126520796908732416-916174578078-1.mp4,位于视频根目录。 新逻辑只扫子目录,因此会判为「缺少视频」。 需要上传的话得把文件移入 <video_dir>/47117207389/ 并改名。
淘宝-1126520796908732416-916174578078-1.mp4
<video_dir>/47117207389/
未验证内容
待负责人真机验证
特别提醒:55066525387 上传成功后视频在货憨憨消失,原因至今未查明 (该视频完全符合四条规则)。本次批量验证时请重点观察 40483130206 上传后过一段时间视频是否仍在。若再次消失, 应暂停批量并单独排查该问题,本工单不解决它。
文档影响:声明需更新业务规则与术语、架构与代码地图两处 Wiki。 尚未执行,待验收通过后与 #15 / #17 / #18 一并处理。
工单保持「待验收」。
No dependencies set.
The note is not visible to the blocked user.
基本信息
依赖与并行
#17 的
d1149b6、0fe24f7与 #18 的16e4f0f均已推送到origin/main,本工单在其之上修改。#17 的真机验收关注上传链路本身,与本工单新增的
批量调度、磁盘扫描和本地预检不重叠。
子项目影响
原始需求
上传状态改为缺少视频,有视频就上传,然后继续。」
子目录里的视频文件是否存在,不存在,改状态,存在就上传。」
3、视频格式:mp4;4、视频像素要求:像素宽高不超过 1280px * 1280px」
要解决什么
现状
#17 实现的上传只支持一次一个商品,且强制
len(productIDs) == 1。选定文件的方式是「取该商品
videos表中第一个status='downloaded'且文件真实存在的记录」——依赖
videos表。这带来两个问题:
这类文件在
videos表里没有记录,当前实现看不见它们。(手动挪文件、换
video_dir、外部程序写入)。目标
批量勾选商品后一次点击完成上传。以磁盘为事实来源,不查
videos表、不关心视频来源。上传前用 ffprobe 按货憨憨的四条规则本地预检,
不合规的跳过而不是白传。
做什么 / 不做什么
videos表upload_status取值及对应的界面显示与筛选local_path补写videos记录upload_status跳过的断点逻辑(原因见下)已确认方案
处理流程
「上传数据」对每个勾选的商品串行执行,任一分支结束后继续下一个商品:
逐个串行,不要用接口的数组批量能力。 接口支持一次传多个商品,
但那样某个商品失败时无法定位是哪个,错误也没法精确写回。
素材上传本来也是一个文件一次。
单商品失败不是全局停止门:网络错误、货憨憨返回失败、回读不通过,
都只影响当前商品,记录后继续下一个。这一点与淘宝登录失效的全局停止门不同。
覆盖策略(负责人 2026-09-03 确认)
batchUpdateShopProductVideo是覆盖语义,一个商品只挂一个视频,传新地址即替换旧的,且不可恢复。
判断依据用实时读取的远端状态,不用本地
video_diagnosis——后者只在「下载数据」时刷新,可能是几天前的。多一次只读请求,
批量二三十个的代价可以忽略。
本地预检规则
30 * 1024 * 1024字节)os.Stat或 ffprobe10.0 <= duration <= 60.0,不取整、不四舍五入format_name含mp4width/height时长必须严格判定:本地已有的
49365131605_1.mp4是 9.985011 秒,差 0.015 秒卡在门槛下,任何取整都会把它误判为合规。
internal/downloader现有的ProbeResult只有Duration/Size/FormatName,需要扩展出Width/Height。ffprobe 调用命令相应调整,保持现有的「ffprobe 找不到就报明确中文错误,不静默跳过」行为。
预检失败的
last_error必须写明违反了哪一条,例如「视频时长 9.99 秒,不满足 10—60 秒要求」,不能只写「视频不合规」。
新增的 upload_status 取值
Go 侧常量沿用现有英文风格(
pending/running/done/failed):UploadSkippedExistingexistingUploadMissingVideomissingUploadInvalidVideoinvalid前端「上传状态」列与筛选下拉框补上这三项。
前端按状态值分支,不得按中文文本分支(AGENTS.md)。
★ 这三个状态不得成为断点 ★
video_status = 'none'曾经因为被断点逻辑永久跳过而造成过问题(见 #15)。本工单不得重蹈覆辙:
upload_status是missing/invalid/existing/failed就把商品排除在处理之外重新读远端,状态随之刷新
下次点上传自然就会得到新结果
界面上也不得因为这些状态禁用勾选框。
确认框
批量时不能逐个弹窗,改为一次汇总。内容至少包含:
前三类的统计由扫盘和本地预检得出,不需要联网;
「已有视频」需要读远端,在确认框阶段不做(否则弹框前要等 N 次请求),
放到实际执行时逐个读。确认框里对这一项写「执行时逐个检查,已有视频的会跳过」。
容量(负责人 2026-09-03 明确决定:不做门禁)
不实现、不猜测、不调用任何容量查询接口。
确认框保留上面那行静态提示,除此之外不做任何容量相关的判断或分支。
这是负责人的明确决定,不是遗漏。
上传成功后补写 videos 记录
手工放进目录的视频在
videos表里没有记录。上传成功后按local_path写入或更新一条:
product_id、local_path、file_size、remote_url、status = uploadedsource_item留空——磁盘上还原不出视频来自哪个淘宝同款local_path的记录则更新,不重复插入ReplaceVideos:那会清掉该商品的其它视频记录进度反馈
批量可能耗时较久,需要进度反馈。复用现有的 logx 运行日志即可,
每个商品处理完输出一行结果。不要新建一套任务队列或进度事件——
internal/task的 Runner 是为取视频设计的(串行 CDP + 并行下载),上传是纯串行 HTTP,套上去反而复杂。上传期间禁用「下载数据」「下载视频」按钮。
预计修改文件:
internal/downloader/downloader.go、downloader_test.go(ProbeResult 增加宽高)internal/store/product.go、product_test.go(三个新状态常量)internal/store/video.go、video_test.go(按 local_path 写入或更新)app.go(批量调度、扫盘、预检)frontend/src/views/ProductListView.vue(汇总确认框、状态显示与筛选)不新增第三方依赖。
internal/下不得 import wails。不新增 migration。需求变化记录
videos表video_diagnosis可能过期设计与原型门禁
无新页面、无导航变化;核心是后端调度与校验逻辑。
文档影响
upload_status取值、「批量永不覆盖、单个可覆盖」的边界、以及这三个状态不作为断点的约定
交付文档影响
任务记录与可选快照
验收标准
<video_dir>/<SafeDirName(itemId)>/*.mp4,不查videos表upload_status = missing,跳过upload_status = invalid,last_error写明违反哪一条,跳过upload_status = existing,跳过,不覆盖local_path写入或更新videos记录,source_item留空,不得使用
ReplaceVideos,不清掉该商品的其它视频记录upload_status跳过商品的断点逻辑;这些状态不禁用勾选internal/未 import wails验证方式
全部用
httptest假服务器,绝对不许连真实货憨憨。单元测试需覆盖:
30*1024*1024字节合规、多 1 字节不合规last_error包含具体数值和规则videos记录被写入/更新,该商品的其它videos记录未被删除真机验证(负责人执行):
风险和回退
逐个串行便于定位;确认框汇总;建议先试 3—5 个。
实际影响是批量上传可能中途因容量不足而连续失败——但失败是单商品失败,
会记录并继续,不会写坏数据。
55066525387上传成功后视频在货憨憨消失,原因至今未查明(该视频完全符合四条规则,不是格式问题)。若批量后大面积复现,
需要暂停并单独排查。本工单不解决这个问题。
git revert即可。已写入货憨憨的视频不会因回退而撤销,需要时在货憨憨界面把
videoUrl清空。最终实施证据
提交:
c6bafd9feat: 批量上传视频,以磁盘为准并按货憨憨规则本地预检 (#19)已推送到
origin/main。改动文件(8 个)
internal/downloader/downloader.go/_test.goProbeResult增加Width/Height;FindFirstMP4扫盘;UploadValidationError四条规则预检internal/store/product.go/_test.goexisting/missing/invalid,上传状态筛选internal/store/video.go/_test.goUpsertUploadedVideo(按product_id+local_path写入或更新)app.gofindUploadVideo(只扫盘)、validateUploadVideo、四条分支frontend/src/views/ProductListView.vueWails 绑定变更:
GetUploadPreview(productIDs []string) (UploadPreview, error)返回值由单商品详情改为批量汇总。
UploadVideos签名未变。下次运行前需重启
run_dev.bat重新生成绑定。验证结果
11 项硬性验收逐条复核,全部通过:实施全程未向 www.huohanhan.com 发出请求;
选文件只依据磁盘扫描(
findUploadVideo不查videos表);时长判定为
Duration < 10.0 || > 60.0直接比较,math.Round仅用于错误文案;existing分支在任何写请求之前 return;三个新状态全项目只写不读、不存在按
upload_status排除商品的逻辑;补写记录用UpsertUploadedVideo而非
ReplaceVideos;Go 侧无任何容量逻辑;前端无绕过绑定的写法且按状态值分支;未新增 migration、依赖、未 import wails;未修改
internal/huohanhan/、internal/task/、internal/store/store.go;未改动cmsp.db、payloads/、config.yaml。预检边界测试(
Test上传视频本地预检边界)对当前库内 4 个商品的预期行为
4048313020649365131605invalid,跳过55066525387existing,批量时跳过47117207389视频/根目录,不在47117207389/子目录missing,跳过最后一个需要注意:该视频是改子目录规则之前下载的,文件名为
淘宝-1126520796908732416-916174578078-1.mp4,位于视频根目录。新逻辑只扫子目录,因此会判为「缺少视频」。
需要上传的话得把文件移入
<video_dir>/47117207389/并改名。未验证内容
httptest假服务器覆盖,从未与真实货憨憨交互。待负责人真机验证
run_dev.bat(绑定签名变了)特别提醒:
55066525387上传成功后视频在货憨憨消失,原因至今未查明(该视频完全符合四条规则)。本次批量验证时请重点观察
40483130206上传后过一段时间视频是否仍在。若再次消失,应暂停批量并单独排查该问题,本工单不解决它。
文档影响:声明需更新业务规则与术语、架构与代码地图两处 Wiki。
尚未执行,待验收通过后与 #15 / #17 / #18 一并处理。
工单保持「待验收」。