chore: 初始开源发布
iotlcm-openapi:对外 OpenAPI 网关(V3 接口 + 旧版兼容接口)。
- Go module:iotlcm-openapi → git.5iot.com/iotlcm-public/iotlcm-openapi(178 文件)
- 凭据与地址脱敏:JWT signingKey、MySQL DSN、Redis 口令、
RabbitMQ DSN 全部改为 ${*} 占位;私网 IP → 文档地址段
- 7 处真实手机号 → 合法 11 位编造号
- 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名
- 移除内部资产:CI 触发器
- Dockerfile 改用公共基础镜像,移除 GOPRIVATE 内网设置
- README 重写(原为 GoFrame 模板占位文案),补 Apache-2.0 LICENSE,
LF 行尾 + .gitattributes
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
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,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user