This commit is contained in:
2026-04-30 15:49:40 +08:00
parent 5425e2ce19
commit 948243b13d
11 changed files with 703 additions and 178 deletions

View File

@@ -3610,6 +3610,14 @@ components:
description: 本次开机运行时间(秒)
nullable: true
type: string
signal_bad_reason:
description: 信号较弱时的怀疑原因提示
nullable: true
type: string
signal_quality:
description: 信号综合判断(信号很好/信号正常/信号较弱/信号很差/暂无数据)
nullable: true
type: string
sinr:
description: 信噪比(dB)
nullable: true
@@ -3906,6 +3914,14 @@ components:
description: 设备本次开机运行时间(秒)
nullable: true
type: string
signal_bad_reason:
description: 信号较弱时的怀疑原因提示
nullable: true
type: string
signal_quality:
description: 信号综合判断(信号很好/信号正常/信号较弱/信号很差/暂无数据)
nullable: true
type: string
sinr:
description: 信噪比(dB)
nullable: true
@@ -13578,9 +13594,11 @@ paths:
- `device_name`: 设备名称
- `device_model`: 设备型号
- `device_type`: 设备类型
- `max_sim_slots`: 最大插槽数默认4
- `imei`: 设备 IMEI
- `manufacturer`: 制造商
- `iccid_1` ~ `iccid_4`: 绑定的卡 ICCID卡必须已存在且未绑定
- `max_sim_slots`: 最大插槽数默认4范围1-4
- `iccid_1` ~ `iccid_4`: 固定槽位的卡 ICCID`iccid_1`=槽1`iccid_2`=槽2`iccid_3`=槽3`iccid_4`=槽4卡必须已存在且未绑定
- 槽位规则:中间留空不会触发前移补位;如果 `max_sim_slots=2`,则 `iccid_3`、`iccid_4` 必须留空,否则该行失败
- 列格式:设置为文本格式(避免长数字被转为科学记数法)
requestBody:
content:

View File

@@ -641,6 +641,8 @@ func mapDeviceGatewayInfoToClientInfo(g *dto.DeviceGatewayInfo) *dto.DeviceRealt
v := int64(*g.Sinr)
info.Sinr = &v
}
info.SignalQuality = g.SignalQuality
info.SignalBadReason = g.SignalBadReason
info.SSID = g.SSID
info.WifiEnabled = g.WifiEnabled
info.WifiPassword = g.WifiPassword

View File

