chore: 初始开源发布
iotlcm-onelink-mock:OneLink 挡板服务(本地测试替身)。 - Go module 迁移至 git.5iot.com/iotlcm-public/iotlcm-onelink-mock - 修复同毫秒内多次生成卡导致 ICCID/IMSI 唯一约束冲突 - 补 Apache-2.0 LICENSE,README 重写并新增 docs/deploy.md - 移除内部部署资产(CI 触发器 / 镜像推送脚本 / 指定环境部署文档) - 测试凭据改为 demo_appid / demo_password,真实手机号与内网 IP 脱敏 - 统一 LF 行尾并加 .gitattributes
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
generatedChanKey = "channel_yd_mock"
|
||||
generatedCardType = "1"
|
||||
generatedPackageCode = "CMIOT-SD-500M-24M-D01"
|
||||
generatedSupplierCode = "SUP_CMCC"
|
||||
generatedMonthlyUsage = 18432.0
|
||||
generatedDailyUsage = 614.4
|
||||
generatedPoolUsageByte = 18874368.0
|
||||
)
|
||||
|
||||
type inboundExportColumn struct {
|
||||
Header string
|
||||
Key string
|
||||
}
|
||||
|
||||
var inboundExportColumns = []inboundExportColumn{
|
||||
{Header: "ICCID*", Key: "ICCID"},
|
||||
{Header: "MSISDN*", Key: "MSISDN"},
|
||||
{Header: "IMSI*", Key: "IMSI"},
|
||||
{Header: "IMEI", Key: "IMEI"},
|
||||
{Header: "通道编号*", Key: "chanKey"},
|
||||
{Header: "卡类型", Key: "cardType"},
|
||||
{Header: "上游套餐编码", Key: "upPkgCode"},
|
||||
{Header: "供应商编码", Key: "suppliersCode"},
|
||||
{Header: "上游激活时间", Key: "upstreamActivationTime"},
|
||||
{Header: "是否贴片转插拔", Key: "insertToPlugEnabled"},
|
||||
{Header: "是否运营商达量断网", Key: "carrierTrafficCutoffEnabled"},
|
||||
{Header: "协议到期时间", Key: "agreementExpiryDate"},
|
||||
{Header: "是否有测试期", Key: "hasTestPeriod"},
|
||||
{Header: "测试期天数", Key: "testPeriodDays"},
|
||||
{Header: "测试期最晚过期日期", Key: "testPeriodLatestExpiryDate"},
|
||||
{Header: "是否有沉默期", Key: "hasSilentPeriod"},
|
||||
{Header: "沉默期天数", Key: "silentPeriodDays"},
|
||||
{Header: "沉默期最晚过期日期", Key: "silentPeriodLatestExpiryDate"},
|
||||
}
|
||||
|
||||
var inboundExportHeaders = func() []string {
|
||||
headers := make([]string, 0, len(inboundExportColumns))
|
||||
for _, column := range inboundExportColumns {
|
||||
headers = append(headers, column.Header)
|
||||
}
|
||||
return headers
|
||||
}()
|
||||
|
||||
// 生成序号分配器。
|
||||
//
|
||||
// 序号直接来自毫秒时间戳时,同一毫秒内的两次生成会得到完全相同的 seq,
|
||||
// 进而生成重复的 ICCID/IMSI,触发 mock_t_sim_card 的唯一约束冲突
|
||||
// (表现为「一键生成」连点两次或两个分组连续生成时报
|
||||
// UNIQUE constraint failed: mock_t_sim_card.imsi)。
|
||||
//
|
||||
// 这里用进程内单调递增的上界保证 seq 严格递增:时间戳只决定起点,
|
||||
// 真正的唯一性由 nextGeneratedSeq 保证。
|
||||
var (
|
||||
generatedSeqMu sync.Mutex
|
||||
generatedSeqLast int64
|
||||
)
|
||||
|
||||
// nextGeneratedSeq 返回严格大于上一次分配值的序号。
|
||||
func nextGeneratedSeq(candidate int64) int64 {
|
||||
generatedSeqMu.Lock()
|
||||
defer generatedSeqMu.Unlock()
|
||||
if candidate <= generatedSeqLast {
|
||||
candidate = generatedSeqLast + 1
|
||||
}
|
||||
generatedSeqLast = candidate
|
||||
return candidate
|
||||
}
|
||||
|
||||
func GenerateSIMCards(batchTime time.Time, count int) []SIMCard {
|
||||
if count <= 0 {
|
||||
return []SIMCard{}
|
||||
}
|
||||
cards := make([]SIMCard, 0, count)
|
||||
base := batchTime.UnixMilli()
|
||||
dbTime := batchTime.Format("2006-01-02 15:04:05")
|
||||
for i := 0; i < count; i++ {
|
||||
seq := nextGeneratedSeq(base + int64(i+1))
|
||||
card := SIMCard{
|
||||
MSISDN: fmt.Sprintf("141%08d", seq%100000000),
|
||||
ICCID: fmt.Sprintf("898604%014d", seq%100000000000000),
|
||||
IMSI: fmt.Sprintf("46004%010d", seq%10000000000),
|
||||
IMEI: fmt.Sprintf("86%013d", seq%10000000000000),
|
||||
Remark: sourceRemark("CMCC", generatedPackageCode, generatedPackageCode),
|
||||
CardStatus: "2",
|
||||
LastChangeDate: dbTime,
|
||||
StopReason: "000000000000",
|
||||
FrozenStatus: "0",
|
||||
FrozenOperTime: batchTime.Format("20060102150405"),
|
||||
OpenDate: dbTime,
|
||||
ActiveDate: dbTime,
|
||||
OnOffStatus: "1",
|
||||
SessionStatus: "01",
|
||||
IP: fmt.Sprintf("10.%d.%d.%d", (seq/10000)%255, (seq/100)%255, seq%255),
|
||||
IPv6Prefix: "",
|
||||
IPv6: "",
|
||||
RAT: "6",
|
||||
CardBindStatus: "1",
|
||||
RegionLimitStatus: "0",
|
||||
AreaRestrictType: "1",
|
||||
RestrictedArea: "110000,310000,440000",
|
||||
MonthUsageKB: generatedMonthlyUsage,
|
||||
PoolUsageBytes: generatedPoolUsageByte,
|
||||
DailyBaseKB: generatedDailyUsage,
|
||||
MonthlyBaseKB: generatedMonthlyUsage,
|
||||
GrowthPerMinuteKB: 0,
|
||||
LastUsageUpdateTime: batchTime.Format(time.RFC3339),
|
||||
APNs: []APN{
|
||||
{Name: "CMIOT", Status: "1", UsageBytes: generatedPoolUsageByte, ModifyTime: batchTime.Format("20060102150405"), ServiceUsageState: "1", PCCs: []PCC{{ServiceCode: "default", Name: "APN 缺省流量", PolicyStatus: "2", ServiceUsageState: "1", ModifyTime: batchTime.Format("20060102150405")}}},
|
||||
},
|
||||
}
|
||||
cards = append(cards, card)
|
||||
}
|
||||
return cards
|
||||
}
|
||||
|
||||
func inboundExportRow(card SIMCard) map[string]string {
|
||||
activation := parseCardTime(card.ActiveDate)
|
||||
return map[string]string{
|
||||
"ICCID": card.ICCID,
|
||||
"MSISDN": card.MSISDN,
|
||||
"IMSI": card.IMSI,
|
||||
"IMEI": card.IMEI,
|
||||
"chanKey": generatedChanKey,
|
||||
"cardType": generatedCardType,
|
||||
"upPkgCode": generatedPackageCode,
|
||||
"suppliersCode": generatedSupplierCode,
|
||||
"upstreamActivationTime": activation.Format("2006-01-02 15:04:05"),
|
||||
"insertToPlugEnabled": "true",
|
||||
"carrierTrafficCutoffEnabled": "false",
|
||||
"agreementExpiryDate": activation.AddDate(1, 0, 0).Format("2006-01-02"),
|
||||
"hasTestPeriod": "true",
|
||||
"testPeriodDays": "30",
|
||||
"testPeriodLatestExpiryDate": activation.AddDate(0, 0, 30).Format("2006-01-02"),
|
||||
"hasSilentPeriod": "false",
|
||||
"silentPeriodDays": "",
|
||||
"silentPeriodLatestExpiryDate": "",
|
||||
}
|
||||
}
|
||||
|
||||
func parseCardTime(value string) time.Time {
|
||||
for _, layout := range []string{"2006-01-02 15:04:05", time.RFC3339, time.RFC3339Nano} {
|
||||
if t, err := time.ParseInLocation(layout, value, time.Local); err == nil {
|
||||
return t
|
||||
}
|
||||
}
|
||||
if unixMilli, err := strconv.ParseInt(value, 10, 64); err == nil {
|
||||
return time.UnixMilli(unixMilli)
|
||||
}
|
||||
return time.Now()
|
||||
}
|
||||
Reference in New Issue
Block a user