You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

161 lines
8.3 KiB

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)
}
// 凭据归一:两张扁平 env 表(global_config / 业务库 config) → svc_config。
// 幂等,每次启动跑,跑完即空转;细节与取舍见 migrate_envtosvc.go。
// 必须排在 ensureConfigTables 之后(要 svc_config 表已存在)。
if apps, e := this.listApps(); e != nil {
this.module.Errorln(e)
} else {
migrateEnvToSvcConfig(this.module.options.EncryptKey, apps)
}
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
}