修复 Context 改造后的编译问题

This commit is contained in:
yml2213
2026-06-10 09:43:49 +08:00
parent b377f8350b
commit 334436f381
13 changed files with 193 additions and 189 deletions
+10 -9
View File
@@ -1,6 +1,7 @@
package e2e
import (
"context"
"database/sql"
"fmt"
"os"
@@ -45,7 +46,7 @@ func TestRentalFullFlowWithMockPayment(t *testing.T) {
}
orderDTO := mustCreateOrder(t, services.order, renter.ID, adjusted.ID)
paymentDTO, err := services.payment.Start(renter.ID, orderDTO.ID, payment.StartPaymentRequest{}, "127.0.0.1")
paymentDTO, err := services.payment.Start(t.Context(), renter.ID, orderDTO.ID, payment.StartPaymentRequest{}, "127.0.0.1")
if err != nil {
t.Fatalf("启动 mock 支付失败: %v", err)
}
@@ -115,7 +116,7 @@ func TestArbitrationReleaseDepositDoesNotRepublishListing(t *testing.T) {
listingDTO := createListingUnderReview(t, services.listing, owner.ID)
approved := adjustAndApproveListing(t, services.listing, adminID, listingDTO.ID)
orderDTO := mustCreateOrder(t, services.order, renter.ID, approved.ID)
if _, err := services.payment.Start(renter.ID, orderDTO.ID, payment.StartPaymentRequest{}, "127.0.0.1"); err != nil {
if _, err := services.payment.Start(t.Context(), renter.ID, orderDTO.ID, payment.StartPaymentRequest{}, "127.0.0.1"); err != nil {
t.Fatalf("启动 mock 支付失败: %v", err)
}
if _, err := services.order.SubmitHandoff(owner.ID, orderDTO.ID, order.SubmitHandoffRequest{Content: "账号:demo,密码:demo-pass"}); err != nil {
@@ -171,7 +172,7 @@ func TestArbitrationReleaseDepositDoesNotRepublishListing(t *testing.T) {
assertEqual(t, "仲裁退款状态", orderRow.RefundStatus, "refunded")
assertEqual(t, "仲裁退款金额", orderRow.RefundAmountCent, int64(15000))
walletAccount, err := services.wallet.Account(owner.ID)
walletAccount, err := services.wallet.Account(t.Context(), owner.ID)
if err != nil {
t.Fatalf("读取号主钱包失败: %v", err)
}
@@ -209,14 +210,14 @@ func newFlowServices(db *gorm.DB) flowServices {
paymentRepo := payment.NewRepository(db, configRepo, orderRepo, walletRepo)
disputeRepo := dispute.NewRepository(db)
orderRepo.SetRefundFunc(func(orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
refund, err := paymentRepo.StartRefund(orderID, refundAmountCent, bizType, remark)
refund, err := paymentRepo.StartRefund(context.Background(), orderID, refundAmountCent, bizType, remark)
if err != nil {
return "", err
}
return refund.Status, nil
})
disputeRepo.SetRefundFunc(func(orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
refund, err := paymentRepo.StartRefund(orderID, refundAmountCent, bizType, remark)
refund, err := paymentRepo.StartRefund(context.Background(), orderID, refundAmountCent, bizType, remark)
if err != nil {
return "", err
}
@@ -238,7 +239,7 @@ func newFlowServices(db *gorm.DB) flowServices {
type fixedConfig map[string]string
func (c fixedConfig) FindValue(key string) (string, error) {
func (c fixedConfig) FindValue(ctx context.Context, key string) (string, error) {
return c[key], nil
}
@@ -392,7 +393,7 @@ func createOwnerPaymentAccount(t *testing.T, service *paymentaccount.Service, ow
func createListingUnderReview(t *testing.T, service *listing.Service, ownerID uint64) *listing.ListingDTO {
t.Helper()
dto, err := service.Create(ownerID, listing.CreateRequest{
dto, err := service.Create(t.Context(), ownerID, listing.CreateRequest{
Title: "E2E 烽火地带账号",
Description: "用于完整链路测试",
ServerRegion: "烽火地带",
@@ -490,7 +491,7 @@ func assertFinanceDashboard(t *testing.T, service *adminfinance.Service, orderNo
func assertWalletAndWithdrawal(t *testing.T, walletService *wallet.Service, withdrawalService *withdrawal.Service, ownerID uint64, adminID uint64, paymentAccountID uint64) {
t.Helper()
accountBefore, err := walletService.Account(ownerID)
accountBefore, err := walletService.Account(t.Context(), ownerID)
if err != nil {
t.Fatalf("读取号主钱包失败: %v", err)
}
@@ -525,7 +526,7 @@ func assertWalletAndWithdrawal(t *testing.T, walletService *wallet.Service, with
assertEqual(t, "提现完成状态", paid.Status, "completed")
assertEqual(t, "提现到账金额", paid.ActualAmountCent, int64(10000))
accountAfter, err := walletService.Account(ownerID)
accountAfter, err := walletService.Account(t.Context(), ownerID)
if err != nil {
t.Fatalf("读取提现后钱包失败: %v", err)
}
@@ -20,7 +20,7 @@ func NewHandler(service *Service) *Handler {
}
func (h *Handler) Captcha(c *gin.Context) {
item, err := h.service.Captcha(c.Request.Context(), )
item, err := h.service.Captcha(c.Request.Context())
if err != nil {
writeAdminAuthError(c, err)
return
@@ -39,11 +39,11 @@ func (s *Service) Refresh(ctx context.Context, refreshToken string) (*auth.Token
return &pair, nil
}
func (s *Service) Captcha(ctx context.Context, ) (*CaptchaDTO, error) {
func (s *Service) Captcha(ctx context.Context) (*CaptchaDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.Captcha(ctx, )
return s.repo.Captcha(ctx)
}
func (s *Service) Login(ctx context.Context, username string, password string, captchaID string, captchaCode string) (LoginResult, error) {
+4 -4
View File
@@ -37,7 +37,7 @@ func (h *Handler) Create(c *gin.Context) {
response.BadRequest(c, "发布信息不完整")
return
}
item, err := h.service.Create(ownerID, req)
item, err := h.service.Create(c.Request.Context(), ownerID, req)
if err != nil {
writeListingError(c, err)
return
@@ -57,7 +57,7 @@ func (h *Handler) ImportExternalUpload(c *gin.Context) {
response.BadRequest(c, "上传 JSON 格式不正确")
return
}
result, err := h.service.ImportExternalUpload(req, ExternalUploadMeta{
result, err := h.service.ImportExternalUpload(c.Request.Context(), req, ExternalUploadMeta{
IP: c.ClientIP(),
UserAgent: c.GetHeader("User-Agent"),
RawPayload: raw,
@@ -90,7 +90,7 @@ func (h *Handler) Update(c *gin.Context) {
response.BadRequest(c, "发布信息不完整")
return
}
item, err := h.service.Update(ownerID, id, req)
item, err := h.service.Update(c.Request.Context(), ownerID, id, req)
if err != nil {
writeListingError(c, err)
return
@@ -108,7 +108,7 @@ func (h *Handler) SubmitReview(c *gin.Context) {
if !ok {
return
}
item, err := h.service.SubmitReview(ownerID, id)
item, err := h.service.SubmitReview(c.Request.Context(), ownerID, id)
if err != nil {
writeListingError(c, err)
return
+16 -15
View File
@@ -1,6 +1,7 @@
package listing
import (
"context"
"encoding/json"
"errors"
"fmt"
@@ -36,7 +37,7 @@ type Service struct {
}
type ConfigReader interface {
FindValue(key string) (string, error)
FindValue(ctx context.Context, key string) (string, error)
}
const (
@@ -77,28 +78,28 @@ func NewService(repo *Repository, config ConfigReader) *Service {
return &Service{repo: repo, config: config}
}
func (s *Service) Create(ownerID uint64, req CreateRequest) (*ListingDTO, error) {
func (s *Service) Create(ctx context.Context, ownerID uint64, req CreateRequest) (*ListingDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if !req.AgreedVirtualAssetSale || !req.AgreedSellerAgreement {
return nil, ErrAgreementRequired
}
rules, err := s.publishRules()
rules, err := s.publishRules(ctx)
if err != nil {
return nil, err
}
if err := validateRequest(req, rules); err != nil {
return nil, err
}
reviewRequired, err := s.reviewRequired()
reviewRequired, err := s.reviewRequired(ctx)
if err != nil {
return nil, err
}
return s.repo.Create(ownerID, req, reviewRequired)
}
func (s *Service) ImportExternalUpload(req ExternalUploadRequest, meta ExternalUploadMeta) (*ExternalUploadResponse, error) {
func (s *Service) ImportExternalUpload(ctx context.Context, req ExternalUploadRequest, meta ExternalUploadMeta) (*ExternalUploadResponse, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
@@ -113,7 +114,7 @@ func (s *Service) ImportExternalUpload(req ExternalUploadRequest, meta ExternalU
if len(items) > maxExternalUploadItems {
return nil, ErrTooManyUploadItems
}
rules, err := s.publishRules()
rules, err := s.publishRules(ctx)
if err != nil {
return nil, err
}
@@ -171,29 +172,29 @@ func (s *Service) ImportExternalUpload(req ExternalUploadRequest, meta ExternalU
return resp, nil
}
func (s *Service) Update(ownerID uint64, id uint64, req UpdateRequest) (*ListingDTO, error) {
func (s *Service) Update(ctx context.Context, ownerID uint64, id uint64, req UpdateRequest) (*ListingDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
rules, err := s.publishRules()
rules, err := s.publishRules(ctx)
if err != nil {
return nil, err
}
if err := validateRequest(req, rules); err != nil {
return nil, err
}
reviewRequired, err := s.reviewRequired()
reviewRequired, err := s.reviewRequired(ctx)
if err != nil {
return nil, err
}
return s.repo.Update(ownerID, id, req, reviewRequired)
}
func (s *Service) SubmitReview(ownerID uint64, id uint64) (*ListingDTO, error) {
func (s *Service) SubmitReview(ctx context.Context, ownerID uint64, id uint64) (*ListingDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
reviewRequired, err := s.reviewRequired()
reviewRequired, err := s.reviewRequired(ctx)
if err != nil {
return nil, err
}
@@ -753,11 +754,11 @@ func hasScreenshotURL(urls []string) bool {
return false
}
func (s *Service) reviewRequired() (bool, error) {
func (s *Service) reviewRequired(ctx context.Context) (bool, error) {
if s.config == nil {
return false, nil
}
value, err := s.config.FindValue(reviewRequiredConfigKey)
value, err := s.config.FindValue(ctx, reviewRequiredConfigKey)
if err != nil {
return false, err
}
@@ -768,12 +769,12 @@ func (s *Service) reviewRequired() (bool, error) {
return required, nil
}
func (s *Service) publishRules() (publishRules, error) {
func (s *Service) publishRules(ctx context.Context) (publishRules, error) {
rules := publishRules{FireLevelMin: defaultFireLevelMin}
if s.config == nil {
return rules, nil
}
value, err := s.config.FindValue(publishOptionsConfigKey)
value, err := s.config.FindValue(ctx, publishOptionsConfigKey)
if err != nil {
return rules, err
}
@@ -61,7 +61,7 @@ func TestValidateRequestRequiresDepositAboveConsumables(t *testing.T) {
func TestCreateRequiresPublishAgreements(t *testing.T) {
service := NewService(&Repository{}, nil)
_, err := service.Create(1, CreateRequest{})
_, err := service.Create(t.Context(), 1, CreateRequest{})
if err != ErrAgreementRequired {
t.Fatalf("expected ErrAgreementRequired, got %v", err)
}
+8 -8
View File
@@ -37,7 +37,7 @@ func (h *Handler) Start(c *gin.Context) {
}
var req StartPaymentRequest
_ = c.ShouldBindJSON(&req)
item, err := h.service.Start(userID, orderID, req, c.ClientIP())
item, err := h.service.Start(c.Request.Context(), userID, orderID, req, c.ClientIP())
if err != nil {
writePaymentError(c, err)
return
@@ -55,7 +55,7 @@ func (h *Handler) Query(c *gin.Context) {
if !ok {
return
}
item, err := h.service.Query(userID, orderID)
item, err := h.service.Query(c.Request.Context(), userID, orderID)
if err != nil {
writePaymentError(c, err)
return
@@ -74,7 +74,7 @@ func (h *Handler) WalletRecharge(c *gin.Context) {
response.BadRequest(c, "充值金额不正确")
return
}
item, err := h.service.StartWalletRecharge(userID, req, c.ClientIP())
item, err := h.service.StartWalletRecharge(c.Request.Context(), userID, req, c.ClientIP())
if err != nil {
writePaymentError(c, err)
return
@@ -92,7 +92,7 @@ func (h *Handler) WalletRechargeQuery(c *gin.Context) {
if !ok {
return
}
item, err := h.service.QueryWalletRecharge(userID, paymentID)
item, err := h.service.QueryWalletRecharge(c.Request.Context(), userID, paymentID)
if err != nil {
writePaymentError(c, err)
return
@@ -105,7 +105,7 @@ func (h *Handler) QueryRefundStatus(c *gin.Context) {
if !ok {
return
}
item, err := h.service.QueryRefundStatus(orderID)
item, err := h.service.QueryRefundStatus(c.Request.Context(), orderID)
if err != nil {
writePaymentError(c, err)
return
@@ -118,7 +118,7 @@ func (h *Handler) AdminList(c *gin.Context) {
if !ok {
return
}
result, err := h.service.AdminList(query)
result, err := h.service.AdminList(c.Request.Context(), query)
if err != nil {
writePaymentError(c, err)
return
@@ -148,7 +148,7 @@ func (h *Handler) LeshuaNotify(c *gin.Context) {
contentType,
rawPayload,
)
result, err := h.service.HandleLeshuaNotify(params, rawPayload, contentType)
result, err := h.service.HandleLeshuaNotify(c.Request.Context(), params, rawPayload, contentType)
if err != nil || result == nil || !result.OK {
log.Printf("[payment] leshua notify failed third_order_id=%s err=%v", params["third_order_id"], err)
c.String(http.StatusOK, "FAIL")
@@ -181,7 +181,7 @@ func (h *Handler) LakalaNotify(c *gin.Context) {
contentType,
rawPayload,
)
result, err := h.service.HandleNotify("lakala", params, rawPayload, contentType, authorization)
result, err := h.service.HandleNotify(c.Request.Context(), "lakala", params, rawPayload, contentType, authorization)
if err != nil || result == nil || !result.OK {
log.Printf("[payment] lakala notify failed third_order_id=%s err=%v", params["third_order_id"], err)
c.JSON(http.StatusOK, gin.H{"code": "FAIL", "message": "失败"})
+107 -107
View File
@@ -135,12 +135,12 @@ func runtimeConfigFromDTO(dto *paymentconfig.ConfigDTO) *runtimePaymentConfig {
}
}
func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentRequest, clientIP string) (*PaymentDTO, error) {
func (r *Repository) Start(ctx context.Context, userID uint64, orderID uint64, req StartPaymentRequest, clientIP string) (*PaymentDTO, error) {
defaultConfig, err := r.defaultRuntimeConfig()
if err != nil {
return nil, ErrPaymentUnavailable
}
payment, orderRow, err := r.preparePayment(userID, orderID, req, *defaultConfig)
payment, orderRow, err := r.preparePayment(ctx, userID, orderID, req, *defaultConfig)
if err != nil {
return nil, err
}
@@ -154,7 +154,7 @@ func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentReques
return &dto, nil
}
if runtimeConfig.isMockMode() {
if err := r.confirmPaid(payment, "2", time.Now(), map[string]string{
if err := r.confirmPaid(ctx, payment, "2", time.Now(), map[string]string{
"mock": "true",
"third_order_id": payment.ThirdOrderID,
"leshua_order_id": payment.ProviderOrderID,
@@ -162,7 +162,7 @@ func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentReques
}, channelSourceMock); err != nil {
return nil, err
}
latest, err := r.findPaymentByID(payment.ID)
latest, err := r.findPaymentByID(ctx, payment.ID)
if err != nil {
return nil, err
}
@@ -176,13 +176,13 @@ func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentReques
return &dto, nil
}
if runtimeConfig.Channel == nil {
_ = r.markPaymentFailed(payment.ID, nil, "payment channel unavailable")
_ = r.markPaymentFailed(ctx, payment.ID, nil, "payment channel unavailable")
return nil, ErrPaymentUnavailable
}
log.Printf("[payment] payment start order_id=%d order_no=%s payment_id=%d provider=%s amount_cent=%d third_order_id=%s",
orderID, orderRow.OrderNo, payment.ID, runtimeConfig.Provider, payment.AmountCent, payment.ThirdOrderID)
resp, err := runtimeConfig.Channel.CreatePayment(context.Background(), channelCreatePaymentRequest{
resp, err := runtimeConfig.Channel.CreatePayment(ctx, channelCreatePaymentRequest{
ThirdOrderID: payment.ThirdOrderID,
AmountCent: payment.AmountCent,
PayWay: payment.PayWay,
@@ -194,18 +194,18 @@ func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentReques
Attach: orderRow.OrderNo,
})
if err != nil {
_ = r.markPaymentFailed(payment.ID, nil, err.Error())
_ = r.markPaymentFailed(ctx, payment.ID, nil, err.Error())
log.Printf("[payment] payment request failed order_id=%d payment_id=%d provider=%s amount_cent=%d err=%v",
orderID, payment.ID, runtimeConfig.Provider, payment.AmountCent, err)
return nil, err
}
if !resp.OK {
_ = r.markPaymentFailed(payment.ID, resp.Raw, resp.ErrorMessage)
_ = r.markPaymentFailed(ctx, payment.ID, resp.Raw, resp.ErrorMessage)
log.Printf("[payment] payment rejected order_id=%d payment_id=%d provider=%s amount_cent=%d code=%s message=%s",
orderID, payment.ID, runtimeConfig.Provider, payment.AmountCent, firstNonEmpty(resp.Raw["code"], resp.Raw["resp_code"], resp.Raw["result_code"]), resp.ErrorMessage)
return nil, ErrPaymentUnavailable
}
if err := r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
if err := r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
"status": "paying",
"provider_order_id": resp.ProviderOrderID,
"pay_way": firstNonEmpty(resp.PayWay, payment.PayWay),
@@ -217,7 +217,7 @@ func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentReques
}).Error; err != nil {
return nil, err
}
latest, err := r.findPaymentByID(payment.ID)
latest, err := r.findPaymentByID(ctx, payment.ID)
if err != nil {
return nil, err
}
@@ -228,7 +228,7 @@ func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentReques
return &dto, nil
}
func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymentRequest, clientIP string) (*PaymentDTO, error) {
func (r *Repository) StartWalletRecharge(ctx context.Context, userID uint64, req WalletRechargePaymentRequest, clientIP string) (*PaymentDTO, error) {
amountCent := req.AmountCent
if userID == 0 || amountCent < moneyCent(MinWalletRechargeAmount) {
return nil, ErrPaymentCannotStart
@@ -237,12 +237,12 @@ func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymen
if err != nil {
return nil, ErrPaymentUnavailable
}
payment, err := r.createWalletRechargePayment(userID, amountCent, req, *runtimeConfig)
payment, err := r.createWalletRechargePayment(ctx, userID, amountCent, req, *runtimeConfig)
if err != nil {
return nil, err
}
if runtimeConfig.isMockMode() {
if err := r.confirmPaid(payment, "2", time.Now(), map[string]string{
if err := r.confirmPaid(ctx, payment, "2", time.Now(), map[string]string{
"mock": "true",
"third_order_id": payment.ThirdOrderID,
"leshua_order_id": payment.ProviderOrderID,
@@ -250,7 +250,7 @@ func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymen
}, channelSourceMock); err != nil {
return nil, err
}
latest, err := r.findPaymentByID(payment.ID)
latest, err := r.findPaymentByID(ctx, payment.ID)
if err != nil {
return nil, err
}
@@ -259,12 +259,12 @@ func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymen
return &dto, nil
}
if runtimeConfig.Channel == nil {
_ = r.markPaymentFailed(payment.ID, nil, "payment channel unavailable")
_ = r.markPaymentFailed(ctx, payment.ID, nil, "payment channel unavailable")
return nil, ErrPaymentUnavailable
}
log.Printf("[payment] wallet recharge start user_id=%d payment_id=%d provider=%s amount_cent=%d third_order_id=%s",
userID, payment.ID, runtimeConfig.Provider, payment.AmountCent, payment.ThirdOrderID)
resp, err := runtimeConfig.Channel.CreatePayment(context.Background(), channelCreatePaymentRequest{
resp, err := runtimeConfig.Channel.CreatePayment(ctx, channelCreatePaymentRequest{
ThirdOrderID: payment.ThirdOrderID,
AmountCent: payment.AmountCent,
PayWay: payment.PayWay,
@@ -276,18 +276,18 @@ func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymen
Attach: payment.PaymentNo,
})
if err != nil {
_ = r.markPaymentFailed(payment.ID, nil, err.Error())
_ = r.markPaymentFailed(ctx, payment.ID, nil, err.Error())
log.Printf("[payment] wallet recharge request failed user_id=%d payment_id=%d provider=%s amount_cent=%d err=%v",
userID, payment.ID, runtimeConfig.Provider, payment.AmountCent, err)
return nil, err
}
if !resp.OK {
_ = r.markPaymentFailed(payment.ID, resp.Raw, resp.ErrorMessage)
_ = r.markPaymentFailed(ctx, payment.ID, resp.Raw, resp.ErrorMessage)
log.Printf("[payment] wallet recharge rejected user_id=%d payment_id=%d provider=%s amount_cent=%d code=%s message=%s",
userID, payment.ID, runtimeConfig.Provider, payment.AmountCent, firstNonEmpty(resp.Raw["code"], resp.Raw["resp_code"], resp.Raw["result_code"]), resp.ErrorMessage)
return nil, ErrPaymentUnavailable
}
if err := r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
if err := r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
"status": "paying",
"provider_order_id": resp.ProviderOrderID,
"pay_way": firstNonEmpty(resp.PayWay, payment.PayWay),
@@ -299,7 +299,7 @@ func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymen
}).Error; err != nil {
return nil, err
}
latest, err := r.findPaymentByID(payment.ID)
latest, err := r.findPaymentByID(ctx, payment.ID)
if err != nil {
return nil, err
}
@@ -310,9 +310,9 @@ func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymen
return &dto, nil
}
func (r *Repository) QueryWalletRecharge(userID uint64, paymentID uint64) (*PaymentDTO, error) {
func (r *Repository) QueryWalletRecharge(ctx context.Context, userID uint64, paymentID uint64) (*PaymentDTO, error) {
var payment model.PaymentOrder
if err := r.db.Where("id = ? AND user_id = ? AND order_id = 0", paymentID, userID).First(&payment).Error; err != nil {
if err := r.db.WithContext(ctx).Where("id = ? AND user_id = ? AND order_id = 0", paymentID, userID).First(&payment).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, ErrPaymentNotFound
}
@@ -329,14 +329,14 @@ func (r *Repository) QueryWalletRecharge(userID uint64, paymentID uint64) (*Paym
if runtimeConfig.Channel == nil {
return nil, ErrPaymentUnavailable
}
resp, err := runtimeConfig.Channel.QueryPayment(context.Background(), payment.ThirdOrderID, payment.ProviderOrderID)
resp, err := runtimeConfig.Channel.QueryPayment(ctx, payment.ThirdOrderID, payment.ProviderOrderID)
if err != nil {
return nil, err
}
if err := r.applyChannelStatus(&payment, resp.Status, resp.PayTime, resp.Raw, channelSourceQuery); err != nil {
if err := r.applyChannelStatus(ctx, &payment, resp.Status, resp.PayTime, resp.Raw, channelSourceQuery); err != nil {
return nil, err
}
latest, err := r.findPaymentByID(payment.ID)
latest, err := r.findPaymentByID(ctx, payment.ID)
if err != nil {
return nil, err
}
@@ -344,9 +344,9 @@ func (r *Repository) QueryWalletRecharge(userID uint64, paymentID uint64) (*Paym
return &dto, nil
}
func (r *Repository) Query(userID uint64, orderID uint64) (*PaymentDTO, error) {
func (r *Repository) Query(ctx context.Context, userID uint64, orderID uint64) (*PaymentDTO, error) {
var payment model.PaymentOrder
if err := r.db.Where("order_id = ? AND user_id = ? AND biz_type = ?", orderID, userID, "order_pay").Order("id DESC").First(&payment).Error; err != nil {
if err := r.db.WithContext(ctx).Where("order_id = ? AND user_id = ? AND biz_type = ?", orderID, userID, "order_pay").Order("id DESC").First(&payment).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, ErrPaymentNotFound
}
@@ -363,14 +363,14 @@ func (r *Repository) Query(userID uint64, orderID uint64) (*PaymentDTO, error) {
if runtimeConfig.Channel == nil {
return nil, ErrPaymentUnavailable
}
resp, err := runtimeConfig.Channel.QueryPayment(context.Background(), payment.ThirdOrderID, payment.ProviderOrderID)
resp, err := runtimeConfig.Channel.QueryPayment(ctx, payment.ThirdOrderID, payment.ProviderOrderID)
if err != nil {
return nil, err
}
if err := r.applyChannelStatus(&payment, resp.Status, resp.PayTime, resp.Raw, channelSourceQuery); err != nil {
if err := r.applyChannelStatus(ctx, &payment, resp.Status, resp.PayTime, resp.Raw, channelSourceQuery); err != nil {
return nil, err
}
latest, err := r.findPaymentByID(payment.ID)
latest, err := r.findPaymentByID(ctx, payment.ID)
if err != nil {
return nil, err
}
@@ -378,17 +378,17 @@ func (r *Repository) Query(userID uint64, orderID uint64) (*PaymentDTO, error) {
return &dto, nil
}
func (r *Repository) HandleLeshuaNotify(params map[string]string, rawPayload string, contentType string) (*NotifyResult, error) {
return r.HandleNotify("leshua", params, rawPayload, contentType, "")
func (r *Repository) HandleLeshuaNotify(ctx context.Context, params map[string]string, rawPayload string, contentType string) (*NotifyResult, error) {
return r.HandleNotify(ctx, "leshua", params, rawPayload, contentType, "")
}
func (r *Repository) HandleNotify(provider string, params map[string]string, rawPayload string, contentType string, authorization string) (*NotifyResult, error) {
func (r *Repository) HandleNotify(ctx context.Context, provider string, params map[string]string, rawPayload string, contentType string, authorization string) (*NotifyResult, error) {
// 退款通知会携带 merchant_refund_id 或 leshua_refund_id。
if params["merchant_refund_id"] != "" || params["leshua_refund_id"] != "" || params["provider_refund_id"] != "" {
return r.HandleRefundNotify(provider, params, rawPayload, contentType, authorization)
return r.HandleRefundNotify(ctx, provider, params, rawPayload, contentType, authorization)
}
payment, err := r.findPaymentForNotify(params)
payment, err := r.findPaymentForNotify(ctx, params)
if err != nil {
return nil, err
}
@@ -396,28 +396,28 @@ func (r *Repository) HandleNotify(provider string, params map[string]string, raw
if err != nil {
return nil, ErrPaymentUnavailable
}
verify, err := r.verifyNotify(payment, runtimeConfig, params, rawPayload, contentType, authorization)
verify, err := r.verifyNotify(ctx, payment, runtimeConfig, params, rawPayload, contentType, authorization)
if err != nil {
return nil, err
}
if amount := parseCent(params["amount"]); amount > 0 && amount != payment.AmountCent {
if err := r.recordNotifyDiagnostic(payment.ID, params, rawPayload, contentType, verify, "amount_mismatch"); err != nil {
if err := r.recordNotifyDiagnostic(ctx, payment.ID, params, rawPayload, contentType, verify, "amount_mismatch"); err != nil {
log.Printf("[payment] %s notify diagnostic save failed third_order_id=%s err=%v", runtimeConfig.Provider, params["third_order_id"], err)
}
return nil, ErrPaymentVerifyFailed
}
raw := withNotifyDiagnostic(params, rawPayload, contentType, verify, "verified")
if err := r.applyChannelStatus(payment, normalizeNotifyPaymentStatus(runtimeConfig.Provider, params["status"]), params["pay_time"], raw, channelSourceNotify); err != nil {
if err := r.applyChannelStatus(ctx, payment, normalizeNotifyPaymentStatus(runtimeConfig.Provider, params["status"]), params["pay_time"], raw, channelSourceNotify); err != nil {
return nil, err
}
return &NotifyResult{OK: true, Message: "000000"}, nil
}
// StartRefund 创建退款单,并在本地落库后调用乐刷退款接口。
func (r *Repository) StartRefund(orderID uint64, refundAmountCent int64, bizType string, remark string) (*RefundDTO, error) {
func (r *Repository) StartRefund(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (*RefundDTO, error) {
var originalPayment model.PaymentOrder
if err := r.db.Where("order_id = ? AND status = 'paid' AND biz_type = 'order_pay'", orderID).Order("id DESC").First(&originalPayment).Error; err != nil {
if err := r.db.WithContext(ctx).Where("order_id = ? AND status = 'paid' AND biz_type = 'order_pay'", orderID).Order("id DESC").First(&originalPayment).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, ErrPaymentNotFound
}
@@ -429,7 +429,7 @@ func (r *Repository) StartRefund(orderID uint64, refundAmountCent int64, bizType
}
var existingRefund model.PaymentOrder
err = r.db.Where("order_id = ? AND biz_type = ? AND status NOT IN ('failed')", orderID, bizType).Order("id DESC").First(&existingRefund).Error
err = r.db.WithContext(ctx).Where("order_id = ? AND biz_type = ? AND status NOT IN ('failed')", orderID, bizType).Order("id DESC").First(&existingRefund).Error
if err == nil {
dto := toRefundDTO(existingRefund)
return &dto, nil
@@ -468,32 +468,32 @@ func (r *Repository) StartRefund(orderID uint64, refundAmountCent int64, bizType
if remark != "" {
refundOrder.RawResponse = datatypes.JSON([]byte(fmt.Sprintf(`{"mock":"true","remark":"%s"}`, remark)))
}
if err := r.db.Create(&refundOrder).Error; err != nil {
if err := r.db.WithContext(ctx).Create(&refundOrder).Error; err != nil {
return nil, err
}
r.recordConfigUsage(runtimeConfig, &refundOrder)
if err := r.updateOrderRefundStatus(orderID, refundAmountCent); err != nil {
if err := r.updateOrderRefundStatus(ctx, orderID, refundAmountCent); err != nil {
log.Printf("[payment] mock update order refund status failed order_id=%d err=%v", orderID, err)
}
dto := toRefundDTO(refundOrder)
return &dto, nil
}
if err := r.db.Create(&refundOrder).Error; err != nil {
if err := r.db.WithContext(ctx).Create(&refundOrder).Error; err != nil {
return nil, err
}
log.Printf("[payment] refund start order_id=%d order_no=%s payment_id=%d biz_type=%s provider=%s amount_cent=%d merchant_refund_id=%s origin_third_order_id=%s origin_provider_order_id=%s",
orderID, originalPayment.OrderNo, refundOrder.ID, bizType, runtimeConfig.Provider, refundAmountCent, merchantRefundID, originalPayment.ThirdOrderID, refundOriginProviderOrderID(originalPayment))
r.recordConfigUsage(runtimeConfig, &refundOrder)
if err := r.markOrderRefunding(orderID, refundAmountCent); err != nil {
if err := r.markOrderRefunding(ctx, orderID, refundAmountCent); err != nil {
log.Printf("[payment] mark order refunding failed order_id=%d err=%v", orderID, err)
}
if runtimeConfig.Channel == nil {
_ = r.markRefundFailed(refundOrder.ID, orderID, refundAmountCent, map[string]string{"error": "payment channel unavailable"})
_ = r.markRefundFailed(ctx, refundOrder.ID, orderID, refundAmountCent, map[string]string{"error": "payment channel unavailable"})
return nil, ErrPaymentUnavailable
}
resp, err := runtimeConfig.Channel.CreateRefund(context.Background(), channelCreateRefundRequest{
resp, err := runtimeConfig.Channel.CreateRefund(ctx, channelCreateRefundRequest{
ThirdOrderID: originalPayment.ThirdOrderID,
ProviderOrderID: refundOriginProviderOrderID(originalPayment),
MerchantRefundID: merchantRefundID,
@@ -503,13 +503,13 @@ func (r *Repository) StartRefund(orderID uint64, refundAmountCent int64, bizType
Remark: remark,
})
if err != nil {
_ = r.markRefundFailed(refundOrder.ID, orderID, refundAmountCent, map[string]string{"error": err.Error()})
_ = r.markRefundFailed(ctx, refundOrder.ID, orderID, refundAmountCent, map[string]string{"error": err.Error()})
log.Printf("[payment] refund request failed order_id=%d payment_id=%d biz_type=%s provider=%s amount_cent=%d err=%v",
orderID, refundOrder.ID, bizType, runtimeConfig.Provider, refundAmountCent, err)
return nil, err
}
if !resp.OK {
_ = r.markRefundFailed(refundOrder.ID, orderID, refundAmountCent, resp.Raw)
_ = r.markRefundFailed(ctx, refundOrder.ID, orderID, refundAmountCent, resp.Raw)
log.Printf("[payment] refund rejected order_id=%d payment_id=%d biz_type=%s provider=%s amount_cent=%d code=%s message=%s",
orderID, refundOrder.ID, bizType, runtimeConfig.Provider, refundAmountCent, firstNonEmpty(resp.Raw["code"], resp.Raw["resp_code"], resp.Raw["result_code"]), resp.ErrorMessage)
return nil, ErrPaymentUnavailable
@@ -524,7 +524,7 @@ func (r *Repository) StartRefund(orderID uint64, refundAmountCent int64, bizType
} else if resp.Status == "failed" {
refundStatus = "failed"
}
if err := r.db.Model(&model.PaymentOrder{}).Where("id = ?", refundOrder.ID).Updates(map[string]any{
if err := r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", refundOrder.ID).Updates(map[string]any{
"status": refundStatus,
"provider_order_id": resp.ProviderRefundID,
"raw_request": jsonMap(resp.RawRequest),
@@ -535,12 +535,12 @@ func (r *Repository) StartRefund(orderID uint64, refundAmountCent int64, bizType
}
if refundStatus == "refunded" {
_ = r.updateOrderRefundStatus(orderID, refundAmountCent)
_ = r.updateOrderRefundStatus(ctx, orderID, refundAmountCent)
refundOrder.PaidAt = paidAt
} else if refundStatus == "failed" {
_ = r.markOrderRefundFailed(orderID, refundAmountCent)
_ = r.markOrderRefundFailed(ctx, orderID, refundAmountCent)
} else {
_ = r.markOrderRefunding(orderID, refundAmountCent)
_ = r.markOrderRefunding(ctx, orderID, refundAmountCent)
}
refundOrder.Status = refundStatus
refundOrder.ProviderOrderID = resp.ProviderRefundID
@@ -552,9 +552,9 @@ func (r *Repository) StartRefund(orderID uint64, refundAmountCent int64, bizType
}
// QueryRefundStatus 查询订单最近一笔退款状态。
func (r *Repository) QueryRefundStatus(orderID uint64) (*RefundDTO, error) {
func (r *Repository) QueryRefundStatus(ctx context.Context, orderID uint64) (*RefundDTO, error) {
var payment model.PaymentOrder
if err := r.db.Where("order_id = ? AND biz_type IN ?", orderID, refundBizTypes).Order("id DESC").First(&payment).Error; err != nil {
if err := r.db.WithContext(ctx).Where("order_id = ? AND biz_type IN ?", orderID, refundBizTypes).Order("id DESC").First(&payment).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, ErrPaymentNotFound
}
@@ -571,7 +571,7 @@ func (r *Repository) QueryRefundStatus(orderID uint64) (*RefundDTO, error) {
if runtimeConfig.Channel == nil {
return nil, ErrPaymentUnavailable
}
resp, err := runtimeConfig.Channel.QueryRefund(context.Background(), channelQueryRefundRequest{
resp, err := runtimeConfig.Channel.QueryRefund(ctx, channelQueryRefundRequest{
ThirdOrderID: payment.ThirdOrderID,
MerchantRefundID: payment.ThirdOrderID,
ProviderRefundID: payment.ProviderOrderID,
@@ -581,7 +581,7 @@ func (r *Repository) QueryRefundStatus(orderID uint64) (*RefundDTO, error) {
}
if resp.Status == "refunded" {
now := time.Now()
if err := r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
if err := r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
"status": "refunded",
"paid_at": now,
"raw_response": jsonMap(withRawSource(resp.Raw, channelSourceQuery)),
@@ -590,26 +590,26 @@ func (r *Repository) QueryRefundStatus(orderID uint64) (*RefundDTO, error) {
}
payment.Status = "refunded"
payment.PaidAt = &now
_ = r.updateOrderRefundStatus(orderID, payment.AmountCent)
_ = r.updateOrderRefundStatus(ctx, orderID, payment.AmountCent)
} else if resp.Status == "failed" {
if err := r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
if err := r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
"status": "failed",
"raw_response": jsonMap(withRawSource(resp.Raw, channelSourceQuery)),
}).Error; err != nil {
return nil, err
}
payment.Status = "failed"
_ = r.markOrderRefundFailed(orderID, payment.AmountCent)
_ = r.markOrderRefundFailed(ctx, orderID, payment.AmountCent)
}
dto := toRefundDTO(payment)
return &dto, nil
}
func (r *Repository) AdminList(query AdminPaymentQuery) (*PaginatedResult, error) {
db := r.db.Table("payment_orders AS p").
func (r *Repository) AdminList(ctx context.Context, query AdminPaymentQuery) (*PaginatedResult, error) {
db := r.db.WithContext(ctx).Table("payment_orders AS p").
Select("p.*, COALESCE(u.phone, '') AS user_phone").
Joins("LEFT JOIN users AS u ON u.id = p.user_id")
countDB := r.db.Model(&model.PaymentOrder{})
countDB := r.db.WithContext(ctx).Model(&model.PaymentOrder{})
if query.UserID > 0 {
db = db.Where("p.user_id = ?", query.UserID)
countDB = countDB.Where("user_id = ?", query.UserID)
@@ -651,8 +651,8 @@ func (r *Repository) AdminList(query AdminPaymentQuery) (*PaginatedResult, error
}
// HandleRefundNotify 处理渠道退款通知。
func (r *Repository) HandleRefundNotify(provider string, params map[string]string, rawPayload string, contentType string, authorization string) (*NotifyResult, error) {
payment, err := r.findRefundPaymentForNotify(params)
func (r *Repository) HandleRefundNotify(ctx context.Context, provider string, params map[string]string, rawPayload string, contentType string, authorization string) (*NotifyResult, error) {
payment, err := r.findRefundPaymentForNotify(ctx, params)
if err != nil {
return nil, err
}
@@ -660,7 +660,7 @@ func (r *Repository) HandleRefundNotify(provider string, params map[string]strin
if err != nil {
return nil, ErrPaymentUnavailable
}
verify, err := r.verifyNotify(payment, runtimeConfig, params, rawPayload, contentType, authorization)
verify, err := r.verifyNotify(ctx, payment, runtimeConfig, params, rawPayload, contentType, authorization)
if err != nil {
return nil, err
}
@@ -670,7 +670,7 @@ func (r *Repository) HandleRefundNotify(provider string, params map[string]strin
switch status {
case "refunded":
now := time.Now()
if err := r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
if err := r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
"status": "refunded",
"paid_at": now,
"notified_at": now,
@@ -678,16 +678,16 @@ func (r *Repository) HandleRefundNotify(provider string, params map[string]strin
}).Error; err != nil {
return nil, err
}
_ = r.updateOrderRefundStatus(payment.OrderID, payment.AmountCent)
_ = r.updateOrderRefundStatus(ctx, payment.OrderID, payment.AmountCent)
case "failed":
r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
"status": "failed",
"notified_at": time.Now(),
"raw_response": jsonMap(raw),
})
_ = r.markOrderRefundFailed(payment.OrderID, payment.AmountCent)
_ = r.markOrderRefundFailed(ctx, payment.OrderID, payment.AmountCent)
default:
r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
"status": "refunding",
"raw_response": jsonMap(raw),
})
@@ -696,42 +696,42 @@ func (r *Repository) HandleRefundNotify(provider string, params map[string]strin
}
// updateOrderRefundStatus 更新订单退款成功状态。
func (r *Repository) updateOrderRefundStatus(orderID uint64, refundAmountCent int64) error {
func (r *Repository) updateOrderRefundStatus(ctx context.Context, orderID uint64, refundAmountCent int64) error {
now := time.Now()
return r.db.Model(&model.RentalOrder{}).Where("id = ?", orderID).Updates(map[string]any{
return r.db.WithContext(ctx).Model(&model.RentalOrder{}).Where("id = ?", orderID).Updates(map[string]any{
"refund_status": "refunded",
"refund_amount_cent": refundAmountCent,
"refunded_at": now,
}).Error
}
func (r *Repository) markOrderRefunding(orderID uint64, refundAmountCent int64) error {
return r.db.Model(&model.RentalOrder{}).Where("id = ?", orderID).Updates(map[string]any{
func (r *Repository) markOrderRefunding(ctx context.Context, orderID uint64, refundAmountCent int64) error {
return r.db.WithContext(ctx).Model(&model.RentalOrder{}).Where("id = ?", orderID).Updates(map[string]any{
"refund_status": "refunding",
"refund_amount_cent": refundAmountCent,
"refunded_at": nil,
}).Error
}
func (r *Repository) markOrderRefundFailed(orderID uint64, refundAmountCent int64) error {
return r.db.Model(&model.RentalOrder{}).Where("id = ?", orderID).Updates(map[string]any{
func (r *Repository) markOrderRefundFailed(ctx context.Context, orderID uint64, refundAmountCent int64) error {
return r.db.WithContext(ctx).Model(&model.RentalOrder{}).Where("id = ?", orderID).Updates(map[string]any{
"refund_status": "failed",
"refund_amount_cent": refundAmountCent,
"refunded_at": nil,
}).Error
}
func (r *Repository) markRefundFailed(paymentID uint64, orderID uint64, refundAmountCent int64, raw map[string]string) error {
func (r *Repository) markRefundFailed(ctx context.Context, paymentID uint64, orderID uint64, refundAmountCent int64, raw map[string]string) error {
if raw == nil {
raw = map[string]string{"error": "refund failed"}
}
if err := r.db.Model(&model.PaymentOrder{}).Where("id = ?", paymentID).Updates(map[string]any{
if err := r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", paymentID).Updates(map[string]any{
"status": "failed",
"raw_response": jsonMap(raw),
}).Error; err != nil {
return err
}
return r.markOrderRefundFailed(orderID, refundAmountCent)
return r.markOrderRefundFailed(ctx, orderID, refundAmountCent)
}
// toRefundDTO 将支付表里的退款单转换为接口 DTO。
@@ -751,10 +751,10 @@ func toRefundDTO(payment model.PaymentOrder) RefundDTO {
}
}
func (r *Repository) preparePayment(userID uint64, orderID uint64, req StartPaymentRequest, runtimeConfig runtimePaymentConfig) (*model.PaymentOrder, *model.RentalOrder, error) {
func (r *Repository) preparePayment(ctx context.Context, userID uint64, orderID uint64, req StartPaymentRequest, runtimeConfig runtimePaymentConfig) (*model.PaymentOrder, *model.RentalOrder, error) {
var paymentID uint64
var orderRow model.RentalOrder
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var row model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Where("id = ? AND renter_id = ?", orderID, userID).
@@ -807,7 +807,7 @@ func (r *Repository) preparePayment(userID uint64, orderID uint64, req StartPaym
if err != nil {
return nil, nil, err
}
payment, err := r.findPaymentByID(paymentID)
payment, err := r.findPaymentByID(ctx, paymentID)
if err != nil {
return nil, nil, err
}
@@ -875,7 +875,7 @@ func newOrderPayment(row model.RentalOrder, amountCent int64, req StartPaymentRe
return payment, nil
}
func (r *Repository) createWalletRechargePayment(userID uint64, amountCent int64, req WalletRechargePaymentRequest, runtimeConfig runtimePaymentConfig) (*model.PaymentOrder, error) {
func (r *Repository) createWalletRechargePayment(ctx context.Context, userID uint64, amountCent int64, req WalletRechargePaymentRequest, runtimeConfig runtimePaymentConfig) (*model.PaymentOrder, error) {
paymentNo, err := newPaymentNo()
if err != nil {
return nil, err
@@ -899,13 +899,13 @@ func (r *Repository) createWalletRechargePayment(userID uint64, amountCent int64
payment.ProviderOrderID = "MOCK" + paymentNo
payment.TDCode = "mock://payment/recharge/" + paymentNo
}
if err := r.db.Create(&payment).Error; err != nil {
if err := r.db.WithContext(ctx).Create(&payment).Error; err != nil {
return nil, err
}
return &payment, nil
}
func (r *Repository) applyChannelStatus(payment *model.PaymentOrder, status string, payTime string, raw map[string]string, source string) error {
func (r *Repository) applyChannelStatus(ctx context.Context, payment *model.PaymentOrder, status string, payTime string, raw map[string]string, source string) error {
switch status {
case "paid":
paidAt := parseChannelTime(payTime)
@@ -913,17 +913,17 @@ func (r *Repository) applyChannelStatus(payment *model.PaymentOrder, status stri
now := time.Now()
paidAt = &now
}
return r.confirmPaid(payment, status, *paidAt, raw, source)
return r.confirmPaid(ctx, payment, status, *paidAt, raw, source)
case "closed":
return r.updateChannelStatus(payment.ID, "closed", raw, source)
return r.updateChannelStatus(ctx, payment.ID, "closed", raw, source)
case "failed":
return r.updateChannelStatus(payment.ID, "failed", raw, source)
return r.updateChannelStatus(ctx, payment.ID, "failed", raw, source)
default:
return r.updateChannelStatus(payment.ID, "paying", raw, source)
return r.updateChannelStatus(ctx, payment.ID, "paying", raw, source)
}
}
func (r *Repository) updateChannelStatus(paymentID uint64, status string, raw map[string]string, source string) error {
func (r *Repository) updateChannelStatus(ctx context.Context, paymentID uint64, status string, raw map[string]string, source string) error {
updates := map[string]any{
"status": status,
"raw_response": jsonMap(withRawSource(raw, source)),
@@ -931,16 +931,16 @@ func (r *Repository) updateChannelStatus(paymentID uint64, status string, raw ma
if source == channelSourceNotify {
updates["notified_at"] = time.Now()
}
return r.db.Model(&model.PaymentOrder{}).Where("id = ?", paymentID).Updates(updates).Error
return r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", paymentID).Updates(updates).Error
}
func (r *Repository) confirmPaid(payment *model.PaymentOrder, status string, paidAt time.Time, raw map[string]string, source string) error {
func (r *Repository) confirmPaid(ctx context.Context, payment *model.PaymentOrder, status string, paidAt time.Time, raw map[string]string, source string) error {
if payment.Status != "paid" {
if payment.OrderID == 0 {
if r.walletRepo == nil {
return ErrDependencyUnavailable
}
if err := r.walletRepo.ConfirmRechargeFromChannel(context.Background(), payment.UserID, firstNonEmpty(payment.ProviderOrderID, payment.PaymentNo), payment.AmountCent); err != nil {
if err := r.walletRepo.ConfirmRechargeFromChannel(ctx, payment.UserID, firstNonEmpty(payment.ProviderOrderID, payment.PaymentNo), payment.AmountCent); err != nil {
return err
}
} else {
@@ -961,34 +961,34 @@ func (r *Repository) confirmPaid(payment *model.PaymentOrder, status string, pai
if source == channelSourceNotify {
updates["notified_at"] = time.Now()
}
return r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(updates).Error
return r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(updates).Error
}
func (r *Repository) markPaymentFailed(paymentID uint64, raw map[string]string, message string) error {
func (r *Repository) markPaymentFailed(ctx context.Context, paymentID uint64, raw map[string]string, message string) error {
if raw == nil {
raw = map[string]string{"error": message}
}
return r.db.Model(&model.PaymentOrder{}).Where("id = ?", paymentID).Updates(map[string]any{
return r.db.WithContext(ctx).Model(&model.PaymentOrder{}).Where("id = ?", paymentID).Updates(map[string]any{
"status": "failed",
"raw_response": jsonMap(raw),
}).Error
}
func (r *Repository) findPaymentByID(paymentID uint64) (*model.PaymentOrder, error) {
func (r *Repository) findPaymentByID(ctx context.Context, paymentID uint64) (*model.PaymentOrder, error) {
var payment model.PaymentOrder
if err := r.db.First(&payment, paymentID).Error; err != nil {
if err := r.db.WithContext(ctx).First(&payment, paymentID).Error; err != nil {
return nil, err
}
return &payment, nil
}
func (r *Repository) findPaymentForNotify(params map[string]string) (*model.PaymentOrder, error) {
func (r *Repository) findPaymentForNotify(ctx context.Context, params map[string]string) (*model.PaymentOrder, error) {
thirdOrderID := params["third_order_id"]
if thirdOrderID == "" {
return nil, ErrPaymentNotFound
}
var payment model.PaymentOrder
if err := r.db.Where("third_order_id = ?", thirdOrderID).First(&payment).Error; err != nil {
if err := r.db.WithContext(ctx).Where("third_order_id = ?", thirdOrderID).First(&payment).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, ErrPaymentNotFound
}
@@ -997,13 +997,13 @@ func (r *Repository) findPaymentForNotify(params map[string]string) (*model.Paym
return &payment, nil
}
func (r *Repository) findRefundPaymentForNotify(params map[string]string) (*model.PaymentOrder, error) {
func (r *Repository) findRefundPaymentForNotify(ctx context.Context, params map[string]string) (*model.PaymentOrder, error) {
merchantRefundID := params["merchant_refund_id"]
if merchantRefundID == "" {
return nil, ErrPaymentNotFound
}
var payment model.PaymentOrder
if err := r.db.Where("third_order_id = ?", merchantRefundID).First(&payment).Error; err != nil {
if err := r.db.WithContext(ctx).Where("third_order_id = ?", merchantRefundID).First(&payment).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, ErrPaymentNotFound
}
@@ -1012,7 +1012,7 @@ func (r *Repository) findRefundPaymentForNotify(params map[string]string) (*mode
return &payment, nil
}
func (r *Repository) verifyNotify(payment *model.PaymentOrder, runtimeConfig *runtimePaymentConfig, params map[string]string, rawPayload string, contentType string, authorization string) (channelVerifyNotifyResult, error) {
func (r *Repository) verifyNotify(ctx context.Context, payment *model.PaymentOrder, runtimeConfig *runtimePaymentConfig, params map[string]string, rawPayload string, contentType string, authorization string) (channelVerifyNotifyResult, error) {
var verify channelVerifyNotifyResult
if runtimeConfig.isMockMode() {
return verify, nil
@@ -1032,7 +1032,7 @@ func (r *Repository) verifyNotify(payment *model.PaymentOrder, runtimeConfig *ru
verify.ParamKeys,
firstNonEmpty(verify.BaseString["notify_key"], verify.BaseString["notify_cert"]),
)
if err := r.recordNotifyDiagnostic(payment.ID, params, rawPayload, contentType, verify, "verify_failed"); err != nil {
if err := r.recordNotifyDiagnostic(ctx, payment.ID, params, rawPayload, contentType, verify, "verify_failed"); err != nil {
log.Printf("[payment] %s notify diagnostic save failed payment_id=%d err=%v", runtimeConfig.Provider, payment.ID, err)
}
return verify, ErrPaymentVerifyFailed
@@ -1231,12 +1231,12 @@ func withRawSource(raw map[string]string, source string) map[string]string {
return out
}
func (r *Repository) recordNotifyDiagnostic(paymentID uint64, params map[string]string, rawPayload string, contentType string, verify channelVerifyNotifyResult, status string) error {
func (r *Repository) recordNotifyDiagnostic(ctx context.Context, paymentID uint64, params map[string]string, rawPayload string, contentType string, verify channelVerifyNotifyResult, status string) error {
if paymentID == 0 {
return nil
}
raw := withNotifyDiagnostic(params, rawPayload, contentType, verify, status)
return r.db.Model(&model.PaymentOrder{}).
return r.db.WithContext(ctx).Model(&model.PaymentOrder{}).
Where("id = ?", paymentID).
Update("raw_response", jsonMap(raw)).Error
}
@@ -246,7 +246,7 @@ func TestRuntimePaymentConfigRequiredFields(t *testing.T) {
func TestServiceStartRequiresNonZeroOrderID(t *testing.T) {
svc := &Service{repo: &Repository{}}
_, err := svc.Start(1, 0, StartPaymentRequest{}, "127.0.0.1")
_, err := svc.Start(t.Context(), 1, 0, StartPaymentRequest{}, "127.0.0.1")
if !errors.Is(err, ErrPaymentCannotStart) {
t.Fatalf("error = %v, want ErrPaymentCannotStart", err)
}
@@ -255,7 +255,7 @@ func TestServiceStartRequiresNonZeroOrderID(t *testing.T) {
func TestServiceStartRequiresNonZeroUserID(t *testing.T) {
svc := &Service{repo: &Repository{}}
_, err := svc.Start(0, 100, StartPaymentRequest{}, "127.0.0.1")
_, err := svc.Start(t.Context(), 0, 100, StartPaymentRequest{}, "127.0.0.1")
if !errors.Is(err, ErrPaymentCannotStart) {
t.Fatalf("error = %v, want ErrPaymentCannotStart", err)
}
@@ -264,12 +264,12 @@ func TestServiceStartRequiresNonZeroUserID(t *testing.T) {
func TestServiceStartRefundRequiresPositiveAmount(t *testing.T) {
svc := &Service{repo: &Repository{}}
_, err := svc.StartRefund(100, 0, "cancel_refund", "test")
_, err := svc.StartRefund(t.Context(), 100, 0, "cancel_refund", "test")
if !errors.Is(err, ErrRefundCannotStart) {
t.Fatalf("error = %v, want ErrRefundCannotStart", err)
}
_, err = svc.StartRefund(100, -1000, "cancel_refund", "test")
_, err = svc.StartRefund(t.Context(), 100, -1000, "cancel_refund", "test")
if !errors.Is(err, ErrRefundCannotStart) {
t.Fatalf("error = %v, want ErrRefundCannotStart", err)
}
@@ -278,7 +278,7 @@ func TestServiceStartRefundRequiresPositiveAmount(t *testing.T) {
func TestServiceStartRefundRequiresNonZeroOrderID(t *testing.T) {
svc := &Service{repo: &Repository{}}
_, err := svc.StartRefund(0, 1000, "cancel_refund", "test")
_, err := svc.StartRefund(t.Context(), 0, 1000, "cancel_refund", "test")
if !errors.Is(err, ErrRefundCannotStart) {
t.Fatalf("error = %v, want ErrRefundCannotStart", err)
}
+19 -18
View File
@@ -1,6 +1,7 @@
package payment
import (
"context"
"errors"
"strings"
)
@@ -33,81 +34,81 @@ func NewService(repo *Repository, appEnv ...string) *Service {
}
}
func (s *Service) Start(userID uint64, orderID uint64, req StartPaymentRequest, clientIP string) (*PaymentDTO, error) {
func (s *Service) Start(ctx context.Context, userID uint64, orderID uint64, req StartPaymentRequest, clientIP string) (*PaymentDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if userID == 0 || orderID == 0 {
return nil, ErrPaymentCannotStart
}
return s.repo.Start(userID, orderID, req, clientIP)
return s.repo.Start(ctx, userID, orderID, req, clientIP)
}
func (s *Service) Query(userID uint64, orderID uint64) (*PaymentDTO, error) {
func (s *Service) Query(ctx context.Context, userID uint64, orderID uint64) (*PaymentDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if userID == 0 || orderID == 0 {
return nil, ErrPaymentNotFound
}
return s.repo.Query(userID, orderID)
return s.repo.Query(ctx, userID, orderID)
}
func (s *Service) StartWalletRecharge(userID uint64, req WalletRechargePaymentRequest, clientIP string) (*PaymentDTO, error) {
func (s *Service) StartWalletRecharge(ctx context.Context, userID uint64, req WalletRechargePaymentRequest, clientIP string) (*PaymentDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if !s.walletRechargeEnabled {
return nil, ErrWalletRechargeDisabled
}
return s.repo.StartWalletRecharge(userID, req, clientIP)
return s.repo.StartWalletRecharge(ctx, userID, req, clientIP)
}
func (s *Service) QueryWalletRecharge(userID uint64, paymentID uint64) (*PaymentDTO, error) {
func (s *Service) QueryWalletRecharge(ctx context.Context, userID uint64, paymentID uint64) (*PaymentDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if userID == 0 || paymentID == 0 {
return nil, ErrPaymentNotFound
}
return s.repo.QueryWalletRecharge(userID, paymentID)
return s.repo.QueryWalletRecharge(ctx, userID, paymentID)
}
func (s *Service) HandleLeshuaNotify(params map[string]string, rawPayload string, contentType string) (*NotifyResult, error) {
func (s *Service) HandleLeshuaNotify(ctx context.Context, params map[string]string, rawPayload string, contentType string) (*NotifyResult, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.HandleLeshuaNotify(params, rawPayload, contentType)
return s.repo.HandleLeshuaNotify(ctx, params, rawPayload, contentType)
}
func (s *Service) HandleNotify(provider string, params map[string]string, rawPayload string, contentType string, authorization string) (*NotifyResult, error) {
func (s *Service) HandleNotify(ctx context.Context, provider string, params map[string]string, rawPayload string, contentType string, authorization string) (*NotifyResult, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.HandleNotify(provider, params, rawPayload, contentType, authorization)
return s.repo.HandleNotify(ctx, provider, params, rawPayload, contentType, authorization)
}
func (s *Service) StartRefund(orderID uint64, refundAmountCent int64, bizType string, remark string) (*RefundDTO, error) {
func (s *Service) StartRefund(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (*RefundDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 || refundAmountCent <= 0 {
return nil, ErrRefundCannotStart
}
return s.repo.StartRefund(orderID, refundAmountCent, bizType, remark)
return s.repo.StartRefund(ctx, orderID, refundAmountCent, bizType, remark)
}
func (s *Service) QueryRefundStatus(orderID uint64) (*RefundDTO, error) {
func (s *Service) QueryRefundStatus(ctx context.Context, orderID uint64) (*RefundDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 {
return nil, ErrPaymentNotFound
}
return s.repo.QueryRefundStatus(orderID)
return s.repo.QueryRefundStatus(ctx, orderID)
}
func (s *Service) AdminList(query AdminPaymentQuery) (*PaginatedResult, error) {
func (s *Service) AdminList(ctx context.Context, query AdminPaymentQuery) (*PaginatedResult, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
@@ -120,5 +121,5 @@ func (s *Service) AdminList(query AdminPaymentQuery) (*PaginatedResult, error) {
if query.PageSize > 100 {
query.PageSize = 100
}
return s.repo.AdminList(query)
return s.repo.AdminList(ctx, query)
}
@@ -8,7 +8,7 @@ import (
// TestServiceDependencyChecks 测试 Service 依赖检查
func TestServiceStartWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
_, err := svc.Start(1, 100, StartPaymentRequest{}, "127.0.0.1")
_, err := svc.Start(t.Context(), 1, 100, StartPaymentRequest{}, "127.0.0.1")
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("Start() error = %v, want ErrDependencyUnavailable", err)
}
@@ -18,13 +18,13 @@ func TestServiceStartWithInvalidParams(t *testing.T) {
svc := &Service{repo: &Repository{}}
// 测试 userID 为 0
_, err := svc.Start(0, 100, StartPaymentRequest{}, "127.0.0.1")
_, err := svc.Start(t.Context(), 0, 100, StartPaymentRequest{}, "127.0.0.1")
if !errors.Is(err, ErrPaymentCannotStart) {
t.Fatalf("Start() error = %v, want ErrPaymentCannotStart", err)
}
// 测试 orderID 为 0
_, err = svc.Start(1, 0, StartPaymentRequest{}, "127.0.0.1")
_, err = svc.Start(t.Context(), 1, 0, StartPaymentRequest{}, "127.0.0.1")
if !errors.Is(err, ErrPaymentCannotStart) {
t.Fatalf("Start() error = %v, want ErrPaymentCannotStart", err)
}
@@ -32,7 +32,7 @@ func TestServiceStartWithInvalidParams(t *testing.T) {
func TestServiceQueryWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
_, err := svc.Query(1, 100)
_, err := svc.Query(t.Context(), 1, 100)
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("Query() error = %v, want ErrDependencyUnavailable", err)
}
@@ -41,7 +41,7 @@ func TestServiceQueryWithNilRepo(t *testing.T) {
func TestServiceQueryWithInvalidParams(t *testing.T) {
svc := &Service{repo: &Repository{}}
_, err := svc.Query(0, 100)
_, err := svc.Query(t.Context(), 0, 100)
if !errors.Is(err, ErrPaymentNotFound) {
t.Fatalf("Query() error = %v, want ErrPaymentNotFound", err)
}
@@ -49,7 +49,7 @@ func TestServiceQueryWithInvalidParams(t *testing.T) {
func TestServiceStartRefundWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
_, err := svc.StartRefund(100, 1000, "cancel_refund", "test")
_, err := svc.StartRefund(t.Context(), 100, 1000, "cancel_refund", "test")
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("StartRefund() error = %v, want ErrDependencyUnavailable", err)
}
@@ -59,13 +59,13 @@ func TestServiceStartRefundWithInvalidParams(t *testing.T) {
svc := &Service{repo: &Repository{}}
// 测试 orderID 为 0
_, err := svc.StartRefund(0, 1000, "cancel_refund", "test")
_, err := svc.StartRefund(t.Context(), 0, 1000, "cancel_refund", "test")
if !errors.Is(err, ErrRefundCannotStart) {
t.Fatalf("StartRefund() error = %v, want ErrRefundCannotStart", err)
}
// 测试金额为 0
_, err = svc.StartRefund(100, 0, "cancel_refund", "test")
_, err = svc.StartRefund(t.Context(), 100, 0, "cancel_refund", "test")
if !errors.Is(err, ErrRefundCannotStart) {
t.Fatalf("StartRefund() error = %v, want ErrRefundCannotStart", err)
}
@@ -73,7 +73,7 @@ func TestServiceStartRefundWithInvalidParams(t *testing.T) {
func TestServiceWalletRechargeDisabledInProduction(t *testing.T) {
svc := NewService(&Repository{}, "production")
_, err := svc.StartWalletRecharge(1, WalletRechargePaymentRequest{AmountCent: 1000}, "127.0.0.1")
_, err := svc.StartWalletRecharge(t.Context(), 1, WalletRechargePaymentRequest{AmountCent: 1000}, "127.0.0.1")
if !errors.Is(err, ErrWalletRechargeDisabled) {
t.Fatalf("StartWalletRecharge() error = %v, want ErrWalletRechargeDisabled", err)
}
@@ -19,7 +19,7 @@ func NewHandler(service *Service) *Handler {
}
func (h *Handler) List(c *gin.Context) {
items, err := h.service.List(c.Request.Context(), )
items, err := h.service.List(c.Request.Context())
if err != nil {
writeConfigError(c, err)
return
@@ -28,7 +28,7 @@ func (h *Handler) List(c *gin.Context) {
}
func (h *Handler) PublishOptions(c *gin.Context) {
options, err := h.service.PublishOptions(c.Request.Context(), )
options, err := h.service.PublishOptions(c.Request.Context())
if err != nil {
writeConfigError(c, err)
return
@@ -37,7 +37,7 @@ func (h *Handler) PublishOptions(c *gin.Context) {
}
func (h *Handler) SalePriceConfig(c *gin.Context) {
config, err := h.service.SalePriceConfig(c.Request.Context(), )
config, err := h.service.SalePriceConfig(c.Request.Context())
if err != nil {
writeConfigError(c, err)
return
@@ -46,7 +46,7 @@ func (h *Handler) SalePriceConfig(c *gin.Context) {
}
func (h *Handler) OrderAgreements(c *gin.Context) {
agreements, err := h.service.OrderAgreements(c.Request.Context(), )
agreements, err := h.service.OrderAgreements(c.Request.Context())
if err != nil {
writeConfigError(c, err)
return
@@ -55,7 +55,7 @@ func (h *Handler) OrderAgreements(c *gin.Context) {
}
func (h *Handler) ListingPublishAgreements(c *gin.Context) {
agreements, err := h.service.ListingPublishAgreements(c.Request.Context(), )
agreements, err := h.service.ListingPublishAgreements(c.Request.Context())
if err != nil {
writeConfigError(c, err)
return
@@ -64,7 +64,7 @@ func (h *Handler) ListingPublishAgreements(c *gin.Context) {
}
func (h *Handler) PostRentalNotice(c *gin.Context) {
notice, err := h.service.PostRentalNotice(c.Request.Context(), )
notice, err := h.service.PostRentalNotice(c.Request.Context())
if err != nil {
writeConfigError(c, err)
return
@@ -73,7 +73,7 @@ func (h *Handler) PostRentalNotice(c *gin.Context) {
}
func (h *Handler) HomeAnnouncements(c *gin.Context) {
announcements, err := h.service.HomeAnnouncements(c.Request.Context(), )
announcements, err := h.service.HomeAnnouncements(c.Request.Context())
if err != nil {
writeConfigError(c, err)
return
@@ -82,7 +82,7 @@ func (h *Handler) HomeAnnouncements(c *gin.Context) {
}
func (h *Handler) HomeConfig(c *gin.Context) {
config, err := h.service.HomeConfig(c.Request.Context(), )
config, err := h.service.HomeConfig(c.Request.Context())
if err != nil {
writeConfigError(c, err)
return
+3 -2
View File
@@ -1,6 +1,7 @@
package router
import (
"context"
"os"
"strings"
@@ -164,7 +165,7 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
// Inject refund function into order repo to avoid circular dependency
if orderRepo != nil && paymentRepo != nil {
orderRepo.SetRefundFunc(func(orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
dto, err := paymentRepo.StartRefund(orderID, refundAmountCent, bizType, remark)
dto, err := paymentRepo.StartRefund(context.Background(), orderID, refundAmountCent, bizType, remark)
if err != nil {
return "", err
}
@@ -200,7 +201,7 @@ func New(cfg config.Config, deps Dependencies, logger *zap.Logger) *gin.Engine {
}
if disputeRepo != nil && paymentRepo != nil {
disputeRepo.SetRefundFunc(func(orderID uint64, refundAmountCent int64, bizType string, remark string) (string, error) {
dto, err := paymentRepo.StartRefund(orderID, refundAmountCent, bizType, remark)
dto, err := paymentRepo.StartRefund(context.Background(), orderID, refundAmountCent, bizType, remark)
if err != nil {
return "", err
}