From c250a476510545e020ed6aee4c4503c4cbe95329 Mon Sep 17 00:00:00 2001 From: huang Date: Thu, 23 Apr 2026 16:58:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8D=A1=E4=B8=8D=E6=BF=80=E6=B4=BB=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/device/service.go | 24 +++---- internal/service/enterprise_card/service.go | 6 +- internal/service/iot_card/service.go | 4 +- internal/store/postgres/iot_card_store.go | 70 ++++++++++++++++++++- 4 files changed, 82 insertions(+), 22 deletions(-) diff --git a/internal/service/device/service.go b/internal/service/device/service.go index cd97d25..ebffad7 100644 --- a/internal/service/device/service.go +++ b/internal/service/device/service.go @@ -881,13 +881,11 @@ func (s *Service) StopDevice(ctx context.Context, deviceID uint) (*dto.DeviceSus } now := time.Now() - if dbErr := s.db.WithContext(ctx).Model(&model.IotCard{}). - Where("id = ?", card.ID). - Updates(map[string]any{ - "network_status": constants.NetworkStatusOffline, - "stopped_at": now, - "stop_reason": constants.StopReasonManual, - }).Error; dbErr != nil { + if dbErr := s.iotCardStore.UpdateFields(ctx, card.ID, map[string]any{ + "network_status": constants.NetworkStatusOffline, + "stopped_at": now, + "stop_reason": constants.StopReasonManual, + }); dbErr != nil { log.Error("设备停机-更新卡状态失败", zap.Uint("card_id", card.ID), zap.Error(dbErr)) @@ -986,13 +984,11 @@ func (s *Service) StartDevice(ctx context.Context, deviceID uint) error { } now := time.Now() - if dbErr := s.db.WithContext(ctx).Model(&model.IotCard{}). - Where("id = ?", card.ID). - Updates(map[string]any{ - "network_status": constants.NetworkStatusOnline, - "resumed_at": now, - "stop_reason": "", - }).Error; dbErr != nil { + if dbErr := s.iotCardStore.UpdateFields(ctx, card.ID, map[string]any{ + "network_status": constants.NetworkStatusOnline, + "resumed_at": now, + "stop_reason": "", + }); dbErr != nil { log.Error("设备复机-更新卡状态失败", zap.Uint("card_id", card.ID), zap.Error(dbErr)) diff --git a/internal/service/enterprise_card/service.go b/internal/service/enterprise_card/service.go index a76c71d..ca17dce 100644 --- a/internal/service/enterprise_card/service.go +++ b/internal/service/enterprise_card/service.go @@ -373,7 +373,7 @@ func (s *Service) updateCardNetworkStatus(ctx context.Context, enterpriseID, car return errors.New(errors.CodeForbidden, "无权限操作此卡") } - return s.db.WithContext(ctx).Model(&model.IotCard{}). - Where("id = ?", cardID). - Update("network_status", networkStatus).Error + return s.iotCardStore.UpdateFields(ctx, cardID, map[string]any{ + "network_status": networkStatus, + }) } diff --git a/internal/service/iot_card/service.go b/internal/service/iot_card/service.go index 36f01e4..3e1d2b3 100644 --- a/internal/service/iot_card/service.go +++ b/internal/service/iot_card/service.go @@ -877,9 +877,7 @@ func (s *Service) RefreshCardDataFromGateway(ctx context.Context, iccid string) } } - if err := s.db.WithContext(ctx).Model(&model.IotCard{}). - Where("id = ?", card.ID). - Updates(updates).Error; err != nil { + if err := s.iotCardStore.UpdateFields(ctx, card.ID, updates); err != nil { return errors.Wrap(errors.CodeInternalError, err, "更新卡数据失败") } diff --git a/internal/store/postgres/iot_card_store.go b/internal/store/postgres/iot_card_store.go index f002e78..27b2c8f 100644 --- a/internal/store/postgres/iot_card_store.go +++ b/internal/store/postgres/iot_card_store.go @@ -165,11 +165,77 @@ func (s *IotCardStore) Update(ctx context.Context, card *model.IotCard) error { return s.db.WithContext(ctx).Save(card).Error } -// UpdateFields 按 ID 部分更新卡字段,供轮询系统的 Handler 使用 +// UpdateFields 按 ID 部分更新卡字段 +// 当更新包含 network_status/real_name_status/card_category 时,自动同步 activation_status func (s *IotCardStore) UpdateFields(ctx context.Context, cardID uint, fields map[string]any) error { + if len(fields) == 0 { + return nil + } + + updates := make(map[string]any, len(fields)+1) + for k, v := range fields { + updates[k] = v + } + + if shouldSyncActivationStatus(fields) { + updates["activation_status"] = buildActivationStatusExpr(fields) + } + return s.db.WithContext(ctx).Model(&model.IotCard{}). Where("id = ?", cardID). - Updates(fields).Error + Updates(updates).Error +} + +func shouldSyncActivationStatus(fields map[string]any) bool { + if fields == nil { + return false + } + if _, ok := fields["network_status"]; ok { + return true + } + if _, ok := fields["real_name_status"]; ok { + return true + } + if _, ok := fields["card_category"]; ok { + return true + } + return false +} + +// buildActivationStatusExpr 构造激活状态表达式(与本次更新同 SQL 执行) +// 规则:开机 且(行业卡 或 已实名)=> 已激活;否则未激活 +// 若本次未更新某字段,则回退使用该字段当前值。 +func buildActivationStatusExpr(fields map[string]any) any { + var networkStatus any + var cardCategory any + var realNameStatus any + + if v, ok := fields["network_status"]; ok { + networkStatus = v + } + if v, ok := fields["card_category"]; ok { + cardCategory = v + } + if v, ok := fields["real_name_status"]; ok { + realNameStatus = v + } + + return gorm.Expr(` + CASE + WHEN COALESCE(?, network_status) = ? + AND (COALESCE(?, card_category) = ? OR COALESCE(?, real_name_status) = ?) + THEN ? + ELSE ? + END`, + networkStatus, + constants.NetworkStatusOnline, + cardCategory, + constants.CardCategoryIndustry, + realNameStatus, + constants.RealNameStatusVerified, + constants.ActivationStatusActive, + constants.ActivationStatusInactive, + ) } // UpdatePollingStatus 更新卡的轮询启用状态