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 已说明。
This commit is contained in:
MiFaZhan
2026-10-23 14:46:05 +08:00
commit 59eb34976a
1132 changed files with 138197 additions and 0 deletions
@@ -0,0 +1,178 @@
package outbound
import (
"context"
"fmt"
"strings"
)
func validateRows(ctx context.Context, rows []*Row, opts PreviewOptions, repo *Repository) error {
identityFirstRow := make(map[string]*Row, len(rows))
iccids := make([]string, 0, len(rows))
msisdns := make([]string, 0, len(rows))
imsis := make([]string, 0, len(rows))
for _, row := range rows {
row.Iccid = strings.TrimSpace(row.Iccid)
row.Msisdn = strings.TrimSpace(row.Msisdn)
row.Imsi = strings.TrimSpace(row.Imsi)
row.RangeCodeType = rowIdentityCodeType(row)
key := rowIdentityKey(row)
if key == "" {
row.AddReason(rowIdentityLabel(row.RangeCodeType) + "不能为空")
continue
}
identityKey := row.RangeCodeType + ":" + key
if firstRow, ok := identityFirstRow[identityKey]; ok {
label := rowIdentityLabel(row.RangeCodeType)
row.AddReason(fmt.Sprintf("%s与第%d行重复", label, firstRow.RowNo))
firstRow.AddReason(fmt.Sprintf("%s与第%d行重复", label, row.RowNo))
continue
}
identityFirstRow[identityKey] = row
switch row.RangeCodeType {
case "msisdn":
msisdns = append(msisdns, key)
case "imsi":
imsis = append(imsis, key)
default:
iccids = append(iccids, key)
}
}
stockByIccid, err := repo.GetStockCardsByIccids(ctx, iccids)
if err != nil {
return err
}
stockByMsisdn, err := repo.GetStockCardsByMsisdns(ctx, msisdns)
if err != nil {
return err
}
stockByImsi, err := repo.GetStockCardsByImsis(ctx, imsis)
if err != nil {
return err
}
rowStock := make(map[*Row]*StockCard, len(rows))
resolvedIccidFirstRow := make(map[string]*Row, len(rows))
resolvedIccids := make([]string, 0, len(rows))
for _, row := range rows {
if !row.Valid() {
continue
}
stock := stockForRow(row, stockByIccid, stockByMsisdn, stockByImsi)
if stock == nil {
row.AddReason(rowIdentityLabel(row.RangeCodeType) + "不存在库存记录")
continue
}
providedIccid := row.Iccid
providedMsisdn := row.Msisdn
providedImsi := row.Imsi
row.Iccid = strings.TrimSpace(stock.Iccid)
row.Msisdn = chooseString(row.Msisdn, stock.Msisdn)
row.Imsi = chooseString(row.Imsi, stock.Imsi)
if row.Iccid == "" {
row.AddReason("库存卡ICCID为空")
continue
}
if firstRow, ok := resolvedIccidFirstRow[row.Iccid]; ok {
row.AddReason(fmt.Sprintf("ICCID与第%d行指向同一库存卡", firstRow.RowNo))
firstRow.AddReason(fmt.Sprintf("ICCID与第%d行指向同一库存卡", row.RowNo))
continue
}
resolvedIccidFirstRow[row.Iccid] = row
if stock.CreatedDept != opts.DeptId {
row.AddReason("ICCID不属于当前机构库存")
continue
}
if opts.StrictStockIdentity {
if providedIccid != "" && providedIccid != strings.TrimSpace(stock.Iccid) {
row.AddReason("ICCID与库存记录不一致")
continue
}
if providedMsisdn != "" && providedMsisdn != strings.TrimSpace(stock.Msisdn) {
row.AddReason("MSISDN与库存记录不一致")
continue
}
if providedImsi != "" && providedImsi != strings.TrimSpace(stock.Imsi) {
row.AddReason("IMSI与库存记录不一致")
continue
}
}
row.SourceBatchNo = strings.TrimSpace(stock.BatchNo)
rowStock[row] = stock
resolvedIccids = append(resolvedIccids, row.Iccid)
}
formalByIccid, err := repo.GetExistingFormalIccids(ctx, resolvedIccids)
if err != nil {
return err
}
for _, row := range rows {
if !row.Valid() || rowStock[row] == nil {
continue
}
if formalByIccid[row.Iccid] {
row.AddReason("ICCID已存在正式卡记录")
}
}
return nil
}
func rowIdentityCodeType(row *Row) string {
if row == nil {
return "iccid"
}
if codeType := normalizeRangeCodeType(row.RangeCodeType); codeType != "" {
return codeType
}
switch {
case strings.TrimSpace(row.Iccid) != "":
return "iccid"
case strings.TrimSpace(row.Msisdn) != "":
return "msisdn"
case strings.TrimSpace(row.Imsi) != "":
return "imsi"
default:
return "iccid"
}
}
func rowIdentityKey(row *Row) string {
if row == nil {
return ""
}
switch row.RangeCodeType {
case "msisdn":
return strings.TrimSpace(row.Msisdn)
case "imsi":
return strings.TrimSpace(row.Imsi)
default:
return strings.TrimSpace(row.Iccid)
}
}
func rowIdentityLabel(codeType string) string {
switch codeType {
case "msisdn":
return "MSISDN"
case "imsi":
return "IMSI"
default:
return "ICCID"
}
}
func stockForRow(row *Row, stockByIccid, stockByMsisdn, stockByImsi map[string]*StockCard) *StockCard {
if row == nil {
return nil
}
switch row.RangeCodeType {
case "msisdn":
return stockByMsisdn[strings.TrimSpace(row.Msisdn)]
case "imsi":
return stockByImsi[strings.TrimSpace(row.Imsi)]
default:
return stockByIccid[strings.TrimSpace(row.Iccid)]
}
}