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.
 
 
 
 
 
 

1119 lines
37 KiB

package api
import (
"yunyan/comm"
"yunyan/lego/core"
"yunyan/lego/core/cbase"
"yunyan/lego/sys/mysql"
"yunyan/lego/sys/postgres"
"yunyan/pb"
"yunyan/sys/appstat"
"fmt"
"gorm.io/gorm"
)
// 代理模型
type modelComp struct {
cbase.ModuleCompBase
module *API
}
// 组件初始化接口
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.(*API)
if err = mysql.CreateTable(comm.TableAdmin, &pb.DBAdminUser{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableUserUseLog, &pb.DBUserUseLog{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableAdminResLog, &pb.DBAdminResourceLog{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableAppConfig, &pb.DBAppConfigItem{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableAppConfig, &pb.DBAppConfigItem{}); err != nil {
this.module.Errorln(err)
}
if err = postgres.CreateTable(comm.TableGlobalConfig, &pb.DBGlobalConfigItem{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableAgent, &pb.DBAgent{}); err != nil {
this.module.Errorln(err)
}
if err = postgres.CreateTable(comm.TableMcp, &pb.DBMcpServer{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableEchomeetRecord, &pb.DBEchoMeetRecord{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableEchomeetTemplate, &pb.DBEchoMeetTemplate{}); err != nil {
this.module.Errorln(err)
} else {
mysql.Exec(fmt.Sprintf("ALTER TABLE %s AUTO_INCREMENT = %d", comm.TableEchomeetTemplate, 10000))
}
if err = postgres.CreateTable(comm.TableEchomeetTemplate, &pb.DBEchoMeetTemplate{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableWakeupVoice, &pb.DBWakeupVoice{}); err != nil {
this.module.Errorln(err)
}
if err = postgres.CreateTable(comm.TableSolutionProvider, &pb.DBSolutionProvider{}); err != nil {
this.module.Errorln(err)
}
if err = postgres.CreateTable(comm.TableBrand, &pb.DBBrand{}); err != nil {
this.module.Errorln(err)
} else {
//设置brand表的主键从0xA001开始(仅空表生效,跨 MySQL/Postgres 通用)
postgres.AutoIncrementStart(comm.TableBrand, "id", 0xA001)
}
if err = postgres.CreateTable(comm.TableProductionBatch, &pb.DBProductionBatch{}); err != nil {
this.module.Errorln(err)
}
if err = postgres.CreateTable(comm.TableProduct, &pb.DBProduct{}); err != nil {
this.module.Errorln(err)
} else {
//设置product表的主键从0xB001开始(仅空表生效,跨 MySQL/Postgres 通用)
postgres.AutoIncrementStart(comm.TableProduct, "id", 0xB001)
}
if err = postgres.CreateTable(comm.TableProductVersion, &pb.DBProductVersion{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableChannelApp, &pb.DBChannelApp{}); err != nil {
this.module.Errorln(err)
}
if err = postgres.CreateTable(comm.TableLicense, &pb.DBAuthCode{}); err != nil {
this.module.Errorln(err)
return err
}
if err = postgres.CreateTable(comm.TableFactoryPublicCode, &pb.DBFactoryPublicCode{}); err != nil {
this.module.Errorln(err)
return err
}
if err = mysql.CreateTable(comm.TableGoods, &pb.DBGoods{}); err != nil {
this.module.Errorln(err)
return err
}
if err = postgres.CreateTable(comm.TableWakeupVoice, &pb.DBWakeupVoice{}); err != nil {
this.module.Errorln(err)
return err
} else {
//设置wakeupvoice表的主键从0xD001开始(仅空表生效,跨 MySQL/Postgres 通用)
postgres.AutoIncrementStart(comm.TableWakeupVoice, "id", 0xD001)
}
if err = mysql.CreateTable(comm.TableAppStatDaily, &pb.DBAppStat{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableAppStatMonthly, &pb.DBAppStat{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableAppStatYearly, &pb.DBAppStat{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableAppStatGlobal, &pb.DBAppStat{}); err != nil {
this.module.Errorln(err)
}
if err = mysql.CreateTable(comm.TableProductStat, &pb.DBProductStat{}); err != nil {
this.module.Errorln(err)
}
// AutoMigrate 不会主动放宽已存在列的 varchar 宽度,
// 历史库 stat_date 可能是更短的 varchar(N<10),导致写入 "YYYY-MM-DD" 报 1406 Data too long。
// 这里强制把 4 张表的 stat_date 拉到 varchar(20),幂等。
for _, tbl := range []string{
comm.TableAppStatDaily,
comm.TableAppStatMonthly,
comm.TableAppStatYearly,
comm.TableAppStatGlobal,
} {
if e := mysql.Exec(fmt.Sprintf("ALTER TABLE %s MODIFY COLUMN stat_date VARCHAR(20) NOT NULL", tbl)).Error; e != nil {
this.module.Errorln(e)
}
}
model := &pb.DBAdminUser{
Account: this.module.options.AdninAccount,
Password: this.module.options.AdninPassword,
Identity: pb.Identity_Admin,
}
// 执行分页查询
mysql.Insert(comm.TableAdmin, model)
return
}
func (this *modelComp) Start() (err error) {
if err = this.ModuleCompBase.Start(); err != nil {
return err
}
var products []*pb.DBProduct
if err = postgres.Find(comm.TableProduct, &products, ""); err != nil && err != mysql.ErrNoDocuments {
this.module.Errorln(err)
return err
}
for _, v := range products {
if err = postgres.CreateTable(fmt.Sprintf("%s_%x", comm.TableLicense, v.Id), &pb.DBAuthCode{}); err != nil {
this.module.Errorln(err)
return err
}
}
// product_stat 为空(首次部署 / 从未点过重建)时做一次初始扫描,
// 避免代理仪表盘在首次重建前激活数/绑定用户数全部显示 0。
var statCount int64
if cerr := mysql.Table(comm.TableProductStat).Count(&statCount).Error; cerr != nil {
this.module.Errorln(cerr)
} else if statCount == 0 {
if n, rerr := this.rebuildProductStats(); rerr != nil {
this.module.Errorln(rerr)
} else {
this.module.Infof("product_stat 初始扫描完成, rows=%d", n)
}
}
return
}
// 寻找账号
func (this *modelComp) findforaccount(account string) (user *pb.DBAdminUser, err error) {
user = &pb.DBAdminUser{}
err = mysql.FindOne(comm.TableAdmin, user, "account=?", account)
return
}
// 添加管理员账号
func (this *modelComp) addAdminUser(user *pb.DBAdminUser) (err error) {
err = mysql.Insert(comm.TableAdmin, user)
return
}
// 删除管理员账号
func (this *modelComp) delAdminUser(account string) (err error) {
err = mysql.Delete(comm.TableAdmin, "account=?", account)
return
}
// 更新管理员账号
func (this *modelComp) updateAdminUser(user *pb.DBAdminUser) (err error) {
err = mysql.Save(comm.TableAdmin, user)
return
}
// 查询管理员账号列表
func (this *modelComp) getAdminUsers() (users []*pb.DBAdminUser, err error) {
err = mysql.Find(comm.TableAdmin, &users, "")
return
}
// adjustAdminBalance 用 SQL 表达式 (col = col + ?) 原子调整指定账号的资源点余额。
// 仅写入 delta != 0 的列,避免 Save 覆盖其它字段,也避免并发派发互相覆盖。
// 返回 rowsAffected 用于校验账号是否存在。delta 可正(增加/赠送) 可负(扣减/派发)。
func (this *modelComp) adjustAdminBalance(account string, dVipday, dAichat, dTradeSec, dMeetSec int64) (rowsAffected int64, err error) {
updates := map[string]any{}
if dVipday != 0 {
updates["vipday_balance"] = gorm.Expr("vipday_balance + ?", dVipday)
}
if dAichat != 0 {
updates["aichatintegral_balance"] = gorm.Expr("aichatintegral_balance + ?", dAichat)
}
if dTradeSec != 0 {
updates["tradesecond_balance"] = gorm.Expr("tradesecond_balance + ?", dTradeSec)
}
if dMeetSec != 0 {
updates["meetsecond_balance"] = gorm.Expr("meetsecond_balance + ?", dMeetSec)
}
if len(updates) == 0 {
return
}
tx := mysql.Table(comm.TableAdmin).Where("account=?", account).Updates(updates)
rowsAffected = tx.RowsAffected
err = tx.Error
return
}
// 寻找用户
func (this *modelComp) finduser(uid string) (user *pb.DBUser, err error) {
user = &pb.DBUser{}
err = mysql.FindOne(comm.TableUser, user, "uid=?", uid)
return
}
// 寻找用户 手机号
func (this *modelComp) finduserbyphone(phone string) (user *pb.DBUser, err error) {
user = &pb.DBUser{}
err = mysql.FindOne(comm.TableUser, user, "phone=?", phone)
return
}
// 寻找用户 邮箱
func (this *modelComp) finduserbyemail(mail string) (user *pb.DBUser, err error) {
user = &pb.DBUser{}
err = mysql.FindOne(comm.TableUser, user, "mail=?", mail)
return
}
// 寻找用户
func (this *modelComp) finduserdevice(uid string) (models []*pb.DBUserDivice, err error) {
models = make([]*pb.DBUserDivice, 0)
err = mysql.Find(comm.TableUserdevice, &models, "uid=?", uid)
return
}
// 寻找用户
func (this *modelComp) deluserdevice(uid string) (err error) {
err = mysql.Delete(comm.TableUserdevice, "uid=?", uid)
return
}
// 更新用户信息
func (this *modelComp) updateuser(user *pb.DBUser) (err error) {
err = mysql.Save(comm.TableUser, user)
return
}
// 配置项
func (this *modelComp) config() (config []*pb.DBAppConfigItem, err error) {
config = make([]*pb.DBAppConfigItem, 0)
err = mysql.Find(comm.TableAppConfig, &config, "")
return
}
// 添加配置
func (this *modelComp) addconfig(config ...*pb.DBAppConfigItem) (err error) {
err = mysql.Insert(comm.TableAppConfig, config)
return
}
// 更新配置
func (this *modelComp) updateconfig(config ...*pb.DBAppConfigItem) (err error) {
err = mysql.Save(comm.TableAppConfig, config)
return
}
func (this *modelComp) delconfig(id uint64) (err error) {
err = mysql.Delete(comm.TableAppConfig, "id=?", id)
return
}
// 全局第三方服务配置
func (this *modelComp) globalconfig() (config []*pb.DBGlobalConfigItem, err error) {
config = make([]*pb.DBGlobalConfigItem, 0)
err = postgres.Find(comm.TableGlobalConfig, &config, "")
return
}
func (this *modelComp) globalconfigbyregion(region pb.Region) (config []*pb.DBGlobalConfigItem, err error) {
config = make([]*pb.DBGlobalConfigItem, 0)
err = postgres.Find(comm.TableGlobalConfig, &config, "region=?", region)
return
}
func (this *modelComp) addglobalconfig(config ...*pb.DBGlobalConfigItem) (err error) {
err = postgres.Insert(comm.TableGlobalConfig, config)
return
}
func (this *modelComp) updateglobalconfig(config ...*pb.DBGlobalConfigItem) (err error) {
err = postgres.Save(comm.TableGlobalConfig, config)
return
}
func (this *modelComp) delglobalconfig(ids []uint64) (err error) {
err = postgres.Delete(comm.TableGlobalConfig, "id IN ?", ids)
return
}
// 智能体
func (this *modelComp) agents() (agents []*pb.DBAgent, err error) {
agents = make([]*pb.DBAgent, 0)
err = mysql.Find(comm.TableAgent, &agents, "")
return
}
// 智能体
func (this *modelComp) agent(id string) (agent *pb.DBAgent, err error) {
agent = &pb.DBAgent{}
err = mysql.Find(comm.TableAgent, agent, "id=?", id)
return
}
// 智能体
func (this *modelComp) addagent(agent *pb.DBAgent) (err error) {
err = mysql.Insert(comm.TableAgent, agent)
return
}
func (this *modelComp) updateagent(agent *pb.DBAgent) (err error) {
err = mysql.Save(comm.TableAgent, agent)
return
}
func (this *modelComp) delagent(id string) (err error) {
err = mysql.Delete(comm.TableAgent, "id=?", id)
return
}
// Mcp 服务
func (this *modelComp) mcpservers(region pb.Region) (servers []*pb.DBMcpServer, err error) {
servers = make([]*pb.DBMcpServer, 0)
err = postgres.Find(comm.TableMcp, &servers, "region=?", region)
return
}
// Mcp 服务
func (this *modelComp) mcpserver(id string) (server *pb.DBMcpServer, err error) {
server = &pb.DBMcpServer{}
err = postgres.Find(comm.TableMcp, server, "id=?", id)
return
}
func (this *modelComp) addmcpservers(server []*pb.DBMcpServer) (err error) {
err = postgres.Insert(comm.TableMcp, server)
return
}
func (this *modelComp) updatemcpservers(server []*pb.DBMcpServer) (err error) {
err = postgres.Save(comm.TableMcp, server)
return
}
func (this *modelComp) delmcpservers(ids []string) (err error) {
err = postgres.Delete(comm.TableMcp, "id IN ?", ids)
return
}
// 厂商列表
func (this *modelComp) factorys() (models []*pb.DBBrand, err error) {
models = make([]*pb.DBBrand, 0)
err = postgres.Find(comm.TableBrand, &models, "")
return
}
func (this *modelComp) factory(id uint32) (model *pb.DBBrand, err error) {
model = &pb.DBBrand{}
err = postgres.Find(comm.TableBrand, model, "id=?", id)
return
}
func (this *modelComp) addfactory(model *pb.DBBrand) (err error) {
err = postgres.Insert(comm.TableBrand, model)
return
}
func (this *modelComp) savefactory(model *pb.DBBrand) (err error) {
err = postgres.Save(comm.TableBrand, model)
return
}
func (this *modelComp) delfactory(id uint32) (err error) {
err = postgres.Delete(comm.TableBrand, "id=?", id)
return
}
// 查询设备
func (this *modelComp) products() (models []*pb.DBProduct, err error) {
models = make([]*pb.DBProduct, 0)
err = postgres.Find(comm.TableProduct, &models, "")
return
}
// 查询设备
func (this *modelComp) product(id uint32) (model *pb.DBProduct, err error) {
model = &pb.DBProduct{}
err = postgres.FindOne(comm.TableProduct, model, "id=?", id)
return
}
// 更新设备
func (this *modelComp) addproducts(products ...*pb.DBProduct) (err error) {
if err = postgres.Insert(comm.TableProduct, products); err != nil {
return err
}
for _, v := range products {
if err = postgres.CreateTable(fmt.Sprintf("%s_%x", comm.TableLicense, v.Id), &pb.DBAuthCode{}); err != nil {
this.module.Errorln(err)
return err
}
}
return
}
// 更新设备信息
func (this *modelComp) updateproducts(equipment *pb.DBProduct) (err error) {
err = postgres.Save(comm.TableProduct, equipment)
return
}
func (this *modelComp) delproducts(id uint32) (err error) {
err = postgres.Delete(comm.TableProduct, "id=?", id)
return
}
// 更新产品版本
func (this *modelComp) addproductversion(version *pb.DBProductVersion) (err error) {
err = postgres.Insert(comm.TableProductVersion, version)
return
}
// 查询设备
func (this *modelComp) productversions(pid uint32) (models []*pb.DBProductVersion, err error) {
models = make([]*pb.DBProductVersion, 0)
err = postgres.Find(comm.TableProductVersion, &models, "productid=?", pid)
return
}
// 查询单个产品版本
func (this *modelComp) productversion(id uint32) (model *pb.DBProductVersion, err error) {
model = &pb.DBProductVersion{}
err = postgres.FindOne(comm.TableProductVersion, model, "id=?", id)
return
}
// 更新产品版本
func (this *modelComp) delproductversion(id uint32) (err error) {
err = postgres.Delete(comm.TableProductVersion, "id=?", id)
return
}
// ChannelApp--------------------------------------------------------------
// 查询设备
func (this *modelComp) channelapps() (models []*pb.DBChannelApp, err error) {
models = make([]*pb.DBChannelApp, 0)
err = mysql.Find(comm.TableChannelApp, &models, "")
return
}
// 查询设备
func (this *modelComp) channelapp(channel int32) (model *pb.DBChannelApp, err error) {
model = &pb.DBChannelApp{}
err = mysql.FindOne(comm.TableChannelApp, model, "channel=?", channel)
return
}
// 更新设备
func (this *modelComp) addchannelapp(channelapp ...*pb.DBChannelApp) (err error) {
err = mysql.Insert(comm.TableChannelApp, channelapp)
return
}
// 更新设备信息
func (this *modelComp) updatechannelapp(channelapp *pb.DBChannelApp) (err error) {
err = mysql.Save(comm.TableChannelApp, channelapp)
return
}
func (this *modelComp) delchannelapp(channel int32) (err error) {
err = mysql.Delete(comm.TableChannelApp, "channel=?", channel)
return
}
// 生产批次台账------------------------------
func (this *modelComp) productionbatches(query interface{}, args ...interface{}) (models []*pb.DBProductionBatch, err error) {
models = make([]*pb.DBProductionBatch, 0)
err = postgres.Find(comm.TableProductionBatch, &models, query, args...)
return
}
func (this *modelComp) productionbatch(id uint32) (model *pb.DBProductionBatch, err error) {
model = &pb.DBProductionBatch{}
err = postgres.FindOne(comm.TableProductionBatch, &model, "id=?", id)
return
}
func (this *modelComp) addproductionbatch(model *pb.DBProductionBatch) (err error) {
err = postgres.Insert(comm.TableProductionBatch, model)
return
}
// 删除指定批次台账
func (this *modelComp) delproductionbatch(id uint32) (err error) {
err = postgres.Delete(comm.TableProductionBatch, "id=?", id)
return
}
// allocSerialRange 原子分配产品级序号区间,返回自增后的游标值(区间末端 end),
// 区间为 [end-total+1, end]。用 UPDATE ... RETURNING 单语句保证并发安全。
func (this *modelComp) allocSerialRange(pid uint32, total int64) (end int64, err error) {
err = postgres.Raw(
"UPDATE "+comm.TableProduct+" SET serialcursor = serialcursor + ? WHERE id = ? RETURNING serialcursor",
total, pid,
).Scan(&end).Error
return
}
// 统计指定批次中已被使用(status>0 或 uid非空)的授权码数量
func (this *modelComp) countUsedAuthCodesByBatch(pid uint32, batchid uint32) (used int64, err error) {
err = postgres.Table(fmt.Sprintf("%s_%x", comm.TableLicense, pid)).
Where("batchid=? AND (status>0 OR uid<>'')", batchid).Count(&used).Error
return
}
// 删除指定批次的全部授权码
func (this *modelComp) delAuthCodesByBatch(pid uint32, batchid uint32) (affected int64, err error) {
tx := postgres.Table(fmt.Sprintf("%s_%x", comm.TableLicense, pid)).
Where("batchid=?", batchid).Delete(&pb.DBAuthCode{})
affected = tx.RowsAffected
err = tx.Error
return
}
// 厂家设备------------------------------
func (this *modelComp) factoryDevics(pid uint32, query interface{}, args ...interface{}) (models []*pb.DBAuthCode, err error) {
models = make([]*pb.DBAuthCode, 0)
err = postgres.Find(fmt.Sprintf("%s_%x", comm.TableLicense, pid), &models, query, args...)
return
}
// 分页查询授权码(支持 ORDER BY + LIMIT/OFFSET),同时返回命中总数
func (this *modelComp) factoryDevicsPaged(pid uint32, query, orderClause string, limit, offset int, args ...interface{}) (models []*pb.DBAuthCode, total int64, err error) {
models = make([]*pb.DBAuthCode, 0)
tName := fmt.Sprintf("%s_%x", comm.TableLicense, pid)
tx := postgres.Table(tName)
if query != "" {
tx = tx.Where(query, args...)
}
if err = tx.Count(&total).Error; err != nil {
return
}
tx2 := postgres.Table(tName)
if query != "" {
tx2 = tx2.Where(query, args...)
}
if orderClause != "" {
tx2 = tx2.Order(orderClause)
}
if limit > 0 {
tx2 = tx2.Limit(limit)
}
if offset > 0 {
tx2 = tx2.Offset(offset)
}
err = tx2.Find(&models).Error
return
}
// 批量设置设备码封禁状态(disabled: 0 正常, -1 禁用),返回实际更新条数
func (this *modelComp) setFactoryDevicsDisabled(pid uint32, codes []string, disabled int32) (affected int64, err error) {
tx := postgres.Table(fmt.Sprintf("%s_%x", comm.TableLicense, pid)).
Where("code IN ?", codes).Update("disabled", disabled)
affected = tx.RowsAffected
err = tx.Error
return
}
func (this *modelComp) factoryDevic(pid uint32, code string) (model *pb.DBAuthCode, err error) {
model = &pb.DBAuthCode{}
err = postgres.FindOne(fmt.Sprintf("%s_%x", comm.TableLicense, pid), &model, "code=?", code)
return
}
func (this *modelComp) factoryDevicforuid(pid uint32, uid string) (models []*pb.DBAuthCode, err error) {
models = make([]*pb.DBAuthCode, 0)
err = postgres.Find(fmt.Sprintf("%s_%x", comm.TableLicense, pid), &models, "uid=?", uid)
return
}
func (this *modelComp) addfactoryDevics(pid uint32, models []*pb.DBAuthCode) (err error) {
// DBAuthCode 有 12 列,单条 INSERT 的预编译占位符上限是 65535(MySQL Error 1390)。
// 大批量会溢出,这里按 1000 行分批写入。
err = postgres.Table(fmt.Sprintf("%s_%x", comm.TableLicense, pid)).CreateInBatches(models, 1000).Error
return
}
func (this *modelComp) updatefactoryDevic(pid uint32, models *pb.DBAuthCode) (err error) {
err = postgres.Save(fmt.Sprintf("%s_%x", comm.TableLicense, pid), &models)
return
}
func (this *modelComp) updatefactoryDevics(pid uint32, models []*pb.DBAuthCode) (err error) {
err = postgres.Save(fmt.Sprintf("%s_%x", comm.TableLicense, pid), &models)
return
}
// decrProductStatActivated 将 product_stat.activated_count 原地减 1(下限为 0)。
// 在后台重置设备码(同步删除了对应 userdevice 行)时调用,保证仪表盘数据即时准确,
// 无需等待全量重建统计。
func (this *modelComp) decrProductStatActivated(productid uint32) (err error) {
err = mysql.Exec(
"UPDATE "+comm.TableProductStat+
" SET activated_count = GREATEST(0, activated_count - 1)"+
" WHERE productid = ?",
productid,
).Error
return
}
// 厂家公码---------------------------------------------------------------------
// 按品牌商 + 状态过滤查询公码列表(brandid=0 不过滤品牌商,statusFilter=-1 不过滤状态)
func (this *modelComp) factoryPublicCodes(brandid uint32, statusFilter int32) (models []*pb.DBFactoryPublicCode, err error) {
models = make([]*pb.DBFactoryPublicCode, 0)
tx := postgres.Table(comm.TableFactoryPublicCode)
if brandid != 0 {
tx = tx.Where("brandid=?", brandid)
}
if statusFilter >= 0 {
tx = tx.Where("status=?", statusFilter)
}
err = tx.Order("createtime DESC").Find(&models).Error
return
}
func (this *modelComp) factoryPublicCode(code string) (model *pb.DBFactoryPublicCode, err error) {
model = &pb.DBFactoryPublicCode{}
err = postgres.FindOne(comm.TableFactoryPublicCode, model, "code=?", code)
return
}
func (this *modelComp) addFactoryPublicCode(model *pb.DBFactoryPublicCode) (err error) {
err = postgres.Insert(comm.TableFactoryPublicCode, model)
return
}
func (this *modelComp) saveFactoryPublicCode(model *pb.DBFactoryPublicCode) (err error) {
err = postgres.Save(comm.TableFactoryPublicCode, model)
return
}
func (this *modelComp) delFactoryPublicCode(code string) (err error) {
err = postgres.Delete(comm.TableFactoryPublicCode, "code=?", code)
return
}
// 会议记录模板管理---------------------------------------------------------------------
// 列表查询:排除 outline / template 大文本字段,避免传输过大
func (this *modelComp) gettemplates() (models []*pb.DBEchoMeetTemplate, err error) {
models = make([]*pb.DBEchoMeetTemplate, 0)
err = postgres.Table(comm.TableEchomeetTemplate).
Select("id, tid, source, title, description, language, ttype, tags, icon, sort").
Order("sort DESC").
Find(&models).Error
return
}
// 详情查询:返回完整字段(含 outline / template)
func (this *modelComp) gettemplate(id uint64) (model *pb.DBEchoMeetTemplate, err error) {
model = &pb.DBEchoMeetTemplate{}
err = postgres.FindOne(comm.TableEchomeetTemplate, &model, "id=?", id)
return
}
func (this *modelComp) deltemplates(ids []uint64) (err error) {
err = postgres.Delete(comm.TableEchomeetTemplate, "id IN ?", ids)
return
}
func (this *modelComp) addtemplates(templates []*pb.DBEchoMeetTemplate) (err error) {
err = postgres.Insert(comm.TableEchomeetTemplate, templates)
return
}
func (this *modelComp) updatetemplates(templates []*pb.DBEchoMeetTemplate) (err error) {
err = postgres.Save(comm.TableEchomeetTemplate, templates)
return
}
// 支付商品---------------------------------------------------------------------
// 查询支付商品
func (this *modelComp) goodss() (models []*pb.DBGoods, err error) {
models = make([]*pb.DBGoods, 0)
err = mysql.Find(comm.TableGoods, &models, "")
return
}
// 查询支付商品详情
func (this *modelComp) goods(id string) (model *pb.DBGoods, err error) {
model = &pb.DBGoods{}
err = mysql.FindOne(comm.TableGoods, &model, "id=?", id)
return
}
// 添加支付商品
func (this *modelComp) addgoods(product *pb.DBGoods) (err error) {
err = mysql.Insert(comm.TableGoods, product)
return
}
// 更新支付商品
func (this *modelComp) updategoods(product *pb.DBGoods) (err error) {
err = mysql.Save(comm.TableGoods, product)
return
}
// 删除支付商品
func (this *modelComp) delgoods(id string) (err error) {
err = mysql.Delete(comm.TableGoods, "id=?", id)
return
}
// 唤醒词管理---------------------------------------------------------------------
// 查询唤醒语音列表
func (this *modelComp) wakeupvoices() (models []*pb.DBWakeupVoice, err error) {
models = make([]*pb.DBWakeupVoice, 0)
err = postgres.Find(comm.TableWakeupVoice, &models, "")
return
}
// 查询唤醒语音详情
func (this *modelComp) wakeupvoice(id uint32) (model *pb.DBWakeupVoice, err error) {
model = &pb.DBWakeupVoice{}
err = postgres.FindOne(comm.TableWakeupVoice, &model, "id=?", id)
return
}
// 添加唤醒语音
func (this *modelComp) addwakeupvoice(voice *pb.DBWakeupVoice) (err error) {
err = postgres.Insert(comm.TableWakeupVoice, voice)
return
}
// 更新唤醒语音
func (this *modelComp) updatewakeupvoice(voice *pb.DBWakeupVoice) (err error) {
err = postgres.Save(comm.TableWakeupVoice, voice)
return
}
// 删除唤醒语音
func (this *modelComp) delwakeupvoice(id uint32) (err error) {
err = postgres.Delete(comm.TableWakeupVoice, "id=?", id)
return
}
func (this *modelComp) getPayOrders(where string, page, size int32, args ...interface{}) (models []*pb.DBPayOrder, total int64, err error) {
models = make([]*pb.DBPayOrder, 0)
tx := mysql.Table(comm.TablePayOrder)
if where != "" {
tx = tx.Where(where, args...)
}
if err = tx.Count(&total).Error; err != nil {
this.module.Errorln(err)
return
}
offset := (page - 1) * size
if offset < 0 {
offset = 0
}
if err = tx.Order("create_time DESC").Offset(int(offset)).Limit(int(size)).Find(&models).Error; err != nil {
this.module.Errorln(err)
return
}
return
}
// 查询用户会议记录
func (this *modelComp) getUserEchomeetRecords(uid string, start, end int64) (models []*pb.DBEchoMeetRecord, err error) {
models = make([]*pb.DBEchoMeetRecord, 0)
where := "uid = ?"
args := []interface{}{uid}
if start > 0 {
where += " AND creationtime >= ?"
args = append(args, start)
}
if end > 0 {
where += " AND creationtime <= ?"
args = append(args, end)
}
err = mysql.Table(comm.TableEchomeetRecord).
Omit("original", "translate", "summary").
Where(where, args...).
Order("creationtime DESC").
Find(&models).Error
return
}
// 查询用户使用量日志
func (this *modelComp) getUserUseLog(query string, args ...interface{}) (models []*pb.DBUserUseLog, err error) {
models = make([]*pb.DBUserUseLog, 0)
err = mysql.Find(comm.TableUserUseLog, &models, query, args...)
return
}
func (this *modelComp) addIntegralLog(log *pb.DBUserUseLog) (err error) {
err = mysql.Insert(comm.TableUserUseLog, log)
return
}
// 查询App综合统计(日/月/年)
func (this *modelComp) getAppStats(statType int32, startDate, endDate string) (stats []*pb.DBAppStat, err error) {
tbl := appstat.TableByType(statType)
stats = make([]*pb.DBAppStat, 0)
err = mysql.Find(tbl, &stats, "stat_date >= ? AND stat_date <= ?", startDate, endDate)
return
}
// 查询全局累计统计(单行)
func (this *modelComp) getGlobalStat() (stat *pb.DBAppStat, err error) {
stat = &pb.DBAppStat{StatDate: "all"}
if err = mysql.FindOne(comm.TableAppStatGlobal, stat, "stat_date=?", "all"); err != nil {
// 不存在则返回空行,不视为错误
stat = &pb.DBAppStat{StatDate: "all"}
err = nil
}
return
}
// 累计注册人数(用户表 COUNT(*))
func (this *modelComp) countTotalUsers() (total int64, err error) {
err = mysql.Table(comm.TableUser).Count(&total).Error
return
}
// 累计充值人数(付费订单表 COUNT DISTINCT uid, status=已支付)
func (this *modelComp) countTotalPayUsers() (total int64, err error) {
err = mysql.Table(comm.TablePayOrder).
Where("status=?", int32(pb.PayOrderStatus_PAY_ORDER_PAID)).
Distinct("uid").
Count(&total).Error
return
}
// agentBatchStats 代理的累计生产批次和设备码总量(production_batch 维度)。
// 入参 brandIDs 为品牌商id集合(原厂家维度),productIDs 为产品id集合。
func (this *modelComp) agentBatchStats(brandIDs, productIDs []uint32) (batchCount, deviceCount int64, err error) {
type sumResult struct{ Total int64 }
var cond string
var args []interface{}
if len(brandIDs) > 0 && len(productIDs) > 0 {
cond = "brandid IN ? OR productid IN ?"
args = []interface{}{brandIDs, productIDs}
} else if len(brandIDs) > 0 {
cond = "brandid IN ?"
args = []interface{}{brandIDs}
} else if len(productIDs) > 0 {
cond = "productid IN ?"
args = []interface{}{productIDs}
}
txCount := postgres.Table(comm.TableProductionBatch)
txSum := postgres.Table(comm.TableProductionBatch)
if cond != "" {
txCount = txCount.Where(cond, args...)
txSum = txSum.Where(cond, args...)
}
if err = txCount.Count(&batchCount).Error; err != nil {
return
}
var r sumResult
if err = txSum.Select("COALESCE(SUM(devicenum), 0) as total").Scan(&r).Error; err != nil {
return
}
deviceCount = r.Total
return
}
// agentDeviceUIDs 返回绑定了指定产品设备的去重用户 uid 列表。
// 按 ServiceDB 的 userdevice 表实时统计(per-后台口径),不读共享 AdminDB 的 license 分表,
// 供充值 / 消耗统计按用户聚合使用。
// 注:激活数 / 绑定用户数的展示值改读 product_stat 快照(见 sumProductStats)。
func (this *modelComp) agentDeviceUIDs(productIDs []uint32) (uids []string, err error) {
uids = make([]string, 0)
if len(productIDs) == 0 {
return
}
err = mysql.Table(comm.TableUserdevice).
Where("productid IN ? AND uid != ''", productIDs).
Distinct("uid").Pluck("uid", &uids).Error
return
}
// sumProductStats 读取 product_stat 快照,汇总指定产品的激活设备数与绑定用户数。
// product_stat 由「重建统计」时全量扫描 userdevice 落库(见 rebuildProductStats)。
// 注:bound_user_count 为各产品独立去重值,跨产品求和时同时绑定多产品的用户会被重复计数;
// 单产品(仪表盘按 product_id 查看)口径精确。
func (this *modelComp) sumProductStats(productIDs []uint32) (activatedCount, boundUserCount int64, err error) {
if len(productIDs) == 0 {
return
}
type row struct {
Activated int64 `gorm:"column:activated"`
Bound int64 `gorm:"column:bound"`
}
var r row
err = mysql.Table(comm.TableProductStat).
Select("COALESCE(SUM(activated_count),0) AS activated, COALESCE(SUM(bound_user_count),0) AS bound").
Where("productid IN ?", productIDs).Scan(&r).Error
activatedCount, boundUserCount = r.Activated, r.Bound
return
}
// agentPayStats 代理用户的充值订单数和累计金额
func (this *modelComp) agentPayStats(uids []string) (orderCount, totalAmount int64, err error) {
if len(uids) == 0 {
return
}
type sumResult struct{ Total int64 }
paid := int32(pb.PayOrderStatus_PAY_ORDER_PAID)
if err = mysql.Table(comm.TablePayOrder).
Where("uid IN ? AND status = ?", uids, paid).Count(&orderCount).Error; err != nil {
return
}
var r sumResult
if err = mysql.Table(comm.TablePayOrder).
Where("uid IN ? AND status = ?", uids, paid).
Select("COALESCE(SUM(amount), 0) as total").Scan(&r).Error; err != nil {
return
}
totalAmount = r.Total
return
}
// userstatistics 表实际列名(gorm 默认命名策略将 Trademodel1Time 转为 trademodel1_time)
const (
colTradeTimeSum = "IFNULL(trademodel1_time,0)+IFNULL(trademodel2_time,0)+IFNULL(trademodel3_time,0)+IFNULL(trademodel4_time,0)"
colTradeNumSum = "IFNULL(trademodel1_num,0)+IFNULL(trademodel2_num,0)+IFNULL(trademodel3_num,0)+IFNULL(trademodel4_num,0)"
)
// agentUsageStats 名下用户累计翻译/会议消耗汇总。uids 为空返回 0。
func (this *modelComp) agentUsageStats(uids []string) (tradeTime, tradeCount, meetTime, meetCount int64, err error) {
if len(uids) == 0 {
return
}
type sumRow struct {
TradeTime int64 `gorm:"column:trade_time"`
TradeCount int64 `gorm:"column:trade_count"`
MeetTime int64 `gorm:"column:meet_time"`
MeetCount int64 `gorm:"column:meet_count"`
}
var r sumRow
err = mysql.Table(comm.TableUserStatistics).
Select("COALESCE(SUM("+colTradeTimeSum+"),0) AS trade_time, "+
"COALESCE(SUM("+colTradeNumSum+"),0) AS trade_count, "+
"COALESCE(SUM(meettime),0) AS meet_time, "+
"COALESCE(SUM(meetnum),0) AS meet_count").
Where("uid IN ?", uids).Scan(&r).Error
if err != nil {
return
}
tradeTime, tradeCount, meetTime, meetCount = r.TradeTime, r.TradeCount, r.MeetTime, r.MeetCount
return
}
// userRankRow 排行榜单行原始数据(不含 user 信息,需另查 user 表填充)
type userRankRow struct {
Uid string `gorm:"column:uid"`
TradeTime int64 `gorm:"column:trade_time"`
TradeCount int64 `gorm:"column:trade_count"`
MeetTime int64 `gorm:"column:meet_time"`
MeetCount int64 `gorm:"column:meet_count"`
}
// topUserRankByTrade 按翻译总时长降序取 Top N(4 个翻译模式时长合计)
func (this *modelComp) topUserRankByTrade(limit int) (rows []*userRankRow, err error) {
rows = make([]*userRankRow, 0)
err = mysql.Table(comm.TableUserStatistics).
Select("uid, (" + colTradeTimeSum + ") AS trade_time, (" + colTradeNumSum + ") AS trade_count, IFNULL(meettime,0) AS meet_time, IFNULL(meetnum,0) AS meet_count").
Where("(" + colTradeTimeSum + ") > 0").
Order("trade_time DESC").
Limit(limit).
Scan(&rows).Error
return
}
// topUserRankByMeet 按会议时长降序取 Top N
func (this *modelComp) topUserRankByMeet(limit int) (rows []*userRankRow, err error) {
rows = make([]*userRankRow, 0)
err = mysql.Table(comm.TableUserStatistics).
Select("uid, (" + colTradeTimeSum + ") AS trade_time, (" + colTradeNumSum + ") AS trade_count, IFNULL(meettime,0) AS meet_time, IFNULL(meetnum,0) AS meet_count").
Where("IFNULL(meettime,0) > 0").
Order("meet_time DESC").
Limit(limit).
Scan(&rows).Error
return
}
// findUsersByUids 批量查 user 表,返回 uid -> DBUser
func (this *modelComp) findUsersByUids(uids []string) (m map[string]*pb.DBUser, err error) {
m = make(map[string]*pb.DBUser, len(uids))
if len(uids) == 0 {
return
}
var users []*pb.DBUser
if err = mysql.Table(comm.TableUser).Where("uid IN ?", uids).Find(&users).Error; err != nil {
return
}
for _, u := range users {
m[u.Uid] = u
}
return
}
// addAdminResourceLog 写入超管→后台账号 的资源点流水
func (this *modelComp) addAdminResourceLog(log *pb.DBAdminResourceLog) (err error) {
err = mysql.Insert(comm.TableAdminResLog, log)
return
}
// getAdminResourceLogs 查询资源流水(按账号 + 分页)
func (this *modelComp) getAdminResourceLogs(account string, page, size int32) (logs []*pb.DBAdminResourceLog, total int64, err error) {
logs = make([]*pb.DBAdminResourceLog, 0)
tx := mysql.Table(comm.TableAdminResLog)
if account != "" {
tx = tx.Where("from_account=? OR to_account=?", account, account)
}
if err = tx.Count(&total).Error; err != nil {
return
}
if size <= 0 {
size = 20
}
if page <= 0 {
page = 1
}
offset := (page - 1) * size
err = tx.Order("ts DESC").Offset(int(offset)).Limit(int(size)).Find(&logs).Error
return
}
// sumAdminResourceFlow 汇总某账号的累计接收/派发量(vipday/aichat/trade-sec/meet-sec)
// 接收: to_account=account 的 SUM
// 派发: extra=account 的 useruselog SUM(即名下用户被赠送的)
func (this *modelComp) sumAdminReceivedResource(account string) (vipday, aichat, tradesec, meetsec int64, err error) {
type row struct {
Vipday int64 `gorm:"column:vipday"`
Aichat int64 `gorm:"column:aichat"`
Tradesec int64 `gorm:"column:tradesec"`
Meetsec int64 `gorm:"column:meetsec"`
}
var r row
err = mysql.Table(comm.TableAdminResLog).
Select("COALESCE(SUM(vipday),0) AS vipday, COALESCE(SUM(aichatintegral),0) AS aichat, "+
"COALESCE(SUM(tradesecond),0) AS tradesec, COALESCE(SUM(meetsecond),0) AS meetsec").
Where("to_account=?", account).Scan(&r).Error
if err != nil {
return
}
vipday, aichat, tradesec, meetsec = r.Vipday, r.Aichat, r.Tradesec, r.Meetsec
return
}
func (this *modelComp) sumAdminDispatchedResource(account string) (vipday, aichat, tradesec, meetsec int64, err error) {
type row struct {
Vipday int64 `gorm:"column:vipday"`
Aichat int64 `gorm:"column:aichat"`
Tradesec int64 `gorm:"column:tradesec"`
Meetsec int64 `gorm:"column:meetsec"`
}
var r row
// 注意:DBUserUseLog.Addvipday 的 gorm tag 写法 `gorm:"viptime"` 不是有效的自定义列名语法
// (正确应为 `gorm:"column:viptime"`),GORM 会忽略该 tag 并按默认命名策略,
// 表中实际列名为 addvipday;此处以实际列名为准。
err = mysql.Table(comm.TableUserUseLog).
Select("COALESCE(SUM(addvipday),0) AS vipday, COALESCE(SUM(addagentintegral),0) AS aichat, "+
"COALESCE(SUM(addtradesecond),0) AS tradesec, COALESCE(SUM(addmeetsecond),0) AS meetsec").
Where("logtype=? AND extra=?", int32(pb.UserLogType_AdminGive), account).Scan(&r).Error
if err != nil {
return
}
vipday, aichat, tradesec, meetsec = r.Vipday, r.Aichat, r.Tradesec, r.Meetsec
return
}
// getUserUseLogsPaged 用户使用量日志分页查询(流水)
// operator 非空时按 extra=operator 过滤(仅 AdminGive 类型的 extra 是操作人账号)
func (this *modelComp) getUserUseLogsPaged(uid string, logtype pb.UserLogType, operator string, page, size int32) (logs []*pb.DBUserUseLog, total int64, err error) {
logs = make([]*pb.DBUserUseLog, 0)
tx := mysql.Table(comm.TableUserUseLog)
if uid != "" {
tx = tx.Where("uid=?", uid)
}
if logtype != pb.UserLogType_UserLogTypeUnknown {
tx = tx.Where("logtype=?", int32(logtype))
}
if operator != "" {
tx = tx.Where("extra=?", operator)
}
if err = tx.Count(&total).Error; err != nil {
return
}
if size <= 0 {
size = 10
}
if page <= 0 {
page = 1
}
offset := (page - 1) * size
err = tx.Order("ts DESC").Offset(int(offset)).Limit(int(size)).Find(&logs).Error
return
}