chore: 初始开源发布
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),属既有缺陷,本仓未引入亦未修复。
This commit is contained in:
@@ -0,0 +1,693 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user