Files
junhong_cmp_fiber/internal/application/packagesilence/order_cancel.go
邹进 d3face6f66
Some checks failed
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Has been cancelled
fix: 延期订单超时不关闭、无法继续支付、不能作废
- 支付宝查单把 ACQ.TRADE_NOT_EXIST 当成查单失败,恢复任务永远判为未知,订单停在待支付;
  同一套餐再下单时旧单也关不掉,新单无法创建。改为按"渠道无此订单"处理,过支付截止后关闭
- 延期订单详情对未过期的待支付订单返回 qr_content,后台可展示二维码、H5 可继续支付
- 新增作废待支付订单:后台 POST /orders/:id/close、H5 POST /orders/:id/cancel;作废前先查单,已支付则按支付成功办理
2026-11-07 20:31:45 +09:00

102 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package packagesilence
import (
"context"
"strings"
"gorm.io/gorm"
agentrechargeApp "github.com/break/junhong_cmp_fiber/internal/application/agentrecharge"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/internal/model/dto"
"github.com/break/junhong_cmp_fiber/internal/store/postgres"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/break/junhong_cmp_fiber/pkg/errors"
)
// CancelOrderCommand 是作废待支付延期订单的命令:Scope.CustomerID 非 0 为 H5 客户作废自己的订单,为 0 为后台运营作废。
type CancelOrderCommand struct {
OrderID uint
Scope Scope
Reason string
}
// CancelPendingOrder 作废未支付的在线延期订单,作废后同一套餐可以重新下单。
//
// 作废前先按冻结商户查单:渠道已支付则按支付成功处理(照常延期)并拒绝作废;
// 其余情况(未支付、渠道无此订单、查单失败)直接关闭。关闭后才到达的支付会进入异常并由恢复任务原路退款,不会吞款。
func (s *ExtensionService) CancelPendingOrder(ctx context.Context, command CancelOrderCommand) (*dto.SilenceExtensionOrderResponse, error) {
if command.Scope.CustomerID == 0 {
if err := s.requirePermission(ctx, constants.PermissionPackageSilenceExtend); err != nil {
return nil, err
}
}
order, err := postgres.NewPackageSilenceOrderStore(s.db).GetByID(ctx, command.OrderID)
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "延期订单不存在")
}
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询延期订单失败")
}
if command.Scope.CustomerID != 0 && (order.CustomerID == nil || *order.CustomerID != command.Scope.CustomerID) {
return nil, errors.New(errors.CodeNotFound, "延期订单不存在")
}
if order.Status != constants.SilenceOrderStatusPendingPayment {
return nil, errors.New(errors.CodeInvalidStatus, "只有待支付的延期订单可以作废")
}
paid, err := s.confirmIfPaid(ctx, order)
if err != nil {
return nil, err
}
if paid {
return nil, errors.New(errors.CodeInvalidStatus, "该订单已支付,已按支付成功办理延期,不能作废")
}
reason := "手动作废"
if command.Scope.CustomerID != 0 {
reason = "客户作废"
}
if extra := strings.TrimSpace(command.Reason); extra != "" {
reason += ":" + extra
}
if err := s.closeOrder(ctx, order.ID, reason); err != nil {
return nil, err
}
current, err := postgres.NewPackageSilenceOrderStore(s.db).GetByID(ctx, order.ID)
if err != nil {
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询延期订单失败")
}
return s.orderResponse(ctx, current, false)
}
// confirmIfPaid 查询订单下待支付的支付单;渠道已支付时走支付确认并返回 true。查单失败不阻止作废。
func (s *ExtensionService) confirmIfPaid(ctx context.Context, order *model.PackageSilenceExtensionOrder) (bool, error) {
if order.PaymentMethod == model.PaymentByOffline {
return false, nil
}
var payments []model.Payment
if err := s.db.WithContext(ctx).Where("order_type = ? AND order_id = ? AND status = ?", constants.PaymentOrderTypeSilenceExtension, order.ID, model.PaymentRecordStatusPending).
Order("id DESC").Find(&payments).Error; err != nil {
return false, errors.Wrap(errors.CodeDatabaseError, err, "查询延期支付单失败")
}
for index := range payments {
payment := &payments[index]
state, query, err := s.queryPayment(ctx, payment)
if err != nil || state != agentrechargeApp.OnlinePaymentStatePaid {
continue
}
paidAt := s.now()
if query.PaidAt != nil {
paidAt = *query.PaidAt
}
if err := s.ConfirmPayment(ctx, PaymentConfirmCommand{
PaymentNo: payment.PaymentNo, ThirdPartyTradeNo: query.ThirdPartyTradeNo, Amount: query.Amount, PaidAt: paidAt,
}); err != nil {
return false, err
}
return true, nil
}
return false, nil
}