From 37e113bbf4510dbb259fda535584488a4d2f6c35 Mon Sep 17 00:00:00 2001 From: huang Date: Thu, 23 Apr 2026 15:33:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=9F=A5=E8=AF=A2=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/model/dto/order_dto.go | 3 +- internal/service/order/service.go | 83 ++++++++++++++++++++++++++ internal/store/postgres/order_store.go | 35 ++++++++--- 3 files changed, 113 insertions(+), 8 deletions(-) diff --git a/internal/model/dto/order_dto.go b/internal/model/dto/order_dto.go index 0fd3136..522f767 100644 --- a/internal/model/dto/order_dto.go +++ b/internal/model/dto/order_dto.go @@ -24,12 +24,13 @@ type OrderListRequest struct { PaymentStatus *int `json:"payment_status" query:"payment_status" validate:"omitempty,min=1,max=4" minimum:"1" maximum:"4" description:"支付状态 (1:待支付, 2:已支付, 3:已取消, 4:已退款)"` PaymentMethod string `json:"payment_method" query:"payment_method" validate:"omitempty,oneof=wallet wechat alipay offline" description:"支付方式 (wallet:钱包支付, wechat:微信支付, alipay:支付宝支付, offline:线下支付)"` OrderType string `json:"order_type" query:"order_type" validate:"omitempty,oneof=single_card device" description:"订单类型 (single_card:单卡购买, device:设备购买)"` + SellerShopID *uint `json:"seller_shop_id" query:"seller_shop_id" validate:"omitempty,min=1" minimum:"1" description:"所属代理商ID(销售来源店铺ID)"` OrderNo string `json:"order_no" query:"order_no" validate:"omitempty,max=30" maxLength:"30" description:"订单号(精确查询)"` PurchaseRole string `json:"purchase_role" query:"purchase_role" validate:"omitempty,oneof=self_purchase purchased_by_parent purchased_by_platform purchase_for_subordinate" description:"订单角色 (self_purchase:自己购买, purchased_by_parent:上级代理购买, purchased_by_platform:平台代购, purchase_for_subordinate:给下级购买)"` StartTime *time.Time `json:"start_time" query:"start_time" description:"创建时间起始"` EndTime *time.Time `json:"end_time" query:"end_time" description:"创建时间结束"` IsExpired *bool `json:"is_expired" query:"is_expired" description:"是否已过期 (true:已过期, false:未过期)"` - Identifier string `json:"identifier" query:"identifier" validate:"omitempty,max=100" maxLength:"100" description:"资产标识符(ICCID 或 VirtualNo)精确查询"` + Identifier string `json:"identifier" query:"identifier" validate:"omitempty,max=100" maxLength:"100" description:"资产标识符(支持 ICCID/VirtualNo/IMEI/SN/MSISDN,按资产解析后查询对应订单)"` BuyerPhone string `json:"buyer_phone" query:"buyer_phone" validate:"omitempty,max=20" maxLength:"20" description:"买家手机号精确查询"` } diff --git a/internal/service/order/service.go b/internal/service/order/service.go index 0983c63..bfdf1d9 100644 --- a/internal/service/order/service.go +++ b/internal/service/order/service.go @@ -1087,6 +1087,9 @@ func (s *Service) List(ctx context.Context, req *dto.OrderListRequest, buyerType if req.PurchaseRole != "" { filters["purchase_role"] = req.PurchaseRole } + if req.SellerShopID != nil { + filters["seller_shop_id"] = *req.SellerShopID + } if req.StartTime != nil { filters["start_time"] = req.StartTime } @@ -1094,7 +1097,18 @@ func (s *Service) List(ctx context.Context, req *dto.OrderListRequest, buyerType filters["end_time"] = req.EndTime } if req.Identifier != "" { + resolvedIotCardID, resolvedDeviceID, err := s.resolveOrderListAssetIdentifier(ctx, req.Identifier) + if err != nil { + return nil, err + } + // 保留快照标识匹配,并叠加资产 ID 匹配(由 Store 层统一按 OR 组合) filters["identifier"] = req.Identifier + if resolvedIotCardID != nil { + filters["iot_card_id"] = *resolvedIotCardID + } + if resolvedDeviceID != nil { + filters["device_id"] = *resolvedDeviceID + } } if req.BuyerPhone != "" { filters["buyer_phone"] = req.BuyerPhone @@ -1134,6 +1148,75 @@ func (s *Service) List(ctx context.Context, req *dto.OrderListRequest, buyerType }, nil } +// resolveOrderListAssetIdentifier 将订单列表的统一资产标识解析为卡或设备 ID +// 支持 ICCID、VirtualNo、IMEI、SN、MSISDN,与资产解析接口的识别口径保持一致。 +func (s *Service) resolveOrderListAssetIdentifier(ctx context.Context, identifier string) (*uint, *uint, error) { + if identifier == "" { + return nil, nil, nil + } + + if s.assetIdentifierStore != nil { + regRecord, regErr := s.assetIdentifierStore.FindByIdentifier(ctx, identifier) + if regErr != nil { + return nil, nil, errors.Wrap(errors.CodeDatabaseError, regErr, "查询资产标识符失败") + } + if regRecord != nil { + switch regRecord.AssetType { + case model.AssetTypeIotCard: + card, cardErr := s.iotCardStore.GetByID(ctx, regRecord.AssetID) + if cardErr == nil && card != nil { + cardID := card.ID + return &cardID, nil, nil + } + if cardErr != nil && cardErr != gorm.ErrRecordNotFound { + return nil, nil, errors.Wrap(errors.CodeDatabaseError, cardErr, "查询物联网卡失败") + } + case model.AssetTypeDevice: + device, deviceErr := s.deviceStore.GetByID(ctx, regRecord.AssetID) + if deviceErr == nil && device != nil { + deviceID := device.ID + return nil, &deviceID, nil + } + if deviceErr != nil && deviceErr != gorm.ErrRecordNotFound { + return nil, nil, errors.Wrap(errors.CodeDatabaseError, deviceErr, "查询设备失败") + } + } + } + } + + device, deviceErr := s.deviceStore.GetByIdentifier(ctx, identifier) + if deviceErr == nil && device != nil { + deviceID := device.ID + return nil, &deviceID, nil + } + if deviceErr != nil && deviceErr != gorm.ErrRecordNotFound { + return nil, nil, errors.Wrap(errors.CodeDatabaseError, deviceErr, "查询设备失败") + } + + var card model.IotCard + query := s.db.WithContext(ctx).Model(&model.IotCard{}) + query = middleware.ApplyShopFilter(ctx, query) + + conditions := []string{"virtual_no = ?", "msisdn = ?", "iccid = ?"} + args := []any{identifier, identifier, identifier} + if len(identifier) == 19 { + conditions = append(conditions, "iccid_19 = ?") + args = append(args, identifier) + } else if len(identifier) == 20 { + conditions = append(conditions, "iccid_20 = ?") + args = append(args, identifier) + } + + if err := query.Where(strings.Join(conditions, " OR "), args...).First(&card).Error; err == nil { + cardID := card.ID + return &cardID, nil, nil + } else if err != gorm.ErrRecordNotFound { + return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询物联网卡失败") + } + + return nil, nil, nil +} + func (s *Service) Cancel(ctx context.Context, id uint, buyerType string, buyerID uint) error { order, err := s.orderStore.GetByID(ctx, id) if err != nil { diff --git a/internal/store/postgres/order_store.go b/internal/store/postgres/order_store.go index f6e6771..0fe03f1 100644 --- a/internal/store/postgres/order_store.go +++ b/internal/store/postgres/order_store.go @@ -124,11 +124,35 @@ func (s *OrderStore) List(ctx context.Context, opts *store.QueryOptions, filters if v, ok := filters["purchase_role"]; ok { query = query.Where("purchase_role = ?", v) } - if v, ok := filters["iot_card_id"]; ok { - query = query.Where("iot_card_id = ?", v) + identifierValue, hasIdentifier := filters["identifier"] + iotCardIDValue, hasIotCardID := filters["iot_card_id"] + deviceIDValue, hasDeviceID := filters["device_id"] + if hasIdentifier || hasIotCardID || hasDeviceID { + assetQuery := s.db + hasAnyAssetCondition := false + if hasIdentifier { + assetQuery = assetQuery.Where("asset_identifier = ?", identifierValue) + hasAnyAssetCondition = true + } + if hasIotCardID { + if hasAnyAssetCondition { + assetQuery = assetQuery.Or("iot_card_id = ?", iotCardIDValue) + } else { + assetQuery = assetQuery.Where("iot_card_id = ?", iotCardIDValue) + hasAnyAssetCondition = true + } + } + if hasDeviceID { + if hasAnyAssetCondition { + assetQuery = assetQuery.Or("device_id = ?", deviceIDValue) + } else { + assetQuery = assetQuery.Where("device_id = ?", deviceIDValue) + } + } + query = query.Where(assetQuery) } - if v, ok := filters["device_id"]; ok { - query = query.Where("device_id = ?", v) + if v, ok := filters["seller_shop_id"]; ok { + query = query.Where("seller_shop_id = ?", v) } if v, ok := filters["start_time"]; ok { query = query.Where("created_at >= ?", v) @@ -136,9 +160,6 @@ func (s *OrderStore) List(ctx context.Context, opts *store.QueryOptions, filters if v, ok := filters["end_time"]; ok { query = query.Where("created_at <= ?", v) } - if v, ok := filters["identifier"]; ok { - query = query.Where("asset_identifier = ?", v) - } if v, ok := filters["buyer_phone"]; ok { query = query.Where("buyer_phone = ?", v) }