384 lines
16 KiB
Go
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
|
|
}
|