This commit is contained in:
toom1996
2026-08-26 10:45:21 +08:00
parent 3cf1368e68
commit 30c9f21da4
48 changed files with 3442 additions and 0 deletions

106
scripts/i18n/main.go Normal file
View File

@ -0,0 +1,106 @@
// Command i18n_migrate 落地方案 A 的 i18n 列(加列 + 回填)。
//
// 项目已移除 AutoMigrate,表结构由外部 SQL 托管(见 scripts/sql/002_i18n_columns.sql,
// 那是给人 review 的规范底稿)。本程序是「没有 mysql 客户端时」的执行器:
// - ALTER 走 ensureColumn:先查 information_schema,缺才加,可安全重跑(幂等)。
// - 回填 UPDATE 内联且幂等,重跑无害。
//
// 不写任何业务逻辑。
package main
import (
"fmt"
"log"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
const dsn = "root:root@tcp(127.0.0.1:3306)/db?charset=utf8mb4&parseTime=True&loc=Local"
const han = "[一-鿿]"
func main() {
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("open:", err)
}
// 1) 幂等加列(与 002_i18n_columns.sql 对齐)
ensureColumn(db, "brand", "name_en", "VARCHAR(255) NOT NULL DEFAULT '' AFTER name")
ensureColumn(db, "brand", "name_cn", "VARCHAR(255) NOT NULL DEFAULT '' AFTER name_en")
ensureColumn(db, "brand_runway", "title_en", "VARCHAR(255) NOT NULL DEFAULT '' AFTER title")
ensureColumn(db, "brand_runway", "title_cn", "VARCHAR(255) NOT NULL DEFAULT '' AFTER title_en")
ensureColumn(db, "brand_runway", "description_en", "VARCHAR(1024) NOT NULL DEFAULT '' AFTER description")
ensureColumn(db, "brand_runway", "description_cn", "VARCHAR(1024) NOT NULL DEFAULT '' AFTER description_en")
// 2) 回填品牌名:从 name / show_name 拆拉丁与中文(幂等)
db.Exec(fmt.Sprintf(`
UPDATE brand SET
name_en = CASE
WHEN TRIM(REGEXP_REPLACE(name, '%s', '')) <> '' THEN TRIM(REGEXP_REPLACE(name, '%s', ''))
WHEN show_name LIKE '%%/%%' THEN TRIM(SUBSTRING_INDEX(show_name, '/', 1))
ELSE ''
END,
name_cn = CASE
WHEN show_name LIKE '%%/%%' AND SUBSTRING_INDEX(show_name, '/', -1) REGEXP '%s'
THEN TRIM(SUBSTRING_INDEX(show_name, '/', -1))
WHEN show_name REGEXP '%s' THEN TRIM(show_name)
WHEN name REGEXP '%s' THEN TRIM(name)
ELSE ''
END`, han, han, han, han, han))
// 3) 回填走秀:title / description 当前全英文,直接进 *_en(幂等)
db.Exec(`UPDATE brand_runway SET title_en = title, description_en = description, title_cn = '', description_cn = ''`)
// 4) 校验
printCounts(db)
fmt.Println("migration done.")
}
func ensureColumn(db *gorm.DB, table, col, def string) {
var n int64
db.Raw("SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='db' AND TABLE_NAME=? AND COLUMN_NAME=?", table, col).Scan(&n)
if n > 0 {
fmt.Printf(" skip %s.%s (exists)\n", table, col)
return
}
stmt := fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s %s", table, col, def)
fmt.Println(" +", stmt)
if r := db.Exec(stmt); r.Error != nil {
log.Fatalf("alter %s.%s failed: %v", table, col, r.Error)
}
}
func printCounts(db *gorm.DB) {
type c struct {
Tot, En, Cn, Both, Dcn int64
}
var b c
db.Raw(`SELECT COUNT(*) tot,
SUM(CASE WHEN name_en<>'' THEN 1 ELSE 0 END) en,
SUM(CASE WHEN name_cn<>'' THEN 1 ELSE 0 END) cn,
SUM(CASE WHEN name_en<>'' AND name_cn<>'' THEN 1 ELSE 0 END) both_cnt
FROM brand`).Scan(&b)
fmt.Printf(" brand: total=%d name_en=%d name_cn=%d both=%d\n", b.Tot, b.En, b.Cn, b.Both)
var r c
db.Raw(`SELECT COUNT(*) tot,
SUM(CASE WHEN title_en<>'' THEN 1 ELSE 0 END) en,
SUM(CASE WHEN title_cn<>'' THEN 1 ELSE 0 END) cn,
SUM(CASE WHEN description_cn<>'' THEN 1 ELSE 0 END) dcn
FROM brand_runway WHERE is_deleted=0`).Scan(&r)
fmt.Printf(" runway: total=%d title_en=%d title_cn=%d desc_cn=%d\n", r.Tot, r.En, r.Cn, r.Dcn)
type s struct {
Name string
ShowName string
NameEn string
NameCn string
}
var samples []s
db.Raw("SELECT name, show_name, name_en, name_cn FROM brand WHERE name REGEXP ? OR show_name REGEXP ? ORDER BY id LIMIT 8", han, han).Scan(&samples)
fmt.Println(" brand samples:")
for _, x := range samples {
fmt.Printf(" name=%-18q show=%-26q -> en=%-18q cn=%-18q\n", x.Name, x.ShowName, x.NameEn, x.NameCn)
}
}