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,61 @@
|
||||
package packagecustomer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consts"
|
||||
mq "git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/library/rabbitmq"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model"
|
||||
)
|
||||
|
||||
func PublishSync(ctx context.Context, message model.PackageCustomerSyncMessage) error {
|
||||
message.TenantId = strings.TrimSpace(message.TenantId)
|
||||
message.Trigger = strings.TrimSpace(message.Trigger)
|
||||
message.SourceBatchNo = strings.TrimSpace(message.SourceBatchNo)
|
||||
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")
|
||||
}
|
||||
if message.Trigger == "" {
|
||||
message.Trigger = "worker"
|
||||
}
|
||||
if err := mq.Publish(ctx, consts.QueuePackageCustomerSync, message); err != nil {
|
||||
return gerror.Wrapf(err, "publish package customer sync failed, queue:%s", consts.QueuePackageCustomerSync)
|
||||
}
|
||||
g.Log().Infof(ctx, "package customer sync published, tenantId:%s, deptId:%d, trigger:%s, sourceBatchNo:%s",
|
||||
message.TenantId, message.DeptId, message.Trigger, message.SourceBatchNo)
|
||||
return nil
|
||||
}
|
||||
|
||||
func PublishSyncSafe(ctx context.Context, message model.PackageCustomerSyncMessage) {
|
||||
if err := PublishSync(ctx, message); err != nil {
|
||||
g.Log().Warningf(ctx, "package customer sync publish skipped, tenantId:%s, deptId:%d, trigger:%s, sourceBatchNo:%s, err:%+v",
|
||||
message.TenantId, message.DeptId, message.Trigger, message.SourceBatchNo, err)
|
||||
}
|
||||
}
|
||||
|
||||
func PublishSyncForDeptsSafe(ctx context.Context, tenantId string, userId int64, deptIds []int64, trigger, sourceBatchNo string) {
|
||||
seen := make(map[int64]struct{}, len(deptIds))
|
||||
for _, deptId := range deptIds {
|
||||
if deptId <= 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[deptId]; ok {
|
||||
continue
|
||||
}
|
||||
seen[deptId] = struct{}{}
|
||||
PublishSyncSafe(ctx, model.PackageCustomerSyncMessage{
|
||||
TenantId: tenantId,
|
||||
DeptId: deptId,
|
||||
UserId: userId,
|
||||
Trigger: trigger,
|
||||
SourceBatchNo: sourceBatchNo,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
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"
|
||||
}
|
||||
Reference in New Issue
Block a user