2bec257289
iotlcm-worker-svr:异步执行中枢(RabbitMQ 消费者 + 全量同步编排)。 - Go module:iotlcm-worker-svr → git.5iot.com/iotlcm-public/iotlcm-worker-svr(214 文件) - 凭据与内部标识脱敏:MySQL/Redis 凭据、私网与生产 IP、内网域名、 库名 xiujie_admin → iotlcm、哨兵名占位 - 159 处真实手机号 → 合法 11 位编造号(保留号段与后 4 位,维持互异, 不破坏「校验 MSISDN 格式」等测试断言) - 移除内部资产:CI 触发器、Jenkins 触发测试文档、预演门禁与镜像 digest 记录 - Dockerfile 改用公共基础镜像(golang:1.25-alpine → alpine:3.23), 移除写死的 git 凭据 - README 重写(架构/全量同步模型/配置/已知失败),补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 已知:3 个测试包在源仓同一提交上同样失败(client/upstream、 service/allocate、service/inbound),属既有缺陷,本仓未引入亦未修复。
694 lines
21 KiB
Go
694 lines
21 KiB
Go
package batchresult
|
|
|
|
import (
|
|
"context"
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/xuri/excelize/v2"
|
|
)
|
|
|
|
const configOssURLPathKey = "sys.oss.urlPath"
|
|
|
|
type Row struct {
|
|
RowNo int
|
|
ObjectCode string
|
|
TargetCode string
|
|
PackageCode string
|
|
EffectiveMode string
|
|
ICCID string
|
|
MSISDN string
|
|
IMSI string
|
|
TargetDeptName string
|
|
StatusBefore int
|
|
TargetStatus int
|
|
Operation string
|
|
ApnName string
|
|
Success bool
|
|
Reason string
|
|
UpstreamCode int
|
|
OrderNum string
|
|
}
|
|
|
|
type Request struct {
|
|
BatchNo string
|
|
BatchType int
|
|
TenantID string
|
|
Rows []Row
|
|
}
|
|
|
|
type Artifact struct {
|
|
FileName string
|
|
OriginalName string
|
|
FileSuffix string
|
|
Path string
|
|
LocalPath string
|
|
URL string
|
|
Size int64
|
|
MD5 string
|
|
}
|
|
|
|
type Generator interface {
|
|
Create(ctx context.Context, request Request) (Artifact, error)
|
|
}
|
|
|
|
type generator struct{}
|
|
|
|
func New() Generator {
|
|
return &generator{}
|
|
}
|
|
|
|
func (gtr *generator) Create(ctx context.Context, request Request) (Artifact, error) {
|
|
if request.BatchNo == "" || filepath.Base(request.BatchNo) != request.BatchNo {
|
|
return Artifact{}, gerror.New("invalid batch number")
|
|
}
|
|
uploadPath := g.Cfg().MustGet(ctx, "system.upload.local.path").String()
|
|
if uploadPath == "" {
|
|
return Artifact{}, gerror.New("system.upload.local.path is empty")
|
|
}
|
|
tenantID := request.TenantID
|
|
if tenantID == "" {
|
|
tenantID = "default"
|
|
}
|
|
resultDir, err := resultDir(request.BatchType)
|
|
if err != nil {
|
|
return Artifact{}, err
|
|
}
|
|
objectDir := filepath.Join(tenantID, "user", "file", time.Now().Format("20060102"), resultDir)
|
|
physicalUploadPath := resultPhysicalUploadRoot(uploadPath)
|
|
directory := filepath.Join(physicalUploadPath, objectDir)
|
|
if err = os.MkdirAll(directory, 0o755); err != nil {
|
|
return Artifact{}, gerror.Wrap(err, "create batch result directory")
|
|
}
|
|
fileName := request.BatchNo + "-result.xlsx"
|
|
filePath := filepath.Join(directory, fileName)
|
|
logicalPath := filepath.Join(uploadPath, objectDir, fileName)
|
|
if err = writeWorkbook(filePath, request); err != nil {
|
|
return Artifact{}, err
|
|
}
|
|
artifact, err := inspectArtifact(ctx, filePath, logicalPath, fileName, tenantID)
|
|
if err != nil {
|
|
_ = os.Remove(filePath)
|
|
return Artifact{}, err
|
|
}
|
|
return artifact, nil
|
|
}
|
|
|
|
func resultPhysicalUploadRoot(uploadPath string) string {
|
|
uploadPath = strings.TrimSpace(uploadPath)
|
|
for _, envName := range []string{"IOTLCM_WORKER_UPLOAD_ROOT", "IOTLCM_UPLOAD_ROOT"} {
|
|
if value := strings.TrimSpace(os.Getenv(envName)); value != "" {
|
|
return value
|
|
}
|
|
}
|
|
for _, candidate := range uploadRootEquivalentCandidates(uploadPath) {
|
|
if samePath(candidate, uploadPath) {
|
|
continue
|
|
}
|
|
for _, resolved := range resolvedRootCandidates(candidate) {
|
|
if info, err := os.Stat(resolved); err == nil && info.IsDir() {
|
|
return resolved
|
|
}
|
|
}
|
|
}
|
|
return uploadPath
|
|
}
|
|
|
|
func uploadRootEquivalentCandidates(root string) []string {
|
|
root = strings.TrimSpace(root)
|
|
if root == "" {
|
|
return nil
|
|
}
|
|
candidates := []string{root}
|
|
normalized := filepath.ToSlash(filepath.Clean(root))
|
|
if normalized == "resource/upload" || strings.HasSuffix(normalized, "/resource/upload") {
|
|
prefix := strings.TrimSuffix(normalized, "resource/upload")
|
|
candidates = appendUniqueString(candidates, filepath.FromSlash(prefix+"upload"))
|
|
}
|
|
return candidates
|
|
}
|
|
|
|
func resolvedRootCandidates(root string) []string {
|
|
root = strings.TrimSpace(root)
|
|
if root == "" {
|
|
return nil
|
|
}
|
|
if filepath.IsAbs(root) {
|
|
return []string{root}
|
|
}
|
|
candidates := []string{root}
|
|
for _, base := range localBaseDirs() {
|
|
candidates = appendUniqueString(candidates, filepath.Join(base, root))
|
|
}
|
|
return candidates
|
|
}
|
|
|
|
func localBaseDirs() []string {
|
|
var dirs []string
|
|
if wd, err := os.Getwd(); err == nil && strings.TrimSpace(wd) != "" {
|
|
dirs = appendUniqueString(dirs, wd)
|
|
}
|
|
if exe, err := os.Executable(); err == nil && strings.TrimSpace(exe) != "" {
|
|
dirs = appendUniqueString(dirs, filepath.Dir(exe))
|
|
}
|
|
return dirs
|
|
}
|
|
|
|
func samePath(a, b string) bool {
|
|
return filepath.Clean(strings.TrimSpace(a)) == filepath.Clean(strings.TrimSpace(b))
|
|
}
|
|
|
|
func appendUniqueString(values []string, value string) []string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return values
|
|
}
|
|
for _, existing := range values {
|
|
if samePath(existing, value) {
|
|
return values
|
|
}
|
|
}
|
|
return append(values, value)
|
|
}
|
|
|
|
func resultDir(batchType int) (string, error) {
|
|
if batchType == 11 {
|
|
return filepath.Join("sim-card-add-package", "result"), nil
|
|
}
|
|
if batchType == 12 {
|
|
return filepath.Join("sim-pool-add-package", "result"), nil
|
|
}
|
|
switch batchType {
|
|
case 1:
|
|
return filepath.Join("sim-card-inbound", "result"), nil
|
|
case 3:
|
|
return filepath.Join("sim-card-allocate", "result"), nil
|
|
case 4:
|
|
return filepath.Join("sim-card-renew-change-prepare", "result"), nil
|
|
case 101:
|
|
return filepath.Join("sim-card-renew", "result"), nil
|
|
case 102:
|
|
return filepath.Join("sim-card-change", "result"), nil
|
|
case 103:
|
|
return filepath.Join("sim-card-start", "result"), nil
|
|
case 104:
|
|
return filepath.Join("sim-card-stop", "result"), nil
|
|
case 105:
|
|
return filepath.Join("sim-card-restart", "result"), nil
|
|
case 106:
|
|
return filepath.Join("sim-card-cancel", "result"), nil
|
|
case 107:
|
|
return filepath.Join("sim-card-data-cut", "result"), nil
|
|
case 108:
|
|
return filepath.Join("sim-card-unsub", "result"), nil
|
|
case 109:
|
|
return filepath.Join("sim-card-data-start", "result"), nil
|
|
case 110:
|
|
return filepath.Join("sim-card-data-stop", "result"), nil
|
|
case 40:
|
|
return filepath.Join("sim-warning-config", "result"), nil
|
|
case 41:
|
|
return filepath.Join("sim-card-traffic-cutoff-config", "result"), nil
|
|
default:
|
|
return "", gerror.Newf("unsupported batch type %d", batchType)
|
|
}
|
|
}
|
|
|
|
func writeWorkbook(path string, request Request) error {
|
|
file := excelize.NewFile()
|
|
defer func() { _ = file.Close() }()
|
|
|
|
const resultSheet = "处理结果"
|
|
defaultSheet := file.GetSheetName(0)
|
|
if err := file.SetSheetName(defaultSheet, resultSheet); err != nil {
|
|
return gerror.Wrap(err, "name result sheet")
|
|
}
|
|
if request.BatchType == 1 {
|
|
return writeInboundWorkbook(file, resultSheet, path, request)
|
|
}
|
|
if request.BatchType == 3 {
|
|
return writeAllocateWorkbook(file, resultSheet, path, request)
|
|
}
|
|
if request.BatchType == 11 || request.BatchType == 12 {
|
|
return writeAddPackageWorkbook(file, resultSheet, path, request)
|
|
}
|
|
if request.BatchType == 106 {
|
|
return writeCancelWorkbook(file, resultSheet, path, request)
|
|
}
|
|
if request.BatchType == 40 || request.BatchType == 41 {
|
|
return writeWarningConfigWorkbook(file, resultSheet, path, request)
|
|
}
|
|
if isDataServiceBatch(request.BatchType) {
|
|
return writeDataServiceWorkbook(file, resultSheet, path, request)
|
|
}
|
|
return writeLifecycleWorkbook(file, resultSheet, path, request)
|
|
}
|
|
|
|
func writeAllocateWorkbook(file *excelize.File, sheet, path string, request Request) error {
|
|
headers := []interface{}{"行号", "ICCID", "MSISDN", "IMSI", "目标客户", "执行结果", "失败原因"}
|
|
if err := file.SetSheetRow(sheet, "A1", &headers); err != nil {
|
|
return gerror.Wrap(err, "write allocate result headers")
|
|
}
|
|
for index, row := range request.Rows {
|
|
status := "失败"
|
|
if row.Success {
|
|
status = "成功"
|
|
}
|
|
values := []interface{}{row.RowNo, row.ICCID, row.MSISDN, row.IMSI, row.TargetDeptName, status, row.Reason}
|
|
cell, err := excelize.CoordinatesToCellName(1, index+2)
|
|
if err != nil {
|
|
return gerror.Wrap(err, "locate allocate result row")
|
|
}
|
|
if err = file.SetSheetRow(sheet, cell, &values); err != nil {
|
|
return gerror.Wrap(err, "write allocate result row")
|
|
}
|
|
}
|
|
if err := file.SetColWidth(sheet, "A", "A", 10); err != nil {
|
|
return gerror.Wrap(err, "format allocate result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "B", "E", 24); err != nil {
|
|
return gerror.Wrap(err, "format allocate result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "F", "G", 24); err != nil {
|
|
return gerror.Wrap(err, "format allocate result workbook")
|
|
}
|
|
if err := file.SaveAs(path); err != nil {
|
|
return gerror.Wrap(err, "save allocate result workbook")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeWarningConfigWorkbook(file *excelize.File, sheet, path string, request Request) error {
|
|
headers := []interface{}{"行号", "对象编码", "执行结果", "失败原因"}
|
|
if err := file.SetSheetRow(sheet, "A1", &headers); err != nil {
|
|
return gerror.Wrap(err, "write warning config result headers")
|
|
}
|
|
for index, row := range request.Rows {
|
|
status := "失败"
|
|
if row.Success {
|
|
status = "成功"
|
|
}
|
|
objectCode := row.ObjectCode
|
|
if objectCode == "" {
|
|
objectCode = row.ICCID
|
|
}
|
|
values := []interface{}{row.RowNo, objectCode, status, row.Reason}
|
|
cell, err := excelize.CoordinatesToCellName(1, index+2)
|
|
if err != nil {
|
|
return gerror.Wrap(err, "locate warning config result row")
|
|
}
|
|
if err = file.SetSheetRow(sheet, cell, &values); err != nil {
|
|
return gerror.Wrap(err, "write warning config result row")
|
|
}
|
|
}
|
|
if err := file.SetColWidth(sheet, "A", "A", 10); err != nil {
|
|
return gerror.Wrap(err, "format warning config result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "B", "B", 28); err != nil {
|
|
return gerror.Wrap(err, "format warning config result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "C", "D", 24); err != nil {
|
|
return gerror.Wrap(err, "format warning config result workbook")
|
|
}
|
|
if err := file.SaveAs(path); err != nil {
|
|
return gerror.Wrap(err, "save warning config result workbook")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeAddPackageWorkbook(file *excelize.File, sheet, path string, request Request) error {
|
|
headers := []interface{}{"行号", "目标编码", "加油包编码", "生效方式", "执行结果", "失败原因", "订单号"}
|
|
if err := file.SetSheetRow(sheet, "A1", &headers); err != nil {
|
|
return gerror.Wrap(err, "write add package result headers")
|
|
}
|
|
for index, row := range request.Rows {
|
|
status := "失败"
|
|
if row.Success {
|
|
status = "成功"
|
|
}
|
|
targetCode := row.ObjectCode
|
|
if targetCode == "" {
|
|
targetCode = row.TargetCode
|
|
}
|
|
values := []interface{}{row.RowNo, targetCode, row.PackageCode, addPackageEffectiveModeText(row.EffectiveMode), status, row.Reason, row.OrderNum}
|
|
cell, err := excelize.CoordinatesToCellName(1, index+2)
|
|
if err != nil {
|
|
return gerror.Wrap(err, "locate add package result row")
|
|
}
|
|
if err = file.SetSheetRow(sheet, cell, &values); err != nil {
|
|
return gerror.Wrap(err, "write add package result row")
|
|
}
|
|
}
|
|
if err := file.SetColWidth(sheet, "A", "A", 10); err != nil {
|
|
return gerror.Wrap(err, "format add package result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "B", "D", 24); err != nil {
|
|
return gerror.Wrap(err, "format add package result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "E", "G", 22); err != nil {
|
|
return gerror.Wrap(err, "format add package result workbook")
|
|
}
|
|
if err := file.SaveAs(path); err != nil {
|
|
return gerror.Wrap(err, "save add package result workbook")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func addPackageEffectiveModeText(mode string) string {
|
|
switch strings.TrimSpace(mode) {
|
|
case "", "current_month", "immediate", "this_month", "本月生效", "本月", "当月":
|
|
return "本月生效"
|
|
case "next_month", "nextMonth", "次月生效", "次月", "下月", "下月生效":
|
|
return "次月生效"
|
|
default:
|
|
return strings.TrimSpace(mode)
|
|
}
|
|
}
|
|
|
|
func isDataServiceBatch(batchType int) bool {
|
|
return batchType == 107 || batchType == 109 || batchType == 110
|
|
}
|
|
|
|
func writeInboundWorkbook(file *excelize.File, sheet, path string, request Request) error {
|
|
headers := []interface{}{"行号", "ICCID", "执行结果", "失败原因"}
|
|
if err := file.SetSheetRow(sheet, "A1", &headers); err != nil {
|
|
return gerror.Wrap(err, "write inbound result headers")
|
|
}
|
|
for index, row := range request.Rows {
|
|
status := "失败"
|
|
if row.Success {
|
|
status = "成功"
|
|
}
|
|
values := []interface{}{row.RowNo, row.ICCID, status, row.Reason}
|
|
cell, err := excelize.CoordinatesToCellName(1, index+2)
|
|
if err != nil {
|
|
return gerror.Wrap(err, "locate inbound result row")
|
|
}
|
|
if err = file.SetSheetRow(sheet, cell, &values); err != nil {
|
|
return gerror.Wrap(err, "write inbound result row")
|
|
}
|
|
}
|
|
if err := file.SetColWidth(sheet, "A", "A", 10); err != nil {
|
|
return gerror.Wrap(err, "format inbound result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "B", "B", 24); err != nil {
|
|
return gerror.Wrap(err, "format inbound result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "C", "D", 24); err != nil {
|
|
return gerror.Wrap(err, "format inbound result workbook")
|
|
}
|
|
if err := file.SaveAs(path); err != nil {
|
|
return gerror.Wrap(err, "save inbound result workbook")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeLifecycleWorkbook(file *excelize.File, sheet, path string, request Request) error {
|
|
headers := []interface{}{"行号", "ICCID", "MSISDN", "IMSI", "执行结果", "失败原因", "上游状态码", "上游订单号"}
|
|
if err := file.SetSheetRow(sheet, "A1", &headers); err != nil {
|
|
return gerror.Wrap(err, "write lifecycle result headers")
|
|
}
|
|
for index, row := range request.Rows {
|
|
status := "失败"
|
|
if row.Success {
|
|
status = "成功"
|
|
}
|
|
values := []interface{}{row.RowNo, row.ICCID, row.MSISDN, row.IMSI, status, row.Reason, row.UpstreamCode, row.OrderNum}
|
|
cell, err := excelize.CoordinatesToCellName(1, index+2)
|
|
if err != nil {
|
|
return gerror.Wrap(err, "locate lifecycle result row")
|
|
}
|
|
if err = file.SetSheetRow(sheet, cell, &values); err != nil {
|
|
return gerror.Wrap(err, "write lifecycle result row")
|
|
}
|
|
}
|
|
if err := file.SetColWidth(sheet, "A", "A", 10); err != nil {
|
|
return gerror.Wrap(err, "format lifecycle result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "B", "D", 24); err != nil {
|
|
return gerror.Wrap(err, "format lifecycle result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "E", "H", 20); err != nil {
|
|
return gerror.Wrap(err, "format lifecycle result workbook")
|
|
}
|
|
if err := file.SaveAs(path); err != nil {
|
|
return gerror.Wrap(err, "save lifecycle result workbook")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeCancelWorkbook(file *excelize.File, sheet, path string, request Request) error {
|
|
headers := []interface{}{"行号", "ICCID", "MSISDN", "IMSI", "原卡状态", "目标卡状态", "执行结果", "失败原因"}
|
|
if err := file.SetSheetRow(sheet, "A1", &headers); err != nil {
|
|
return gerror.Wrap(err, "write cancel result headers")
|
|
}
|
|
for index, row := range request.Rows {
|
|
status := "失败"
|
|
if row.Success {
|
|
status = "成功"
|
|
}
|
|
values := []interface{}{row.RowNo, row.ICCID, row.MSISDN, row.IMSI, row.StatusBefore, row.TargetStatus, status, row.Reason}
|
|
cell, err := excelize.CoordinatesToCellName(1, index+2)
|
|
if err != nil {
|
|
return gerror.Wrap(err, "locate cancel result row")
|
|
}
|
|
if err = file.SetSheetRow(sheet, cell, &values); err != nil {
|
|
return gerror.Wrap(err, "write cancel result row")
|
|
}
|
|
}
|
|
if err := file.SetColWidth(sheet, "A", "A", 10); err != nil {
|
|
return gerror.Wrap(err, "format cancel result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "B", "D", 24); err != nil {
|
|
return gerror.Wrap(err, "format cancel result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "E", "H", 20); err != nil {
|
|
return gerror.Wrap(err, "format cancel result workbook")
|
|
}
|
|
if err := file.SaveAs(path); err != nil {
|
|
return gerror.Wrap(err, "save cancel result workbook")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeDataServiceWorkbook(file *excelize.File, sheet, path string, request Request) error {
|
|
headers := []interface{}{"行号", "ICCID", "MSISDN", "IMSI", "操作", "APN", "执行结果", "失败原因", "上游状态码", "上游订单号"}
|
|
if err := file.SetSheetRow(sheet, "A1", &headers); err != nil {
|
|
return gerror.Wrap(err, "write data service result headers")
|
|
}
|
|
for index, row := range request.Rows {
|
|
status := "失败"
|
|
if row.Success {
|
|
status = "成功"
|
|
}
|
|
values := []interface{}{row.RowNo, row.ICCID, row.MSISDN, row.IMSI, row.Operation, row.ApnName, status, row.Reason, row.UpstreamCode, row.OrderNum}
|
|
cell, err := excelize.CoordinatesToCellName(1, index+2)
|
|
if err != nil {
|
|
return gerror.Wrap(err, "locate data service result row")
|
|
}
|
|
if err = file.SetSheetRow(sheet, cell, &values); err != nil {
|
|
return gerror.Wrap(err, "write data service result row")
|
|
}
|
|
}
|
|
if err := file.SetColWidth(sheet, "A", "A", 10); err != nil {
|
|
return gerror.Wrap(err, "format data service result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "B", "D", 24); err != nil {
|
|
return gerror.Wrap(err, "format data service result workbook")
|
|
}
|
|
if err := file.SetColWidth(sheet, "E", "J", 20); err != nil {
|
|
return gerror.Wrap(err, "format data service result workbook")
|
|
}
|
|
if err := file.SaveAs(path); err != nil {
|
|
return gerror.Wrap(err, "save data service result workbook")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func inspectArtifact(ctx context.Context, localPath, logicalPath, fileName string, tenantID string) (Artifact, error) {
|
|
file, err := os.Open(localPath)
|
|
if err != nil {
|
|
return Artifact{}, gerror.Wrap(err, "open result file")
|
|
}
|
|
defer file.Close()
|
|
hash := md5.New()
|
|
size, err := io.Copy(hash, file)
|
|
if err != nil {
|
|
return Artifact{}, gerror.Wrap(err, "hash result file")
|
|
}
|
|
return Artifact{
|
|
FileName: fileName,
|
|
OriginalName: fileName,
|
|
FileSuffix: "xlsx",
|
|
Path: logicalPath,
|
|
LocalPath: localPath,
|
|
URL: localUploadURL(ctx, logicalPath, tenantID),
|
|
Size: size,
|
|
MD5: hex.EncodeToString(hash.Sum(nil)),
|
|
}, nil
|
|
}
|
|
|
|
func RemoveLocalFile(artifact Artifact) {
|
|
path := strings.TrimSpace(artifact.LocalPath)
|
|
if path == "" {
|
|
path = strings.TrimSpace(artifact.Path)
|
|
}
|
|
if path != "" {
|
|
_ = os.Remove(path)
|
|
}
|
|
}
|
|
|
|
func localUploadURL(ctx context.Context, filePath string, tenantID string) string {
|
|
filePath = strings.TrimSpace(filepath.ToSlash(filepath.Clean(filePath)))
|
|
if filePath == "" {
|
|
return ""
|
|
}
|
|
|
|
if configValue := loadUploadURLPathConfig(ctx, tenantID); configValue != "" {
|
|
if strings.Contains(configValue, "|") {
|
|
split := strings.SplitN(configValue, "|", 2)
|
|
from := strings.TrimSpace(filepath.ToSlash(split[0]))
|
|
to := strings.TrimSpace(filepath.ToSlash(split[1]))
|
|
if suffix, ok := localUploadMappedSuffix(filePath, from); ok {
|
|
return joinUploadURL(to, suffix)
|
|
}
|
|
} else {
|
|
if suffix := localUploadRelativePath(filePath); suffix != "" {
|
|
return joinUploadURL(configValue, suffix)
|
|
}
|
|
return joinUploadURL(configValue, filePath)
|
|
}
|
|
}
|
|
|
|
if suffix := localUploadRelativePath(filePath); suffix != "" {
|
|
return joinUploadURL("/upload", suffix)
|
|
}
|
|
return filePath
|
|
}
|
|
|
|
func loadUploadURLPathConfig(ctx context.Context, tenantID string) (configValue string) {
|
|
defer func() {
|
|
if recover() != nil {
|
|
configValue = ""
|
|
}
|
|
}()
|
|
for _, tenant := range []string{strings.TrimSpace(tenantID), "000000"} {
|
|
if tenant == "" {
|
|
continue
|
|
}
|
|
var row struct {
|
|
ConfigValue string `orm:"config_value"`
|
|
}
|
|
err := g.DB().Model("sys_config").Safe().Ctx(ctx).
|
|
Fields("config_value").
|
|
Where("tenant_id", tenant).
|
|
Where("config_key", configOssURLPathKey).
|
|
Limit(1).
|
|
Scan(&row)
|
|
if err == nil && strings.TrimSpace(row.ConfigValue) != "" {
|
|
return strings.TrimSpace(row.ConfigValue)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func localUploadMappedSuffix(filePath string, from string) (string, bool) {
|
|
from = strings.Trim(strings.TrimSpace(filepath.ToSlash(from)), "/")
|
|
if from == "" {
|
|
return "", false
|
|
}
|
|
normalized := strings.TrimSpace(filepath.ToSlash(filePath))
|
|
marker := "/" + from + "/"
|
|
if idx := strings.Index(normalized, marker); idx >= 0 {
|
|
return strings.TrimPrefix(normalized[idx+len(marker):], "/"), true
|
|
}
|
|
prefix := from + "/"
|
|
if strings.HasPrefix(normalized, prefix) {
|
|
return strings.TrimPrefix(normalized[len(prefix):], "/"), true
|
|
}
|
|
if strings.Trim(normalized, "/") == from {
|
|
return "", true
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func localUploadRelativePath(filePath string) string {
|
|
normalized := filepath.ToSlash(filepath.Clean(strings.TrimSpace(filePath)))
|
|
for _, marker := range []string{
|
|
"resource/upload/",
|
|
"/resource/upload/",
|
|
"/app/resource/upload/",
|
|
"/app/upload/",
|
|
"upload/",
|
|
"/upload/",
|
|
} {
|
|
if idx := strings.Index(normalized, marker); idx >= 0 {
|
|
return strings.TrimPrefix(normalized[idx+len(marker):], "/")
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func joinUploadURL(base string, suffix string) string {
|
|
base = strings.TrimRight(strings.TrimSpace(filepath.ToSlash(base)), "/")
|
|
suffix = strings.TrimLeft(strings.TrimSpace(filepath.ToSlash(suffix)), "/")
|
|
if base == "" {
|
|
base = "/upload"
|
|
}
|
|
if suffix == "" {
|
|
return base
|
|
}
|
|
return base + "/" + suffix
|
|
}
|
|
|
|
func TaskName(batchType int) string {
|
|
if batchType == 11 {
|
|
return "单卡加油包结果"
|
|
}
|
|
if batchType == 12 {
|
|
return "流量池加油包结果"
|
|
}
|
|
switch batchType {
|
|
case 1:
|
|
return "SIM卡批量入库结果"
|
|
case 3:
|
|
return "卡划拨结果"
|
|
case 4:
|
|
return "卡续费/变更预处理结果"
|
|
case 101:
|
|
return "卡续费结果"
|
|
case 102:
|
|
return "卡变更套餐结果"
|
|
case 103:
|
|
return "SIM卡批量复机结果"
|
|
case 104:
|
|
return "SIM卡批量停机结果"
|
|
case 105:
|
|
return "SIM卡批量重启结果"
|
|
case 106:
|
|
return "卡销户结果"
|
|
case 108:
|
|
return "SIM卡批量退订结果"
|
|
case 107:
|
|
return "SIM卡达量断网结果"
|
|
case 109:
|
|
return "卡开数据结果"
|
|
case 110:
|
|
return "卡停数据结果"
|
|
case 40:
|
|
return "SIM卡预警配置结果"
|
|
case 41:
|
|
return "达量断网配置结果"
|
|
default:
|
|
return fmt.Sprintf("SIM卡批量任务%d结果", batchType)
|
|
}
|
|
}
|