Files
iotlcm/server/internal/logic/iot/t_add_package_async.go
T
MiFaZhan 59eb34976a chore: 初始开源发布
iotlcm-admin-svr:管理后台服务。

- Go module:iotlcm → git.5iot.com/iotlcm-public/iotlcm-admin-svr(729 文件)
- 凭据与地址脱敏:JWT signingKey(含代码里的 MustGet 默认值)、
  MySQL DSN、Redis 口令、告警邮件发件人邮箱全部改为占位;
  私网 IP → 文档地址段;库名 xiujie_admin → iotlcm
- 真实手机号/邮箱 → 合法 11 位编造号 / example.com
- 移除内部资产:CI 触发器、过时 DDL(sql/db_iotlcm.sql)、
  私有依赖 go get 行、明文 registry 登录口令
- 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名
- Dockerfile 改用公共基础镜像
- 拼错的 RADEME.md → README.md,README 重写
- 需要真实数据库的用例改为可用 IOTLCM_TEST_WITH_DB=1 显式启用
- 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes

数据库 schema 不随本仓发布(生产 DDL 已过时),README 已说明。
2026-10-23 14:48:30 +08:00

313 lines
12 KiB
Go

package iot
import (
"context"
"strings"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
"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/logic/iot/simcardbatch/addpackage"
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
"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/encoding/gjson"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
func (s *sIotTSimCardBindPkg) PrepareInput(ctx context.Context, in *model.TSimCardBindPkgAddPackageInputPrepareParam) (*model.TSimCardBindPkgAddPackageInputPrepareRes, error) {
if in == nil {
in = &model.TSimCardBindPkgAddPackageInputPrepareParam{}
}
result, err := addpackage.NewCardSubmitter().PrepareInput(ctx, in.Input, addpackage.SubmitOptions{
PackageCode: in.PackageCode,
Remark: in.Remark,
EffectiveMode: in.EffectiveMode,
})
if err != nil {
return nil, err
}
return (*model.TSimCardBindPkgAddPackageInputPrepareRes)(addPackagePrepareRes(result)), nil
}
func (s *sIotTSimCardBindPkg) PrepareResult(ctx context.Context, batchNo string) (*model.TSimCardBindPkgAddPackagePrepareResultRes, error) {
res, err := loadAddPackagePrepareResult(ctx, batchNo, consts.BatchTypeCardAddPackage)
if err != nil {
return nil, err
}
return (*model.TSimCardBindPkgAddPackagePrepareResultRes)(res), nil
}
func (s *sIotTSimCardBindPkg) ConfirmBatch(ctx context.Context, in *model.TSimCardBindPkgAddPackageBatchConfirmParam) (*model.TSimCardBindPkgAddPackageBatchConfirmRes, error) {
res, err := confirmAddPackageBatch(ctx, (*model.TSimAddPackageBatchConfirmParam)(in), consts.BatchTypeCardAddPackage)
if err != nil {
return nil, err
}
return (*model.TSimCardBindPkgAddPackageBatchConfirmRes)(res), nil
}
func (s *sIotTSimPoolBindPackage) PrepareInput(ctx context.Context, in *model.TSimPoolBindPackageAddPackageInputPrepareParam) (*model.TSimPoolBindPackageAddPackageInputPrepareRes, error) {
if in == nil {
in = &model.TSimPoolBindPackageAddPackageInputPrepareParam{}
}
result, err := addpackage.NewPoolSubmitter().PrepareInput(ctx, in.Input, addpackage.SubmitOptions{
PackageCode: in.PackageCode,
Remark: in.Remark,
EffectiveMode: in.EffectiveMode,
})
if err != nil {
return nil, err
}
return (*model.TSimPoolBindPackageAddPackageInputPrepareRes)(addPackagePrepareRes(result)), nil
}
func (s *sIotTSimPoolBindPackage) PrepareResult(ctx context.Context, batchNo string) (*model.TSimPoolBindPackageAddPackagePrepareResultRes, error) {
res, err := loadAddPackagePrepareResult(ctx, batchNo, consts.BatchTypePoolAddPackage)
if err != nil {
return nil, err
}
return (*model.TSimPoolBindPackageAddPackagePrepareResultRes)(res), nil
}
func (s *sIotTSimPoolBindPackage) ConfirmBatch(ctx context.Context, in *model.TSimPoolBindPackageAddPackageBatchConfirmParam) (*model.TSimPoolBindPackageAddPackageBatchConfirmRes, error) {
res, err := confirmAddPackageBatch(ctx, (*model.TSimAddPackageBatchConfirmParam)(in), consts.BatchTypePoolAddPackage)
if err != nil {
return nil, err
}
return (*model.TSimPoolBindPackageAddPackageBatchConfirmRes)(res), nil
}
func addPackagePrepareRes(result *addpackage.SubmitResult) *model.TSimAddPackagePrepareRes {
res := &model.TSimAddPackagePrepareRes{
BatchType: consts.BatchTypePrepare,
BatchAsyncStatus: consts.BatchAsyncStatusPending,
RunResult: consts.BatchRunResultPending,
Status: "pending",
}
if result == nil {
return res
}
res.BatchNo = result.BatchNo
res.BatchType = result.BatchType
res.TargetBatchType = result.TargetBatchType
res.Total = result.Total
res.ValidCount = result.ValidCount
res.FailCount = result.FailCount
return res
}
func confirmAddPackageBatch(ctx context.Context, in *model.TSimAddPackageBatchConfirmParam, targetBatchType int) (*model.TSimAddPackageBatchConfirmRes, error) {
if in == nil {
in = &model.TSimAddPackageBatchConfirmParam{}
}
in.BatchNo = strings.TrimSpace(in.BatchNo)
if in.BatchNo == "" {
return nil, gerror.New("批次号不能为空")
}
if targetBatchType != consts.BatchTypeCardAddPackage && targetBatchType != consts.BatchTypePoolAddPackage {
return nil, gerror.New("加油包目标批次类型不正确")
}
effectiveMode, err := addpackage.NormalizeEffectiveMode(in.EffectiveMode)
if err != nil {
return nil, err
}
paymentMode, offlineContractCompleted, err := normalizeAddPackagePaymentMode(ctx, in.PaymentMode, in.OfflineContractCompleted)
if err != nil {
return nil, err
}
var converted *addpackage.AsyncConfirmResult
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
var convertErr error
converted, convertErr = addpackage.ConvertPrepareToAsync(ctx, tx, &addpackage.AsyncConfirmInput{
BatchNo: in.BatchNo,
TargetBatchType: targetBatchType,
PackageCode: in.PackageCode,
Remark: in.Remark,
PaymentMode: paymentMode,
OfflineContractCompleted: offlineContractCompleted,
EffectiveMode: effectiveMode,
Operator: currentOperator(ctx),
})
return convertErr
})
if err != nil {
return nil, err
}
if converted == nil {
return nil, gerror.New("加油包确认结果为空")
}
publishPendingExecute(ctx, converted.BatchNo, converted.BatchType, contexts.GetTenantId(ctx))
return &model.TSimAddPackageBatchConfirmRes{
BatchNo: converted.BatchNo,
BatchType: converted.BatchType,
BatchAsyncStatus: converted.BatchAsyncStatus,
PackageCode: strings.TrimSpace(in.PackageCode),
EffectiveMode: effectiveMode,
Status: "pending",
}, nil
}
func loadAddPackagePrepareResult(ctx context.Context, batchNo string, targetBatchType int) (*model.TSimAddPackagePrepareResultRes, error) {
batchNo = strings.TrimSpace(batchNo)
if batchNo == "" {
return nil, gerror.New("批次号不能为空")
}
columns := dao.BatchMain.Columns()
batch := &entity.BatchMain{}
if err := dao.BatchMain.Ctx(ctx).
Where(columns.BatchNo, batchNo).
Where(columns.TenantId, contexts.GetTenantId(ctx)).
Where(columns.CreatedDept, contexts.GetDeptId(ctx)).
Scan(batch); err != nil {
return nil, gerror.Wrap(err, "查询加油包预处理批次失败")
}
if batch.Id == 0 {
return nil, gerror.New("加油包预处理批次不存在或不可见")
}
if !isAddPackagePrepareOrTarget(batch, targetBatchType) {
return nil, gerror.New("批次类型不匹配")
}
asyncStatus, runResult, status := prepareRuntimeStatus(batch.BatchAsyncStatus, batch.RunResult)
res := &model.TSimAddPackagePrepareResultRes{
BatchNo: batch.BatchNo,
BatchType: batch.BatchType,
TargetBatchType: targetBatchType,
Status: status,
BatchAsyncStatus: asyncStatus,
RunResult: runResult,
Total: batch.TotalCount,
ValidCount: batch.ValidCount,
SuccessCount: batch.SuccessCount,
FailCount: batch.FailCount,
FailReason: strings.TrimSpace(batch.FailReason),
StartTime: batch.StartTime,
EndTime: batch.EndTime,
}
fillAddPackagePrepareFields(res, batch.BatchInfo)
if res.Quantity <= 0 {
res.Quantity = firstPositive(batch.SuccessCount, batch.ValidCount, batch.TotalCount)
}
if res.TotalAmount <= 0 && res.UnitPrice > 0 && res.Quantity > 0 {
res.TotalAmount = roundAmount(res.UnitPrice * float64(res.Quantity))
}
fillAddPackagePaymentView(ctx, res)
return res, nil
}
func isAddPackagePrepareOrTarget(batch *entity.BatchMain, targetBatchType int) bool {
if batch == nil {
return false
}
if targetBatchType != consts.BatchTypeCardAddPackage && targetBatchType != consts.BatchTypePoolAddPackage {
return false
}
if batch.BatchInfo != nil && batch.BatchInfo.Get("targetBatchType").Int() == targetBatchType {
return true
}
return batch.BatchType == targetBatchType
}
func prepareRuntimeStatus(asyncStatusValue int, runResultValue int) (int, int, string) {
asyncStatus := consts.BatchAsyncStatusPending
runResult := consts.BatchRunResultPending
status := "pending"
switch {
case asyncStatusValue == consts.BatchAsyncStatusProcessing || runResultValue == consts.BatchRunResultRunning:
asyncStatus = consts.BatchAsyncStatusProcessing
runResult = consts.BatchRunResultRunning
status = "processing"
case asyncStatusValue == consts.BatchAsyncStatusSuccess && runResultValue == consts.BatchRunResultSuccess:
asyncStatus = consts.BatchAsyncStatusSuccess
runResult = consts.BatchRunResultSuccess
status = "success"
case asyncStatusValue == consts.BatchAsyncStatusFailed || runResultValue == consts.BatchRunResultFailed:
asyncStatus = consts.BatchAsyncStatusFailed
runResult = consts.BatchRunResultFailed
status = "failed"
case asyncStatusValue == consts.BatchAsyncStatusPartialSuccess || runResultValue == consts.BatchRunResultPartialSuccess:
asyncStatus = consts.BatchAsyncStatusFailed
runResult = consts.BatchRunResultFailed
status = "failed"
}
return asyncStatus, runResult, status
}
func fillAddPackagePrepareFields(res *model.TSimAddPackagePrepareResultRes, info *gjson.Json) {
if res == nil || info == nil {
return
}
res.PackageCode = jsonString(info, "packageCode", "priceSnapshot.packageCode")
res.PackageName = jsonString(info, "packageName", "priceSnapshot.packageName")
res.FlowMb = roundAmount(jsonFloat64(info, "flowMb", "priceSnapshot.flowMb"))
res.UnitPrice = roundAmount(jsonFloat64(info, "unitPrice", "priceSnapshot.unitPrice"))
res.TotalAmount = roundAmount(jsonFloat64(info, "totalAmount"))
res.Quantity = jsonInt(info, "quantity")
res.EffectiveMode = jsonString(info, "effectiveMode")
if res.EffectiveMode == "" {
res.EffectiveMode = addpackage.EffectiveModeCurrentMonth
}
res.EffectiveModeText = jsonString(info, "effectiveModeText")
if res.EffectiveModeText == "" {
res.EffectiveModeText = addpackage.EffectiveModeText(res.EffectiveMode)
}
res.EffectiveTime = jsonString(info, "effectiveTime")
res.ExpectedExpireTime = jsonString(info, "expectedExpireTime")
res.PaymentAccounts = addPackagePaymentAccountsFromInfo(info)
res.AvailablePackages = addPackagePackageOptionsFromInfo(info)
res.Snapshot = info
}
func addPackagePaymentAccountsFromInfo(info *gjson.Json) []*model.TSimAddPackagePaymentAccountModel {
value := jsonField(info, "paymentAccounts")
if value == nil || value.IsNil() {
return nil
}
var accounts []*model.TSimAddPackagePaymentAccountModel
if err := gconv.Structs(value.Interfaces(), &accounts); err != nil {
return nil
}
return accounts
}
func addPackagePackageOptionsFromInfo(info *gjson.Json) []*model.TSimAddPackageOptionModel {
value := jsonField(info, "availablePackages", "packageOptions")
if value == nil || value.IsNil() {
return nil
}
var options []*model.TSimAddPackageOptionModel
if err := gconv.Structs(value.Interfaces(), &options); err != nil {
return nil
}
return options
}
func fillAddPackagePaymentView(ctx context.Context, res *model.TSimAddPackagePrepareResultRes) {
if res == nil {
return
}
isTop, _, err := service.AgentDept().IsTopDept(ctx)
if err == nil {
res.IsTopDept = isTop
res.DefaultPaymentMode = addPackageDefaultPaymentMode(isTop)
}
balance, err := queryAddPackageAvailableBalance(ctx, contexts.GetDeptId(ctx))
if err == nil {
res.AvailableBalance = balance
}
}
func firstPositive(values ...int) int {
for _, value := range values {
if value > 0 {
return value
}
}
return 0
}