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),属既有缺陷,本仓未引入亦未修复。
178 lines
5.0 KiB
Go
178 lines
5.0 KiB
Go
package packagecustomer
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model"
|
|
)
|
|
|
|
const (
|
|
sysDeptTable = "sys_dept"
|
|
cardInfoTable = "t_sim_card_info"
|
|
flowPackageTable = "t_sim_flow_package_info"
|
|
packageCustomerTable = "t_package_customer"
|
|
)
|
|
|
|
var ErrDeptNotFound = gerror.New("package customer sync dept not found")
|
|
|
|
type Service struct {
|
|
db gdb.DB
|
|
}
|
|
|
|
type deptRow struct {
|
|
DeptId int64 `orm:"dept_id"`
|
|
ParentId int64 `orm:"parent_id"`
|
|
Ancestors string `orm:"ancestors"`
|
|
DeptName string `orm:"dept_name"`
|
|
}
|
|
|
|
type packageRow struct {
|
|
PackageCode string `orm:"package_code"`
|
|
PackageName string `orm:"package_name"`
|
|
}
|
|
|
|
func New() *Service {
|
|
return &Service{}
|
|
}
|
|
|
|
func (s *Service) SyncDeptPackages(ctx context.Context, message model.PackageCustomerSyncMessage) error {
|
|
message.TenantId = strings.TrimSpace(message.TenantId)
|
|
message.Trigger = strings.TrimSpace(message.Trigger)
|
|
if message.TenantId == "" {
|
|
return gerror.New("package customer sync tenantId is empty")
|
|
}
|
|
if message.DeptId <= 0 {
|
|
return gerror.New("package customer sync deptId is empty")
|
|
}
|
|
var packageCount int
|
|
err := s.database().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
|
dept, err := s.lockDept(ctx, tx, message.TenantId, message.DeptId)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
packages, err := s.loadPackages(ctx, tx, message.TenantId, dept)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
packageCount = len(packages)
|
|
return s.rebuild(ctx, tx, message.TenantId, dept, message.UserId, packages)
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
g.Log().Infof(ctx, "package customer sync finished, tenantId:%s, deptId:%d, packages:%d, trigger:%s, sourceBatchNo:%s",
|
|
message.TenantId, message.DeptId, packageCount, message.Trigger, message.SourceBatchNo)
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) database() gdb.DB {
|
|
if s.db != nil {
|
|
return s.db
|
|
}
|
|
return g.DB()
|
|
}
|
|
|
|
func (s *Service) lockDept(ctx context.Context, tx gdb.TX, tenantId string, deptId int64) (*deptRow, error) {
|
|
var dept *deptRow
|
|
err := tx.Ctx(ctx).Model(sysDeptTable).Safe().
|
|
Fields("dept_id", "parent_id", "ancestors", "dept_name").
|
|
Where("tenant_id", tenantId).
|
|
Where("dept_id", deptId).
|
|
WhereNull("deleted_at").
|
|
LockUpdate().
|
|
Limit(1).
|
|
Scan(&dept)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "load package customer sync dept failed")
|
|
}
|
|
if dept == nil || dept.DeptId <= 0 {
|
|
return nil, ErrDeptNotFound
|
|
}
|
|
return dept, nil
|
|
}
|
|
|
|
func (s *Service) loadPackages(ctx context.Context, tx gdb.TX, tenantId string, dept *deptRow) ([]packageRow, error) {
|
|
if deptIsTop(dept) {
|
|
var packages []packageRow
|
|
err := tx.Ctx(ctx).Model(flowPackageTable).Safe().
|
|
Fields("package_code", "package_name").
|
|
Where("tenant_id", tenantId).
|
|
Scan(&packages)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "load top dept packages failed")
|
|
}
|
|
return packages, nil
|
|
}
|
|
|
|
var packages []packageRow
|
|
err := tx.Ctx(ctx).Model(cardInfoTable+" c").Safe().
|
|
LeftJoin(flowPackageTable+" p", "c.current_package_code = p.package_code").
|
|
Fields("DISTINCT c.current_package_code AS package_code", "p.package_name AS package_name").
|
|
Where("c.tenant_id", tenantId).
|
|
Where("c.dept_id", dept.DeptId).
|
|
Where("p.tenant_id", tenantId).
|
|
Where("c.current_package_code IS NOT NULL").
|
|
Where("c.current_package_code <> ?", "").
|
|
Where("p.package_code IS NOT NULL").
|
|
Scan(&packages)
|
|
if err != nil {
|
|
return nil, gerror.Wrap(err, "load dept packages failed")
|
|
}
|
|
return packages, nil
|
|
}
|
|
|
|
func (s *Service) rebuild(ctx context.Context, tx gdb.TX, tenantId string, dept *deptRow, userId int64, packages []packageRow) error {
|
|
if _, err := tx.Ctx(ctx).Model(packageCustomerTable).Safe().
|
|
Where("tenant_id", tenantId).
|
|
Where("dept_id", dept.DeptId).
|
|
Delete(); err != nil {
|
|
return gerror.Wrap(err, "clear dept package customer relation failed")
|
|
}
|
|
|
|
now := gtime.Now()
|
|
rows := make([]g.Map, 0, len(packages))
|
|
seen := make(map[string]struct{}, len(packages))
|
|
for _, item := range packages {
|
|
packageCode := strings.TrimSpace(item.PackageCode)
|
|
if packageCode == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[packageCode]; ok {
|
|
continue
|
|
}
|
|
seen[packageCode] = struct{}{}
|
|
rows = append(rows, g.Map{
|
|
"package_code": packageCode,
|
|
"package_name": strings.TrimSpace(item.PackageName),
|
|
"dept_id": dept.DeptId,
|
|
"dept_name": strings.TrimSpace(dept.DeptName),
|
|
"tenant_id": tenantId,
|
|
"created_dept": dept.DeptId,
|
|
"created_by": userId,
|
|
"created_at": now,
|
|
"updated_by": userId,
|
|
"updated_at": now,
|
|
})
|
|
}
|
|
if len(rows) == 0 {
|
|
return nil
|
|
}
|
|
if _, err := tx.Ctx(ctx).Model(packageCustomerTable).Safe().Data(rows).Insert(); err != nil {
|
|
return gerror.Wrap(err, "insert dept package customer relation failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func deptIsTop(dept *deptRow) bool {
|
|
if dept == nil {
|
|
return false
|
|
}
|
|
return dept.ParentId == 0 || strings.Trim(dept.Ancestors, ",") == "0"
|
|
}
|