新增字段
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m7s

This commit is contained in:
2026-05-12 14:10:48 +08:00
parent 8f3a68a673
commit a5388446d3
13 changed files with 278 additions and 182 deletions

View File

@@ -145,7 +145,7 @@ func (s *Service) buildDeviceResolveResponse(ctx context.Context, device *model.
AssetID: device.ID,
VirtualNo: device.VirtualNo,
Status: device.Status,
StatusName: constants.GetIotCardStatusName(device.Status),
StatusName: constants.GetDeviceStatusName(device.Status),
BatchNo: device.BatchNo,
ShopID: device.ShopID,
SeriesID: device.SeriesID,
@@ -226,6 +226,13 @@ func (s *Service) buildDeviceResolveResponse(ctx context.Context, device *model.
// 查当前主套餐
s.fillPackageInfo(ctx, resp, "device", device.ID)
activationStatuses, err := s.deviceStore.GetActivationStatusMap(ctx, []uint{device.ID})
if err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询设备激活状态失败")
}
resp.ActivationStatus = activationStatuses[device.ID]
resp.ActivationStatusName = constants.GetActivationStatusName(resp.ActivationStatus)
// 查 shop 名称
s.fillShopName(ctx, resp)
@@ -241,32 +248,33 @@ func (s *Service) buildDeviceResolveResponse(ctx context.Context, device *model.
// buildCardResolveResponse 构建卡类型的资产解析响应
func (s *Service) buildCardResolveResponse(ctx context.Context, card *model.IotCard) (*dto.AssetResolveResponse, error) {
resp := &dto.AssetResolveResponse{
AssetType: "card",
AssetID: card.ID,
VirtualNo: card.VirtualNo,
Status: card.Status,
StatusName: constants.GetIotCardStatusName(card.Status),
BatchNo: card.BatchNo,
ShopID: card.ShopID,
SeriesID: card.SeriesID,
ActivatedAt: card.ActivatedAt,
CreatedAt: card.CreatedAt,
UpdatedAt: card.UpdatedAt,
RealNameStatus: card.RealNameStatus,
RealNameAt: card.FirstRealnameAt,
RealnamePolicy: card.RealnamePolicy,
NetworkStatus: card.NetworkStatus,
GatewayExtend: card.GatewayExtend,
ICCID: card.ICCID,
CarrierID: card.CarrierID,
CarrierType: card.CarrierType,
CarrierName: card.CarrierName,
MSISDN: card.MSISDN,
IMSI: card.IMSI,
CardCategory: card.CardCategory,
Supplier: card.Supplier,
ActivationStatus: card.ActivationStatus,
EnablePolling: card.EnablePolling,
AssetType: "card",
AssetID: card.ID,
VirtualNo: card.VirtualNo,
Status: card.Status,
StatusName: constants.GetIotCardStatusName(card.Status),
BatchNo: card.BatchNo,
ShopID: card.ShopID,
SeriesID: card.SeriesID,
ActivatedAt: card.ActivatedAt,
CreatedAt: card.CreatedAt,
UpdatedAt: card.UpdatedAt,
RealNameStatus: card.RealNameStatus,
RealNameAt: card.FirstRealnameAt,
RealnamePolicy: card.RealnamePolicy,
NetworkStatus: card.NetworkStatus,
GatewayExtend: card.GatewayExtend,
ICCID: card.ICCID,
CarrierID: card.CarrierID,
CarrierType: card.CarrierType,
CarrierName: card.CarrierName,
MSISDN: card.MSISDN,
IMSI: card.IMSI,
CardCategory: card.CardCategory,
Supplier: card.Supplier,
ActivationStatus: card.ActivationStatus,
ActivationStatusName: constants.GetActivationStatusName(card.ActivationStatus),
EnablePolling: card.EnablePolling,
}
// 查绑定设备

View File

@@ -93,6 +93,9 @@ func (s *Service) List(ctx context.Context, req *dto.ListDeviceRequest) (*dto.Li
if req.Status != nil {
filters["status"] = *req.Status
}
if req.ActivationStatus != nil {
filters["activation_status"] = *req.ActivationStatus
}
shopIDs, hasShopIDs := normalizeShopIDs(req.ShopIDs)
if hasShopIDs {
filters["shop_ids"] = shopIDs
@@ -129,14 +132,19 @@ func (s *Service) List(ctx context.Context, req *dto.ListDeviceRequest) (*dto.Li
shopMap := s.loadShopData(ctx, devices)
seriesMap := s.loadSeriesNames(ctx, devices)
bindingCounts, err := s.getBindingCounts(ctx, s.extractDeviceIDs(devices))
deviceIDs := s.extractDeviceIDs(devices)
bindingCounts, err := s.getBindingCounts(ctx, deviceIDs)
if err != nil {
return nil, err
}
activationStatuses, err := s.deviceStore.GetActivationStatusMap(ctx, deviceIDs)
if err != nil {
return nil, err
}
list := make([]*dto.DeviceResponse, 0, len(devices))
for _, device := range devices {
item := s.toDeviceResponse(device, shopMap, seriesMap, bindingCounts)
item := s.toDeviceResponse(device, shopMap, seriesMap, bindingCounts, activationStatuses)
list = append(list, item)
}
@@ -183,8 +191,12 @@ func (s *Service) Get(ctx context.Context, id uint) (*dto.DeviceResponse, error)
if err != nil {
return nil, err
}
activationStatuses, err := s.deviceStore.GetActivationStatusMap(ctx, []uint{device.ID})
if err != nil {
return nil, err
}
return s.toDeviceResponse(device, shopMap, seriesMap, bindingCounts), nil
return s.toDeviceResponse(device, shopMap, seriesMap, bindingCounts, activationStatuses), nil
}
// GetByDeviceNo 通过设备号获取设备详情
@@ -203,8 +215,12 @@ func (s *Service) GetByDeviceNo(ctx context.Context, deviceNo string) (*dto.Devi
if err != nil {
return nil, err
}
activationStatuses, err := s.deviceStore.GetActivationStatusMap(ctx, []uint{device.ID})
if err != nil {
return nil, err
}
return s.toDeviceResponse(device, shopMap, seriesMap, bindingCounts), nil
return s.toDeviceResponse(device, shopMap, seriesMap, bindingCounts, activationStatuses), nil
}
// GetByIdentifier 通过任意标识符获取设备详情
@@ -224,8 +240,12 @@ func (s *Service) GetByIdentifier(ctx context.Context, identifier string) (*dto.
if err != nil {
return nil, err
}
activationStatuses, err := s.deviceStore.GetActivationStatusMap(ctx, []uint{device.ID})
if err != nil {
return nil, err
}
return s.toDeviceResponse(device, shopMap, seriesMap, bindingCounts), nil
return s.toDeviceResponse(device, shopMap, seriesMap, bindingCounts, activationStatuses), nil
}
// GetDeviceByIdentifier 通过任意标识符获取设备模型(内部使用,不转为 DTO
@@ -884,31 +904,34 @@ func (s *Service) extractDeviceIDs(devices []*model.Device) []uint {
return ids
}
func (s *Service) toDeviceResponse(device *model.Device, shopMap map[uint]string, seriesMap map[uint]string, bindingCounts map[uint]int64) *dto.DeviceResponse {
func (s *Service) toDeviceResponse(device *model.Device, shopMap map[uint]string, seriesMap map[uint]string, bindingCounts map[uint]int64, activationStatuses map[uint]int) *dto.DeviceResponse {
activationStatus := activationStatuses[device.ID]
resp := &dto.DeviceResponse{
ID: device.ID,
VirtualNo: device.VirtualNo,
IMEI: device.IMEI,
SN: device.SN,
DeviceName: device.DeviceName,
DeviceModel: device.DeviceModel,
DeviceType: device.DeviceType,
MaxSimSlots: device.MaxSimSlots,
Manufacturer: device.Manufacturer,
BatchNo: device.BatchNo,
ShopID: device.ShopID,
Status: device.Status,
StatusName: s.getDeviceStatusName(device.Status),
BoundCardCount: int(bindingCounts[device.ID]),
SeriesID: device.SeriesID,
ActivatedAt: device.ActivatedAt,
CreatedAt: device.CreatedAt,
UpdatedAt: device.UpdatedAt,
OnlineStatus: device.OnlineStatus,
LastOnlineTime: device.LastOnlineTime,
SoftwareVersion: device.SoftwareVersion,
SwitchMode: device.SwitchMode,
LastGatewaySyncAt: device.LastGatewaySyncAt,
ID: device.ID,
VirtualNo: device.VirtualNo,
IMEI: device.IMEI,
SN: device.SN,
DeviceName: device.DeviceName,
DeviceModel: device.DeviceModel,
DeviceType: device.DeviceType,
MaxSimSlots: device.MaxSimSlots,
Manufacturer: device.Manufacturer,
BatchNo: device.BatchNo,
ShopID: device.ShopID,
Status: device.Status,
StatusName: constants.GetDeviceStatusName(device.Status),
ActivationStatus: activationStatus,
ActivationStatusName: constants.GetActivationStatusName(activationStatus),
BoundCardCount: int(bindingCounts[device.ID]),
SeriesID: device.SeriesID,
ActivatedAt: device.ActivatedAt,
CreatedAt: device.CreatedAt,
UpdatedAt: device.UpdatedAt,
OnlineStatus: device.OnlineStatus,
LastOnlineTime: device.LastOnlineTime,
SoftwareVersion: device.SoftwareVersion,
SwitchMode: device.SwitchMode,
LastGatewaySyncAt: device.LastGatewaySyncAt,
}
if device.ShopID != nil && *device.ShopID > 0 {
@@ -922,21 +945,6 @@ func (s *Service) toDeviceResponse(device *model.Device, shopMap map[uint]string
return resp
}
func (s *Service) getDeviceStatusName(status int) string {
switch status {
case constants.DeviceStatusInStock:
return "在库"
case constants.DeviceStatusDistributed:
return "已分销"
case constants.DeviceStatusActivated:
return "已激活"
case constants.DeviceStatusSuspended:
return "已停用"
default:
return "未知"
}
}
func (s *Service) buildAllocationRecords(devices []*model.Device, successDeviceIDs []uint, fromShopID *uint, toShopID uint, operatorID uint, allocationNo, remark string) []*model.AssetAllocationRecord {
successIDSet := make(map[uint]bool)
for _, id := range successDeviceIDs {
@@ -1312,6 +1320,7 @@ func hasDeviceSeriesBindingFilters(req *dto.BatchSetDeviceSeriesBindngRequest) b
return req.VirtualNo != "" ||
req.DeviceName != "" ||
req.Status != nil ||
req.ActivationStatus != nil ||
req.ShopID != nil ||
len(req.ShopIDs) > 0 ||
req.FilterSeriesID != nil ||
@@ -1355,6 +1364,9 @@ func buildDeviceSeriesBindingFilters(req *dto.BatchSetDeviceSeriesBindngRequest)
if req.Status != nil {
filters["status"] = *req.Status
}
if req.ActivationStatus != nil {
filters["activation_status"] = *req.ActivationStatus
}
shopIDs, hasShopIDs := normalizeShopIDs(req.ShopIDs)
if hasShopIDs {
filters["shop_ids"] = shopIDs

View File

@@ -604,23 +604,8 @@ func (s *ActivationService) syncCarrierStatusActivated(ctx context.Context, tx *
}
if usage.UsageType == constants.PackageUsageTypeDevice || usage.DeviceID > 0 || carrierType == constants.AssetTypeDevice {
deviceID := usage.DeviceID
if deviceID == 0 {
deviceID = carrierID
}
if deviceID == 0 {
return
}
if err := tx.WithContext(ctx).
Model(&model.Device{}).
Where("id = ? AND status < ?", deviceID, constants.DeviceStatusActivated).
Update("status", constants.DeviceStatusActivated).Error; err != nil {
s.logger.Warn("套餐激活后更新设备状态失败",
zap.Uint("device_id", deviceID),
zap.Uint("usage_id", usage.ID),
zap.Error(err))
}
// 设备激活状态由“生效中主套餐 + 任意绑定卡已实名”动态计算,
// 不再写入设备归属状态字段。
return
}