Files
iotlcm/openapi/internal/logic/common/renew.go

334 lines
10 KiB
Go

package common
import (
"context"
"strconv"
"strings"
"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/os/gtime"
"git.5iot.com/iotlcm-public/iotlcm-openapi/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-openapi/internal/dao"
"git.5iot.com/iotlcm-public/iotlcm-openapi/internal/library/batchmetadata"
"git.5iot.com/iotlcm-public/iotlcm-openapi/internal/library/contexts"
"git.5iot.com/iotlcm-public/iotlcm-openapi/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-openapi/internal/model/entity"
)
// BuildRenewBatchNo 生成续费类批次号。
func (s *sCommon) BuildRenewBatchNo(ctx context.Context) (string, error) {
return s.BuildBatchNo(ctx, consts.BatchNoPrefixRenew)
}
// SubmitRenew 创建卡续费批次并投递到 worker 异步执行。
func (s *sCommon) SubmitRenew(ctx context.Context, in *model.CommonRenewParam) (*model.CommonRenewRes, error) {
return s.submitRenew(ctx, in, false, batchmetadata.Bulk)
}
func (s *sCommon) submitRenew(ctx context.Context, in *model.CommonRenewParam, waitForCompletion bool, executionMode int) (*model.CommonRenewRes, error) {
if in == nil {
return nil, gerror.New("续费参数不能为空")
}
duration := in.DurationYears
if duration <= 0 {
duration = in.Validity
}
if duration <= 0 {
return nil, gerror.New("续费有效期必须大于0")
}
packageID := strings.TrimSpace(in.PackageId)
cards, err := normalizeRenewCards(in.Cards)
if err != nil {
return nil, err
}
tenantID := strings.TrimSpace(contexts.GetTenantId(ctx))
deptID := contexts.GetAgentDeptId(ctx)
userID := contexts.GetUserId(ctx)
if tenantID == "" || deptID <= 0 {
return nil, gerror.New("当前接口调用方信息不完整")
}
if userID <= 0 {
userID = deptID
}
deptIDs, err := s.loadRenewAccessibleDeptIds(ctx, tenantID, deptID)
if err != nil {
return nil, err
}
identifiers := make([]cardIdentifier, 0, len(cards))
for _, card := range cards {
identifiers = append(identifiers, cardIdentifier{Msisdn: card.Msisdn, Iccid: card.Iccid, Imsi: card.Imsi})
}
cardMap, err := s.loadCardsByIdentifiers(ctx, tenantID, deptIDs, identifiers)
if err != nil {
return nil, err
}
for index := range cards {
dbCard := cardMap[cardIdentifierKey(identifiers[index])]
if !cardMatchesIdentifier(dbCard, identifiers[index]) {
return nil, gerror.New("卡片不存在或无权限")
}
cards[index].Iccid = strings.TrimSpace(dbCard.Iccid)
cards[index].Msisdn = strings.TrimSpace(dbCard.Msisdn)
cards[index].Imsi = strings.TrimSpace(dbCard.Imsi)
}
iccids := make([]string, 0, len(cards))
for _, card := range cards {
iccids = append(iccids, card.Iccid)
}
batchNo, err := s.BuildRenewBatchNo(ctx)
if err != nil {
return nil, err
}
now := gtime.Now()
remark := strings.TrimSpace(in.Remark)
batchInfo := gjson.New(map[string]any{
"durationValue": duration,
"durationYears": duration,
"durationMonths": duration,
"packageId": packageID,
"paymentMode": "balance",
"remark": remark,
"quantity": len(cards),
"submittedFrom": "openapi",
"submittedAt": now.Format("Y-m-d H:i:s"),
"runtimeResolveRenewDetail": true,
})
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
main := &entity.BatchMain{
BatchNo: batchNo,
BatchType: consts.BatchTypeRenew,
BatchStatus: consts.BatchStatusNormal,
BatchAsyncStatus: consts.BatchAsyncStatusPending,
SourceType: consts.BatchSourceTypeBatchInput,
DeptId: deptID,
TotalCount: len(cards),
ValidCount: len(cards),
RunResult: consts.BatchRunResultPending,
BatchInfo: batchInfo,
Remark: gjson.New(map[string]any{"remark": remark}),
TenantId: tenantID,
CreatedDept: deptID,
CreatedBy: userID,
CreatedAt: now,
UpdatedBy: userID,
UpdatedAt: now,
}
if err := s.SaveBatchMain(ctx, tx, main, executionMode); err != nil {
return err
}
details := make([]*entity.BatchDetail, 0, len(cards))
for i, inputCard := range cards {
dbCard := cardMap[cardIdentifierKey(cardIdentifier{Msisdn: inputCard.Msisdn, Iccid: inputCard.Iccid, Imsi: inputCard.Imsi})]
snapshot := renewDetailSnapshot(inputCard, dbCard)
details = append(details, &entity.BatchDetail{
BatchNo: batchNo,
BatchType: consts.BatchTypeRenew,
DetailStatus: consts.BatchDetailStatusPending,
BlankOutStatus: consts.BlankOutStatusNo,
RowNo: i + 1,
ItemId: inputCard.Iccid,
Snapshot: snapshot,
TenantId: tenantID,
CreatedDept: deptID,
CreatedBy: userID,
CreatedAt: now,
UpdatedBy: userID,
UpdatedAt: now,
})
}
return s.SaveBatchDetails(ctx, tx, details)
})
if err != nil {
return nil, err
}
message := model.CommonBatchExecuteMessage{BatchNo: batchNo, BatchType: consts.BatchTypeRenew}
if err := s.Publish(ctx, consts.QueueBatchExecute, message); err != nil {
return nil, gerror.Wrap(err, "投递续费异步消息失败")
}
if waitForCompletion {
if _, err := s.waitBatchCompletion(ctx, tenantID, deptID, batchNo); err != nil {
return nil, err
}
}
return &model.CommonRenewRes{
BatchNo: batchNo,
BatchType: consts.BatchTypeRenew,
Total: len(cards),
}, nil
}
func buildRenewSubmitRemark(ctx context.Context) string {
agent := contexts.GetAgentUser(ctx)
if agent == nil {
return "OpenAPI续费调用"
}
deptName := strings.TrimSpace(agent.DeptName)
deptKey := strings.TrimSpace(agent.DeptKey)
switch {
case deptName != "" && deptKey != "":
return "OpenAPI续费调用方:" + deptName + "(" + deptKey + ")"
case deptName != "":
return "OpenAPI续费调用方:" + deptName
case deptKey != "":
return "OpenAPI续费调用方:" + deptKey
default:
return "OpenAPI续费调用"
}
}
func normalizeRenewCards(input []model.CommonRenewCard) ([]model.CommonRenewCard, error) {
if len(input) == 0 {
return nil, gerror.New("续费卡不能为空")
}
seen := make(map[string]struct{}, len(input))
cards := make([]model.CommonRenewCard, 0, len(input))
for _, item := range input {
item.Iccid = strings.TrimSpace(item.Iccid)
item.Msisdn = strings.TrimSpace(item.Msisdn)
item.Imsi = strings.TrimSpace(item.Imsi)
item.PackageId = strings.TrimSpace(item.PackageId)
identifier := cardIdentifier{Msisdn: item.Msisdn, Iccid: item.Iccid, Imsi: item.Imsi}
if err := validateCardIdentifier(identifier); err != nil {
return nil, err
}
key := cardIdentifierKey(identifier)
if _, ok := seen[key]; ok {
return nil, gerror.Newf("卡片标识重复:%s", key)
}
seen[key] = struct{}{}
cards = append(cards, item)
}
return cards, nil
}
func (s *sCommon) loadRenewAccessibleDeptIds(ctx context.Context, tenantID string, deptID int64) ([]int64, error) {
deptIDs := []int64{deptID}
columns := dao.SysDept.Columns()
array, err := dao.SysDept.Ctx(ctx).
Fields(columns.DeptId).
Where(columns.TenantId, tenantID).
WhereNull(columns.DeletedAt).
Where("dept_id = ? OR ancestors LIKE ?", deptID, "%,"+strconv.FormatInt(deptID, 10)+",%").
Array()
if err != nil {
return nil, gerror.Wrap(err, "查询续费数据权限失败")
}
if len(array) == 0 {
return deptIDs, nil
}
seen := map[int64]struct{}{}
deptIDs = deptIDs[:0]
for _, item := range array {
id := item.Int64()
if id <= 0 {
continue
}
if _, ok := seen[id]; ok {
continue
}
seen[id] = struct{}{}
deptIDs = append(deptIDs, id)
}
if len(deptIDs) == 0 {
deptIDs = append(deptIDs, deptID)
}
return deptIDs, nil
}
func (s *sCommon) loadRenewCards(ctx context.Context, tenantID string, deptIDs []int64, iccids []string) (map[string]*entity.TSimCardInfo, error) {
cardMap := make(map[string]*entity.TSimCardInfo, len(iccids))
if len(iccids) == 0 {
return cardMap, nil
}
columns := dao.TSimCardInfo.Columns()
var rows []*entity.TSimCardInfo
err := dao.TSimCardInfo.Ctx(ctx).
Where(columns.TenantId, tenantID).
WhereIn(columns.DeptId, deptIDs).
WhereIn(columns.Iccid, iccids).
Scan(&rows)
if err != nil {
return nil, gerror.Wrap(err, "查询续费卡失败")
}
for _, row := range rows {
if row == nil {
continue
}
row.Iccid = strings.TrimSpace(row.Iccid)
if row.Iccid != "" {
cardMap[row.Iccid] = row
}
}
return cardMap, nil
}
func renewDetailSnapshot(input model.CommonRenewCard, card *entity.TSimCardInfo) *gjson.Json {
data := map[string]any{
"iccid": input.Iccid,
"targetCode": input.Iccid,
}
if input.Msisdn != "" {
data["msisdn"] = input.Msisdn
}
if input.Imsi != "" {
data["imsi"] = input.Imsi
}
if input.PackageId != "" {
data["packageId"] = input.PackageId
data["packageCode"] = input.PackageId
}
if card != nil {
if input.Msisdn == "" {
data["msisdn"] = strings.TrimSpace(card.Msisdn)
}
if input.Imsi == "" {
data["imsi"] = strings.TrimSpace(card.Imsi)
}
if input.PackageId == "" {
data["packageCode"] = strings.TrimSpace(card.CurrentPackageCode)
}
data["currentPackageCode"] = strings.TrimSpace(card.CurrentPackageCode)
data["outboundBatchNo"] = strings.TrimSpace(card.OutboundBatchNo)
}
return gjson.New(data)
}
func (s *sCommon) SubmitSingleRenew(ctx context.Context, card model.CommonRenewCard, appID, packageID, remark string, validity int) (*model.CommonSingleRenewRes, error) {
card.PackageId = packageID
batch, err := s.submitRenew(ctx, &model.CommonRenewParam{
AppId: appID, PackageId: packageID, Validity: validity, DurationYears: validity, Remark: remark,
Cards: []model.CommonRenewCard{card},
}, true, batchmetadata.Single)
if err != nil {
return nil, err
}
return s.loadSingleRenewResult(ctx, batch.BatchNo, packageID, validity)
}
func (s *sCommon) SubmitBatchRenew(ctx context.Context, appID string, cards []model.CommonRenewCard, iccids []string, packageID, remark string, validity int) (*model.CommonRenewRes, error) {
if len(cards) == 0 {
cards = make([]model.CommonRenewCard, 0, len(iccids))
for _, iccid := range iccids {
cards = append(cards, model.CommonRenewCard{Iccid: iccid, PackageId: packageID})
}
}
for index := range cards {
if cards[index].PackageId == "" {
cards[index].PackageId = packageID
}
}
return s.SubmitRenew(ctx, &model.CommonRenewParam{
AppId: appID, PackageId: packageID, Validity: validity, DurationYears: validity, Remark: remark, Cards: cards,
})
}