fix(promotion-import): 实例(一)(二)多备选源路径;目录内唯一 mov/jpg 自动匹配

Made-with: Cursor
This commit is contained in:
whm
2026-03-21 21:47:06 +08:00
parent 1710a11dad
commit 6d049fe0e8
4 changed files with 141 additions and 56 deletions

View File

@@ -57,21 +57,77 @@ func mimeForExt(ext string) string {
}
}
// 与 scripts/sync-video-assets-to-social.sh 一致:源相对「视频发布」目录,目标为 promotion/social 下英文名
var mappings = []struct {
SrcRel string // 相对 视频发布/
Dst string // 仅文件名,落在 promotion/social/
}{
{"宇恒一号操作计算软件实例(一)/宣传片-封面.jpg", "video-calc-demo-1-cover.jpg"},
{"宇恒一号操作计算软件实例(一)/宣传片.mov", "video-calc-demo-1.mov"},
{"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg", "video-calc-demo-2-cover.jpg"},
{"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov", "video-calc-demo-2.mov"},
{"宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg", "video-aiword-cover.jpg"},
{"宇恒一号AIWord简介/宇恒一号AIWord简介.mov", "video-aiword.mov"},
{"宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg", "video-voice-office-cover.jpg"},
{"宇恒一号语音办公实例/宇恒一号语音办公实例.mov", "video-voice-office.mov"},
{"宇恒一号AI 全自动办发票/宇恒一号AI 全自动办发票-封面.jpg", "video-invoice-ai-cover.jpg"},
{"宇恒一号AI 全自动办发票/宇恒一号AI 全自动办发票.mov", "video-invoice-ai.mov"},
// importRule按顺序尝试 SrcRels若均不存在且 FallbackScanDir 非空,则在该子目录下「仅一个 .mov/.jpg 时」自动选用(兼容实际文件夹命名与文件名不一致)
type importRule struct {
SrcRels []string
Dst string
FallbackScanDir string // 相对 视频发布/,仅当目标为视频时用 .mov封面用 .jpg
}
// 与 sync-video-assets-to-social.sh 对齐,并增加备选路径(线上常见「实例(一)」内不叫宣传片.mov 的情况)
var mappings = []importRule{
{[]string{
"宇恒一号操作计算软件实例(一)/宣传片-封面.jpg",
"宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一)-封面.jpg",
}, "video-calc-demo-1-cover.jpg", "宇恒一号操作计算软件实例(一)"},
{[]string{
"宇恒一号操作计算软件实例(一)/宣传片.mov",
"宇恒一号操作计算软件实例(一)/宇恒一号操作计算软件实例(一).mov",
}, "video-calc-demo-1.mov", "宇恒一号操作计算软件实例(一)"},
{[]string{
"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二)-封面.jpg",
"宇恒一号操作计算软件实例(二)/宣传片-封面.jpg",
}, "video-calc-demo-2-cover.jpg", "宇恒一号操作计算软件实例(二)"},
{[]string{
"宇恒一号操作计算软件实例(二)/宇恒一号操作计算软件实例(二).mov",
"宇恒一号操作计算软件实例(二)/宣传片.mov",
}, "video-calc-demo-2.mov", "宇恒一号操作计算软件实例(二)"},
{[]string{"宇恒一号AIWord简介/宇恒一号AIWord简介-封面.jpg"}, "video-aiword-cover.jpg", "宇恒一号AIWord简介"},
{[]string{"宇恒一号AIWord简介/宇恒一号AIWord简介.mov"}, "video-aiword.mov", "宇恒一号AIWord简介"},
{[]string{"宇恒一号语音办公实例/宇恒一号语音办公实例-封面.jpg"}, "video-voice-office-cover.jpg", "宇恒一号语音办公实例"},
{[]string{"宇恒一号语音办公实例/宇恒一号语音办公实例.mov"}, "video-voice-office.mov", "宇恒一号语音办公实例"},
{[]string{"宇恒一号AI 全自动办发票/宇恒一号AI 全自动办发票-封面.jpg"}, "video-invoice-ai-cover.jpg", "宇恒一号AI 全自动办发票"},
{[]string{"宇恒一号AI 全自动办发票/宇恒一号AI 全自动办发票.mov"}, "video-invoice-ai.mov", "宇恒一号AI 全自动办发票"},
}
func resolveSourceFile(videoPublish string, rule importRule) (absPath, relChosen string, ok bool) {
for _, rel := range rule.SrcRels {
p := filepath.Join(videoPublish, filepath.FromSlash(rel))
if st, err := os.Stat(p); err == nil && !st.IsDir() {
return p, rel, true
}
}
if rule.FallbackScanDir == "" {
return "", "", false
}
ext := strings.ToLower(filepath.Ext(rule.Dst))
if ext != ".mov" && ext != ".jpg" && ext != ".jpeg" {
return "", "", false
}
dir := filepath.Join(videoPublish, filepath.FromSlash(rule.FallbackScanDir))
entries, err := os.ReadDir(dir)
if err != nil {
return "", "", false
}
var matches []string
for _, e := range entries {
if e.IsDir() {
continue
}
ne := strings.ToLower(filepath.Ext(e.Name()))
if ext == ".mov" && ne == ".mov" {
matches = append(matches, e.Name())
}
if (ext == ".jpg" || ext == ".jpeg") && (ne == ".jpg" || ne == ".jpeg") {
matches = append(matches, e.Name())
}
}
if len(matches) != 1 {
return "", "", false
}
rel := filepath.ToSlash(filepath.Join(rule.FallbackScanDir, matches[0]))
p := filepath.Join(videoPublish, filepath.FromSlash(rel))
return p, rel, true
}
func main() {
@@ -144,9 +200,9 @@ func main() {
ok, skip, fail := 0, 0, 0
for _, m := range mappings {
from := filepath.Join(videoPublish, filepath.FromSlash(m.SrcRel))
if _, err := os.Stat(from); err != nil {
log.Printf("SKIP 源文件不存在: %s", from)
from, srcRelUsed, found := resolveSourceFile(videoPublish, m)
if !found {
log.Printf("SKIP 源文件不存在(已试备选路径): dst=%s dir=%s", m.Dst, m.FallbackScanDir)
skip++
continue
}
@@ -168,7 +224,7 @@ func main() {
}
if err := copyFile(from, destPath); err != nil {
log.Printf("FAIL 复制 %s: %v", m.SrcRel, err)
log.Printf("FAIL 复制 %s: %v", srcRelUsed, err)
fail++
continue
}
@@ -193,7 +249,7 @@ func main() {
"downloadable": false,
"created_at": time.Now().Format(time.RFC3339),
"import_source": "video_publish_legacy",
"source_relpath": m.SrcRel,
"source_relpath": srcRelUsed,
"promotion_alias": filepath.ToSlash(filepath.Join("promotion", "social", m.Dst)),
}
if _, err := coll.InsertOne(ctx, doc); err != nil {
@@ -201,7 +257,7 @@ func main() {
fail++
continue
}
log.Printf("OK %s -> %s", m.SrcRel, relPath)
log.Printf("OK %s -> %s", srcRelUsed, relPath)
ok++
}