package console import ( "yunyan/comm" "yunyan/lego/sys/postgres" ) // 芯片厂商档案(chip_vendor)——原来是 apps/proto/db.proto 里硬编码的 SolutionVendor 枚举, // 加一个厂商要同步改三处代码再重新发版,运营自己加不了,故转成表。 // // **Id 就是 CID**(芯片方案标识,十六进制看,如 恒玄=0xABF4),与方案商的 BID、产品的 PID 同一套路: // 手工指定的有含义编码、不自增、后台可改。`solution_provider.vendor` 存的就是它。 // // ⚠️ 尚未确定 CID 的厂商先用历史占位 id(杰里=1 / 蓝汛=2 / 博通=4,即原枚举值), // 等真实 CID 定下来在后台改号即可(改号会级联更新方案商的 vendor 引用,见 renameChipVendorId)。 // 主键不能为空,所以「先空着」只能表现为占位值、不能是 0。 // // 用本地 gorm struct 而不是 pb:这张表只服务后台,客户端协议不涉及, // 走 pb 就得重新生成 22 个 .pb.go(见 CLAUDE.md 关于 protoc-gen-go 版本的告警),没必要。 type ChipVendor struct { // Id 即 CID:手工指定、不自增。改它会级联改方案商的 vendor,必须走 renameChipVendorId。 Id uint32 `gorm:"primaryKey;column:id" json:"id"` Name string `gorm:"column:name;size:64;index" json:"name"` // 厂商名,如 杰里/蓝汛/恒玄/博通 Enabled bool `gorm:"column:enabled" json:"enabled"` // 停用后不在方案商的「芯片」下拉里出现,但存量方案商仍显示其名字 Sort int32 `gorm:"column:sort" json:"sort"` // 列表排序,小的在前;相同则按 id Createtime int64 `gorm:"column:createtime" json:"createtime"` Updatetime int64 `gorm:"column:updatetime" json:"updatetime"` } func (ChipVendor) TableName() string { return comm.TableChipVendor } // chipVendorSeeds 原 SolutionVendor 枚举的 4 个取值。 // 恒玄的 CID 已知(0xABF4)故直接用作 id;其余三家 CID 未定,沿用原枚举值作占位 id, // 后台确定后改号即可。不替它们猜值——CID 要烧进固件,猜错比留占位难查得多。 var chipVendorSeeds = []ChipVendor{ {Id: 1, Name: "杰里"}, {Id: 2, Name: "蓝汛"}, {Id: 0xABF4, Name: "恒玄"}, {Id: 4, Name: "博通"}, } // migrateChipVendorCidToId 一次性迁移:把独立的 cid 列并进主键 id。 // // 上一版把 CID 做成了独立字段(id 自增、cid 另存),本版改成「id 即 CID」。已部署过上一版的库里 // 存着 id=3 / cid=44020 的恒玄,必须把它搬到 id=44020 并同步改掉方案商的 vendor 引用, // 否则方案商会指向一个不存在的芯片厂商。 // // 幂等:迁移完就把 cid 列删掉,之后本函数直接跳过。 func migrateChipVendorCidToId() error { // 探测 cid 列是否还在(不在 = 已迁移过,或本来就是新库) var n int64 if err := postgres.Raw( "SELECT COUNT(*) FROM information_schema.columns WHERE table_name=? AND column_name='cid'", comm.TableChipVendor).Scan(&n).Error; err != nil { return err } if n == 0 { return nil } type cidRow struct { Id uint32 `gorm:"column:id"` Cid uint32 `gorm:"column:cid"` } var rows []cidRow if err := postgres.Raw( "SELECT id, cid FROM " + comm.TableChipVendor + " WHERE cid <> 0 AND cid <> id").Scan(&rows).Error; err != nil { return err } for _, r := range rows { // 目标 id 已被别人占用就跳过,留给人工处理——静默合并两条厂商记录比不迁移危险得多。 var taken int64 if err := postgres.Table(comm.TableChipVendor).Where("id=?", r.Cid).Count(&taken).Error; err != nil { return err } if taken > 0 { continue } if err := renameChipVendorId(r.Id, r.Cid); err != nil { return err } } if res := postgres.Exec("ALTER TABLE " + comm.TableChipVendor + " DROP COLUMN IF EXISTS cid"); res.Error != nil { return res.Error } return nil } // ensureChipVendorSeed 幂等补种 4 条已知厂商。 // // 按【名称】判存在而不是按 id:id 就是 CID、后台可改,按 id 判会在厂商改过号之后 // 把旧 id 那条重新种回来,变成同名两条。同时也避开 id 被别的厂商占用的情况。 func ensureChipVendorSeed() error { for _, v := range chipVendorSeeds { if res := postgres.Exec( "INSERT INTO "+comm.TableChipVendor+" (id, name, enabled, sort, createtime, updatetime) "+ "SELECT ?, ?, true, 0, 0, 0 "+ "WHERE NOT EXISTS (SELECT 1 FROM "+comm.TableChipVendor+" WHERE name=?) "+ " AND NOT EXISTS (SELECT 1 FROM "+comm.TableChipVendor+" WHERE id=?)", v.Id, v.Name, v.Name, v.Id); res.Error != nil { return res.Error } } return nil } // ---- 芯片厂商 CRUD(console 主库,与方案商/品牌商同库)---- func dvChipVendors(sys *appConn) (models []*ChipVendor, err error) { models = make([]*ChipVendor, 0) err = sys.AdminDB().Find(comm.TableChipVendor, &models, "") return } func dvChipVendor(sys *appConn, id uint32) (model *ChipVendor, err error) { model = &ChipVendor{} // 用 FindOne:不存在时返回 ErrRecordNotFound,存在性校验才有效(Find 对空结果不报错)。 err = sys.AdminDB().FindOne(comm.TableChipVendor, model, "id=?", id) return } func dvAddChipVendor(sys *appConn, model *ChipVendor) (err error) { err = sys.AdminDB().Insert(comm.TableChipVendor, model) return } func dvSaveChipVendor(sys *appConn, model *ChipVendor) (err error) { err = sys.AdminDB().Save(comm.TableChipVendor, model) return } func dvDelChipVendor(sys *appConn, id uint32) (err error) { err = sys.AdminDB().Delete(comm.TableChipVendor, "id=?", id) return } // dvCountProvidersByVendor 统计挂在某芯片厂商下的方案商数量,删除/改号前用它做引用检查。 func dvCountProvidersByVendor(sys *appConn, vendorId uint32) (n int64, err error) { err = sys.AdminDB().Table(comm.TableSolutionProvider).Where("vendor=?", vendorId).Count(&n).Error return }