2ed5dfa6a1
iotlcm-app:小程序端 + 用户端服务。 - app/:uni-app 小程序前端(基于 unibest,MIT) - 微信 AppID → YOUR_WX_APPID,uni-app AppID → __UNI__YOURAPPID - 内网后端地址 → admin.iotlcm.example.com - 应用标题与 unibest 模板 README 重写(保留上游致谢与 MIT 声明) - server/:GoFrame 用户端服务(基于 XiuAdmin,Apache-2.0) - module xiuadmin → git.5iot.com/iotlcm-public/iotlcm-app(360 文件) - 支付回调地址 → app.iotlcm.example.com,商户名与应用名泛化 - 真实手机号 → 合法 11 位编造号(保留号段前 3 位 + 后 4 位) - 依赖号码尾部的断言同步更新 - 补 server/LICENSE(Apache-2.0) - 移除内部资产:CI 触发器 - 补根 README,LF 行尾 + .gitattributes 已知:TestSysDept_RefreshDeptAncestors 在源仓同一提交上同样失败,属既有缺陷。
539 lines
15 KiB
Go
539 lines
15 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/consts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model"
|
|
"git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity"
|
|
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
func (s *sAppCard) cardRowToModel(ctx context.Context, row *appCardQueryRow, user *entity.SysUser, ownerType string, canBusiness bool) *model.AppCardModel {
|
|
if row == nil {
|
|
return nil
|
|
}
|
|
canBusiness = canBusiness && s.canOperateCard(ctx, user, row)
|
|
remain := row.PackageTotalFlowMb - row.UsedFlowMb
|
|
if remain < 0 {
|
|
remain = 0
|
|
}
|
|
usedPercent := float64(0)
|
|
if row.PackageTotalFlowMb > 0 {
|
|
usedPercent = row.UsedFlowMb / row.PackageTotalFlowMb * 100
|
|
}
|
|
isBound := false
|
|
if user != nil && s.isTerminalUser(ctx, user) && ownerType == appCardOwnerTerminal {
|
|
isBound = true
|
|
} else if user != nil {
|
|
isBound = s.isCardBoundToUser(ctx, row.Iccid, user)
|
|
}
|
|
currentPackageCode := appCardCurrentPackageCode(row)
|
|
packageName := appCardPackageName(row)
|
|
packageType := appCardPackageType(row)
|
|
categoryCode := appCardCategoryCode(row)
|
|
categoryName := appCardCategoryName(row)
|
|
abnormalReasons := appCardAbnormalReasons(row.BossCardStatus, row.PackageStatus)
|
|
deptName := ""
|
|
deptKey := ""
|
|
deptId := int64(0)
|
|
tenantId := ""
|
|
chanName := ""
|
|
chanAlias := ""
|
|
if ownerType == appCardOwnerAgent {
|
|
actualDeptId := appCardOwnerDeptId(row)
|
|
if user != nil && !s.isTerminalUser(ctx, user) && user.DeptId > 0 && actualDeptId > 0 {
|
|
if displayDept, err := appEnterpriseDisplayDept(ctx, row.TenantId, user.DeptId, actualDeptId); err == nil && displayDept != nil && displayDept.DeptId > 0 {
|
|
deptId = displayDept.DeptId
|
|
deptName = strings.TrimSpace(displayDept.DeptName)
|
|
deptKey = strings.TrimSpace(displayDept.DeptKey)
|
|
}
|
|
} else {
|
|
deptId = actualDeptId
|
|
deptName = appCardOwnerDeptName(row)
|
|
deptKey = appCardOwnerDeptKey(row)
|
|
}
|
|
tenantId = row.TenantId
|
|
chanName = row.ChanName
|
|
chanAlias = row.ChanAlias
|
|
}
|
|
return &model.AppCardModel{
|
|
Id: row.Id,
|
|
Iccid: row.Iccid,
|
|
Msisdn: row.Msisdn,
|
|
Imsi: row.Imsi,
|
|
Imei: row.Imei,
|
|
RegionCode: row.RegionCode,
|
|
DeptId: deptId,
|
|
DeptName: deptName,
|
|
DeptKey: deptKey,
|
|
CurrentPackageCode: currentPackageCode,
|
|
PackageName: packageName,
|
|
PackageType: packageType,
|
|
CategoryCode: categoryCode,
|
|
CategoryName: categoryName,
|
|
PackageStatus: row.PackageStatus,
|
|
AccessType: row.AccessType,
|
|
BossCardStatus: row.BossCardStatus,
|
|
AbnormalReason: strings.Join(abnormalReasons, "、"),
|
|
AbnormalReasons: abnormalReasons,
|
|
CardType: row.CardType,
|
|
NetModel: row.NetModel,
|
|
PackageTotalFlowMb: row.PackageTotalFlowMb,
|
|
UsedFlowMb: row.UsedFlowMb,
|
|
RemainFlowMb: remain,
|
|
UsedFlowPercent: usedPercent,
|
|
FlowPkgBeginAt: row.FlowPkgBeginAt,
|
|
FlowPkgEndAt: row.FlowPkgEndAt,
|
|
DataServiceStatus: row.DataServiceStatus,
|
|
PackageTotalSms: row.PackageTotalSms,
|
|
UsedSms: row.UsedSms,
|
|
SmsServiceStatus: row.SmsServiceStatus,
|
|
PackageTotalVoice: row.PackageTotalVoice,
|
|
UsedVoice: row.UsedVoice,
|
|
VoiceServiceStatus: row.VoiceServiceStatus,
|
|
RoamServiceStatus: row.RoamServiceStatus,
|
|
StatusSyncTime: row.StatusSyncTime,
|
|
FlowSyncTime: row.FlowSyncTime,
|
|
ChanKey: "",
|
|
ChanName: chanName,
|
|
ChanAlias: chanAlias,
|
|
Remark: row.Remark,
|
|
TenantId: tenantId,
|
|
TerminalName: row.TerminalName,
|
|
IsBound: isBound,
|
|
CanBusiness: canBusiness,
|
|
CanAllocate: s.canAllocateCard(ctx, user, row, ownerType, canBusiness),
|
|
OwnerType: ownerType,
|
|
DataSource: "local",
|
|
}
|
|
}
|
|
|
|
func (s *sAppCard) canAllocateCard(ctx context.Context, user *entity.SysUser, row *appCardQueryRow, ownerType string, canBusiness bool) bool {
|
|
return user != nil &&
|
|
row != nil &&
|
|
ownerType == appCardOwnerAgent &&
|
|
!s.isTerminalUser(ctx, user) &&
|
|
canBusiness &&
|
|
user.DeptId > 0 &&
|
|
row.DeptId == user.DeptId
|
|
}
|
|
|
|
func (s *sAppCard) publicCardRowToModel(ctx context.Context, row *appCardQueryRow, cardValue string) *model.AppCardModel {
|
|
card := s.cardRowToModel(ctx, row, nil, appCardOwnerPublic, false)
|
|
if card == nil {
|
|
return nil
|
|
}
|
|
|
|
card.Id = 0
|
|
card.DeptId = 0
|
|
card.DeptName = ""
|
|
card.DeptKey = ""
|
|
card.ChanKey = ""
|
|
card.ChanName = ""
|
|
card.ChanAlias = ""
|
|
card.Remark = ""
|
|
card.TenantId = ""
|
|
card.TerminalName = ""
|
|
card.IsBound = false
|
|
card.CanBusiness = false
|
|
card.CanAllocate = false
|
|
card.OwnerType = appCardOwnerPublic
|
|
|
|
return card
|
|
}
|
|
|
|
func (s *sAppCard) upstreamPublicCardToModel(info *appSimBasicInfoOutput, cardValue string) *model.AppCardModel {
|
|
if info == nil {
|
|
return nil
|
|
}
|
|
cardStatus := info.CardStatus
|
|
if cardStatus == 0 {
|
|
cardStatus = consts.V3ApiSimBossCardStatusActivated
|
|
}
|
|
packageCode, packageName := appOneLinkMockPackageFromRemark(info.Remark)
|
|
abnormalReasons := appCardAbnormalReasons(cardStatus, 0)
|
|
return &model.AppCardModel{
|
|
Iccid: appFirstNotEmpty(info.Iccid, strings.TrimSpace(cardValue)),
|
|
Msisdn: info.Msisdn,
|
|
Imsi: info.Imsi,
|
|
Imei: info.Imei,
|
|
CurrentPackageCode: packageCode,
|
|
PackageName: packageName,
|
|
BossCardStatus: cardStatus,
|
|
AbnormalReason: strings.Join(abnormalReasons, "、"),
|
|
AbnormalReasons: abnormalReasons,
|
|
StatusSyncTime: appParseOneLinkTime(info.LastChangeDate),
|
|
FlowPkgBeginAt: appParseOneLinkTime(info.ActiveDate),
|
|
OwnerType: appCardOwnerPublic,
|
|
DataSource: info.Provider,
|
|
UpstreamProvider: info.Provider,
|
|
IsBound: false,
|
|
CanBusiness: false,
|
|
CanAllocate: false,
|
|
}
|
|
}
|
|
|
|
func (s *sAppCard) packageRowToModel(row *appPackageQueryRow, ownerType string, canBusiness bool) *model.AppPackageModel {
|
|
if row == nil {
|
|
return nil
|
|
}
|
|
status, statusType := appPackageStatus(row.EffectiveStatus, row.ExpectedExpireTime)
|
|
return &model.AppPackageModel{
|
|
Id: row.Id,
|
|
SubscriptionNo: row.SubscriptionNo,
|
|
PackageCode: row.PackageCode,
|
|
PackageName: row.PackageName,
|
|
PackageType: row.PackageType,
|
|
PackageTypeName: appPackageTypeName(row.PackageType),
|
|
CategoryCode: row.CategoryCode,
|
|
CategoryName: row.CategoryName,
|
|
Iccid: row.Iccid,
|
|
Msisdn: row.Msisdn,
|
|
CardId: row.CardId,
|
|
CardValue: appFirstNotEmpty(row.Msisdn, row.Iccid),
|
|
CardRemark: row.CardRemark,
|
|
TerminalName: row.TerminalName,
|
|
BusinessType: row.BusinessType,
|
|
BusinessTypeName: appBusinessTypeName(row.BusinessType),
|
|
EffectiveStatus: row.EffectiveStatus,
|
|
Status: status,
|
|
StatusType: statusType,
|
|
FilterValue: row.PackageCode,
|
|
EffectiveTime: row.EffectiveTime,
|
|
ExpireTime: row.ExpectedExpireTime,
|
|
ValidText: appPackageValidText(row.EffectiveTime, row.ExpectedExpireTime),
|
|
EffectText: appPackageEffectText(row.EffectiveStatus),
|
|
FlowMb: row.FlowMb,
|
|
FlowText: appFlowText(row.FlowMb),
|
|
OwnerType: ownerType,
|
|
CanBusiness: canBusiness,
|
|
}
|
|
}
|
|
|
|
func (s *sAppCard) publicPackageRowToModel(card *appCardQueryRow, pkg *appPublicPackageQueryRow) *model.AppPackageModel {
|
|
if card == nil || pkg == nil {
|
|
return nil
|
|
}
|
|
effectiveStatus := appEffectiveStatusFromCardPackageStatus(card.PackageStatus)
|
|
status, statusType := appPackageStatus(effectiveStatus, card.FlowPkgEndAt)
|
|
flowMb := pkg.FlowMb
|
|
if flowMb <= 0 {
|
|
flowMb = card.PackageTotalFlowMb
|
|
}
|
|
return &model.AppPackageModel{
|
|
Id: pkg.Id,
|
|
PackageCode: pkg.PackageCode,
|
|
PackageName: pkg.PackageName,
|
|
PackageType: pkg.PackageType,
|
|
PackageTypeName: appPackageTypeName(pkg.PackageType),
|
|
CategoryCode: pkg.CategoryCode,
|
|
CategoryName: pkg.CategoryName,
|
|
Iccid: card.Iccid,
|
|
Msisdn: card.Msisdn,
|
|
CardId: card.Id,
|
|
CardValue: appFirstNotEmpty(card.Msisdn, card.Iccid),
|
|
CardRemark: card.Remark,
|
|
TerminalName: card.TerminalName,
|
|
BusinessTypeName: "当前套餐",
|
|
EffectiveStatus: effectiveStatus,
|
|
Status: status,
|
|
StatusType: statusType,
|
|
FilterValue: pkg.PackageCode,
|
|
EffectiveTime: card.FlowPkgBeginAt,
|
|
ExpireTime: card.FlowPkgEndAt,
|
|
ValidText: appPackageValidText(card.FlowPkgBeginAt, card.FlowPkgEndAt),
|
|
EffectText: appPackageEffectText(effectiveStatus),
|
|
FlowMb: flowMb,
|
|
FlowText: appFlowText(flowMb),
|
|
OwnerType: appCardOwnerPublic,
|
|
CanBusiness: false,
|
|
}
|
|
}
|
|
|
|
func appSanitizePublicPackageModel(pkg *model.AppPackageModel, cardValue string) *model.AppPackageModel {
|
|
if pkg == nil {
|
|
return nil
|
|
}
|
|
pkg.Id = 0
|
|
pkg.SubscriptionNo = ""
|
|
pkg.CardId = 0
|
|
pkg.CardValue = appFirstNotEmpty(pkg.Msisdn, pkg.Iccid, strings.TrimSpace(cardValue))
|
|
pkg.CardRemark = ""
|
|
pkg.TerminalName = ""
|
|
pkg.BusinessType = 0
|
|
pkg.BusinessTypeName = "当前套餐"
|
|
pkg.OwnerType = appCardOwnerPublic
|
|
pkg.CanBusiness = false
|
|
|
|
return pkg
|
|
}
|
|
|
|
func appPackageStatus(status int, expireAt *gtime.Time) (string, string) {
|
|
if expireAt != nil && !expireAt.IsZero() && expireAt.Before(gtime.Now()) {
|
|
return "已过期", "danger"
|
|
}
|
|
if status == 1 && expireAt != nil && !expireAt.IsZero() && expireAt.Before(gtime.Now().AddDate(0, 0, 30)) {
|
|
return "即将到期", "warning"
|
|
}
|
|
switch status {
|
|
case 0:
|
|
return "待生效", "primary"
|
|
case 1:
|
|
return "生效中", "success"
|
|
case 2:
|
|
return "已过期", "danger"
|
|
case 3:
|
|
return "已终止", "danger"
|
|
case 4:
|
|
return "已作废", "danger"
|
|
default:
|
|
return "未知", "primary"
|
|
}
|
|
}
|
|
|
|
func appCardAbnormalReasons(bossCardStatus int, packageStatus int) []string {
|
|
reasons := make([]string, 0, 2)
|
|
if packageStatus == consts.PackageStatusExpired {
|
|
reasons = append(reasons, "套餐已过期")
|
|
}
|
|
switch bossCardStatus {
|
|
case 0:
|
|
reasons = append(reasons, "卡状态未知")
|
|
case consts.V3ApiSimBossCardStatusStopped:
|
|
reasons = append(reasons, "卡状态停机")
|
|
case 8:
|
|
reasons = append(reasons, "卡状态预销户")
|
|
case 9:
|
|
reasons = append(reasons, "卡状态已销户")
|
|
case consts.V3ApiSimBossCardStatusPendingActivate,
|
|
consts.V3ApiSimBossCardStatusActivated,
|
|
6,
|
|
7:
|
|
default:
|
|
reasons = append(reasons, fmt.Sprintf("未知卡状态:%d", bossCardStatus))
|
|
}
|
|
return reasons
|
|
}
|
|
|
|
func appEffectiveStatusFromCardPackageStatus(status int) int {
|
|
switch status {
|
|
case 1:
|
|
return 0
|
|
case 2:
|
|
return 1
|
|
case 3:
|
|
return 2
|
|
default:
|
|
return 4
|
|
}
|
|
}
|
|
|
|
func appPackageTypeName(packageType int) string {
|
|
switch packageType {
|
|
case 101:
|
|
return "单卡套餐"
|
|
case 102:
|
|
return "池套餐"
|
|
case 103:
|
|
return "单卡加油包"
|
|
case 104:
|
|
return "池卡加油包"
|
|
default:
|
|
return gconv.String(packageType)
|
|
}
|
|
}
|
|
|
|
func appBusinessTypeName(businessType int) string {
|
|
switch businessType {
|
|
case 0:
|
|
return "订购"
|
|
case 1:
|
|
return "变更"
|
|
case 2:
|
|
return "续费"
|
|
default:
|
|
return gconv.String(businessType)
|
|
}
|
|
}
|
|
|
|
func appPackageValidText(beginAt, expireAt *gtime.Time) string {
|
|
if beginAt == nil || beginAt.IsZero() || expireAt == nil || expireAt.IsZero() {
|
|
return ""
|
|
}
|
|
return beginAt.Format("Y-m-d") + " 至 " + expireAt.Format("Y-m-d")
|
|
}
|
|
|
|
func appPackageEffectText(status int) string {
|
|
switch status {
|
|
case 0:
|
|
return "套餐待生效"
|
|
case 1:
|
|
return "套餐生效中"
|
|
case 2:
|
|
return "套餐已过期"
|
|
case 3:
|
|
return "套餐已终止"
|
|
case 4:
|
|
return "套餐已作废"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func appFlowText(flowMb float64) string {
|
|
if flowMb <= 0 {
|
|
return ""
|
|
}
|
|
if flowMb >= 1024 {
|
|
return fmt.Sprintf("%.2fGB", flowMb/1024)
|
|
}
|
|
return fmt.Sprintf("%.0fMB", flowMb)
|
|
}
|
|
|
|
func appFirstNotEmpty(values ...string) string {
|
|
for _, value := range values {
|
|
if strings.TrimSpace(value) != "" {
|
|
return value
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func appCardOwnerDeptId(row *appCardQueryRow) int64 {
|
|
if row == nil {
|
|
return 0
|
|
}
|
|
if row.TerminalDeptId > 0 {
|
|
return row.TerminalDeptId
|
|
}
|
|
return row.DeptId
|
|
}
|
|
|
|
func appCardOwnerDeptName(row *appCardQueryRow) string {
|
|
if row == nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(row.OwnerDeptName)
|
|
}
|
|
|
|
func appCardOwnerDeptKey(row *appCardQueryRow) string {
|
|
if row == nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(row.OwnerDeptKey)
|
|
}
|
|
|
|
func appCardCurrentPackageCode(row *appCardQueryRow) string {
|
|
if row == nil {
|
|
return ""
|
|
}
|
|
if strings.TrimSpace(row.TerminalCurrentPackageCode) != "" {
|
|
return strings.TrimSpace(row.TerminalCurrentPackageCode)
|
|
}
|
|
return strings.TrimSpace(row.CurrentPackageCode)
|
|
}
|
|
|
|
func appCardPackageName(row *appCardQueryRow) string {
|
|
if row == nil {
|
|
return ""
|
|
}
|
|
terminalPackageCode := strings.TrimSpace(row.TerminalCurrentPackageCode)
|
|
if terminalPackageCode != "" {
|
|
if strings.TrimSpace(row.TerminalPackageName) != "" {
|
|
return row.TerminalPackageName
|
|
}
|
|
if terminalPackageCode != strings.TrimSpace(row.CurrentPackageCode) {
|
|
return ""
|
|
}
|
|
}
|
|
return row.PackageName
|
|
}
|
|
|
|
func appCardPackageType(row *appCardQueryRow) int {
|
|
if row == nil {
|
|
return 0
|
|
}
|
|
terminalPackageCode := strings.TrimSpace(row.TerminalCurrentPackageCode)
|
|
if terminalPackageCode != "" {
|
|
if row.TerminalPackageType > 0 {
|
|
return row.TerminalPackageType
|
|
}
|
|
if terminalPackageCode != strings.TrimSpace(row.CurrentPackageCode) {
|
|
return 0
|
|
}
|
|
}
|
|
return row.PackageType
|
|
}
|
|
|
|
func appCardCategoryCode(row *appCardQueryRow) string {
|
|
if row == nil {
|
|
return ""
|
|
}
|
|
terminalPackageCode := strings.TrimSpace(row.TerminalCurrentPackageCode)
|
|
if terminalPackageCode != "" {
|
|
if strings.TrimSpace(row.TerminalCategoryCode) != "" {
|
|
return row.TerminalCategoryCode
|
|
}
|
|
if terminalPackageCode != strings.TrimSpace(row.CurrentPackageCode) {
|
|
return ""
|
|
}
|
|
}
|
|
return row.CategoryCode
|
|
}
|
|
|
|
func appCardCategoryName(row *appCardQueryRow) string {
|
|
if row == nil {
|
|
return ""
|
|
}
|
|
terminalPackageCode := strings.TrimSpace(row.TerminalCurrentPackageCode)
|
|
if terminalPackageCode != "" {
|
|
if strings.TrimSpace(row.TerminalCategoryName) != "" {
|
|
return row.TerminalCategoryName
|
|
}
|
|
if terminalPackageCode != strings.TrimSpace(row.CurrentPackageCode) {
|
|
return ""
|
|
}
|
|
}
|
|
return row.CategoryName
|
|
}
|
|
|
|
func appOneLinkMockPackageFromRemark(remark string) (packageCode string, packageName string) {
|
|
for _, part := range strings.Split(remark, ";") {
|
|
item := strings.TrimSpace(part)
|
|
if item == "" {
|
|
continue
|
|
}
|
|
key, value, ok := strings.Cut(item, "=")
|
|
if !ok {
|
|
continue
|
|
}
|
|
switch strings.TrimSpace(key) {
|
|
case "package":
|
|
packageCode = strings.TrimSpace(value)
|
|
case "name":
|
|
packageName = strings.TrimSpace(value)
|
|
}
|
|
}
|
|
if packageName == "" {
|
|
packageName = packageCode
|
|
}
|
|
return packageCode, packageName
|
|
}
|
|
|
|
func appParseOneLinkTime(value string) *gtime.Time {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return nil
|
|
}
|
|
parsed := gtime.NewFromStr(value)
|
|
if parsed == nil || parsed.IsZero() {
|
|
return nil
|
|
}
|
|
return parsed
|
|
}
|