// Package gen // @Link https://github.com/xiujiecn/xiu-admin // @Copyright Copyright (c) 2026 LiXiujie // @Author Lxj // @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE // @AutoGenerate Version package iot import ( "context" "fmt" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/handler" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/xgorm/hook" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/convert" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/utility/excel" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gctx" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" ) type sIotTSimFlowPool struct{} func NewIotTSimFlowPool() *sIotTSimFlowPool { return &sIotTSimFlowPool{} } func init() { service.RegisterIotTSimFlowPool(NewIotTSimFlowPool()) } // Model 流量池表ORM模型 func (s *sIotTSimFlowPool) Model(ctx context.Context, option ...*handler.Option) *gdb.Model { if len(option) == 0 { option = append(option, &handler.Option{ FilterTenant: true, FilterAuth: true, }) } return handler.Model(dao.TSimFlowPool.Ctx(ctx), option...) } // List 获取流量池表列表 func (s *sIotTSimFlowPool) List(ctx context.Context, in *model.TSimFlowPoolListParam) (list []*model.TSimFlowPoolListModel, totalCount int, err error) { mod := s.Model(ctx) poolPackage := dao.TSimFlowPackageInfo.Table() flowPool := dao.TSimFlowPool.Table() sysDept := dao.SysDept.Table() mod = mod.FieldsPrefix(flowPool, model.TSimFlowPoolListModel{}) // package_codes stores the actual pool package codes. pool_package_code is // a pool dimension/configuration value and must not be used to collect names. mod = mod.Fields("GROUP_CONCAT(DISTINCT " + poolPackage + "." + dao.TSimFlowPackageInfo.Columns().PackageName + " ORDER BY " + poolPackage + "." + dao.TSimFlowPackageInfo.Columns().PackageName + " SEPARATOR ',') as poolPackageName") mod = mod.Fields(sysDept + "." + dao.SysDept.Columns().DeptName + " as deptName") // 查询主键ID if in.Id > 0 { mod = mod.Where(dao.TSimFlowPool.Columns().Id, in.Id) } // 查询客户编号 if in.DeptId > 0 { mod = mod.Where(dao.TSimFlowPool.Columns().DeptId, in.DeptId) } // 查询流量池编号 if in.FlowPoolCode != "" { mod = mod.Where(dao.TSimFlowPool.Columns().FlowPoolCode, in.FlowPoolCode) } // 查询状态:0 正常、1 停用、2 锁定 if in.PoolStatus != nil { mod = mod.Where(dao.TSimFlowPool.Columns().PoolStatus, *in.PoolStatus) } // 查询创建时间 if len(in.CreatedAt) == 2 { in.CreatedAt[1] = in.CreatedAt[1].EndOfDay() } if len(in.CreatedAt) == 2 { mod = mod.WhereBetween(dao.TSimFlowPool.Columns().CreatedAt, in.CreatedAt[0], in.CreatedAt[1]) } // 分页 mod = mod.Page(in.Page, in.PageSize) // 排序 mod = mod.OrderDesc(dao.TSimFlowPool.Columns().Id) mod = mod.LeftJoin(poolPackage, fmt.Sprintf( "%s.%s = 102 AND JSON_CONTAINS(%s.%s, JSON_QUOTE(%s.%s))", poolPackage, dao.TSimFlowPackageInfo.Columns().PackageType, flowPool, dao.TSimFlowPool.Columns().PackageCodes, poolPackage, dao.TSimFlowPackageInfo.Columns().PackageCode, )) mod = mod.LeftJoin(sysDept, sysDept+"."+dao.SysDept.Columns().DeptId+"="+flowPool+"."+dao.TSimFlowPool.Columns().DeptId) // 按流量池主键分组(因为使用了聚合函数 GROUP_CONCAT) mod = mod.Group(flowPool + ".id") // 操作人摘要信息 mod = mod.Hook(hook.MemberSummary) // 查询数据 if err = mod.ScanAndCount(&list, &totalCount, false); err != nil { err = gerror.Wrap(err, "获取流量池表列表失败,请稍后重试!") return } return } // Export 导出流量池表 func (s *sIotTSimFlowPool) Export(ctx context.Context, in *model.TSimFlowPoolListParam) (err error) { list, totalCount, err := s.List(ctx, in) if err != nil { return } // 字段的排序是依据tags的字段顺序,如果你不想使用默认的排序方式,可以直接定义 tags = []string{"字段名称", "字段名称2", ...} tags, err := convert.GetEntityDescTags(model.TSimFlowPoolExportModel{}) if err != nil { return } var ( fileName = "导出流量池表-" + gctx.CtxId(ctx) sheetName = fmt.Sprintf("索引条件共%v行,共%v页,当前导出是第%v页,本页共%v行", totalCount, (totalCount+in.PageSize-1)/in.PageSize, in.Page, len(list)) exports []model.TSimFlowPoolExportModel ) if err = gconv.Scan(list, &exports); err != nil { return } err = excel.ExportByStructs(ctx, tags, exports, fileName, sheetName) return } // Edit 修改/新增流量池表 func (s *sIotTSimFlowPool) Edit(ctx context.Context, in *model.TSimFlowPoolEditParam) (err error) { // 验证'FlowPoolCode'唯一 if err = xgorm.IsUnique(ctx, &dao.TSimFlowPool, g.Map{dao.TSimFlowPool.Columns().FlowPoolCode: in.FlowPoolCode}, "流量池编号已存在", in.Id); err != nil { return } return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { // 修改 if in.Id > 0 { updateBy := contexts.GetUserId(ctx) in.UpdatedBy = &updateBy in.UpdatedAt = gtime.Now() if _, err = s.Model(ctx). Fields(model.TSimFlowPoolUpdateFields{}). WherePri(in.Id).Data(in).Update(); err != nil { err = gerror.Wrap(err, "修改流量池表失败,请稍后重试!") } return } // 新增 tenantId := contexts.GetTenantId(ctx) in.TenantId = &tenantId deptId := contexts.GetDeptId(ctx) in.CreatedDept = &deptId userid := contexts.GetUserId(ctx) in.CreatedBy = &userid in.CreatedAt = gtime.Now() if _, err = s.Model(ctx, &handler.Option{FilterAuth: false}). Fields(model.TSimFlowPoolInsertFields{}). Data(in).Insert(); err != nil { err = gerror.Wrap(err, "新增流量池表失败,请稍后重试!") } return }) } // Delete 删除流量池表 func (s *sIotTSimFlowPool) Delete(ctx context.Context, in *model.TSimFlowPoolDeleteParam) (err error) { if _, err = s.Model(ctx).WherePri(in.Id).Unscoped().Delete(); err != nil { err = gerror.Wrap(err, "删除流量池表失败,请稍后重试!") return } return } // View 获取流量池表指定信息 func (s *sIotTSimFlowPool) View(ctx context.Context, in *model.TSimFlowPoolViewParam) (res *model.TSimFlowPoolViewModel, err error) { mod := s.Model(ctx) poolPackage := dao.TSimFlowPackageInfo.Table() flowPool := dao.TSimFlowPool.Table() sysDept := dao.SysDept.Table() mod = mod.WherePri(in.Id) mod = mod.LeftJoin(poolPackage, fmt.Sprintf( "%s.%s = 102 AND JSON_CONTAINS(%s.%s, JSON_QUOTE(%s.%s))", poolPackage, dao.TSimFlowPackageInfo.Columns().PackageType, flowPool, dao.TSimFlowPool.Columns().PackageCodes, poolPackage, dao.TSimFlowPackageInfo.Columns().PackageCode, )) mod = mod.LeftJoin(sysDept, sysDept+"."+dao.SysDept.Columns().DeptId+"="+flowPool+"."+dao.TSimFlowPool.Columns().DeptId) mod = mod.FieldsPrefix(flowPool, model.TSimFlowPoolListModel{}) mod = mod.Fields("GROUP_CONCAT(DISTINCT " + poolPackage + "." + dao.TSimFlowPackageInfo.Columns().PackageName + " ORDER BY " + poolPackage + "." + dao.TSimFlowPackageInfo.Columns().PackageName + " SEPARATOR ',') as poolPackageName") mod = mod.Fields(sysDept + "." + dao.SysDept.Columns().DeptName + " as deptName") mod = mod.Group(flowPool + ".id") if err = mod.Hook(hook.MemberSummary).Scan(&res); err != nil { err = gerror.Wrap(err, "获取流量池表信息,请稍后重试!") return } return } // SyncPoolCard 流量池同步卡信息(调用计算方法) func (s *sIotTSimFlowPool) SyncPoolCard(ctx context.Context, deptId int64) (err error) { // 调用卡信息表获取套餐选项 packageOptions, err := service.IotTSimCardInfo().GetCardPackageOptions(ctx, deptId) if err != nil { return gerror.Wrap(err, "获取卡套餐选项失败,请稍后重试!") } g.Log().Info(ctx, "packageOptions 卡套餐列表长度(当前所有生效中的套餐):%v", len(packageOptions)) g.Log().Info(ctx, "packageOptions 卡套餐列表:%v", packageOptions) // 提取套餐编码列表 var cardPackageCodes []string for _, pkg := range packageOptions { cardPackageCodes = append(cardPackageCodes, pkg.CurrentPackageCode) } g.Log().Info(ctx, "cardPackageCodes 卡套餐编码列表长度:%v", len(cardPackageCodes)) g.Log().Info(ctx, "cardPackageCodes 卡套餐编码列表(卡信息表该用户当前所有生效中的套餐):%v", cardPackageCodes) if len(cardPackageCodes) == 0 { g.Log().Info(ctx, "SyncPoolCard 卡套餐列表为空,跳过同步") return nil } // 调用流量套餐服务获取对应的池套餐编码列表 poolPackageMap102, poolPackageMap104, err := service.IotTSimFlowPackageInfo().GetPoolPackagesByCardPackages(ctx, cardPackageCodes) if err != nil { return gerror.Wrap(err, "获取池套餐列表失败,请稍后重试!") } g.Log().Info(ctx, "poolPackageMap102 102池套餐编码列表:%v", poolPackageMap102) //{"dx-1g-1y":400,"dx-1g-1y-130":400,"dx-300m-3y":300}池套餐 g.Log().Info(ctx, "poolPackageMap104 104池套餐编码列表:%v", poolPackageMap104) //{"dx-30g-1y":400}加油包 // 合并所有池套餐编码 var poolPackageCodes []int64 seenPoolPackageCodes := make(map[int64]bool) for _, code := range poolPackageMap102 { if !seenPoolPackageCodes[code] { seenPoolPackageCodes[code] = true poolPackageCodes = append(poolPackageCodes, code) } } for _, code := range poolPackageMap104 { if !seenPoolPackageCodes[code] { seenPoolPackageCodes[code] = true poolPackageCodes = append(poolPackageCodes, code) } } g.Log().Info(ctx, "poolPackageCodes 池套餐列表:%v", poolPackageCodes) // [400,300] if len(poolPackageCodes) == 0 { g.Log().Info(ctx, "SyncPoolCard 池套餐列表为空,跳过同步") return nil } // 根据 deptId 查询已有的流量池 var existingPools []*model.TSimFlowPoolListModel existingPoolMod := dao.TSimFlowPool.Ctx(ctx). Where(dao.TSimFlowPool.Columns().DeptId, deptId). WhereIn(dao.TSimFlowPool.Columns().PoolPackageCode, poolPackageCodes). OrderDesc(dao.TSimFlowPool.Columns().Id) if tenantId := contexts.GetTenantId(ctx); tenantId != "" { existingPoolMod = existingPoolMod.Where(dao.TSimFlowPool.Columns().TenantId, tenantId) } err = existingPoolMod.Scan(&existingPools) if err != nil { return gerror.Wrap(err, "获取已有流量池列表失败,请稍后重试!") } // 构建已有流量池的 map,key 为 poolPackageCode existingPoolMap := make(map[int64]*model.TSimFlowPoolListModel) duplicatePoolIdsMap := make(map[int64][]int64) for _, pool := range existingPools { if _, ok := existingPoolMap[pool.PoolPackageCode]; ok { duplicatePoolIdsMap[pool.PoolPackageCode] = append(duplicatePoolIdsMap[pool.PoolPackageCode], pool.Id) continue } existingPoolMap[pool.PoolPackageCode] = pool } // 生成流量池编号前缀:POOL + 毫秒时间戳 flowPoolCodePrefix := fmt.Sprintf("POOL%d", gtime.TimestampMilli()) // 开启事务 err = s.Model(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { for i, poolPackageCode := range poolPackageCodes { // [400,300] now := gtime.Now() var keepPoolId int64 // 分别收集102套餐编码和104加油包套餐编码 var codesForPackage102 []string var codesForPackage104 []string for cardCode, pcCode := range poolPackageMap102 { //{"dx-1g-1y":400,"dx-1g-1y-130":400,"dx-300m-3y":300}池套餐 if pcCode == poolPackageCode { codesForPackage102 = append(codesForPackage102, cardCode) } } for cardCode, pcCode := range poolPackageMap104 { //{"dx-30g-1y":400}加油包 if pcCode == poolPackageCode { codesForPackage104 = append(codesForPackage104, cardCode) } } g.Log().Info(ctx, "池套餐编码:", poolPackageCode, " 102套餐列表:", codesForPackage102, " 104加油包列表:", codesForPackage104) // 调用计算流量池统计数据方法 poolStats, err := service.IotTSimCardInfo().CalculatePoolStatistics(ctx, &model.CalculatePoolStatisticsReq{ DeptId: deptId, PoolPackageCode: poolPackageCode, CardCodes102: codesForPackage102, CardCodes104: codesForPackage104, }) if err != nil { return gerror.Wrap(err, "计算流量池统计数据失败,请稍后重试!") } // 生成流量池编号 flowPoolCode := fmt.Sprintf("%s%02d", flowPoolCodePrefix, i+1) // 判断是否存在 // 只存储102套餐编码,不存储104加油包编码 if existingPool, ok := existingPoolMap[poolPackageCode]; ok { keepPoolId = existingPool.Id // 存在则更新 g.Log().Info(ctx, "准备更新流量池, poolId:", existingPool.Id, "poolPackageCode:", poolPackageCode) g.Log().Info(ctx, "更新数据: PoolPackageFlowMb:", poolStats.PoolPackageFlowMb, "AddOnFlowMb:", poolStats.AddOnFlowMb, "TotalFlowMb:", poolStats.TotalFlowMb) result, err := tx.Model(dao.TSimFlowPool.Table()).WherePri(existingPool.Id).Data(map[string]interface{}{ dao.TSimFlowPool.Columns().FlowPoolCode: flowPoolCode, dao.TSimFlowPool.Columns().DeptId: deptId, dao.TSimFlowPool.Columns().PackageCodes: gjson.New(codesForPackage102), dao.TSimFlowPool.Columns().CardCount: poolStats.TotalCardCount, dao.TSimFlowPool.Columns().CardFlowMb: poolStats.CardFlowMb, dao.TSimFlowPool.Columns().PoolPackageFlowMb: poolStats.PoolPackageFlowMb, dao.TSimFlowPool.Columns().AddOnFlowMb: poolStats.AddOnFlowMb, dao.TSimFlowPool.Columns().TotalFlowMb: poolStats.TotalFlowMb, dao.TSimFlowPool.Columns().UsedFlowMb: poolStats.TotalUsedFlowMb, dao.TSimFlowPool.Columns().RemainFlowMb: poolStats.RemainFlowMb, dao.TSimFlowPool.Columns().PoolStatus: poolStats.PoolStatus, }).Update() if err != nil { return gerror.Wrap(err, "更新流量池失败,请稍后重试!") } affected, _ := result.RowsAffected() g.Log().Info(ctx, "更新流量池成功, poolId:", existingPool.Id, "affected:", affected, "poolPackageCode:", poolPackageCode, "remainFlowMb:", poolStats.RemainFlowMb) } else { // 不存在则插入 result, err := tx.Model(dao.TSimFlowPool.Table()).Data(map[string]interface{}{ dao.TSimFlowPool.Columns().FlowPoolCode: flowPoolCode, dao.TSimFlowPool.Columns().DeptId: deptId, dao.TSimFlowPool.Columns().TenantId: contexts.GetTenantId(ctx), dao.TSimFlowPool.Columns().CreatedDept: contexts.GetDeptId(ctx), dao.TSimFlowPool.Columns().CreatedBy: contexts.GetUserId(ctx), dao.TSimFlowPool.Columns().CreatedAt: now, dao.TSimFlowPool.Columns().UpdatedAt: now, dao.TSimFlowPool.Columns().PackageCodes: gjson.New(codesForPackage102), dao.TSimFlowPool.Columns().PoolPackageCode: poolPackageCode, dao.TSimFlowPool.Columns().CardCount: poolStats.TotalCardCount, dao.TSimFlowPool.Columns().CardFlowMb: poolStats.CardFlowMb, dao.TSimFlowPool.Columns().PoolPackageFlowMb: poolStats.PoolPackageFlowMb, dao.TSimFlowPool.Columns().AddOnFlowMb: poolStats.AddOnFlowMb, dao.TSimFlowPool.Columns().TotalFlowMb: poolStats.TotalFlowMb, dao.TSimFlowPool.Columns().UsedFlowMb: poolStats.TotalUsedFlowMb, dao.TSimFlowPool.Columns().RemainFlowMb: poolStats.RemainFlowMb, dao.TSimFlowPool.Columns().PoolStatus: poolStats.PoolStatus, }).Insert() if err != nil { return gerror.Wrap(err, "插入流量池失败,请稍后重试!") } keepPoolId, _ = result.LastInsertId() g.Log().Info(ctx, "插入流量池成功, poolPackageCode:", poolPackageCode, "remainFlowMb:", poolStats.RemainFlowMb) } if keepPoolId > 0 && len(duplicatePoolIdsMap[poolPackageCode]) > 0 { _, err = tx.Model(dao.TSimFlowPool.Table()). WhereIn(dao.TSimFlowPool.Columns().Id, duplicatePoolIdsMap[poolPackageCode]). Delete() if err != nil { return gerror.Wrap(err, "清理重复流量池失败,请稍后重试!") } } if len(codesForPackage102) > 0 { _, err = tx.Model(dao.TSimCardInfo.Table()). Where(dao.TSimCardInfo.Columns().DeptId, deptId). WhereIn(dao.TSimCardInfo.Columns().CurrentPackageCode, codesForPackage102). Data(g.Map{ dao.TSimCardInfo.Columns().IsFlowPool: 1, dao.TSimCardInfo.Columns().FlowPoolCode: flowPoolCode, dao.TSimCardInfo.Columns().UpdatedBy: contexts.GetUserId(ctx), dao.TSimCardInfo.Columns().UpdatedAt: now, }).Update() if err != nil { return gerror.Wrap(err, "同步流量池卡归属失败,请稍后重试!") } } } return nil }) if err != nil { return err } g.Log().Info(ctx, "SyncPoolCard 同步完成, 共处理:", len(poolPackageCodes), "个流量池") return nil } // GetCardsByPackageCodes 根据套餐编码列表获取卡列表 func (s *sIotTSimFlowPool) GetCardsByPackageCodes(ctx context.Context, req *model.GetCardsByPackageCodesParam) (list []*model.TSimCardInfoListModel, totalCount int, err error) { deptId := contexts.GetDeptId(ctx) g.Log().Info(ctx, "GetCardsByPackageCodes - deptId:", deptId, "FlowPoolCode:", req.FlowPoolCode) // 从流量池表查询套餐编码列表和部门ID type FlowPoolInfo struct { DeptId int64 `json:"deptId" dc:"部门ID"` PackageCodes *gjson.Json `json:"packageCodes" dc:"套餐编号列表"` } var flowPool FlowPoolInfo // 先用 FlowPoolCode 查询 err = dao.TSimFlowPool.Ctx(ctx). Where(dao.TSimFlowPool.Columns().FlowPoolCode, req.FlowPoolCode). Fields(dao.TSimFlowPool.Columns().DeptId, dao.TSimFlowPool.Columns().PackageCodes). Scan(&flowPool) if err != nil || flowPool.PackageCodes == nil { g.Log().Warning(ctx, "未找到流量池记录, FlowPoolCode:", req.FlowPoolCode, "err:", err) return } g.Log().Info(ctx, "GetCardsByPackageCodes - flowPool.DeptId:", flowPool.DeptId) // 解析 JSON 为字符串数组 if flowPool.PackageCodes != nil { var packageCodes []string _ = flowPool.PackageCodes.Scan(&packageCodes) req.PackageCodes = packageCodes g.Log().Info(ctx, "GetCardsByPackageCodes - packageCodes:", req.PackageCodes) } // 将流量池的 deptId 放到请求参数中 req.DeptId = flowPool.DeptId return service.IotTSimCardInfo().GetCardsByPackageCodes(ctx, req) }