打通 Client↔Admin 闭环的一环:录入 PDD 链接 → 创建采集任务 → 客户端领走去采 → 规格和价格显示在页面上。#16 写的 MarkCollecting 和 SoftDeletePddProduct 至此才有生产调用方。 链接解析严格、不做容错兜底:goods_id 上有 UNIQUE 约束,防重全靠它。 猜一个的话同一商品会存成好几行、采好几遍,规格映射还说不清指向哪一行。 短链一律拒绝,卡域名是为了拦"粘了淘宝链接"这种失误。 创建采集任务先占状态再建任务:MarkCollecting 只在 pending/failed 时成功, 同时充当"有没有人已经在采"的判断,与建任务在同一事务里, 所以并发点多次只会建出一个任务(实测 4 并发 → 1 条)。 submit.go 的 collectedData 增加 Dimensions —— 没有它就只能按 Go 的 map 遍历,而 map 无序,同一商品每次刷新"颜色/尺码"的先后都可能变。 顺带修 #16 一处缺陷:EnsurePddProduct 复活分支清空了 skus_json 却漏了 title,导致复活后状态显示"未采集"但标题还留着旧值。 审查打回一次:清理"PDD 链接唯一的录入口"这一过期说法(全库 5 处), 以及 shopee.go 里"空 → no_link"的过期 TODO —— no_link 已在 #16 从 CHECK 约束删除,照写会直接撞约束。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
93 lines
3.5 KiB
HTML
93 lines
3.5 KiB
HTML
{{define "pdd/edit_modal"}}
|
||
{{/* 双击一行时弹出的内容。
|
||
这是一个**片段**,不是整页——外面的弹窗壳子在 pdd/list.html 里。
|
||
|
||
规格表由服务端渲染是有意的:维度顺序、价格格式、price_cent 为 null
|
||
的处理都是业务规则,放到前端去拼就没人维护得住了。 */}}
|
||
{{with .D}}
|
||
<div class="modal-head">
|
||
<h2>PDD 商品 {{.GoodsID}}</h2>
|
||
<button type="button" class="modal-x" data-modal-close aria-label="关闭">×</button>
|
||
</div>
|
||
|
||
{{/* 「重新采集」自己一个表单,只提交这一个商品。
|
||
HTML 不允许表单套表单,所以它写在保存表单外面,
|
||
按钮靠 form="pdd-recollect-form" 挂过来。 */}}
|
||
<form id="pdd-recollect-form" method="post" action="/pdd/collect" hidden>
|
||
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
|
||
<input type="hidden" name="ids" value="{{.GoodsID}}">
|
||
</form>
|
||
|
||
<form method="post" action="/pdd/save">
|
||
<input type="hidden" name="csrf_token" value="{{$.CSRFToken}}">
|
||
<input type="hidden" name="id" value="{{.ID}}">
|
||
|
||
<div class="modal-body">
|
||
<dl class="detail">
|
||
<dt>商品 ID</dt><dd>{{.GoodsID}}<small>(只读,由链接解析得出,不能改)</small></dd>
|
||
|
||
<dt><label for="detail-url">PDD 链接</label></dt>
|
||
<dd><input id="detail-url" type="text" name="url" value="{{.URL}}"
|
||
autocomplete="off" class="wide"></dd>
|
||
|
||
<dt>标题</dt><dd>{{.Title}}<small>(只读,采集后自动回填)</small></dd>
|
||
<dt>采集状态</dt><dd>{{.StatusText}}</dd>
|
||
<dt>失败原因</dt><dd>{{.CollectMsg}}</dd>
|
||
<dt>采集时间</dt><dd>{{.CollectedAt}}</dd>
|
||
{{if .ArtifactRef}}
|
||
{{/* 诊断产物只报位置、不上传文件:告诉操作员去哪台机器的哪个目录捞截图 */}}
|
||
<dt>诊断产物</dt><dd><code>{{.ArtifactRef}}</code></dd>
|
||
{{end}}
|
||
</dl>
|
||
|
||
<h3>规格{{if .Collected}}({{len .SKUs}} 个){{end}}</h3>
|
||
|
||
{{if .SkusError}}
|
||
<p class="missing">{{.SkusError}}</p>
|
||
{{else if not .Collected}}
|
||
<p class="hint">
|
||
尚未采集。关掉这个弹窗,勾选这一行再点「创建采集任务」,
|
||
等客户端领走执行完,规格和价格就会出现在这里。
|
||
</p>
|
||
{{else if not .SKUs}}
|
||
<p class="missing">
|
||
采集完成但一个规格都没有。这通常说明商品已下架,或者采集时没点开规格面板——
|
||
建议重新采集一次。
|
||
</p>
|
||
{{else}}
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
{{/* 列顺序按采集结果里的 dimensions 定,不是 Go map 的随机顺序 */}}
|
||
{{range .DimensionNames}}<th>{{.}}</th>{{end}}
|
||
<th>价格</th>
|
||
<th>有货</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{{range .SKUs}}
|
||
<tr>
|
||
{{range .Options}}<td>{{.}}</td>{{end}}
|
||
{{/* 价格底层存的是整数分。采不到价格显示「未采到」,
|
||
绝不显示 ¥0.00——那会让人以为是白菜价 */}}
|
||
<td>{{.PriceText}}</td>
|
||
<td>{{.Available}}</td>
|
||
</tr>
|
||
{{end}}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
{{end}}
|
||
</div>
|
||
|
||
<div class="modal-foot">
|
||
<button type="submit" form="pdd-recollect-form">重新采集</button>
|
||
<span class="grow"></span>
|
||
<button type="button" data-modal-close>取消</button>
|
||
<button type="submit" class="primary">保存</button>
|
||
</div>
|
||
</form>
|
||
{{end}}
|
||
{{end}}
|