@@ -184,6 +184,8 @@ type DeviceGatewayInfo struct {
Rsrq *int `json:"rsrq,omitempty" description:"参考信号接收质量(dB)"`
Rssi *string `json:"rssi,omitempty" description:"接收信号强度"`
Sinr *int `json:"sinr,omitempty" description:"信噪比(dB)"`
SignalQuality *string `json:"signal_quality,omitempty" description:"信号综合判断(信号很好/信号正常/信号较弱/信号很差/暂无数据)"`
SignalBadReason *string `json:"signal_bad_reason,omitempty" description:"信号较弱时的怀疑原因提示"`
// WiFi 相关
SSID *string `json:"ssid,omitempty" description:"WiFi热点名称"`

View File

@@ -93,6 +93,8 @@ type DeviceRealtimeInfo struct {
Rsrq *int64 `json:"rsrq,omitempty" description:"参考信号接收质量(dB)"`
Rssi *string `json:"rssi,omitempty" description:"接收信号强度"`
Sinr *int64 `json:"sinr,omitempty" description:"信噪比(dB)"`
SignalQuality *string `json:"signal_quality,omitempty" description:"信号综合判断(信号很好/信号正常/信号较弱/信号很差/暂无数据)"`
SignalBadReason *string `json:"signal_bad_reason,omitempty" description:"信号较弱时的怀疑原因提示"`
// === WiFi 相关 ===
SSID *string `json:"ssid,omitempty" description:"WiFi热点名称"`

View File

@@ -481,6 +481,9 @@ func mapSyncRespToDeviceGatewayInfo(r *gateway.SyncDeviceInfoResp) *dto.DeviceGa
IMSI: flexStrPtr(r.IMSI),
Status: flexIntPtr(r.Status),
}
signalQuality, signalBadReason := buildSignalSummary(info.Rsrp, info.Rsrq, info.Rssi, info.Sinr)
info.SignalQuality = strPtr(signalQuality)
info.SignalBadReason = strPtr(signalBadReason)
if r.BatteryLevel != nil {
n := int(*r.BatteryLevel)
info.BatteryLevel = &n

View File

@@ -0,0 +1,159 @@
package asset
import "strings"
const (
signalQualityExcellent = "信号很好"
signalQualityNormal = "信号正常"
signalQualityWeak = "信号较弱"
signalQualityPoor = "信号很差"
signalQualityNoData = "暂无数据"
signalReasonWeakCoverage = "怀疑当前位置信号覆盖较弱"
signalReasonInterference = "怀疑周围干扰较多"
signalReasonBlocked = "怀疑设备所处位置遮挡较强"
signalReasonUnstable = "怀疑网络环境不稳定"
signalReasonUnknown = "暂时无法判断"
)
// buildSignalSummary 根据四个原始信号指标输出统一的信号摘要。
func buildSignalSummary(rsrp, rsrq *int, rssi *string, sinr *int) (string, string) {
score := 0
seen := 0
coverageBad := false
interferenceBad := false
blockedBad := false
unstableBad := false
if rsrp != nil {
seen++
switch {
case *rsrp >= -90:
score += 2
case *rsrp >= -105:
score += 1
case *rsrp >= -115:
score -= 1
coverageBad = true
default:
score -= 2
coverageBad = true
blockedBad = true
}
}
if rsrq != nil {
seen++
switch {
case *rsrq >= -10:
score += 2
case *rsrq >= -15:
score += 1
case *rsrq >= -18:
score -= 1
interferenceBad = true
default:
score -= 2
interferenceBad = true
unstableBad = true
}
}
if sinr != nil {
seen++
switch {
case *sinr >= 20:
score += 2
case *sinr >= 13:
score += 1
case *sinr >= 5:
score -= 1
interferenceBad = true
default:
score -= 2
interferenceBad = true
unstableBad = true
}
}
if rssiValue, ok := parseRSSI(rssi); ok {
seen++
switch {
case rssiValue >= -65:
score += 2
case rssiValue >= -75:
score += 1
case rssiValue >= -85:
score -= 1
coverageBad = true
default:
score -= 2
coverageBad = true
blockedBad = true
}
}
if seen == 0 {
return signalQualityNoData, signalReasonUnknown
}
quality := signalQualityNormal
switch {
case score >= 6:
quality = signalQualityExcellent
case score >= 2:
quality = signalQualityNormal
case score >= -2:
quality = signalQualityWeak
default:
quality = signalQualityPoor
}
if quality == signalQualityExcellent || quality == signalQualityNormal {
return quality, signalReasonUnknown
}
switch {
case blockedBad:
return quality, signalReasonBlocked
case interferenceBad:
return quality, signalReasonInterference
case coverageBad:
return quality, signalReasonWeakCoverage
case unstableBad:
return quality, signalReasonUnstable
default:
return quality, signalReasonUnknown
}
}
func parseRSSI(rssi *string) (int, bool) {
if rssi == nil {
return 0, false
}
value := strings.TrimSpace(*rssi)
if value == "" {
return 0, false
}
sign := 1
number := 0
started := false
for i, ch := range value {
if ch == '-' && i == 0 {
sign = -1
continue
}
if ch >= '0' && ch <= '9' {
started = true
number = number*10 + int(ch-'0')
continue
}
if started {
break
}
}
if !started {
return 0, false
}
return sign * number, true
}

View File

@@ -7,6 +7,7 @@ import (
var snapshotFieldSet = map[string]struct{}{
"id": {},
"virtual_no": {},
"device_virtual_no": {},
"iccid": {},
"imei": {},
"sn": {},
@@ -251,18 +252,26 @@ func getOperationFieldDesc(operationType string) map[string]string {
case "card_allocate", "device_allocate":
return mergeFieldDesc(common, map[string]string{
"target_shop_id": "目标店铺ID",
"target_shop_name": "目标店铺名称",
"to_shop_id": "目标店铺ID",
"to_shop_name": "目标店铺名称",
"device_ids": "设备ID列表",
"device_virtual_nos": "设备虚拟号列表",
"card_ids": "卡ID列表",
"iccids": "卡ICCID列表",
"selection_type": "选择方式",
"new_status": "变更后状态",
})
case "card_recall", "device_recall":
return mergeFieldDesc(common, map[string]string{
"source_shop_id": "来源店铺ID",
"target_shop": "回收后归属店铺(空表示平台库存)",
"source_shop_name": "来源店铺名称",
"target_shop": "回收后归属店铺ID空表示平台库存",
"target_shop_name": "回收后归属店铺名称",
"device_ids": "设备ID列表",
"device_virtual_nos": "设备虚拟号列表",
"card_ids": "卡ID列表",
"iccids": "卡ICCID列表",
"selection_type": "选择方式",
"new_status": "变更后状态",
})
@@ -270,13 +279,20 @@ func getOperationFieldDesc(operationType string) map[string]string {
return map[string]string{
"binding_id": "绑定记录ID",
"slot_position": "插槽位置",
"device_id": "设备ID",
"device_virtual_no": "设备虚拟号",
"device_imei": "设备IMEI",
"iot_card_id": "卡ID",
"iccid": "卡ICCID",
}
case "device_unbind_card":
return map[string]string{
"binding_id": "绑定记录ID",
"device_id": "设备ID",
"device_virtual_no": "设备虚拟号",
"device_imei": "设备IMEI",
"iot_card_id": "卡ID",
"iccid": "卡ICCID",
"unbind": "是否执行解绑",
}
case "device_stop", "device_start":
@@ -297,6 +313,8 @@ func getOperationFieldDesc(operationType string) map[string]string {
}
case "device_switch_card":
return map[string]string{
"device_virtual_no": "设备虚拟号",
"device_imei": "设备IMEI",
"target_iccid": "目标ICCID",
}
case "device_switch_mode":
@@ -312,9 +330,11 @@ func getOperationFieldDesc(operationType string) map[string]string {
"action": "设备恢复出厂动作",
}
case "asset_polling_status", "card_polling_status":
return map[string]string{
return mergeFieldDesc(common, map[string]string{
"enable_polling": "轮询开关true启用/false禁用",
}
"card_ids": "卡ID列表",
"iccids": "卡ICCID列表",
})
case "asset_realname_policy", "card_realname_policy":
return map[string]string{
"realname_policy": "实名策略none/before_order/after_order",
@@ -324,10 +344,13 @@ func getOperationFieldDesc(operationType string) map[string]string {
"real_name_status": "实名状态0未实名/1已实名",
}
case "asset_deactivate", "card_delete", "card_batch_delete", "device_delete":
return map[string]string{
return mergeFieldDesc(common, map[string]string{
"asset_status": "资产状态",
"deleted": "是否删除",
}
"iccids": "卡ICCID列表",
"device_virtual_nos": "设备虚拟号列表",
"device_imei": "设备IMEI",
})
case "device_import_task_create", "iot_card_import_task_create":
return map[string]string{
"batch_no": "批次号",
@@ -337,7 +360,19 @@ func getOperationFieldDesc(operationType string) map[string]string {
"card_category": "卡类型(仅卡导入)",
}
default:
return common
return mergeFieldDesc(common, map[string]string{
"card_id": "卡ID",
"iccid": "卡ICCID",
"iccids": "卡ICCID列表",
"device_id": "设备ID",
"device_virtual_no": "设备虚拟号",
"device_virtual_nos": "设备虚拟号列表",
"device_imei": "设备IMEI",
"device_sn": "设备SN",
"shop_name": "店铺名称",
"target_shop_name": "目标店铺名称",
"source_shop_name": "来源店铺名称",
})
}
}

View File

@@ -2,6 +2,7 @@ package device
import (
"context"
"fmt"
"strings"
"github.com/break/junhong_cmp_fiber/internal/model"
@@ -62,6 +63,8 @@ func (s *Service) logDeviceOperation(
snapshot := deviceSnapshot(device)
beforeContent := stripDeviceOperationMeta(beforeData)
afterContent := stripDeviceOperationMeta(afterData)
enrichDeviceOperationContent(beforeContent, beforeData)
enrichDeviceOperationContent(afterContent, afterData)
params.BeforeData, params.AfterData = assetAuditSvc.WrapOperationContent(beforeContent, afterContent, snapshot)
if device != nil {
@@ -81,7 +84,7 @@ func stripDeviceOperationMeta(data map[string]any) map[string]any {
}
out := make(map[string]any, len(data))
for k, v := range data {
if k == "device" || k == "card" || k == "cards" || k == "asset_snapshot" || k == "operation_content" {
if k == "device" || k == "devices" || k == "card" || k == "cards" || k == "asset_snapshot" || k == "operation_content" {
continue
}
out[k] = v
@@ -92,6 +95,116 @@ func stripDeviceOperationMeta(data map[string]any) map[string]any {
return out
}
func enrichDeviceOperationContent(content map[string]any, raw map[string]any) {
if len(raw) == 0 {
return
}
if deviceRaw, ok := raw["device"].(map[string]any); ok {
mergeReadableDeviceFields(content, deviceRaw)
}
if cardRaw, ok := raw["card"].(map[string]any); ok {
mergeReadableCardFields(content, cardRaw)
}
switch devicesRaw := raw["devices"].(type) {
case []map[string]any:
deviceIDs, virtualNos := collectDeviceReadableLists(devicesRaw)
if len(deviceIDs) > 0 && content["device_ids"] == nil {
content["device_ids"] = deviceIDs
}
if len(virtualNos) > 0 && content["device_virtual_nos"] == nil {
content["device_virtual_nos"] = virtualNos
}
case []any:
devices := make([]map[string]any, 0, len(devicesRaw))
for _, item := range devicesRaw {
deviceMap, ok := item.(map[string]any)
if !ok {
continue
}
devices = append(devices, deviceMap)
}
deviceIDs, virtualNos := collectDeviceReadableLists(devices)
if len(deviceIDs) > 0 && content["device_ids"] == nil {
content["device_ids"] = deviceIDs
}
if len(virtualNos) > 0 && content["device_virtual_nos"] == nil {
content["device_virtual_nos"] = virtualNos
}
}
}
func mergeReadableDeviceFields(content map[string]any, device map[string]any) {
if len(device) == 0 {
return
}
if _, ok := content["device_id"]; !ok {
if v, exists := device["id"]; exists {
content["device_id"] = v
}
}
if _, ok := content["device_virtual_no"]; !ok {
if v := stringifyDeviceAuditValue(device["virtual_no"]); v != "" {
content["device_virtual_no"] = v
}
}
if _, ok := content["device_imei"]; !ok {
if v := stringifyDeviceAuditValue(device["imei"]); v != "" {
content["device_imei"] = v
}
}
if _, ok := content["device_sn"]; !ok {
if v := stringifyDeviceAuditValue(device["sn"]); v != "" {
content["device_sn"] = v
}
}
}
func mergeReadableCardFields(content map[string]any, card map[string]any) {
if len(card) == 0 {
return
}
if _, ok := content["iot_card_id"]; !ok {
if v, exists := card["id"]; exists {
content["iot_card_id"] = v
}
}
if _, ok := content["iccid"]; !ok {
if v := stringifyDeviceAuditValue(card["iccid"]); v != "" {
content["iccid"] = v
}
}
}
func collectDeviceReadableLists(devices []map[string]any) ([]any, []string) {
deviceIDs := make([]any, 0, len(devices))
virtualNos := make([]string, 0, len(devices))
for _, device := range devices {
if id, ok := device["id"]; ok {
deviceIDs = append(deviceIDs, id)
}
if virtualNo := stringifyDeviceAuditValue(device["virtual_no"]); virtualNo != "" {
virtualNos = append(virtualNos, virtualNo)
}
}
return deviceIDs, virtualNos
}
func stringifyDeviceAuditValue(v any) string {
switch vv := v.(type) {
case string:
return vv
case fmt.Stringer:
return vv.String()
default:
if vv == nil {
return ""
}
return fmt.Sprint(vv)
}
}
func deviceSnapshot(device *model.Device) map[string]any {
if device == nil {
return nil
@@ -99,6 +212,8 @@ func deviceSnapshot(device *model.Device) map[string]any {
return map[string]any{
"id": device.ID,
"virtual_no": device.VirtualNo,
"imei": device.IMEI,
"sn": device.SN,
"shop_id": device.ShopID,
"status": device.Status,
"series_id": device.SeriesID,

View File

@@ -450,13 +450,16 @@ func (s *Service) AllocateDevices(ctx context.Context, req *dto.AllocateDevicesR
}, nil
}
beforeData := make([]map[string]any, 0, len(devices))
for _, device := range devices {
beforeData = append(beforeData, deviceSnapshot(device))
}
newStatus := constants.DeviceStatusDistributed
targetShopID := req.TargetShopID
targetShopName := s.getAuditShopName(ctx, &targetShopID)
shopMap := s.loadShopData(ctx, devices)
beforeData := make([]map[string]any, 0, len(devices))
for _, device := range devices {
snapshot := deviceSnapshot(device)
snapshot["shop_name"] = deviceShopMapValue(shopMap, device.ShopID)
beforeData = append(beforeData, snapshot)
}
err = s.db.Transaction(func(tx *gorm.DB) error {
txDeviceStore := postgres.NewDeviceStore(tx, nil)
@@ -493,6 +496,7 @@ func (s *Service) AllocateDevices(ctx context.Context, req *dto.AllocateDevicesR
map[string]any{"devices": beforeData},
map[string]any{
"target_shop_id": req.TargetShopID,
"target_shop_name": targetShopName,
"failed_items": failedItems,
},
len(devices),
@@ -512,6 +516,7 @@ func (s *Service) AllocateDevices(ctx context.Context, req *dto.AllocateDevicesR
map[string]any{"devices": beforeData},
map[string]any{
"target_shop_id": req.TargetShopID,
"target_shop_name": targetShopName,
"device_ids": deviceIDs,
"failed_items": failedItems,
},
@@ -629,11 +634,6 @@ func (s *Service) RecallDevices(ctx context.Context, req *dto.RecallDevicesReque
}, nil
}
beforeData := make([]map[string]any, 0, len(devices))
for _, device := range devices {
beforeData = append(beforeData, deviceSnapshot(device))
}
var newShopID *uint
var newStatus int
if isPlatform {
@@ -643,6 +643,14 @@ func (s *Service) RecallDevices(ctx context.Context, req *dto.RecallDevicesReque
newShopID = operatorShopID
newStatus = constants.DeviceStatusDistributed
}
targetShopName := s.getAuditRecallTargetShopName(ctx, newShopID)
shopMap := s.loadShopData(ctx, devices)
beforeData := make([]map[string]any, 0, len(devices))
for _, device := range devices {
snapshot := deviceSnapshot(device)
snapshot["shop_name"] = deviceShopMapValue(shopMap, device.ShopID)
beforeData = append(beforeData, snapshot)
}
err = s.db.Transaction(func(tx *gorm.DB) error {
txDeviceStore := postgres.NewDeviceStore(tx, nil)
@@ -686,6 +694,7 @@ func (s *Service) RecallDevices(ctx context.Context, req *dto.RecallDevicesReque
map[string]any{
"device_ids": deviceIDs,
"failed_items": failedItems,
"target_shop_name": targetShopName,
},
len(devices),
len(deviceIDs),
@@ -706,6 +715,7 @@ func (s *Service) RecallDevices(ctx context.Context, req *dto.RecallDevicesReque
"device_ids": deviceIDs,
"failed_items": failedItems,
"target_shop": newShopID,
"target_shop_name": targetShopName,
},
len(devices),
len(deviceIDs),
@@ -748,6 +758,31 @@ func (s *Service) validateDirectSubordinate(ctx context.Context, operatorShopID
return nil
}
func (s *Service) getAuditShopName(ctx context.Context, shopID *uint) string {
if shopID == nil || *shopID == 0 {
return ""
}
var shop model.Shop
if err := s.db.WithContext(ctx).Unscoped().First(&shop, *shopID).Error; err != nil {
return ""
}
return shop.ShopName
}
func (s *Service) getAuditRecallTargetShopName(ctx context.Context, shopID *uint) string {
if shopID == nil {
return "平台库存"
}
return s.getAuditShopName(ctx, shopID)
}
func deviceShopMapValue(shopMap map[uint]string, shopID *uint) string {
if shopID == nil {
return ""
}
return shopMap[*shopID]
}
func (s *Service) loadShopData(ctx context.Context, devices []*model.Device) map[uint]string {
shopIDs := make([]uint, 0)
shopIDSet := make(map[uint]bool)

View File

@@ -2,6 +2,7 @@ package iot_card
import (
"context"
"fmt"
"github.com/break/junhong_cmp_fiber/internal/model"
assetAuditSvc "github.com/break/junhong_cmp_fiber/internal/service/asset_audit"
@@ -55,7 +56,11 @@ func normalizeCardAuditPayload(beforeData, afterData map[string]any) (map[string
}
}
}
return assetAuditSvc.WrapOperationContent(stripCardOperationMeta(beforeData), stripCardOperationMeta(afterData), snapshot)
beforeContent := stripCardOperationMeta(beforeData)
afterContent := stripCardOperationMeta(afterData)
enrichCardOperationContent(beforeContent, beforeData)
enrichCardOperationContent(afterContent, afterData)
return assetAuditSvc.WrapOperationContent(beforeContent, afterContent, snapshot)
}
func stripCardOperationMeta(data map[string]any) map[string]any {
@@ -75,6 +80,121 @@ func stripCardOperationMeta(data map[string]any) map[string]any {
return out
}
func enrichCardOperationContent(content map[string]any, raw map[string]any) {
if len(raw) == 0 {
return
}
if cardRaw, ok := raw["card"].(map[string]any); ok {
mergeReadableCardFields(content, cardRaw)
}
if deviceRaw, ok := raw["device"].(map[string]any); ok {
mergeReadableDeviceFields(content, deviceRaw)
}
switch cardsRaw := raw["cards"].(type) {
case []map[string]any:
cardIDs, iccids := collectCardReadableLists(cardsRaw)
if len(cardIDs) > 0 && content["card_ids"] == nil {
content["card_ids"] = cardIDs
}
if len(iccids) > 0 && content["iccids"] == nil {
content["iccids"] = iccids
}
case []any:
cards := make([]map[string]any, 0, len(cardsRaw))
for _, item := range cardsRaw {
cardMap, ok := item.(map[string]any)
if !ok {
continue
}
cards = append(cards, cardMap)
}
cardIDs, iccids := collectCardReadableLists(cards)
if len(cardIDs) > 0 && content["card_ids"] == nil {
content["card_ids"] = cardIDs
}
if len(iccids) > 0 && content["iccids"] == nil {
content["iccids"] = iccids
}
}
}
func mergeReadableCardFields(content map[string]any, card map[string]any) {
if len(card) == 0 {
return
}
if _, ok := content["card_id"]; !ok {
if v, exists := card["id"]; exists {
content["card_id"] = v
}
}
if _, ok := content["iccid"]; !ok {
if v := stringifyCardAuditValue(card["iccid"]); v != "" {
content["iccid"] = v
}
}
if _, ok := content["device_virtual_no"]; !ok {
if v := stringifyCardAuditValue(card["device_virtual_no"]); v != "" {
content["device_virtual_no"] = v
}
}
}
func mergeReadableDeviceFields(content map[string]any, device map[string]any) {
if len(device) == 0 {
return
}
if _, ok := content["device_id"]; !ok {
if v, exists := device["id"]; exists {
content["device_id"] = v
}
}
if _, ok := content["device_virtual_no"]; !ok {
if v := stringifyCardAuditValue(device["virtual_no"]); v != "" {
content["device_virtual_no"] = v
}
}
if _, ok := content["device_imei"]; !ok {
if v := stringifyCardAuditValue(device["imei"]); v != "" {
content["device_imei"] = v
}
}
if _, ok := content["device_sn"]; !ok {
if v := stringifyCardAuditValue(device["sn"]); v != "" {
content["device_sn"] = v
}
}
}
func collectCardReadableLists(cards []map[string]any) ([]any, []string) {
cardIDs := make([]any, 0, len(cards))
iccids := make([]string, 0, len(cards))
for _, card := range cards {
if id, ok := card["id"]; ok {
cardIDs = append(cardIDs, id)
}
if iccid := stringifyCardAuditValue(card["iccid"]); iccid != "" {
iccids = append(iccids, iccid)
}
}
return cardIDs, iccids
}
func stringifyCardAuditValue(v any) string {
switch vv := v.(type) {
case string:
return vv
case fmt.Stringer:
return vv.String()
default:
if vv == nil {
return ""
}
return fmt.Sprint(vv)
}
}
func cardSnapshot(card *model.IotCard) map[string]any {
if card == nil {
return nil
@@ -82,6 +202,7 @@ func cardSnapshot(card *model.IotCard) map[string]any {
return map[string]any{
"id": card.ID,
"iccid": card.ICCID,
"device_virtual_no": card.DeviceVirtualNo,
"shop_id": card.ShopID,
"status": card.Status,
"series_id": card.SeriesID,

View File

@@ -494,12 +494,15 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
}
}
shopMap := s.loadShopNames(ctx, cards)
targetShopName := s.getAuditShopName(ctx, &req.ToShopID)
beforeData := make([]map[string]any, 0, len(cards))
for _, card := range cards {
beforeData = append(beforeData, map[string]any{
"id": card.ID,
"iccid": card.ICCID,
"shop_id": card.ShopID,
"shop_name": shopMapValue(shopMap, card.ShopID),
"status": card.Status,
})
}
@@ -515,6 +518,7 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
},
AfterData: map[string]any{
"to_shop_id": req.ToShopID,
"to_shop_name": targetShopName,
"new_status": newStatus,
"failed_items": failedItems,
},
@@ -702,12 +706,15 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
}
}
shopMap := s.loadShopNames(ctx, successCards)
targetShopName := s.getAuditRecallTargetShopName(ctx, newShopID)
beforeData := make([]map[string]any, 0, len(successCards))
for _, card := range successCards {
beforeData = append(beforeData, map[string]any{
"id": card.ID,
"iccid": card.ICCID,
"shop_id": card.ShopID,
"shop_name": shopMapValue(shopMap, card.ShopID),
"status": card.Status,
})
}
@@ -723,6 +730,7 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
},
AfterData: map[string]any{
"to_shop_id": newShopID,
"target_shop_name": targetShopName,
"new_status": newStatus,
"failed_items": failedItems,
},
@@ -774,6 +782,31 @@ func (s *Service) validateDirectSubordinate(ctx context.Context, operatorShopID
return nil
}
func (s *Service) getAuditShopName(ctx context.Context, shopID *uint) string {
if shopID == nil || *shopID == 0 {
return ""
}
var shop model.Shop
if err := s.db.WithContext(ctx).Unscoped().First(&shop, *shopID).Error; err != nil {
return ""
}
return shop.ShopName
}
func (s *Service) getAuditRecallTargetShopName(ctx context.Context, shopID *uint) string {
if shopID == nil {
return "平台库存"
}
return s.getAuditShopName(ctx, shopID)
}
func shopMapValue(shopMap map[uint]string, shopID *uint) string {
if shopID == nil {
return ""
}
return shopMap[*shopID]
}
func (s *Service) getCardsForAllocation(ctx context.Context, req *dto.AllocateStandaloneCardsRequest, operatorShopID *uint) ([]*model.IotCard, error) {
switch req.SelectionType {
case dto.SelectionTypeList: