Files
iotlcm/server/internal/logic/app/app_card_query.go
T

384 lines
16 KiB
Go

package app
import (
"context"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/library/xgorm/handler"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/os/gtime"
)
const appCardListMaxPageSize = 100
// 处理首页免登录公共查卡,只返回卡基础信息,不开放绑卡、重启等业务能力。
func (s *sAppCard) PublicQuery(ctx context.Context, param *model.AppCardQueryParam) (res *model.AppCardModel, err error) {
card, err := s.findPublicCard(ctx, param.TenantId, param.CardValue)
if err != nil {
return nil, err
}
if card == nil {
return s.publicQueryUpstream(ctx, param.CardValue)
}
return s.publicCardRowToModel(ctx, card, param.CardValue), nil
}
// 查询当前登录用户可见卡片。终端用户走 t_sim_terminal 关联表,
// 企业用户沿用管理平台数据权限查询持卡列表。
func (s *sAppCard) List(ctx context.Context, param *model.AppCardListParam) (items []*model.AppCardModel, total int, err error) {
user, err := appCurrentUser(ctx)
if err != nil {
return nil, 0, err
}
mod := s.visibleCardModel(ctx, user)
if param.AllocateOnly {
if s.isTerminalUser(ctx, user) || user.DeptId <= 0 {
return nil, 0, gerror.New("当前账号不能划拨卡片")
}
if err := appEnsureFeatureAccess(ctx, "card:allocate"); err != nil {
return nil, 0, err
}
mod = mod.Where(dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().DeptId, user.DeptId)
}
mod = s.applyCardFilters(mod, param)
if param.Page <= 0 {
param.Page = 1
}
if param.PageSize <= 0 {
param.PageSize = 10
}
if param.PageSize > appCardListMaxPageSize {
param.PageSize = appCardListMaxPageSize
}
var rows []*appCardQueryRow
includeTotal := param.IncludeTotal == nil || *param.IncludeTotal
queryPageSize := param.PageSize
if !includeTotal {
queryPageSize++
}
query := mod.OrderDesc(dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().Id).
Page(param.Page, queryPageSize)
if includeTotal {
err = query.ScanAndCount(&rows, &total, false)
} else {
err = query.Scan(&rows)
if len(rows) > param.PageSize {
rows = rows[:param.PageSize]
}
}
if err != nil {
return nil, 0, gerror.Wrap(err, "查询卡列表失败,请稍后重试")
}
if err = s.attachCardChannelInfoRows(ctx, rows); err != nil {
return nil, 0, err
}
ownerType := s.ownerType(ctx, user)
items = make([]*model.AppCardModel, 0, len(rows))
for _, row := range rows {
items = append(items, s.cardRowToModel(ctx, row, user, ownerType, true))
}
return items, total, nil
}
// 登录后先查用户可见卡;查不到时回退公共查卡,保证登录后仍能查看公共卡详情。
func (s *sAppCard) Detail(ctx context.Context, param *model.AppCardDetailParam) (res *model.AppCardModel, err error) {
user, err := appCurrentUser(ctx)
if err != nil {
return nil, err
}
card, err := s.findVisibleCard(ctx, user, param.CardValue)
if err != nil {
return nil, err
}
if card != nil {
model := s.attachYesterdayDeptFlow(ctx, s.cardRowToModel(ctx, card, user, s.ownerType(ctx, user), true))
return s.attachAppCardRealtimeSession(ctx, card, model), nil
}
card, err = s.findPublicCard(ctx, user.TenantId, param.CardValue)
if err != nil {
return nil, err
}
if card == nil {
return s.publicQueryUpstream(ctx, param.CardValue)
}
if err = s.attachCardChannelInfo(ctx, card); err != nil {
return nil, err
}
return s.publicCardRowToModel(ctx, card, param.CardValue), nil
}
func (s *sAppCard) publicQueryUpstream(ctx context.Context, cardValue string) (*model.AppCardModel, error) {
provider, err := newAppSimUpstreamProvider(ctx)
if err != nil {
return nil, appErrUpstreamUnavailable(err)
}
info, err := provider.QuerySimBasicInfo(ctx, appSimBasicInfoInputFromCardValue(cardValue))
if err != nil {
return nil, appErrUpstreamUnavailable(err)
}
card := s.upstreamPublicCardToModel(info, cardValue)
if card == nil {
return nil, gerror.New("卡信息不存在,请确认查询号码是否正确")
}
return card, nil
}
func (s *sAppCard) attachYesterdayDeptFlow(ctx context.Context, card *model.AppCardModel) *model.AppCardModel {
if card == nil || card.DeptId <= 0 || strings.TrimSpace(card.TenantId) == "" {
return card
}
statDate := gtime.Now().AddDate(0, 0, -1).Format("Y-m-d")
var daily *entity.TDeptFlowDaily
_ = dao.TDeptFlowDaily.Ctx(ctx).
Where(dao.TDeptFlowDaily.Columns().TenantId, card.TenantId).
Where(dao.TDeptFlowDaily.Columns().DeptId, card.DeptId).
Where(dao.TDeptFlowDaily.Columns().StatDate, statDate).
Limit(1).
Scan(&daily)
if daily == nil || daily.Id == 0 {
return card
}
card.YesterdayFlowStatDate = statDate
if daily.StatDate != nil && !daily.StatDate.IsZero() {
card.YesterdayFlowStatDate = daily.StatDate.Format("Y-m-d")
}
card.YesterdayDeptUsedFlowMb = daily.TotalUsedFlowMb
card.YesterdayDeptSms = daily.TotalSms
card.YesterdayDeptVoiceMin = daily.TotalVoiceMin
return card
}
func (s *sAppCard) findPublicCard(ctx context.Context, tenantId string, cardValue string) (*appCardQueryRow, error) {
mod := s.cardQueryBase(ctx, false)
tenantId = strings.TrimSpace(tenantId)
if tenantId == "" {
tenantId = consts.DefaultSystemTenantCode
}
mod = mod.Where(dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().TenantId, tenantId)
var row *appCardQueryRow
err := mod.
Where(appCardValueCondition(), appCardValueArgs(cardValue)...).
Limit(1).
Scan(&row)
if err != nil {
return nil, err
}
if err = s.attachCardChannelInfo(ctx, row); err != nil {
return nil, err
}
return row, nil
}
func (s *sAppCard) findOpenCard(ctx context.Context, tenantId string, cardValue string) (*appCardQueryRow, error) {
if strings.TrimSpace(cardValue) == "" {
return nil, gerror.New("请输入ICCID、MSISDN、IMSI或IMEI")
}
cardTable := dao.TSimCardInfo.Table()
mod := s.cardQueryBase(ctx, false)
if tenantId = strings.TrimSpace(tenantId); tenantId != "" {
mod = mod.Where(cardTable+"."+dao.TSimCardInfo.Columns().TenantId, tenantId)
}
var row *appCardQueryRow
err := mod.
Where(appCardValueCondition(), appCardValueArgs(cardValue)...).
OrderDesc(cardTable + "." + dao.TSimCardInfo.Columns().Id).
Limit(1).
Scan(&row)
if err != nil {
return nil, err
}
if err = s.attachCardChannelInfo(ctx, row); err != nil {
return nil, err
}
return row, nil
}
func (s *sAppCard) findCardByValue(ctx context.Context, tenantId string, cardValue string) (*entity.TSimCardInfo, error) {
if strings.TrimSpace(cardValue) == "" {
return nil, gerror.New("请输入ICCID、MSISDN、IMSI或IMEI")
}
var card *entity.TSimCardInfo
err := dao.TSimCardInfo.Ctx(ctx).
Where(dao.TSimCardInfo.Columns().TenantId, tenantId).
Where(appCardValueCondition(), appCardValueArgs(cardValue)...).
Limit(1).
Scan(&card)
if err != nil {
return nil, err
}
return card, nil
}
func (s *sAppCard) findVisibleCard(ctx context.Context, user *entity.SysUser, cardValue string) (*appCardQueryRow, error) {
var row *appCardQueryRow
err := s.visibleCardModel(ctx, user).
Where(appCardValueCondition(), appCardValueArgs(cardValue)...).
Limit(1).
Scan(&row)
if err != nil {
return nil, err
}
if err = s.attachCardChannelInfo(ctx, row); err != nil {
return nil, err
}
return row, nil
}
// findDiagnosisCard 查询诊断卡片。企业用户诊断只隔离租户,不套用部门数据范围;
// 终端用户仍沿用已绑定卡范围,避免终端账号读取其他用户的卡片。
func (s *sAppCard) findDiagnosisCard(ctx context.Context, user *entity.SysUser, cardValue string) (*appCardQueryRow, error) {
if user == nil || strings.TrimSpace(user.TenantId) == "" {
return nil, nil
}
if s.isTerminalUser(ctx, user) {
return s.findVisibleCard(ctx, user, cardValue)
}
cardTable := dao.TSimCardInfo.Table()
var row *appCardQueryRow
err := s.cardQueryBase(ctx, false).
Where(cardTable+"."+dao.TSimCardInfo.Columns().TenantId, strings.TrimSpace(user.TenantId)).
Where(appCardValueCondition(), appCardValueArgs(cardValue)...).
OrderDesc(cardTable + "." + dao.TSimCardInfo.Columns().Id).
Limit(1).
Scan(&row)
if err != nil {
return nil, err
}
if err = s.attachCardChannelInfo(ctx, row); err != nil {
return nil, err
}
return row, nil
}
func (s *sAppCard) visibleCardModel(ctx context.Context, user *entity.SysUser) *gdb.Model {
cardTable := dao.TSimCardInfo.Table()
if s.isTerminalUser(ctx, user) {
bindTable := dao.TSimTerminal.Table()
packageAlias := "terminal_package"
categoryAlias := "terminal_category"
packageJoinTable := dao.TSimFlowPackageInfo.Table() + " " + packageAlias
categoryJoinTable := dao.TSimPackageCategory.Table() + " " + categoryAlias
return s.cardQueryBase(ctx, false).
InnerJoin(bindTable, bindTable+"."+dao.TSimTerminal.Columns().Iccid+"="+cardTable+"."+dao.TSimCardInfo.Columns().Iccid).
LeftJoin(packageJoinTable, packageAlias+"."+dao.TSimFlowPackageInfo.Columns().PackageCode+"="+bindTable+"."+appSimTerminalCurrentPackageCodeColumn+" AND "+packageAlias+"."+dao.TSimFlowPackageInfo.Columns().TenantId+"="+bindTable+"."+dao.TSimTerminal.Columns().TenantId).
LeftJoin(categoryJoinTable, categoryAlias+"."+dao.TSimPackageCategory.Columns().CategoryCode+"="+packageAlias+"."+dao.TSimFlowPackageInfo.Columns().CategoryCode+" AND "+categoryAlias+"."+dao.TSimPackageCategory.Columns().TenantId+"="+packageAlias+"."+dao.TSimFlowPackageInfo.Columns().TenantId).
Where(cardTable+"."+dao.TSimCardInfo.Columns().TenantId, user.TenantId).
Where(bindTable+"."+dao.TSimTerminal.Columns().TenantId, user.TenantId).
Where(bindTable+"."+dao.TSimTerminal.Columns().TerminalId, user.UserId).
Fields(bindTable + "." + dao.TSimTerminal.Columns().Id + " as terminal_bind_id").
Fields(bindTable + "." + dao.TSimTerminal.Columns().TerminalId + " as terminal_user_id").
Fields(bindTable + "." + dao.TSimTerminal.Columns().TerminalName + " as terminalName").
Fields(bindTable + "." + appSimTerminalDeptIdColumn + " as terminal_dept_id").
Fields(bindTable + "." + appSimTerminalCurrentPackageCodeColumn + " as terminal_current_package_code").
Fields(packageAlias + "." + dao.TSimFlowPackageInfo.Columns().PackageName + " as terminal_package_name").
Fields(packageAlias + "." + dao.TSimFlowPackageInfo.Columns().PackageType + " as terminal_package_type").
Fields(packageAlias + "." + dao.TSimFlowPackageInfo.Columns().CategoryCode + " as terminal_category_code").
Fields(categoryAlias + "." + dao.TSimPackageCategory.Columns().CategoryName + " as terminal_category_name")
}
return s.enterpriseVisibleCardModel(ctx, user)
}
func (s *sAppCard) enterpriseVisibleCardModel(ctx context.Context, user *entity.SysUser) *gdb.Model {
cardTable := dao.TSimCardInfo.Table()
if user == nil || user.DeptId <= 0 {
return s.cardQueryBase(ctx, false).Where("1=0")
}
mod := s.cardQueryBase(ctx, false).
Where(cardTable+"."+dao.TSimCardInfo.Columns().TenantId, user.TenantId)
return appApplyEnterpriseDataScope(
ctx,
user,
mod,
cardTable+"."+dao.TSimCardInfo.Columns().DeptId,
cardTable+"."+dao.TSimCardInfo.Columns().CreatedBy,
func(legacyMod *gdb.Model) *gdb.Model {
return appLegacyEnterpriseDeptAndSubScope(ctx, user, legacyMod, cardTable+"."+dao.TSimCardInfo.Columns().DeptId)
},
)
}
// 统一卡查询基础 JOIN,其中 current_package_code 对应套餐表 package_code。
// 卡片展示和公共当前套餐详情都依赖这个关联关系。
func (s *sAppCard) cardQueryBase(ctx context.Context, useAuth bool) *gdb.Model {
cardTable := dao.TSimCardInfo.Table()
packageTable := dao.TSimFlowPackageInfo.Table()
categoryTable := dao.TSimPackageCategory.Table()
ownerDeptAlias := "owner_dept"
ownerDeptJoinTable := dao.SysDept.Table() + " " + ownerDeptAlias
mod := dao.TSimCardInfo.Ctx(ctx).
FieldsPrefix(cardTable, entity.TSimCardInfo{}).
Fields(packageTable+"."+dao.TSimFlowPackageInfo.Columns().PackageName+" as packageName").
Fields(packageTable+"."+dao.TSimFlowPackageInfo.Columns().PackageType+" as packageType").
Fields(packageTable+"."+dao.TSimFlowPackageInfo.Columns().CategoryCode+" as categoryCode").
Fields(categoryTable+"."+dao.TSimPackageCategory.Columns().CategoryName+" as categoryName").
Fields(ownerDeptAlias+"."+dao.SysDept.Columns().DeptName+" as ownerDeptName").
Fields(ownerDeptAlias+"."+dao.SysDept.Columns().DeptKey+" as ownerDeptKey").
LeftJoin(packageTable, packageTable+"."+dao.TSimFlowPackageInfo.Columns().PackageCode+"="+cardTable+"."+dao.TSimCardInfo.Columns().CurrentPackageCode+" AND "+packageTable+"."+dao.TSimFlowPackageInfo.Columns().TenantId+"="+cardTable+"."+dao.TSimCardInfo.Columns().TenantId).
LeftJoin(categoryTable, categoryTable+"."+dao.TSimPackageCategory.Columns().CategoryCode+"="+packageTable+"."+dao.TSimFlowPackageInfo.Columns().CategoryCode+" AND "+categoryTable+"."+dao.TSimPackageCategory.Columns().TenantId+"="+packageTable+"."+dao.TSimFlowPackageInfo.Columns().TenantId).
LeftJoin(ownerDeptJoinTable, ownerDeptAlias+"."+dao.SysDept.Columns().DeptId+"="+cardTable+"."+dao.TSimCardInfo.Columns().DeptId+" AND "+ownerDeptAlias+"."+dao.SysDept.Columns().TenantId+"="+cardTable+"."+dao.TSimCardInfo.Columns().TenantId)
if useAuth {
mod = handler.Model(mod, &handler.Option{FilterAuth: true, FilterTenant: true})
}
return mod
}
func (s *sAppCard) applyCardFilters(mod *gdb.Model, param *model.AppCardListParam) *gdb.Model {
if strings.TrimSpace(param.CardValue) != "" {
if param.AllocateOnly {
// 划拨列表支持号码片段检索,详情与执行提交仍按完整号码定位。
value := "%" + strings.TrimSpace(param.CardValue) + "%"
columns := dao.TSimCardInfo.Columns()
table := dao.TSimCardInfo.Table() + "."
mod = mod.Where("("+table+columns.Iccid+" LIKE ? OR "+table+columns.Msisdn+" LIKE ? OR "+table+columns.Imsi+" LIKE ? OR "+table+columns.Imei+" LIKE ?)", value, value, value, value)
} else {
mod = mod.Where(appCardValueCondition(), appCardValueArgs(param.CardValue)...)
}
}
if strings.TrimSpace(param.CategoryCode) != "" {
mod = mod.Where(dao.TSimFlowPackageInfo.Table()+"."+dao.TSimFlowPackageInfo.Columns().CategoryCode, strings.TrimSpace(param.CategoryCode))
}
switch strings.TrimSpace(param.Status) {
case "normal":
mod = mod.Where(dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().BossCardStatus, 2).
WhereNot(dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().PackageStatus, 3)
case "low-flow":
mod = mod.Where(dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().PackageTotalFlowMb+">?", 0).
Where("("+dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().PackageTotalFlowMb+"-"+dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().UsedFlowMb+")/"+dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().PackageTotalFlowMb+" <= ?", 0.2)
case "expiring":
mod = mod.Where(dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().FlowPkgEndAt+" IS NOT NULL").
Where(dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().FlowPkgEndAt+" >= ?", gtime.Now()).
Where(dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().FlowPkgEndAt+" <= ?", gtime.Now().AddDate(0, 0, 30))
case "error":
mod = mod.Where(
"("+dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().PackageStatus+" = ? OR "+dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().BossCardStatus+" IN(?, ?, ?, ?) OR "+dao.TSimCardInfo.Table()+"."+dao.TSimCardInfo.Columns().BossCardStatus+" NOT IN(?, ?, ?, ?, ?, ?, ?, ?))",
consts.PackageStatusExpired,
0,
consts.V3ApiSimBossCardStatusStopped,
8,
9,
0,
consts.V3ApiSimBossCardStatusPendingActivate,
consts.V3ApiSimBossCardStatusActivated,
consts.V3ApiSimBossCardStatusStopped,
6,
7,
8,
9,
)
}
return mod
}