分裂iccid长度
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m47s

This commit is contained in:
2026-04-21 10:55:12 +08:00
parent 9942bbc74e
commit e049080f6c
18 changed files with 883 additions and 60 deletions

View File

@@ -8,8 +8,10 @@ import (
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/errors"
"github.com/break/junhong_cmp_fiber/pkg/logger"
"github.com/jackc/pgx/v5/pgconn"
"github.com/redis/go-redis/v9"
"go.uber.org/zap"
"gorm.io/gorm"
)
@@ -183,21 +185,46 @@ func (s *DeviceSimBindingStore) GetBoundICCIDs(ctx context.Context, iccids []str
return result, nil
}
var bindings []struct {
ICCID string
}
if err := s.db.WithContext(ctx).
Table("tb_device_sim_binding b").
Select("c.iccid").
Joins("JOIN tb_iot_card c ON c.id = b.iot_card_id").
Where("c.iccid IN ? AND b.bind_status = 1 AND c.deleted_at IS NULL", iccids).
Find(&bindings).Error; err != nil {
return nil, err
var list19, list20 []string
for _, id := range iccids {
switch len(id) {
case 19:
list19 = append(list19, id)
case 20:
list20 = append(list20, id)
}
}
for _, b := range bindings {
result[b.ICCID] = true
if len(list19) > 0 {
var rows []struct{ ICCID string }
if err := s.db.WithContext(ctx).
Table("tb_device_sim_binding b").
Select("c.iccid_19 AS iccid").
Joins("JOIN tb_iot_card c ON c.id = b.iot_card_id").
Where("c.iccid_19 IN ? AND b.bind_status = 1 AND c.deleted_at IS NULL", list19).
Find(&rows).Error; err != nil {
return nil, err
}
for _, r := range rows {
result[r.ICCID] = true
}
}
if len(list20) > 0 {
var rows []struct{ ICCID string }
if err := s.db.WithContext(ctx).
Table("tb_device_sim_binding b").
Select("c.iccid_20 AS iccid").
Joins("JOIN tb_iot_card c ON c.id = b.iot_card_id").
Where("c.iccid_20 IN ? AND b.bind_status = 1 AND c.deleted_at IS NULL", list20).
Find(&rows).Error; err != nil {
return nil, err
}
for _, r := range rows {
result[r.ICCID] = true
}
}
return result, nil
}
@@ -206,27 +233,44 @@ func (s *DeviceSimBindingStore) GetBoundICCIDs(ctx context.Context, iccids []str
// currentIccid 为空字符串时,仅执行全部清空(无当前卡的情况)
func (s *DeviceSimBindingStore) UpdateIsCurrentByDeviceID(ctx context.Context, deviceID uint, currentIccid string) error {
return s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
// 第一步:将该设备所有活跃绑定的 is_current 设为 false
if err := tx.Model(&model.DeviceSimBinding{}).
Where("device_id = ? AND bind_status = 1", deviceID).
Update("is_current", false).Error; err != nil {
return err
}
// 第二步:将当前 ICCID 对应的绑定记录设为 is_current = true
if currentIccid == "" {
return nil
}
// 通过子查询找到对应的 iot_card_id避免跨表 JOIN 更新)
var column string
switch len(currentIccid) {
case 19:
column = "iccid_19"
case 20:
column = "iccid_20"
default:
logger.GetAppLogger().Error("UpdateIsCurrentByDeviceID: ICCID 长度异常,跳过 is_current 更新",
zap.String("iccid", currentIccid),
zap.Uint("device_id", deviceID),
zap.Int("length", len(currentIccid)),
)
return nil
}
var iotCardID uint
if err := tx.Table("tb_iot_card").
Select("id").
Where("iccid = ? AND deleted_at IS NULL", currentIccid).
Where(column+" = ? AND deleted_at IS NULL", currentIccid).
Scan(&iotCardID).Error; err != nil {
return err
}
if iotCardID == 0 {
// 未找到对应卡记录,不更新(可能卡尚未入库)
logger.GetAppLogger().Warn("UpdateIsCurrentByDeviceID: 未命中卡记录is_current 不更新",
zap.String("iccid", currentIccid),
zap.Uint("device_id", deviceID),
zap.String("column_used", column),
)
return nil
}
return tx.Model(&model.DeviceSimBinding{}).

View File

@@ -56,13 +56,30 @@ func (s *IotCardStore) GetByID(ctx context.Context, id uint) (*model.IotCard, er
func (s *IotCardStore) GetByICCID(ctx context.Context, iccid string) (*model.IotCard, error) {
var card model.IotCard
query := s.db.WithContext(ctx).Where("iccid = ?", iccid)
// 应用数据权限过滤
var column string
switch len(iccid) {
case 19:
column = "iccid_19"
case 20:
column = "iccid_20"
default:
logger.GetAppLogger().Error("GetByICCID: ICCID 长度异常,拒绝查询",
zap.String("iccid", iccid),
zap.Int("length", len(iccid)),
)
return nil, gorm.ErrRecordNotFound
}
query := s.db.WithContext(ctx).Where(column+" = ?", iccid)
query = middleware.ApplyShopFilter(ctx, query)
if err := query.First(&card).Error; err != nil {
err := query.First(&card).Error
if err == gorm.ErrRecordNotFound {
logger.GetAppLogger().Warn("GetByICCID: 未命中,上游 ICCID 格式可能与存储不一致",
zap.String("iccid", iccid),
zap.String("column_used", column),
)
return nil, err
}
return &card, nil
return &card, err
}
func (s *IotCardStore) GetByIDs(ctx context.Context, ids []uint) ([]*model.IotCard, error) {
@@ -80,8 +97,21 @@ func (s *IotCardStore) GetByIDs(ctx context.Context, ids []uint) ([]*model.IotCa
}
func (s *IotCardStore) ExistsByICCID(ctx context.Context, iccid string) (bool, error) {
var column string
switch len(iccid) {
case 19:
column = "iccid_19"
case 20:
column = "iccid_20"
default:
logger.GetAppLogger().Error("ExistsByICCID: ICCID 长度异常,拒绝查询",
zap.String("iccid", iccid),
zap.Int("length", len(iccid)),
)
return false, nil
}
var count int64
if err := s.db.WithContext(ctx).Model(&model.IotCard{}).Where("iccid = ?", iccid).Count(&count).Error; err != nil {
if err := s.db.WithContext(ctx).Model(&model.IotCard{}).Where(column+" = ?", iccid).Count(&count).Error; err != nil {
return false, err
}
return count > 0, nil
@@ -92,17 +122,42 @@ func (s *IotCardStore) ExistsByICCIDBatch(ctx context.Context, iccids []string)
return make(map[string]bool), nil
}
var existingICCIDs []string
if err := s.db.WithContext(ctx).Model(&model.IotCard{}).
Where("iccid IN ?", iccids).
Pluck("iccid", &existingICCIDs).Error; err != nil {
return nil, err
var list19, list20 []string
for _, id := range iccids {
switch len(id) {
case 19:
list19 = append(list19, id)
case 20:
list20 = append(list20, id)
}
}
result := make(map[string]bool)
for _, iccid := range existingICCIDs {
result[iccid] = true
if len(list19) > 0 {
var found []string
if err := s.db.WithContext(ctx).Model(&model.IotCard{}).
Where("iccid_19 IN ?", list19).
Pluck("iccid_19", &found).Error; err != nil {
return nil, err
}
for _, v := range found {
result[v] = true
}
}
if len(list20) > 0 {
var found []string
if err := s.db.WithContext(ctx).Model(&model.IotCard{}).
Where("iccid_20 IN ?", list20).
Pluck("iccid_20", &found).Error; err != nil {
return nil, err
}
for _, v := range found {
result[v] = true
}
}
return result, nil
}
@@ -689,14 +744,51 @@ func (s *IotCardStore) GetByICCIDs(ctx context.Context, iccids []string) ([]*mod
if len(iccids) == 0 {
return nil, nil
}
var cards []*model.IotCard
query := s.db.WithContext(ctx).Where("iccid IN ?", iccids)
// 应用数据权限过滤
query = middleware.ApplyShopFilter(ctx, query)
if err := query.Find(&cards).Error; err != nil {
return nil, err
var list19, list20 []string
for _, id := range iccids {
switch len(id) {
case 19:
list19 = append(list19, id)
case 20:
list20 = append(list20, id)
}
}
return cards, nil
seen := make(map[uint]struct{})
var all []*model.IotCard
if len(list19) > 0 {
var cards []*model.IotCard
query := s.db.WithContext(ctx).Where("iccid_19 IN ?", list19)
query = middleware.ApplyShopFilter(ctx, query)
if err := query.Find(&cards).Error; err != nil {
return nil, err
}
for _, c := range cards {
if _, dup := seen[c.ID]; !dup {
seen[c.ID] = struct{}{}
all = append(all, c)
}
}
}
if len(list20) > 0 {
var cards []*model.IotCard
query := s.db.WithContext(ctx).Where("iccid_20 IN ?", list20)
query = middleware.ApplyShopFilter(ctx, query)
if err := query.Find(&cards).Error; err != nil {
return nil, err
}
for _, c := range cards {
if _, dup := seen[c.ID]; !dup {
seen[c.ID] = struct{}{}
all = append(all, c)
}
}
}
return all, nil
}
func (s *IotCardStore) GetStandaloneByICCIDRange(ctx context.Context, iccidStart, iccidEnd string, shopID *uint) ([]*model.IotCard, error) {

View File

@@ -41,9 +41,16 @@ func (s *PersonalCustomerICCIDStore) GetByCustomerID(ctx context.Context, custom
// GetByICCID 根据 ICCID 获取所有绑定记录(查询哪些用户使用过这个 ICCID
func (s *PersonalCustomerICCIDStore) GetByICCID(ctx context.Context, iccid string) ([]*model.PersonalCustomerICCID, error) {
var column string
switch len(iccid) {
case 19:
column = "iccid_19"
default:
column = "iccid"
}
var records []*model.PersonalCustomerICCID
if err := s.db.WithContext(ctx).
Where("iccid = ?", iccid).
Where(column+" = ?", iccid).
Order("last_used_at DESC").
Find(&records).Error; err != nil {
return nil, err
@@ -53,9 +60,16 @@ func (s *PersonalCustomerICCIDStore) GetByICCID(ctx context.Context, iccid strin
// GetByCustomerAndICCID 根据客户 ID 和 ICCID 获取绑定记录
func (s *PersonalCustomerICCIDStore) GetByCustomerAndICCID(ctx context.Context, customerID uint, iccid string) (*model.PersonalCustomerICCID, error) {
var column string
switch len(iccid) {
case 19:
column = "iccid_19"
default:
column = "iccid"
}
var record model.PersonalCustomerICCID
if err := s.db.WithContext(ctx).
Where("customer_id = ? AND iccid = ?", customerID, iccid).
Where("customer_id = ? AND "+column+" = ?", customerID, iccid).
First(&record).Error; err != nil {
return nil, err
}
@@ -85,10 +99,17 @@ func (s *PersonalCustomerICCIDStore) Delete(ctx context.Context, id uint) error
// ExistsByCustomerAndICCID 检查客户是否已绑定该 ICCID
func (s *PersonalCustomerICCIDStore) ExistsByCustomerAndICCID(ctx context.Context, customerID uint, iccid string) (bool, error) {
var column string
switch len(iccid) {
case 19:
column = "iccid_19"
default:
column = "iccid"
}
var count int64
if err := s.db.WithContext(ctx).
Model(&model.PersonalCustomerICCID{}).
Where("customer_id = ? AND iccid = ? AND status = ?", customerID, iccid, 1).
Where("customer_id = ? AND "+column+" = ? AND status = ?", customerID, iccid, 1).
Count(&count).Error; err != nil {
return false, err
}
@@ -100,17 +121,20 @@ func (s *PersonalCustomerICCIDStore) ExistsByCustomerAndICCID(ctx context.Contex
func (s *PersonalCustomerICCIDStore) CreateOrUpdateLastUsed(ctx context.Context, customerID uint, iccid string) error {
record, err := s.GetByCustomerAndICCID(ctx, customerID, iccid)
if err == gorm.ErrRecordNotFound {
// 不存在,创建新记录
iccid19 := iccid
if len(iccid) == 20 {
iccid19 = iccid[:19]
}
newRecord := &model.PersonalCustomerICCID{
CustomerID: customerID,
ICCID: iccid,
Status: 1, // 启用
ICCID19: iccid19,
Status: 1,
}
return s.Create(ctx, newRecord)
} else if err != nil {
return err
}
// 存在,更新最后使用时间
return s.UpdateLastUsedAt(ctx, record.ID)
}