chore: 并入 iotlcm-upstream-chan 历史到 upstream-chan/(原提交 1e74497)
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
package upstreamapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/service"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultChannelTestTimeout = 2 * time.Minute
|
||||
defaultChannelTestItemTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
type channelTestOperation struct {
|
||||
key string
|
||||
name string
|
||||
execute func(context.Context, model.SimCardChannelI, *model.SimKey, time.Time) (any, int, string, bool, error)
|
||||
}
|
||||
|
||||
func (u *sUpstreamAPI) TestChannel(ctx context.Context, input *model.ChannelTestInput) (*model.ChannelTestOutput, error) {
|
||||
if input == nil {
|
||||
return nil, gerror.New("channel test input is required")
|
||||
}
|
||||
if input.ChannelID <= 0 || strings.TrimSpace(input.TenantID) == "" {
|
||||
return nil, gerror.New("channel is not accessible")
|
||||
}
|
||||
|
||||
operations, err := selectChannelTestOperations(input.Items)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
totalTimeout := g.Cfg().MustGet(ctx, "upstreamapi.channel_test.timeout", defaultChannelTestTimeout).Duration()
|
||||
if totalTimeout <= 0 {
|
||||
totalTimeout = defaultChannelTestTimeout
|
||||
}
|
||||
itemTimeout := g.Cfg().MustGet(ctx, "upstreamapi.channel_test.item_timeout", defaultChannelTestItemTimeout).Duration()
|
||||
if itemTimeout <= 0 {
|
||||
itemTimeout = defaultChannelTestItemTimeout
|
||||
}
|
||||
testCtx, cancel := context.WithTimeout(ctx, totalTimeout)
|
||||
defer cancel()
|
||||
|
||||
config, err := service.ChannelConfig().LoadChannelForTest(testCtx, input.ChannelID, input.TenantID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrChannelNotAccessible) {
|
||||
return nil, model.ErrChannelNotAccessible
|
||||
}
|
||||
return nil, gerror.Wrap(err, "load channel test configuration")
|
||||
}
|
||||
instance, err := u.newTestChannelInstance(testCtx, config)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "channel test configuration is invalid")
|
||||
}
|
||||
completionCtx, completionCancel := context.WithTimeout(testCtx, itemTimeout)
|
||||
simKey, err := resolveChannelTestSimKey(completionCtx, instance, config.Channel.ChanKey, input)
|
||||
completionCancel()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results := make([]*model.ChannelTestItem, 0, len(operations))
|
||||
now := time.Now()
|
||||
for _, operation := range operations {
|
||||
if testCtx.Err() != nil {
|
||||
results = append(results, channelTestTimeoutResult(operation))
|
||||
continue
|
||||
}
|
||||
results = append(results, executeChannelTestOperation(testCtx, itemTimeout, operation, instance, simKey, now))
|
||||
}
|
||||
return &model.ChannelTestOutput{Items: results}, nil
|
||||
}
|
||||
|
||||
func resolveChannelTestSimKey(ctx context.Context, instance model.SimCardChannelI, chanKey string, input *model.ChannelTestInput) (*model.SimKey, error) {
|
||||
mode := strings.ToLower(strings.TrimSpace(input.InputMode))
|
||||
if mode == "" {
|
||||
mode = model.ChannelTestInputModeComplete
|
||||
}
|
||||
cardType := strings.ToLower(strings.TrimSpace(input.CardValueType))
|
||||
cardValue := strings.TrimSpace(input.CardValue)
|
||||
if mode == model.ChannelTestInputModeComplete {
|
||||
if strings.TrimSpace(input.ICCID) == "" || strings.TrimSpace(input.IMSI) == "" || strings.TrimSpace(input.MSISDN) == "" {
|
||||
return nil, gerror.New("complete mode requires iccid, imsi and msisdn")
|
||||
}
|
||||
return &model.SimKey{ChanKey: chanKey, ICCID: input.ICCID, IMSI: input.IMSI, MSISDN: input.MSISDN}, nil
|
||||
}
|
||||
if mode != model.ChannelTestInputModeSingle && mode != model.ChannelTestInputModeDirectSingle {
|
||||
return nil, gerror.New("unsupported channel test input mode")
|
||||
}
|
||||
if cardValue == "" || !validChannelTestCardType(cardType) {
|
||||
return nil, gerror.New("card value type and value are required")
|
||||
}
|
||||
key := &model.SimKey{ChanKey: chanKey}
|
||||
setChannelTestCardValue(key, cardType, cardValue)
|
||||
if mode == model.ChannelTestInputModeDirectSingle {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Card completion uses the same request-scoped instance as the selected
|
||||
// checks; it never falls back to chanMap or creates a second instance.
|
||||
basic, err := instance.QuerySimBasicInfo(ctx, &model.SimBasicInfoInput{SimKeys: []*model.SimKey{key}})
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, "complete card identifiers")
|
||||
}
|
||||
if len(basic) == 0 || basic[0] == nil || basic[0].Code != consts.ApiStatusSuccess {
|
||||
if len(basic) > 0 && basic[0] != nil && basic[0].Message != "" {
|
||||
return nil, gerror.New(basic[0].Message)
|
||||
}
|
||||
return nil, gerror.New("carrier did not return card identifiers")
|
||||
}
|
||||
completed := basic[0].ResData
|
||||
if strings.TrimSpace(completed.ICCID) == "" || strings.TrimSpace(completed.IMSI) == "" || strings.TrimSpace(completed.MSISDN) == "" {
|
||||
return nil, gerror.New("carrier did not return complete card identifiers")
|
||||
}
|
||||
return &model.SimKey{ChanKey: chanKey, ICCID: completed.ICCID, IMSI: completed.IMSI, MSISDN: completed.MSISDN}, nil
|
||||
}
|
||||
|
||||
func validChannelTestCardType(valueType string) bool {
|
||||
return valueType == "iccid" || valueType == "imsi" || valueType == "msisdn"
|
||||
}
|
||||
|
||||
func setChannelTestCardValue(key *model.SimKey, valueType, value string) {
|
||||
switch valueType {
|
||||
case "iccid":
|
||||
key.ICCID = value
|
||||
case "imsi":
|
||||
key.IMSI = value
|
||||
case "msisdn":
|
||||
key.MSISDN = value
|
||||
}
|
||||
}
|
||||
|
||||
func selectChannelTestOperations(requested []string) ([]channelTestOperation, error) {
|
||||
all := newChannelTestOperations()
|
||||
if len(requested) == 0 {
|
||||
return all, nil
|
||||
}
|
||||
wanted := make(map[string]struct{}, len(requested))
|
||||
for _, key := range requested {
|
||||
key = strings.TrimSpace(key)
|
||||
if _, ok := model.ChannelTestOperationNames[key]; !ok {
|
||||
return nil, gerror.Newf("unsupported channel test item: %s", key)
|
||||
}
|
||||
wanted[key] = struct{}{}
|
||||
}
|
||||
selected := make([]channelTestOperation, 0, len(wanted))
|
||||
for _, operation := range all {
|
||||
if _, ok := wanted[operation.key]; ok {
|
||||
selected = append(selected, operation)
|
||||
}
|
||||
}
|
||||
return selected, nil
|
||||
}
|
||||
|
||||
func executeChannelTestOperation(parent context.Context, itemTimeout time.Duration, operation channelTestOperation, instance model.SimCardChannelI, simKey *model.SimKey, now time.Time) *model.ChannelTestItem {
|
||||
started := time.Now()
|
||||
callCtx, cancel := context.WithTimeout(parent, itemTimeout)
|
||||
defer cancel()
|
||||
data, errorCode, message, success, err := operation.execute(callCtx, instance, simKey, now)
|
||||
result := &model.ChannelTestItem{Key: operation.key, Name: operation.name, Status: "failed", Data: data, ErrorCode: errorCode, DurationMs: time.Since(started).Milliseconds()}
|
||||
if callCtx.Err() != nil {
|
||||
result.Status = "timeout"
|
||||
result.Message = "channel test item timed out"
|
||||
return result
|
||||
}
|
||||
if err != nil {
|
||||
result.Message = err.Error()
|
||||
return result
|
||||
}
|
||||
if !success {
|
||||
result.Message = message
|
||||
return result
|
||||
}
|
||||
result.Status = "success"
|
||||
result.Message = message
|
||||
return result
|
||||
}
|
||||
|
||||
func channelTestTimeoutResult(operation channelTestOperation) *model.ChannelTestItem {
|
||||
return &model.ChannelTestItem{Key: operation.key, Name: operation.name, Status: "timeout", Message: "channel test request timed out"}
|
||||
}
|
||||
|
||||
func newChannelTestOperations() []channelTestOperation {
|
||||
testType := 0
|
||||
return []channelTestOperation{
|
||||
newChannelTestOperation("simBasicInfo", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimBasicInfoItem], error) {
|
||||
return instance.QuerySimBasicInfo(ctx, &model.SimBasicInfoInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
newChannelTestOperation("simSession", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimSessionItem], error) {
|
||||
return instance.QuerySimSession(ctx, &model.SimSessionInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
newChannelTestOperation("simStatus", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimCardStatusItem], error) {
|
||||
return instance.QuerySimCardStatus(ctx, &model.SimCardStatusInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
newChannelTestOperation("simStopReason", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimStopReasonItem], error) {
|
||||
return instance.QuerySimStopReason(ctx, &model.SimStopReasonInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
newChannelTestOperation("simFrozenState", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimFrozenStateItem], error) {
|
||||
return instance.QuerySimFrozenState(ctx, &model.SimFrozenStateInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
newChannelTestOperation("simCardBindStatus", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimCardBindStatusItem], error) {
|
||||
return instance.QuerySimCardBindStatus(ctx, &model.SimCardBindStatusInput{SimKeys: []*model.SimKey{key}, ReqData: model.SimCardBindStatusInputItem{TestType: &testType}})
|
||||
}),
|
||||
newChannelTestOperation("simDeviceStatus", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimDeviceStatusItem], error) {
|
||||
return instance.QuerySimDeviceStatus(ctx, &model.SimDeviceStatusInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
newChannelTestOperation("simRegionLimitStatus", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimRegionLimitStatusItem], error) {
|
||||
return instance.QuerySimRegionLimitStatus(ctx, &model.SimRegionLimitStatusInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
newChannelTestOperation("simImei", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimImeiItem], error) {
|
||||
return instance.QuerySimImei(ctx, &model.SimImeiInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
newChannelTestOperation("simDataUsage", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimDataUsageItem], error) {
|
||||
return instance.QuerySimDataUsage(ctx, &model.SimDataUsageInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
newChannelTestOperation("simDataUsageMonthly", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, now time.Time) ([]*model.SimBaseRes[model.SimDataUsageMonthlyOutputItem], error) {
|
||||
last := now.AddDate(0, -1, 0)
|
||||
return instance.QuerySimDataUsageMonthly(ctx, &model.SimDataUsageMonthlyInput{SimKeys: []*model.SimKey{key}, ReqData: model.SimDataUsageMonthlyInputItem{QueryDate: last.Format("200601")}})
|
||||
}),
|
||||
newChannelTestOperation("simDataUsageDaily", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, now time.Time) ([]*model.SimBaseRes[model.SimDataUsageDailyOutputItem], error) {
|
||||
yesterday := now.AddDate(0, 0, -1)
|
||||
return instance.QuerySimDataUsageDaily(ctx, &model.SimDataUsageDailyInput{SimKeys: []*model.SimKey{key}, ReqData: model.SimDataUsageDailyInputItem{QueryDate: yesterday.Format("20060102")}})
|
||||
}),
|
||||
newChannelTestOperation("simApnInfo", func(ctx context.Context, instance model.SimCardChannelI, key *model.SimKey, _ time.Time) ([]*model.SimBaseRes[model.SimApnInfoItemList], error) {
|
||||
return instance.QuerySimApnInfo(ctx, &model.SimApnInfoInput{SimKeys: []*model.SimKey{key}})
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func newChannelTestOperation[T any](key string, query func(context.Context, model.SimCardChannelI, *model.SimKey, time.Time) ([]*model.SimBaseRes[T], error)) channelTestOperation {
|
||||
return channelTestOperation{key: key, name: model.ChannelTestOperationNames[key], execute: func(ctx context.Context, instance model.SimCardChannelI, simKey *model.SimKey, now time.Time) (any, int, string, bool, error) {
|
||||
output, err := query(ctx, instance, simKey, now)
|
||||
if err != nil {
|
||||
return nil, 0, "", false, err
|
||||
}
|
||||
if len(output) == 0 || output[0] == nil {
|
||||
return nil, 0, "carrier returned no result", false, nil
|
||||
}
|
||||
first := output[0]
|
||||
if first.Code != consts.ApiStatusSuccess {
|
||||
return first.ResData, first.Code, first.Message, false, nil
|
||||
}
|
||||
return first.ResData, 0, first.Message, true, nil
|
||||
}}
|
||||
}
|
||||
Reference in New Issue
Block a user