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.
 
 
 
 
 
 

140 lines
5.1 KiB

package console
import (
"fmt"
"strings"
"sync"
"yunyan/comm"
"yunyan/lego/core"
"yunyan/lego/core/cbase"
"yunyan/lego/sys/mysql"
"yunyan/lego/sys/postgres"
"yunyan/pb"
)
// registryComp 应用业务库连接管理器:按 appId 把注册项的 ServiceDsn 物化成业务库连接并缓存。
//
// 设备库内容(厂家/产品/设备码/公码/出货单)统一在 console 主库(supabase),不分应用、无需选;
// 只有「重置设备码」要清各应用自己的 userdevice/product_stat 时,才按 X-App-Id 取应用业务库。
type registryComp struct {
cbase.ModuleCompBase
module *Console
mu sync.RWMutex
conns map[uint32]mysql.ISys // appId → 业务库(ServiceDsn) 连接
}
func (this *registryComp) 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)
this.conns = make(map[uint32]mysql.ISys)
return
}
// getServiceDB 取(或惰性建)指定应用的业务库连接(用于清 userdevice/product_stat)。
func (this *registryComp) getServiceDB(appId uint32) (mysql.ISys, error) {
if appId == 0 {
return nil, fmt.Errorf("未指定应用(appId=0),重置设备码需先在切换器选择应用(清理该应用的用户绑定)")
}
this.mu.RLock()
if conn, ok := this.conns[appId]; ok {
this.mu.RUnlock()
return conn, nil
}
this.mu.RUnlock()
app, err := this.module.model.getApp(appId)
if err != nil {
return nil, fmt.Errorf("应用未注册或读取失败(appId=%d): %w", appId, err)
}
if !app.Enabled {
return nil, fmt.Errorf("应用已停用(appId=%d, name=%s)", appId, app.Name)
}
this.mu.Lock()
defer this.mu.Unlock()
// 双重检查:可能在抢锁期间别的请求已建好。
if conn, ok := this.conns[appId]; ok {
return conn, nil
}
conn, err := openDriver(app.ServiceDsn)
if err != nil {
return nil, fmt.Errorf("应用业务库连接失败(appId=%d, name=%s): %w", appId, app.Name, err)
}
this.conns[appId] = conn
return conn, nil
}
// invalidate 失效某应用的业务库连接缓存(注册项被改/删时调用),下次访问按新配置重建。
func (this *registryComp) invalidate(appId uint32) {
this.mu.Lock()
delete(this.conns, appId)
this.mu.Unlock()
}
// ============================ 设备域连接 ============================
// appConn 设备管理域用的连接句柄:
// - device 恒为 console 主库(supabase)——厂家/产品/设备码/公码/出货单都在这里;
// - service 仅「重置设备码」时按选中应用填入其业务库——清 userdevice/product_stat。
//
// 暴露 AdminDB()/ServiceDB() 使 model_device.go 的 handler 调用方式保持不变。
type appConn struct {
device mysql.ISys // 设备库 = console 主库(supabase)
service mysql.ISys // 业务库 = 选中应用的 MySQL(仅重置类用到)
}
func (a *appConn) AdminDB() mysql.ISys { return a.device }
func (a *appConn) ServiceDB() mysql.ISys { return a.service }
// consoleDeviceConn 设备域默认连接:device 指向 console 主库(supabase),service 留空。
// 重置类操作再把 service 补成选中应用的业务库。
func consoleDeviceConn() *appConn {
return &appConn{device: postgres.GetSys()}
}
// openDriver 按 DSN 前缀选驱动:postgres:// 走 PostgreSQL,否则 MySQL(应用业务库通常为 MySQL)。
// 直接用子系统的 NewSys 工厂建连;两驱动 ISys 方法集一致,统一用 mysql.ISys 承载。
func openDriver(dsn string) (mysql.ISys, error) {
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
return postgres.NewSys(postgres.SetDsn(dsn))
}
return mysql.NewSys(mysql.SetMySQLDsn(dsn))
}
// ensureDeviceTables 在 console 主库(supabase)幂等建好设备管理域所需的表(含按 product 的 license 分表)。
// 启动时跑一次:设备库内容已集中到 console 自己的库,不再按应用建。
func ensureDeviceTables() error {
adb := postgres.GetSys()
if err := adb.CreateTable(comm.TableFactory, &pb.DBFactory{}); err != nil {
return err
}
_ = adb.AutoIncrementStart(comm.TableFactory, "id", 0xA001)
if err := adb.CreateTable(comm.TableFactoryDeliveryNote, &pb.DBFactoryDeliveryNote{}); err != nil {
return err
}
if err := adb.CreateTable(comm.TableProduct, &pb.DBProduct{}); err != nil {
return err
}
_ = adb.AutoIncrementStart(comm.TableProduct, "id", 0xB001)
if err := adb.CreateTable(comm.TableProductVersion, &pb.DBProductVersion{}); err != nil {
return err
}
if err := adb.CreateTable(comm.TableLicense, &pb.DBFactoryDevics{}); err != nil {
return err
}
if err := adb.CreateTable(comm.TableFactoryPublicCode, &pb.DBFactoryPublicCode{}); err != nil {
return err
}
// 按 product 列表建 license_%x 分表。
var products []*pb.DBProduct
if err := adb.Find(comm.TableProduct, &products, ""); err != nil && err != postgres.ErrNoDocuments {
return err
}
for _, p := range products {
if err := adb.CreateTable(fmt.Sprintf("%s_%x", comm.TableLicense, p.Id), &pb.DBFactoryDevics{}); err != nil {
return err
}
}
return nil
}