chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)

This commit is contained in:
MiFaZhan
2026-11-02 13:27:59 +08:00
1250 changed files with 154111 additions and 0 deletions
@@ -0,0 +1,700 @@
package simwarning
import (
"context"
"database/sql"
"net/mail"
"strings"
"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/handler"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/do"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
func (s *sSimWarning) ConfigList(ctx context.Context, in *model.SimWarningConfigListParam) (list []*model.SimWarningConfigModel, totalCount int, err error) {
if in == nil {
in = &model.SimWarningConfigListParam{}
}
if in.Page <= 0 {
in.Page = 1
}
if in.PageSize <= 0 {
in.PageSize = 20
}
mod := s.configModel(ctx)
columns := dao.TWarnConfig.Columns()
if in.Id > 0 {
mod = mod.Where(columns.Id, in.Id)
}
if in.ItemType > 0 {
mod = mod.Where(columns.ItemType, in.ItemType)
}
if in.ItemId > 0 {
mod = mod.Where(columns.ItemId, in.ItemId)
}
if in.WarnUnit > 0 {
if in.WarnUnit == warnUnitDay || in.WarnUnit == warnUnitMonth {
mod = mod.Where("1 = 0")
} else {
mod = mod.Where(columns.WarnUnit, in.WarnUnit)
}
} else if in.WarnType > 0 {
mod = applyWarnTypeCompatibilityFilter(mod, in.WarnType)
}
if in.IsEnabled != nil {
mod = mod.Where(columns.IsEnabled, *in.IsEnabled)
}
if itemCode := strings.TrimSpace(in.ItemCode); itemCode != "" {
mod = mod.WhereLike(columns.ItemCode, "%"+itemCode+"%")
}
if keyword := strings.TrimSpace(in.Keyword); keyword != "" {
mod = mod.WhereLike(columns.ItemCode, "%"+keyword+"%")
}
err = mod.Page(in.Page, in.PageSize).
OrderDesc(columns.Id).
ScanAndCount(&list, &totalCount, false)
if err != nil {
return nil, 0, gerror.Wrap(err, "获取预警配置列表失败,请稍后重试")
}
if list == nil {
list = []*model.SimWarningConfigModel{}
}
fillConfigWarnTypes(list)
return list, totalCount, nil
}
func (s *sSimWarning) ConfigView(ctx context.Context, in *model.SimWarningConfigViewParam) (res *model.SimWarningConfigModel, err error) {
if in == nil || (in.Id <= 0 && in.ItemId <= 0) {
return nil, gerror.New("配置ID和对象ID不能同时为空")
}
mod := s.configModel(ctx)
columns := dao.TWarnConfig.Columns()
if in.Id > 0 {
mod = mod.Where(columns.Id, in.Id)
} else {
itemType := in.ItemType
if itemType == 0 {
itemType = warnItemTypeCard
}
if itemType != warnItemTypeCard && itemType != warnItemTypeFlowPool {
return nil, gerror.New("预警对象类型无效")
}
warnUnit, err := warnUnitForConfigLookup(itemType, in.WarnType, in.WarnUnit)
if err != nil {
return nil, err
}
mod = mod.Where(columns.ItemType, itemType).
Where(columns.ItemId, in.ItemId).
Where(columns.WarnUnit, warnUnit)
}
if err = mod.Scan(&res); err != nil {
return nil, gerror.Wrap(err, "获取预警配置详情失败,请稍后重试")
}
if res == nil || res.Id == 0 {
return nil, gerror.New("预警配置不存在或无权限访问")
}
if res.WarnUnit == warnUnitDay || res.WarnUnit == warnUnitMonth {
return nil, gerror.New("日期预警由系统默认规则处理,不支持查询用户配置")
}
res.WarnType = deriveWarnType(res.ItemType, res.WarnUnit)
return res, nil
}
func (s *sSimWarning) ConfigSave(ctx context.Context, in *model.SimWarningConfigSaveParam) (res *model.SimWarningConfigSaveModel, err error) {
if in == nil {
return nil, gerror.New("请求参数不能为空")
}
user := contexts.GetUser(ctx)
if user == nil || user.TenantId == "" || user.ID == 0 {
return nil, gerror.New("登录信息已过期")
}
in.ItemCode = strings.TrimSpace(in.ItemCode)
itemType := in.ItemType
if itemType == 0 {
itemType = warnItemTypeCard
}
in.ItemType = itemType
if err = normalizeConfigSaveParam(in); err != nil {
return nil, err
}
var itemCode string
switch in.ItemType {
case warnItemTypeCard:
card, err := s.queryConfigCard(ctx, in.ItemId, in.ItemCode)
if err != nil {
return nil, err
}
in.ItemId = card.Id
itemCode = strings.TrimSpace(in.ItemCode)
if itemCode == "" {
itemCode = strings.TrimSpace(card.Iccid)
}
if itemCode == "" {
itemCode = strings.TrimSpace(card.Msisdn)
}
case warnItemTypeFlowPool:
pool, err := s.queryConfigFlowPool(ctx, in.ItemId, in.ItemCode)
if err != nil {
return nil, err
}
in.ItemId = pool.Id
itemCode = strings.TrimSpace(in.ItemCode)
if itemCode == "" {
itemCode = strings.TrimSpace(pool.FlowPoolCode)
}
default:
return nil, gerror.New("预警对象类型无效")
}
if err = s.ensureConfigWritable(ctx, user.TenantId, in.Id, in.ItemType, in.ItemId, in.WarnUnit); err != nil {
return nil, err
}
now := gtime.Now()
data := buildConfigSaveData(user.TenantId, user.DeptId, user.ID, itemCode, in, now)
columns := dao.TWarnConfig.Columns()
_, err = dao.TWarnConfig.Ctx(ctx).
Data(data).
OnConflict(columns.TenantId, columns.ItemType, columns.ItemId, columns.WarnUnit).
OnDuplicateEx(strings.Join([]string{
columns.Id,
columns.TenantId,
columns.ItemType,
columns.ItemId,
columns.WarnUnit,
columns.CreatedDept,
columns.CreatedBy,
columns.CreatedAt,
}, ",")).
Save()
if err != nil {
return nil, gerror.Wrap(err, "保存预警配置失败,请稍后重试")
}
saved, err := s.findConfigByItem(ctx, user.TenantId, in.ItemType, in.ItemId, in.WarnUnit, true)
if err != nil {
return nil, err
}
if saved == nil || saved.Id == 0 {
return nil, gerror.New("保存预警配置后未查询到配置")
}
return &model.SimWarningConfigSaveModel{Id: saved.Id}, nil
}
func (s *sSimWarning) ConfigStatus(ctx context.Context, in *model.SimWarningConfigStatusParam) (err error) {
if in == nil || in.Id <= 0 {
return gerror.New("配置ID不能为空")
}
if in.IsEnabled != 0 && in.IsEnabled != 1 {
return gerror.New("启停状态无效")
}
if _, err = s.ConfigView(ctx, &model.SimWarningConfigViewParam{Id: in.Id}); err != nil {
return err
}
userId := contexts.GetUserId(ctx)
columns := dao.TWarnConfig.Columns()
result, err := s.configModel(ctx).Where(columns.Id, in.Id).Data(do.TWarnConfig{
IsEnabled: in.IsEnabled,
UpdatedBy: userId,
UpdatedAt: gtime.Now(),
}).Update()
if err != nil {
return gerror.Wrap(err, "启停预警配置失败,请稍后重试")
}
return ensureAffected(result, "预警配置不存在或无权限访问")
}
func (s *sSimWarning) ConfigDelete(ctx context.Context, in *model.SimWarningConfigDeleteParam) (err error) {
if in == nil || in.Id <= 0 {
return gerror.New("配置ID不能为空")
}
if _, err = s.ConfigView(ctx, &model.SimWarningConfigViewParam{Id: in.Id}); err != nil {
return err
}
result, err := s.configModel(ctx).Where(dao.TWarnConfig.Columns().Id, in.Id).Delete()
if err != nil {
return gerror.Wrap(err, "删除预警配置失败,请稍后重试")
}
return ensureAffected(result, "预警配置不存在或无权限访问")
}
func (s *sSimWarning) configModel(ctx context.Context) *gdb.Model {
return handler.Model(dao.TWarnConfig.Ctx(ctx)).
Fields(warningConfigFields()...)
}
func (s *sSimWarning) queryConfigCard(ctx context.Context, itemId int64, itemCode string) (*entity.TSimCardInfo, error) {
itemCode = strings.TrimSpace(itemCode)
if itemId <= 0 && itemCode == "" {
return nil, gerror.New("单卡ID或ICCID/MSISDN/IMSI不能为空")
}
var card *entity.TSimCardInfo
mod := service.IotTSimCardInfo().Model(ctx).Fields(
"id",
"iccid",
"msisdn",
"imsi",
"tenant_id",
)
if itemId > 0 {
mod = mod.Where("id", itemId)
} else {
mod = mod.Where("(iccid = ? OR msisdn = ? OR imsi = ?)", itemCode, itemCode, itemCode)
}
if err := mod.Scan(&card); err != nil {
return nil, gerror.Wrap(err, "查询单卡失败,请稍后重试")
}
if card == nil || card.Id == 0 {
return nil, gerror.New("单卡不存在或无权限访问")
}
return card, nil
}
func (s *sSimWarning) queryConfigFlowPool(ctx context.Context, itemId int64, itemCode string) (*entity.TSimFlowPool, error) {
itemCode = strings.TrimSpace(itemCode)
if itemId <= 0 && itemCode == "" {
return nil, gerror.New("流量池ID或流量池编号不能为空")
}
var pool *entity.TSimFlowPool
mod := service.IotTSimFlowPool().Model(ctx).Fields(
"id",
"flow_pool_code",
"tenant_id",
)
if itemId > 0 {
mod = mod.Where("id", itemId)
} else {
mod = mod.Where("flow_pool_code", itemCode)
}
if err := mod.Scan(&pool); err != nil {
return nil, gerror.Wrap(err, "查询流量池失败,请稍后重试")
}
if pool == nil || pool.Id == 0 {
return nil, gerror.New("流量池不存在或无权限访问")
}
return pool, nil
}
func (s *sSimWarning) ensureConfigWritable(ctx context.Context, tenantId string, id int64, itemType int, itemId int64, warnUnit int) error {
if id > 0 {
existing, err := s.findConfigByID(ctx, tenantId, id, false)
if err != nil {
return err
}
if existing == nil || existing.Id == 0 {
return gerror.New("预警配置不存在")
}
if existing.ItemType != itemType || existing.ItemId != itemId || existing.WarnUnit != warnUnit {
return gerror.New("配置ID与对象或预警维度不匹配")
}
return s.ensureConfigVisible(ctx, existing.Id)
}
existing, err := s.findConfigByItem(ctx, tenantId, itemType, itemId, warnUnit, false)
if err != nil {
return err
}
if existing == nil || existing.Id == 0 {
return nil
}
return s.ensureConfigVisible(ctx, existing.Id)
}
func (s *sSimWarning) ensureConfigVisible(ctx context.Context, id int64) error {
visible, err := s.findConfigByID(ctx, "", id, true)
if err != nil {
return err
}
if visible == nil || visible.Id == 0 {
return gerror.New("预警配置不存在或无权限访问")
}
return nil
}
func (s *sSimWarning) findConfigByID(ctx context.Context, tenantId string, id int64, withAuth bool) (*model.SimWarningConfigModel, error) {
if id <= 0 {
return nil, nil
}
columns := dao.TWarnConfig.Columns()
mod := dao.TWarnConfig.Ctx(ctx).
Fields(warningConfigFields()...).
Where(columns.Id, id)
if tenantId != "" {
mod = mod.Where(columns.TenantId, tenantId)
}
if withAuth {
mod = handler.Model(mod)
}
var res *model.SimWarningConfigModel
if err := mod.Scan(&res); err != nil {
return nil, gerror.Wrap(err, "查询预警配置失败,请稍后重试")
}
if res != nil {
res.WarnType = deriveWarnType(res.ItemType, res.WarnUnit)
}
return res, nil
}
func (s *sSimWarning) findConfigByItem(ctx context.Context, tenantId string, itemType int, itemId int64, warnUnit int, withAuth bool) (*model.SimWarningConfigModel, error) {
if tenantId == "" || itemId <= 0 || warnUnit <= 0 {
return nil, nil
}
if itemType == 0 {
itemType = warnItemTypeCard
}
columns := dao.TWarnConfig.Columns()
mod := dao.TWarnConfig.Ctx(ctx).
Fields(warningConfigFields()...).
Where(columns.TenantId, tenantId).
Where(columns.ItemType, itemType).
Where(columns.ItemId, itemId).
Where(columns.WarnUnit, warnUnit)
if withAuth {
mod = handler.Model(mod)
}
var res *model.SimWarningConfigModel
if err := mod.Scan(&res); err != nil {
return nil, gerror.Wrap(err, "查询预警配置失败,请稍后重试")
}
if res != nil {
res.WarnType = deriveWarnType(res.ItemType, res.WarnUnit)
}
return res, nil
}
func warningConfigFields() []any {
columns := dao.TWarnConfig.Columns()
return []any{
columns.Id,
columns.TenantId,
columns.BatchNo,
columns.ItemType,
columns.ItemId,
columns.ItemCode,
columns.WarnUnit,
columns.FirstWarn,
columns.SecondWarn,
columns.ThirdWarn,
columns.WarnMethods,
columns.WarnPhones,
columns.WarnEmails,
columns.AutoAction,
columns.IsEnabled,
columns.WxTemplateId,
columns.Remark,
columns.CreatedDept,
columns.CreatedBy,
columns.CreatedAt,
columns.UpdatedBy,
columns.UpdatedAt,
columns.AppNotifyEnabled,
}
}
func buildConfigSaveData(tenantId string, deptId int64, userId int64, resolvedItemCode string, in *model.SimWarningConfigSaveParam, now *gtime.Time) do.TWarnConfig {
itemCode := strings.TrimSpace(resolvedItemCode)
if itemCode == "" {
itemCode = strings.TrimSpace(in.ItemCode)
}
isEnabled := 1
if in.IsEnabled != nil {
isEnabled = *in.IsEnabled
}
warnMethods := normalizeConfigWarnMethods(in.WarnMethods)
data := do.TWarnConfig{
TenantId: tenantId,
ItemType: in.ItemType,
ItemId: in.ItemId,
ItemCode: itemCode,
BatchNo: strings.TrimSpace(in.BatchNo),
WarnUnit: in.WarnUnit,
FirstWarn: in.FirstWarn,
SecondWarn: in.SecondWarn,
ThirdWarn: in.ThirdWarn,
WarnMethods: warnMethods,
WarnPhones: strings.TrimSpace(in.WarnPhones),
WarnEmails: strings.TrimSpace(in.WarnEmails),
AutoAction: in.AutoAction,
Remark: strings.TrimSpace(in.Remark),
CreatedDept: deptId,
CreatedBy: userId,
CreatedAt: now,
UpdatedBy: userId,
UpdatedAt: now,
IsEnabled: isEnabled,
}
if in.AppNotifyEnabled != nil {
data.AppNotifyEnabled = *in.AppNotifyEnabled
}
return data
}
func normalizeConfigSaveParam(in *model.SimWarningConfigSaveParam) error {
in.ItemCode = strings.TrimSpace(in.ItemCode)
in.WarnMethods = normalizeConfigWarnMethods(in.WarnMethods)
in.WarnPhones = strings.TrimSpace(in.WarnPhones)
in.WarnEmails = strings.TrimSpace(in.WarnEmails)
in.Remark = strings.TrimSpace(in.Remark)
if in.ItemType == 0 {
in.ItemType = warnItemTypeCard
}
if in.ItemType != warnItemTypeCard && in.ItemType != warnItemTypeFlowPool {
return gerror.New("预警对象类型无效")
}
if in.ItemId <= 0 && in.ItemCode == "" {
if in.ItemType == warnItemTypeFlowPool {
return gerror.New("流量池ID或流量池编号不能为空")
}
return gerror.New("单卡ID或ICCID/MSISDN/IMSI不能为空")
}
if in.WarnUnit == warnUnitDay || in.WarnUnit == warnUnitMonth {
return gerror.New("日期预警由系统默认规则处理,不支持手动配置")
}
if err := normalizeConfigContacts(in); err != nil {
return err
}
if in.IsEnabled != nil && *in.IsEnabled != 0 && *in.IsEnabled != 1 {
return gerror.New("启停状态无效")
}
if in.AppNotifyEnabled != nil && *in.AppNotifyEnabled != 0 && *in.AppNotifyEnabled != 1 {
return gerror.New("小程序订阅通知状态无效")
}
if in.AutoAction != 0 && in.AutoAction != warnAutoActionDataCut {
return gerror.New("第三次是否断网配置无效")
}
if in.ItemType == warnItemTypeFlowPool && in.AutoAction != 0 {
return gerror.New("流量池预警不支持自动断网")
}
in.WarnType = deriveWarnType(in.ItemType, in.WarnUnit)
if err := validateWarnTypeUnit(in.ItemType, in.WarnType, in.WarnUnit); err != nil {
return err
}
if in.ItemType == warnItemTypeCard && in.AutoAction == warnAutoActionDataCut && in.ThirdWarn <= 0 {
return gerror.New("开启第三次断网时必须配置第三次提醒")
}
switch in.WarnType {
case warnTypeCardTraffic, warnTypeFlowPoolPercent:
if in.WarnUnit == warnUnitPercent {
first := normalizePercentThreshold(in.FirstWarn)
second := normalizePercentThreshold(in.SecondWarn)
third := normalizePercentThreshold(in.ThirdWarn)
if first > 100 || second > 100 || third > 100 {
return gerror.New("百分比阈值不能大于100")
}
in.FirstWarn = first
in.SecondWarn = second
in.ThirdWarn = third
}
return validateTrafficThresholds(in.FirstWarn, in.SecondWarn, in.ThirdWarn)
default:
return gerror.New("预警类型无效")
}
}
func normalizeConfigContacts(in *model.SimWarningConfigSaveParam) error {
if hasConfigWarnMethod(in.WarnMethods, "0") {
phones, err := normalizeConfigWarnPhones(in.WarnPhones)
if err != nil {
return err
}
in.WarnPhones = phones
} else {
in.WarnPhones = ""
}
if hasConfigWarnMethod(in.WarnMethods, "1") {
emails, err := normalizeConfigWarnEmails(in.WarnEmails)
if err != nil {
return err
}
in.WarnEmails = emails
} else {
in.WarnEmails = ""
}
return nil
}
func hasConfigWarnMethod(methods string, target string) bool {
for _, method := range splitConfigWarnContactList(methods) {
if method == target {
return true
}
}
return false
}
func normalizeConfigWarnMethods(methods string) string {
selected := map[string]struct{}{
warnMethodPlatform: {},
}
for _, method := range splitConfigWarnContactList(methods) {
switch method {
case warnMethodPlatform, warnMethodEmail, warnMethodSMS, warnMethodPhone:
selected[method] = struct{}{}
}
}
ordered := []string{
warnMethodPlatform,
warnMethodEmail,
warnMethodSMS,
warnMethodPhone,
}
result := make([]string, 0, len(ordered))
for _, method := range ordered {
if _, ok := selected[method]; ok {
result = append(result, method)
}
}
return strings.Join(result, ",")
}
func normalizeConfigWarnEmails(value string) (string, error) {
emails := splitConfigWarnContactList(value)
if len(emails) == 0 {
return "", gerror.New("请选择邮箱提醒时必须填写邮箱地址")
}
result := make([]string, 0, len(emails))
for _, email := range emails {
address, err := mail.ParseAddress(email)
if err != nil || address == nil || strings.TrimSpace(address.Address) == "" {
return "", gerror.Newf("邮箱格式不正确:%s", email)
}
result = append(result, strings.ToLower(strings.TrimSpace(address.Address)))
}
return strings.Join(result, ","), nil
}
func normalizeConfigWarnPhones(value string) (string, error) {
phones := splitConfigWarnContactList(value)
if len(phones) == 0 {
return "", gerror.New("请选择短信提醒时必须填写短信号码")
}
for _, phone := range phones {
if !isValidConfigWarnPhone(phone) {
return "", gerror.Newf("手机号格式不正确:%s", phone)
}
}
return strings.Join(phones, ","), nil
}
func isValidConfigWarnPhone(phone string) bool {
if len(phone) != 11 || phone[0] != '1' {
return false
}
for _, r := range phone {
if r < '0' || r > '9' {
return false
}
}
return phone[1] >= '3' && phone[1] <= '9'
}
func splitConfigWarnContactList(value string) []string {
parts := strings.FieldsFunc(value, func(r rune) bool {
switch r {
case ',', ';', ',', ';', '|', '\n', '\r', '\t', ' ':
return true
default:
return false
}
})
seen := make(map[string]struct{}, len(parts))
result := make([]string, 0, len(parts))
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
key := strings.ToLower(part)
if _, exists := seen[key]; exists {
continue
}
seen[key] = struct{}{}
result = append(result, part)
}
return result
}
func validateTrafficThresholds(first, second, third float64) error {
if first <= 0 {
return gerror.New("第一档阈值必须大于0")
}
if second < 0 || third < 0 {
return gerror.New("预警阈值不能小于0")
}
if second > 0 && second <= first {
return gerror.New("流量预警第二档阈值必须大于第一档")
}
if third > 0 {
if second <= 0 {
return gerror.New("配置第三档时必须先配置第二档")
}
if third <= second {
return gerror.New("流量预警第三档阈值必须大于第二档")
}
}
return nil
}
func warnUnitForConfigLookup(itemType, warnType, warnUnit int) (int, error) {
if warnUnit > 0 {
if warnUnit == warnUnitDay || warnUnit == warnUnitMonth {
return 0, gerror.New("日期预警由系统默认规则处理,不支持查询用户配置")
}
if err := validateWarnTypeUnit(itemType, deriveWarnType(itemType, warnUnit), warnUnit); err != nil {
return 0, err
}
return warnUnit, nil
}
switch warnType {
case warnTypeFlowPoolPercent:
return warnUnitPercent, nil
case warnTypeCardTraffic:
return 0, gerror.New("通过对象ID查询单卡流量预警时必须指定warnUnit")
default:
return 0, gerror.New("通过对象ID查询预警配置时必须指定warnUnit")
}
}
func applyWarnTypeCompatibilityFilter(mod *gdb.Model, warnType int) *gdb.Model {
switch warnType {
case warnTypeCardTraffic:
return mod.Where("item_type = ? AND warn_unit IN (?, ?)", warnItemTypeCard, warnUnitMb, warnUnitPercent)
case warnTypeFlowPoolPercent:
return mod.Where("item_type = ? AND warn_unit = ?", warnItemTypeFlowPool, warnUnitPercent)
default:
return mod.Where("1 = 0")
}
}
func fillConfigWarnTypes(list []*model.SimWarningConfigModel) {
for _, item := range list {
if item == nil {
continue
}
item.WarnType = deriveWarnType(item.ItemType, item.WarnUnit)
}
}
func ensureAffected(result sql.Result, message string) error {
affected, err := result.RowsAffected()
if err != nil {
return err
}
if affected == 0 {
return gerror.New(message)
}
return nil
}