chore: 初始开源发布
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 在源仓同一提交上同样失败,属既有缺陷。
This commit is contained in:
@@ -0,0 +1,538 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user