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) {