package console import ( "yunyan/comm" "yunyan/lego/core" "yunyan/lego/core/cbase" "yunyan/lego/sys/postgres" ) // AppRegistry 应用注册项,存在 console 的公共库(postgres.GetSys(),即 supabase 单库)。 // 每个应用一行,描述「在哪、连哪个业务库、配置事件往哪发」。 // - ServiceDsn → 应用 MySQL 业务库(userdevice/product_stat),仅「重置设备码」清用户绑定时连。 // - DeviceDsn → 已弃用:设备库内容(厂家/产品/设备码)已集中到 console 主库(supabase),注册应用无需再填。 // // 注:DeviceDsn / Redis* 字段为历史保留,已不参与建连。 type AppRegistry struct { Id uint32 `gorm:"primaryKey;autoIncrement;column:id" json:"id"` Name string `gorm:"column:name;size:64" json:"name"` // 显示名,如 "Voitrans(国内)" Region string `gorm:"column:region;size:32" json:"region"` // 区域标记,如 cn/hw BaseUrl string `gorm:"column:base_url;size:255" json:"base_url"` // 应用对外地址(前端跳转/回调) DeviceDsn string `gorm:"column:device_dsn;size:512" json:"device_dsn"` // 已弃用:设备库改走 console 主库(supabase) ServiceDsn string `gorm:"column:service_dsn;size:512" json:"service_dsn"` // 业务库 DSN → 重置设备码时清 userdevice/product_stat RedisAddr string `gorm:"column:redis_addr;size:255" json:"redis_addr"` // Redis 地址,多地址逗号分隔 RedisPwd string `gorm:"column:redis_pwd;size:255" json:"redis_pwd"` RedisDb int `gorm:"column:redis_db" json:"redis_db"` RedisTls bool `gorm:"column:redis_tls" json:"redis_tls"` KeyPrefix string `gorm:"column:key_prefix;size:64" json:"key_prefix"` // Redis key 应用前缀 NatsSubject string `gorm:"column:nats_subject;size:128" json:"nats_subject"` // 配置事件 subject,空则回退默认 NatsStream string `gorm:"column:nats_stream;size:64" json:"nats_stream"` Enabled bool `gorm:"column:enabled" json:"enabled"` // 是否启用(停用的不在切换器展示) Remark string `gorm:"column:remark;size:255" json:"remark"` // AppName 归属的应用名(= app_product.name,空=未分组)。用应用名直接关联——名称唯一即应用ID,不再套整型外键。 AppName string `gorm:"column:app_name;size:64;index" json:"app_name"` Status string `gorm:"column:status;size:16" json:"status"` // existing=已接入现有 / pending=待部署(选了目标环境尚未部署) // EnvId 关联的部署环境(app_environment.id,0=未关联);下面三个是下一期远程部署的应用维度覆盖(本期只存不用)。 EnvId uint32 `gorm:"column:env_id;index" json:"env_id"` // 所属部署环境 → app_environment.id DeployDir string `gorm:"column:deploy_dir;size:255" json:"deploy_dir"` // 覆盖 env.DeployDir(对应 APPS 行远端目录) ImageName string `gorm:"column:image_name;size:128" json:"image_name"` // 覆盖 env.ImageName(共用镜像时留空) ImageTag string `gorm:"column:image_tag;size:64" json:"image_tag"` // 覆盖 env.ImageTag Createtime int64 `gorm:"column:createtime" json:"createtime"` Updatetime int64 `gorm:"column:updatetime" json:"updatetime"` } func (AppRegistry) TableName() string { return comm.TableAppRegistry } // modelComp 注册库 CRUD(console 的公共库,走全局 postgres.GetSys() —— 即 supabase 单库)。 type modelComp struct { cbase.ModuleCompBase module *Console } func (this *modelComp) Init(service core.IService, module core.IModule, comp core.IModuleComp, opt core.IModuleOptions) (err error) { this.ModuleCompBase.Init(service, module, comp, opt) this.module = module.(*Console) if err = postgres.CreateTable(comm.TableAppRegistry, &AppRegistry{}); err != nil { this.module.Errorln(err) } // app_registry 新增的关联/部署列:CreateTable 对已存在表跳过 AutoMigrate,故显式幂等补列。 if err = this.ensureRegistryColumns(); err != nil { this.module.Errorln(err) } // 应用/产品表 + 部署环境表(新表,首次 CreateTable 会正常建)。 if err = postgres.CreateTable(comm.TableAppProduct, &AppProduct{}); err != nil { this.module.Errorln(err) } if err = postgres.CreateTable(comm.TableAppEnvironment, &AppEnvironment{}); err != nil { this.module.Errorln(err) } // 应用×服务的基础设施/运行配置表(业务服务启动 seed、console 后台读写;新表 CreateTable 幂等)。 if err = postgres.CreateTable(comm.TableAppServiceConfig, &comm.AppServiceConfig{}); err != nil { this.module.Errorln(err) } if err = this.ensureAccountTable(); err != nil { this.module.Errorln(err) } // 设备管理域表(厂家/产品/设备码/公码/出货单)建在 console 主库(supabase),启动建一次。 // SkipEnsureTables 打开时跳过(如指向已建好表的生产库、只读查看场景)。 if !this.module.options.SkipEnsureTables { if err = ensureDeviceTables(); err != nil { this.module.Errorln(err) } } // 服务配置域三张共享表(全局环境配置/MCP/会议模板)建在 console 主库。 // CreateTable 对已存在的表是幂等空操作(不 AutoMigrate、不改 schema), // 故即便 SkipEnsureTables=true(生产只读 schema)也安全执行——这三张为新表,需确保存在。 if err = ensureConfigTables(); err != nil { this.module.Errorln(err) } // 月结算单表建在 console 主库。同 ensureConfigTables 的理由(新表、CreateTable 对已存在表是 // 幂等空操作),即便 SkipEnsureTables=true 也要执行——否则结算页查询直接 42P01。 if err = ensureSettlementTable(); err != nil { this.module.Errorln(err) } return } // listApps 返回全部注册应用(按 id 升序)。 func (this *modelComp) listApps() (models []*AppRegistry, err error) { models = make([]*AppRegistry, 0) err = postgres.Find(comm.TableAppRegistry, &models, "") return } // getApp 按 id 取注册项。 func (this *modelComp) getApp(id uint32) (model *AppRegistry, err error) { model = &AppRegistry{} err = postgres.FindOne(comm.TableAppRegistry, model, "id=?", id) return } // getAppByName 按名称取注册项;埋点信封按应用名校验是否为已注册应用(落库前校验)。 func (this *modelComp) getAppByName(name string) (model *AppRegistry, err error) { model = &AppRegistry{} err = postgres.FindOne(comm.TableAppRegistry, model, "name=?", name) return } // addApp 新增注册项(Insert 后 model.Id 被回填)。 func (this *modelComp) addApp(model *AppRegistry) (err error) { err = postgres.Insert(comm.TableAppRegistry, model) return } // saveApp 更新注册项。 func (this *modelComp) saveApp(model *AppRegistry) (err error) { err = postgres.Save(comm.TableAppRegistry, model) return } // delApp 删除注册项。 func (this *modelComp) delApp(id uint32) (err error) { err = postgres.Delete(comm.TableAppRegistry, "id=?", id) return } // ensureRegistryColumns 给已存在的 app_registry 幂等补充「环境关联 + 部署覆盖」列。 // CreateTable 对已存在表跳过 AutoMigrate(见 lego/sys/postgres),故新列须显式 ALTER(IF NOT EXISTS 幂等)。 func (this *modelComp) ensureRegistryColumns() error { stmts := []string{ "ALTER TABLE " + comm.TableAppRegistry + " ADD COLUMN IF NOT EXISTS app_name varchar(64) DEFAULT ''", "ALTER TABLE " + comm.TableAppRegistry + " ADD COLUMN IF NOT EXISTS status varchar(16) DEFAULT 'existing'", "ALTER TABLE " + comm.TableAppRegistry + " ADD COLUMN IF NOT EXISTS env_id bigint DEFAULT 0", "ALTER TABLE " + comm.TableAppRegistry + " ADD COLUMN IF NOT EXISTS deploy_dir varchar(255) DEFAULT ''", "ALTER TABLE " + comm.TableAppRegistry + " ADD COLUMN IF NOT EXISTS image_name varchar(128) DEFAULT ''", "ALTER TABLE " + comm.TableAppRegistry + " ADD COLUMN IF NOT EXISTS image_tag varchar(64) DEFAULT ''", } for _, sql := range stmts { if res := postgres.Exec(sql); res.Error != nil { return res.Error } } return nil }