feat: 单卡回收接口优化 & 店铺禁用登录拦截
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m0s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m0s
单卡回收优化: - 移除 from_shop_id 参数,系统自动识别卡所属店铺 - 保持直属下级限制,混合来源分别处理 - 新增 GetDistributedStandaloneByICCIDRange/GetDistributedStandaloneByFilters 方法 店铺禁用拦截: - 登录时检查关联店铺状态,禁用店铺无法登录 - 新增 CodeShopDisabled 错误码 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -97,7 +97,7 @@ func initServices(s *stores, deps *Dependencies) *services {
|
|||||||
Permission: permissionSvc.New(s.Permission, s.AccountRole, s.RolePermission, account, deps.Redis),
|
Permission: permissionSvc.New(s.Permission, s.AccountRole, s.RolePermission, account, deps.Redis),
|
||||||
PersonalCustomer: personalCustomerSvc.NewService(s.PersonalCustomer, s.PersonalCustomerPhone, deps.VerificationService, deps.JWTManager, deps.WechatOfficialAccount, deps.Logger),
|
PersonalCustomer: personalCustomerSvc.NewService(s.PersonalCustomer, s.PersonalCustomerPhone, deps.VerificationService, deps.JWTManager, deps.WechatOfficialAccount, deps.Logger),
|
||||||
Shop: shopSvc.New(s.Shop, s.Account, s.ShopRole, s.Role),
|
Shop: shopSvc.New(s.Shop, s.Account, s.ShopRole, s.Role),
|
||||||
Auth: authSvc.New(s.Account, s.AccountRole, s.RolePermission, s.Permission, deps.TokenManager, deps.Logger),
|
Auth: authSvc.New(s.Account, s.AccountRole, s.RolePermission, s.Permission, s.Shop, deps.TokenManager, deps.Logger),
|
||||||
ShopCommission: shopCommissionSvc.New(s.Shop, s.Account, s.AgentWallet, s.CommissionWithdrawalRequest, s.CommissionRecord),
|
ShopCommission: shopCommissionSvc.New(s.Shop, s.Account, s.AgentWallet, s.CommissionWithdrawalRequest, s.CommissionRecord),
|
||||||
CommissionWithdrawal: commissionWithdrawalSvc.New(deps.DB, s.Shop, s.Account, s.AgentWallet, s.AgentWalletTransaction, s.CommissionWithdrawalRequest),
|
CommissionWithdrawal: commissionWithdrawalSvc.New(deps.DB, s.Shop, s.Account, s.AgentWallet, s.AgentWalletTransaction, s.CommissionWithdrawalRequest),
|
||||||
CommissionWithdrawalSetting: commissionWithdrawalSettingSvc.New(deps.DB, s.Account, s.CommissionWithdrawalSetting),
|
CommissionWithdrawalSetting: commissionWithdrawalSettingSvc.New(deps.DB, s.Account, s.CommissionWithdrawalSetting),
|
||||||
|
|||||||
@@ -71,10 +71,8 @@ type AllocationFailedItem struct {
|
|||||||
// ========== 回收请求/响应 ==========
|
// ========== 回收请求/响应 ==========
|
||||||
|
|
||||||
// RecallStandaloneCardsRequest 回收单卡请求
|
// RecallStandaloneCardsRequest 回收单卡请求
|
||||||
|
// 系统自动识别卡所属店铺,只要是操作者的直属下级店铺即可回收
|
||||||
type RecallStandaloneCardsRequest struct {
|
type RecallStandaloneCardsRequest struct {
|
||||||
// FromShopID 来源店铺ID(必填,被回收方,必须是直属下级)
|
|
||||||
FromShopID uint `json:"from_shop_id" validate:"required,min=1" required:"true" minimum:"1" description:"来源店铺ID(被回收方)"`
|
|
||||||
|
|
||||||
// SelectionType 选卡方式(必填)
|
// SelectionType 选卡方式(必填)
|
||||||
SelectionType string `json:"selection_type" validate:"required,oneof=list range filter" required:"true" enum:"list,range,filter" description:"选卡方式 (list:ICCID列表, range:号段范围, filter:筛选条件)"`
|
SelectionType string `json:"selection_type" validate:"required,oneof=list range filter" required:"true" enum:"list,range,filter" description:"选卡方式 (list:ICCID列表, range:号段范围, filter:筛选条件)"`
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ type Service struct {
|
|||||||
accountRoleStore *postgres.AccountRoleStore
|
accountRoleStore *postgres.AccountRoleStore
|
||||||
rolePermStore *postgres.RolePermissionStore
|
rolePermStore *postgres.RolePermissionStore
|
||||||
permissionStore *postgres.PermissionStore
|
permissionStore *postgres.PermissionStore
|
||||||
|
shopStore *postgres.ShopStore
|
||||||
tokenManager *auth.TokenManager
|
tokenManager *auth.TokenManager
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
}
|
}
|
||||||
@@ -30,6 +31,7 @@ func New(
|
|||||||
accountRoleStore *postgres.AccountRoleStore,
|
accountRoleStore *postgres.AccountRoleStore,
|
||||||
rolePermStore *postgres.RolePermissionStore,
|
rolePermStore *postgres.RolePermissionStore,
|
||||||
permissionStore *postgres.PermissionStore,
|
permissionStore *postgres.PermissionStore,
|
||||||
|
shopStore *postgres.ShopStore,
|
||||||
tokenManager *auth.TokenManager,
|
tokenManager *auth.TokenManager,
|
||||||
logger *zap.Logger,
|
logger *zap.Logger,
|
||||||
) *Service {
|
) *Service {
|
||||||
@@ -38,6 +40,7 @@ func New(
|
|||||||
accountRoleStore: accountRoleStore,
|
accountRoleStore: accountRoleStore,
|
||||||
rolePermStore: rolePermStore,
|
rolePermStore: rolePermStore,
|
||||||
permissionStore: permissionStore,
|
permissionStore: permissionStore,
|
||||||
|
shopStore: shopStore,
|
||||||
tokenManager: tokenManager,
|
tokenManager: tokenManager,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
@@ -65,6 +68,22 @@ func (s *Service) Login(ctx context.Context, req *dto.LoginRequest, clientIP str
|
|||||||
return nil, errors.New(errors.CodeAccountDisabled, "账号已禁用")
|
return nil, errors.New(errors.CodeAccountDisabled, "账号已禁用")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查店铺状态(代理账号必须关联店铺且店铺必须启用)
|
||||||
|
if account.ShopID != nil && *account.ShopID > 0 {
|
||||||
|
shop, err := s.shopStore.GetByID(ctx, *account.ShopID)
|
||||||
|
if err != nil {
|
||||||
|
if err == gorm.ErrRecordNotFound {
|
||||||
|
s.logger.Warn("登录失败:关联店铺不存在", zap.String("username", req.Username), zap.Uint("shop_id", *account.ShopID))
|
||||||
|
return nil, errors.New(errors.CodeShopNotFound, "关联店铺不存在")
|
||||||
|
}
|
||||||
|
return nil, errors.Wrap(errors.CodeInternalError, err, "查询店铺失败")
|
||||||
|
}
|
||||||
|
if shop.Status != constants.StatusEnabled {
|
||||||
|
s.logger.Warn("登录失败:关联店铺已禁用", zap.String("username", req.Username), zap.Uint("shop_id", *account.ShopID))
|
||||||
|
return nil, errors.New(errors.CodeShopDisabled, "店铺已禁用,无法登录")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
device := req.Device
|
device := req.Device
|
||||||
if device == "" {
|
if device == "" {
|
||||||
device = "web"
|
device = "web"
|
||||||
|
|||||||
@@ -384,10 +384,7 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCardsRequest, operatorID uint, operatorShopID *uint) (*dto.RecallStandaloneCardsResponse, error) {
|
func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCardsRequest, operatorID uint, operatorShopID *uint) (*dto.RecallStandaloneCardsResponse, error) {
|
||||||
if err := s.validateDirectSubordinate(ctx, operatorShopID, req.FromShopID); err != nil {
|
// 1. 查询卡列表
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
cards, err := s.getCardsForRecall(ctx, req)
|
cards, err := s.getCardsForRecall(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -402,7 +399,35 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 收集所有卡的店铺 ID,批量查询店铺信息以验证直属下级关系
|
||||||
|
shopIDSet := make(map[uint]bool)
|
||||||
|
for _, card := range cards {
|
||||||
|
if card.ShopID != nil {
|
||||||
|
shopIDSet[*card.ShopID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shopIDs := make([]uint, 0, len(shopIDSet))
|
||||||
|
for shopID := range shopIDSet {
|
||||||
|
shopIDs = append(shopIDs, shopID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 批量查询店铺,验证哪些是直属下级
|
||||||
|
directSubordinateSet := make(map[uint]bool)
|
||||||
|
if len(shopIDs) > 0 {
|
||||||
|
shops, err := s.shopStore.GetByIDs(ctx, shopIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, shop := range shops {
|
||||||
|
if s.isDirectSubordinate(operatorShopID, shop) {
|
||||||
|
directSubordinateSet[shop.ID] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 检查绑定设备的卡
|
||||||
var cardIDs []uint
|
var cardIDs []uint
|
||||||
|
var successCards []*model.IotCard
|
||||||
var failedItems []dto.AllocationFailedItem
|
var failedItems []dto.AllocationFailedItem
|
||||||
|
|
||||||
boundCardIDs, err := s.iotCardStore.GetBoundCardIDs(ctx, s.extractCardIDs(cards))
|
boundCardIDs, err := s.iotCardStore.GetBoundCardIDs(ctx, s.extractCardIDs(cards))
|
||||||
@@ -414,6 +439,7 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
|||||||
boundCardIDSet[id] = true
|
boundCardIDSet[id] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5. 逐卡验证:绑定设备、所属店铺是否是直属下级
|
||||||
for _, card := range cards {
|
for _, card := range cards {
|
||||||
if boundCardIDSet[card.ID] {
|
if boundCardIDSet[card.ID] {
|
||||||
failedItems = append(failedItems, dto.AllocationFailedItem{
|
failedItems = append(failedItems, dto.AllocationFailedItem{
|
||||||
@@ -423,15 +449,24 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if card.ShopID == nil || *card.ShopID != req.FromShopID {
|
if card.ShopID == nil {
|
||||||
failedItems = append(failedItems, dto.AllocationFailedItem{
|
failedItems = append(failedItems, dto.AllocationFailedItem{
|
||||||
ICCID: card.ICCID,
|
ICCID: card.ICCID,
|
||||||
Reason: "卡不属于指定的店铺",
|
Reason: "卡未分配给任何店铺",
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !directSubordinateSet[*card.ShopID] {
|
||||||
|
failedItems = append(failedItems, dto.AllocationFailedItem{
|
||||||
|
ICCID: card.ICCID,
|
||||||
|
Reason: "卡所属店铺不是您的直属下级",
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
cardIDs = append(cardIDs, card.ID)
|
cardIDs = append(cardIDs, card.ID)
|
||||||
|
successCards = append(successCards, card)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cardIDs) == 0 {
|
if len(cardIDs) == 0 {
|
||||||
@@ -443,6 +478,7 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 6. 执行回收
|
||||||
isPlatform := operatorShopID == nil
|
isPlatform := operatorShopID == nil
|
||||||
var newShopID *uint
|
var newShopID *uint
|
||||||
var newStatus int
|
var newStatus int
|
||||||
@@ -454,6 +490,8 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
|||||||
newStatus = constants.IotCardStatusDistributed
|
newStatus = constants.IotCardStatusDistributed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allocationNo := s.assetAllocationRecordStore.GenerateAllocationNo(ctx, constants.AssetAllocationTypeRecall)
|
||||||
|
|
||||||
err = s.db.Transaction(func(tx *gorm.DB) error {
|
err = s.db.Transaction(func(tx *gorm.DB) error {
|
||||||
txIotCardStore := postgres.NewIotCardStore(tx, nil)
|
txIotCardStore := postgres.NewIotCardStore(tx, nil)
|
||||||
txRecordStore := postgres.NewAssetAllocationRecordStore(tx, nil)
|
txRecordStore := postgres.NewAssetAllocationRecordStore(tx, nil)
|
||||||
@@ -462,8 +500,7 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
allocationNo := s.assetAllocationRecordStore.GenerateAllocationNo(ctx, constants.AssetAllocationTypeRecall)
|
records := s.buildRecallRecords(successCards, operatorShopID, operatorID, allocationNo, req.Remark)
|
||||||
records := s.buildRecallRecords(cards, cardIDs, req.FromShopID, operatorShopID, operatorID, allocationNo, req.Remark)
|
|
||||||
return txRecordStore.BatchCreate(ctx, records)
|
return txRecordStore.BatchCreate(ctx, records)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -482,11 +519,21 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
|
|||||||
TotalCount: len(cards),
|
TotalCount: len(cards),
|
||||||
SuccessCount: len(cardIDs),
|
SuccessCount: len(cardIDs),
|
||||||
FailCount: len(failedItems),
|
FailCount: len(failedItems),
|
||||||
AllocationNo: s.assetAllocationRecordStore.GenerateAllocationNo(ctx, constants.AssetAllocationTypeRecall),
|
AllocationNo: allocationNo,
|
||||||
FailedItems: failedItems,
|
FailedItems: failedItems,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isDirectSubordinate 检查店铺是否是操作者的直属下级
|
||||||
|
func (s *Service) isDirectSubordinate(operatorShopID *uint, shop *model.Shop) bool {
|
||||||
|
if operatorShopID == nil {
|
||||||
|
// 平台用户:直属下级是 parent_id 为空的店铺
|
||||||
|
return shop.ParentID == nil
|
||||||
|
}
|
||||||
|
// 代理用户:直属下级是 parent_id 等于自己的店铺
|
||||||
|
return shop.ParentID != nil && *shop.ParentID == *operatorShopID
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) validateDirectSubordinate(ctx context.Context, operatorShopID *uint, targetShopID uint) error {
|
func (s *Service) validateDirectSubordinate(ctx context.Context, operatorShopID *uint, targetShopID uint) error {
|
||||||
if operatorShopID != nil && *operatorShopID == targetShopID {
|
if operatorShopID != nil && *operatorShopID == targetShopID {
|
||||||
return errors.ErrCannotAllocateToSelf
|
return errors.ErrCannotAllocateToSelf
|
||||||
@@ -537,12 +584,12 @@ func (s *Service) getCardsForAllocation(ctx context.Context, req *dto.AllocateSt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) getCardsForRecall(ctx context.Context, req *dto.RecallStandaloneCardsRequest) ([]*model.IotCard, error) {
|
func (s *Service) getCardsForRecall(ctx context.Context, req *dto.RecallStandaloneCardsRequest) ([]*model.IotCard, error) {
|
||||||
fromShopID := req.FromShopID
|
|
||||||
switch req.SelectionType {
|
switch req.SelectionType {
|
||||||
case dto.SelectionTypeList:
|
case dto.SelectionTypeList:
|
||||||
return s.iotCardStore.GetByICCIDs(ctx, req.ICCIDs)
|
return s.iotCardStore.GetByICCIDs(ctx, req.ICCIDs)
|
||||||
case dto.SelectionTypeRange:
|
case dto.SelectionTypeRange:
|
||||||
return s.iotCardStore.GetStandaloneByICCIDRange(ctx, req.ICCIDStart, req.ICCIDEnd, &fromShopID)
|
// 查询已分配给店铺的单卡(回收场景)
|
||||||
|
return s.iotCardStore.GetDistributedStandaloneByICCIDRange(ctx, req.ICCIDStart, req.ICCIDEnd)
|
||||||
case dto.SelectionTypeFilter:
|
case dto.SelectionTypeFilter:
|
||||||
filters := make(map[string]any)
|
filters := make(map[string]any)
|
||||||
if req.CarrierID != nil {
|
if req.CarrierID != nil {
|
||||||
@@ -551,7 +598,8 @@ func (s *Service) getCardsForRecall(ctx context.Context, req *dto.RecallStandalo
|
|||||||
if req.BatchNo != "" {
|
if req.BatchNo != "" {
|
||||||
filters["batch_no"] = req.BatchNo
|
filters["batch_no"] = req.BatchNo
|
||||||
}
|
}
|
||||||
return s.iotCardStore.GetStandaloneByFilters(ctx, filters, &fromShopID)
|
// 查询已分配给店铺的单卡(回收场景)
|
||||||
|
return s.iotCardStore.GetDistributedStandaloneByFilters(ctx, filters)
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(errors.CodeInvalidParam, "无效的选卡方式")
|
return nil, errors.New(errors.CodeInvalidParam, "无效的选卡方式")
|
||||||
}
|
}
|
||||||
@@ -603,18 +651,9 @@ func (s *Service) buildAllocationRecords(cards []*model.IotCard, successCardIDs
|
|||||||
return records
|
return records
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) buildRecallRecords(cards []*model.IotCard, successCardIDs []uint, fromShopID uint, toShopID *uint, operatorID uint, allocationNo, remark string) []*model.AssetAllocationRecord {
|
func (s *Service) buildRecallRecords(successCards []*model.IotCard, toShopID *uint, operatorID uint, allocationNo, remark string) []*model.AssetAllocationRecord {
|
||||||
successIDSet := make(map[uint]bool)
|
|
||||||
for _, id := range successCardIDs {
|
|
||||||
successIDSet[id] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var records []*model.AssetAllocationRecord
|
var records []*model.AssetAllocationRecord
|
||||||
for _, card := range cards {
|
for _, card := range successCards {
|
||||||
if !successIDSet[card.ID] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
record := &model.AssetAllocationRecord{
|
record := &model.AssetAllocationRecord{
|
||||||
AllocationNo: allocationNo,
|
AllocationNo: allocationNo,
|
||||||
AllocationType: constants.AssetAllocationTypeRecall,
|
AllocationType: constants.AssetAllocationTypeRecall,
|
||||||
@@ -622,7 +661,7 @@ func (s *Service) buildRecallRecords(cards []*model.IotCard, successCardIDs []ui
|
|||||||
AssetID: card.ID,
|
AssetID: card.ID,
|
||||||
AssetIdentifier: card.ICCID,
|
AssetIdentifier: card.ICCID,
|
||||||
FromOwnerType: constants.OwnerTypeShop,
|
FromOwnerType: constants.OwnerTypeShop,
|
||||||
FromOwnerID: &fromShopID,
|
FromOwnerID: card.ShopID, // 从卡的当前所属店铺获取
|
||||||
OperatorID: operatorID,
|
OperatorID: operatorID,
|
||||||
Remark: remark,
|
Remark: remark,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateShopRequest) (*dto.
|
|||||||
// 验证默认角色:必须存在、是客户角色且已启用
|
// 验证默认角色:必须存在、是客户角色且已启用
|
||||||
defaultRole, err := s.roleStore.GetByID(ctx, req.DefaultRoleID)
|
defaultRole, err := s.roleStore.GetByID(ctx, req.DefaultRoleID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New(errors.CodeNotFound, "默认角色不存在")
|
return nil, errors.New(errors.CodeNotFound, "请选择默认角色")
|
||||||
}
|
}
|
||||||
if defaultRole.RoleType != constants.RoleTypeCustomer {
|
if defaultRole.RoleType != constants.RoleTypeCustomer {
|
||||||
return nil, errors.New(errors.CodeInvalidParam, "店铺默认角色必须是客户角色")
|
return nil, errors.New(errors.CodeInvalidParam, "店铺默认角色必须是客户角色")
|
||||||
|
|||||||
@@ -673,6 +673,19 @@ func (s *IotCardStore) GetStandaloneByICCIDRange(ctx context.Context, iccidStart
|
|||||||
return cards, nil
|
return cards, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDistributedStandaloneByICCIDRange 根据号段范围查询已分配给店铺的单卡(用于回收)
|
||||||
|
func (s *IotCardStore) GetDistributedStandaloneByICCIDRange(ctx context.Context, iccidStart, iccidEnd string) ([]*model.IotCard, error) {
|
||||||
|
var cards []*model.IotCard
|
||||||
|
if err := s.db.WithContext(ctx).Model(&model.IotCard{}).
|
||||||
|
Where("is_standalone = true").
|
||||||
|
Where("shop_id IS NOT NULL").
|
||||||
|
Where("iccid >= ? AND iccid <= ?", iccidStart, iccidEnd).
|
||||||
|
Find(&cards).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cards, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *IotCardStore) GetStandaloneByFilters(ctx context.Context, filters map[string]any, shopID *uint) ([]*model.IotCard, error) {
|
func (s *IotCardStore) GetStandaloneByFilters(ctx context.Context, filters map[string]any, shopID *uint) ([]*model.IotCard, error) {
|
||||||
query := s.db.WithContext(ctx).Model(&model.IotCard{}).
|
query := s.db.WithContext(ctx).Model(&model.IotCard{}).
|
||||||
Where("is_standalone = true")
|
Where("is_standalone = true")
|
||||||
@@ -700,6 +713,26 @@ func (s *IotCardStore) GetStandaloneByFilters(ctx context.Context, filters map[s
|
|||||||
return cards, nil
|
return cards, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDistributedStandaloneByFilters 根据筛选条件查询已分配给店铺的单卡(用于回收)
|
||||||
|
func (s *IotCardStore) GetDistributedStandaloneByFilters(ctx context.Context, filters map[string]any) ([]*model.IotCard, error) {
|
||||||
|
query := s.db.WithContext(ctx).Model(&model.IotCard{}).
|
||||||
|
Where("is_standalone = true").
|
||||||
|
Where("shop_id IS NOT NULL")
|
||||||
|
|
||||||
|
if carrierID, ok := filters["carrier_id"].(uint); ok && carrierID > 0 {
|
||||||
|
query = query.Where("carrier_id = ?", carrierID)
|
||||||
|
}
|
||||||
|
if batchNo, ok := filters["batch_no"].(string); ok && batchNo != "" {
|
||||||
|
query = query.Where("batch_no = ?", batchNo)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cards []*model.IotCard
|
||||||
|
if err := query.Find(&cards).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cards, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *IotCardStore) BatchUpdateShopIDAndStatus(ctx context.Context, cardIDs []uint, shopID *uint, status int) error {
|
func (s *IotCardStore) BatchUpdateShopIDAndStatus(ctx context.Context, cardIDs []uint, shopID *uint, status int) error {
|
||||||
if len(cardIDs) == 0 {
|
if len(cardIDs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const (
|
|||||||
CodeEnterpriseCodeExists = 1034 // 企业编号已存在
|
CodeEnterpriseCodeExists = 1034 // 企业编号已存在
|
||||||
CodeCustomerNotFound = 1035 // 个人客户不存在
|
CodeCustomerNotFound = 1035 // 个人客户不存在
|
||||||
CodeCustomerPhoneExists = 1036 // 个人客户手机号已存在
|
CodeCustomerPhoneExists = 1036 // 个人客户手机号已存在
|
||||||
|
CodeShopDisabled = 1037 // 店铺已禁用
|
||||||
|
|
||||||
// 财务相关错误 (1050-1069)
|
// 财务相关错误 (1050-1069)
|
||||||
CodeInvalidStatus = 1050 // 状态不允许此操作
|
CodeInvalidStatus = 1050 // 状态不允许此操作
|
||||||
@@ -179,6 +180,7 @@ var allErrorCodes = []int{
|
|||||||
CodeEnterpriseCodeExists,
|
CodeEnterpriseCodeExists,
|
||||||
CodeCustomerNotFound,
|
CodeCustomerNotFound,
|
||||||
CodeCustomerPhoneExists,
|
CodeCustomerPhoneExists,
|
||||||
|
CodeShopDisabled,
|
||||||
CodeInvalidCredentials,
|
CodeInvalidCredentials,
|
||||||
CodeAccountLocked,
|
CodeAccountLocked,
|
||||||
CodePasswordExpired,
|
CodePasswordExpired,
|
||||||
@@ -295,6 +297,7 @@ var errorMessages = map[int]string{
|
|||||||
CodeEnterpriseCodeExists: "企业编号已存在",
|
CodeEnterpriseCodeExists: "企业编号已存在",
|
||||||
CodeCustomerNotFound: "个人客户不存在",
|
CodeCustomerNotFound: "个人客户不存在",
|
||||||
CodeCustomerPhoneExists: "个人客户手机号已存在",
|
CodeCustomerPhoneExists: "个人客户手机号已存在",
|
||||||
|
CodeShopDisabled: "店铺已禁用",
|
||||||
CodeInvalidStatus: "状态不允许此操作",
|
CodeInvalidStatus: "状态不允许此操作",
|
||||||
CodeInsufficientBalance: "余额不足",
|
CodeInsufficientBalance: "余额不足",
|
||||||
CodeWithdrawalNotFound: "提现申请不存在",
|
CodeWithdrawalNotFound: "提现申请不存在",
|
||||||
|
|||||||
Reference in New Issue
Block a user