驳回接口
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m55s

This commit is contained in:
2026-06-25 17:10:24 +09:00
parent c5871b59a1
commit 8cf921f11c
13 changed files with 455 additions and 20 deletions

View File

@@ -29,6 +29,8 @@ type PollingCallback interface {
OnCardCreated(ctx context.Context, card *model.IotCard)
// OnCardStatusChanged 卡状态变化时的回调
OnCardStatusChanged(ctx context.Context, cardID uint)
// OnBatchCardsStatusChanged 批量卡状态变化时的回调(批量分配/回收场景)
OnBatchCardsStatusChanged(ctx context.Context, cardIDs []uint)
// OnCardDeleted 卡删除时的回调
OnCardDeleted(ctx context.Context, cardID uint)
// OnCardEnabled 卡启用轮询时的回调
@@ -597,6 +599,7 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
newStatus := constants.IotCardStatusDistributed
toShopID := req.ToShopID
allocationNo := s.assetAllocationRecordStore.GenerateAllocationNo(ctx, constants.AssetAllocationTypeAllocate)
err = s.db.Transaction(func(tx *gorm.DB) error {
txIotCardStore := postgres.NewIotCardStore(tx, nil)
@@ -606,7 +609,6 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
return err
}
allocationNo := s.assetAllocationRecordStore.GenerateAllocationNo(ctx, constants.AssetAllocationTypeAllocate)
records := s.buildAllocationRecords(cards, cardIDs, operatorShopID, toShopID, operatorID, allocationNo, req.Remark)
return txRecordStore.BatchCreate(ctx, records)
})
@@ -631,11 +633,14 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
}
s.iotCardStore.InvalidateListCountCache(ctx)
// 通知轮询调度器状态变化(卡被分配后可能需要重新匹配配置
// 通知轮询调度器状态变化(异步执行 + 批量操作,避免 N 次单卡 DB 查询打满连接池
if s.pollingCallback != nil && len(cardIDs) > 0 {
for _, cardID := range cardIDs {
s.pollingCallback.OnCardStatusChanged(ctx, cardID)
}
cardIDsCopy := make([]uint, len(cardIDs))
copy(cardIDsCopy, cardIDs)
cb := s.pollingCallback
go func() {
cb.OnBatchCardsStatusChanged(context.Background(), cardIDsCopy)
}()
}
shopMap := s.loadShopNames(ctx, cards)
@@ -672,7 +677,7 @@ func (s *Service) AllocateCards(ctx context.Context, req *dto.AllocateStandalone
TotalCount: len(cards),
SuccessCount: len(cardIDs),
FailCount: len(failedItems),
AllocationNo: s.assetAllocationRecordStore.GenerateAllocationNo(ctx, constants.AssetAllocationTypeAllocate),
AllocationNo: allocationNo,
FailedItems: failedItems,
}, nil
}
@@ -844,11 +849,14 @@ func (s *Service) RecallCards(ctx context.Context, req *dto.RecallStandaloneCard
}
s.iotCardStore.InvalidateListCountCache(ctx)
// 通知轮询调度器状态变化(卡被回收后可能需要重新匹配配置
// 通知轮询调度器状态变化(异步批量执行,避免回收大批卡时打满 DB 连接池
if s.pollingCallback != nil && len(cardIDs) > 0 {
for _, cardID := range cardIDs {
s.pollingCallback.OnCardStatusChanged(ctx, cardID)
}
cardIDsCopy := make([]uint, len(cardIDs))
copy(cardIDsCopy, cardIDs)
cb := s.pollingCallback
go func() {
cb.OnBatchCardsStatusChanged(context.Background(), cardIDsCopy)
}()
}
shopMap := s.loadShopNames(ctx, successCards)

View File

@@ -60,6 +60,65 @@ func New(
}
}
// AdminList 后台分页查询充值订单列表
func (s *Service) AdminList(ctx context.Context, params *postgres.ListRechargeOrderParams) ([]*model.RechargeOrder, int64, error) {
orders, total, err := s.rechargeOrderStore.List(ctx, params)
if err != nil {
return nil, 0, errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单列表失败")
}
return orders, total, nil
}
// AdminGet 后台查询充值订单详情(含支付记录)
func (s *Service) AdminGet(ctx context.Context, id uint) (*model.RechargeOrder, []*model.Payment, error) {
order, err := s.rechargeOrderStore.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil, errors.New(errors.CodeNotFound, "充值订单不存在")
}
return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单失败")
}
payments, err := s.paymentStore.ListByOrderID(ctx, id, model.PaymentOrderTypeRecharge)
if err != nil {
return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询支付记录失败")
}
return order, payments, nil
}
// Reject 驳回充值订单
// 仅待支付订单可驳回,驳回后状态变为已驳回(终态)
func (s *Service) Reject(ctx context.Context, id uint, rejectionReason string) error {
order, err := s.rechargeOrderStore.GetByID(ctx, id)
if err != nil {
if err == gorm.ErrRecordNotFound {
return errors.New(errors.CodeNotFound, "充值订单不存在")
}
return errors.Wrap(errors.CodeDatabaseError, err, "查询充值订单失败")
}
if order.Status != model.RechargeOrderStatusPending {
return errors.New(errors.CodeInvalidStatus, "仅待支付订单可驳回")
}
if err := s.rechargeOrderStore.UpdateStatusWithRejection(ctx, id, rejectionReason); err != nil {
if err == gorm.ErrRecordNotFound {
// 并发驳回:重查状态给出明确错误
latest, queryErr := s.rechargeOrderStore.GetByID(ctx, id)
if queryErr == nil && latest.Status != model.RechargeOrderStatusPending {
return errors.New(errors.CodeInvalidStatus, "仅待支付订单可驳回")
}
return errors.New(errors.CodeInvalidStatus, "仅待支付订单可驳回")
}
return errors.Wrap(errors.CodeDatabaseError, err, "驳回充值订单失败")
}
s.logger.Info("充值订单驳回成功",
zap.Uint("order_id", id),
zap.String("rejection_reason", rejectionReason),
)
return nil
}
func (s *Service) HandlePaymentCallback(ctx context.Context, paymentNo string, paymentMethod string, transactionID string) error {
payment, err := s.paymentStore.GetByPaymentNo(ctx, paymentNo)
if err != nil {