diff --git a/server/app/goauto/models/schema.go b/server/app/goauto/models/schema.go index 6851d0e..203ea5a 100644 --- a/server/app/goauto/models/schema.go +++ b/server/app/goauto/models/schema.go @@ -605,10 +605,14 @@ func (SYBShop) TableName() string { return "syb_shop" } // rows are operator-maintained. DeletedFlag keeps the composite unique key // reusable after a keyword is soft-deleted. type SYBProductFilter struct { - ID uint64 `json:"id" gorm:"primaryKey;autoIncrement"` - Kind string `json:"kind" gorm:"size:16;not null;index;uniqueIndex:ux_syb_product_filter_key,priority:1;check:ck_syb_product_filter_kind,kind IN ('char','keyword')"` - Keyword string `json:"keyword" gorm:"type:text;not null"` - NormalizedKeyword string `json:"normalizedKeyword" gorm:"type:text;not null;uniqueIndex:ux_syb_product_filter_key,priority:2"` + ID uint64 `json:"id" gorm:"primaryKey;autoIncrement"` + Kind string `json:"kind" gorm:"size:16;not null;index;uniqueIndex:ux_syb_product_filter_key,priority:1;check:ck_syb_product_filter_kind,kind IN ('char','keyword')"` + // Keyword and NormalizedKeyword are sized, not text: MySQL refuses a + // BLOB/TEXT column in a key specification without a prefix length, so the + // unique index below cannot be built on a text column (Error 1170). SQLite + // accepts either, which is why only a real MySQL migration surfaces this. + Keyword string `json:"keyword" gorm:"size:200;not null"` + NormalizedKeyword string `json:"normalizedKeyword" gorm:"size:200;not null;uniqueIndex:ux_syb_product_filter_key,priority:2"` Enabled bool `json:"enabled" gorm:"not null;default:true"` LastHitAt *time.Time `json:"lastHitAt"` LastHitCount *int `json:"lastHitCount"` diff --git a/server/cmd/migrate/migration/version-local/1789112900000_syb_product_filter_table.go b/server/cmd/migrate/migration/version-local/1789112900000_syb_product_filter_table.go new file mode 100644 index 0000000..cdf24d4 --- /dev/null +++ b/server/cmd/migrate/migration/version-local/1789112900000_syb_product_filter_table.go @@ -0,0 +1,63 @@ +package version_local + +import ( + "runtime" + + "go-admin/app/goauto/models" + "go-admin/app/goauto/sybshop" + "go-admin/cmd/migrate/migration" + common "go-admin/common/models" + + "gorm.io/gorm" +) + +func init() { + _, file, _, _ := runtime.Caller(0) + migration.Migrate.SetVersion(migration.GetFilename(file), migrateSYBProductFilterTable) +} + +// migrateSYBProductFilterTable creates syb_product_filter and seeds its rules +// for #269. +// +// `[必须]` Adding the model to migrations.Migrate only covers databases created +// from scratch. Every existing database already has the older version numbers +// recorded in sys_migration, so that list never runs again there — a new model +// needs its own version file, which is what the post-migration table check in +// cmd/migrate enforces. +// +// The seed is deliberately the same set the fresh-database path writes, created +// with FirstOrCreate on the normalized key so running both paths against one +// database cannot produce duplicates. +func migrateSYBProductFilterTable(db *gorm.DB, version string) error { + if err := db.AutoMigrate(&models.SYBProductFilter{}); err != nil { + return err + } + return db.Transaction(func(tx *gorm.DB) error { + // 结构判据 is what defines a 档口 product; the keywords are the forward + // safety net for the day those codes stop carrying "#" (#269). + seed := []struct{ kind, keyword string }{ + {"char", "-"}, + {"char", "#"}, + {"keyword", "CVC短袖220斤"}, + {"keyword", "180克純棉圓領短袖"}, + {"keyword", "拼色純棉短袖"}, + {"keyword", "300斤牛奶絲圓領"}, + {"keyword", "印花大學T"}, + {"keyword", "印花帽T"}, + } + for _, item := range seed { + norm := sybshop.Normalize(item.keyword) + row := models.SYBProductFilter{ + Kind: item.kind, + Keyword: item.keyword, + NormalizedKeyword: norm, + Enabled: true, + } + if err := tx.Where("kind = ? AND normalized_keyword = ?", item.kind, norm). + FirstOrCreate(&row).Error; err != nil { + return err + } + } + return tx.Create(&common.Migration{Version: version}).Error + }) +} diff --git a/web/src/views/goauto/syb-product-filters/index.vue b/web/src/views/goauto/syb-product-filters/index.vue index 86146ee..f662ddd 100644 --- a/web/src/views/goauto/syb-product-filters/index.vue +++ b/web/src/views/goauto/syb-product-filters/index.vue @@ -6,9 +6,6 @@ - - - 重置 @@ -163,7 +160,7 @@ export default { loading: false, chars: [], keywords: [], - query: { keyword: '', enabledOnly: false }, + query: { keyword: '' }, editor: { open: false, saving: false, keyword: '', note: '' } } }, @@ -182,8 +179,7 @@ export default { const res = await listSybProductFilters({ page: 1, pageSize: 200, - keyword: this.query.keyword || undefined, - enabledOnly: this.query.enabledOnly || undefined + keyword: this.query.keyword || undefined }) const rows = (res.data.items || []).map(row => ({ ...row, updating: false })) this.chars = rows.filter(row => row.kind === 'char') @@ -193,7 +189,7 @@ export default { } }, reset() { - this.query = { keyword: '', enabledOnly: false } + this.query = { keyword: '' } this.load() }, formatTime(value) {