继续补齐核心模块 Context 超时控制

This commit is contained in:
yml2213
2026-06-10 11:50:27 +08:00
parent 334436f381
commit d2858c529d
26 changed files with 552 additions and 515 deletions
+13 -11
View File
@@ -1,6 +1,7 @@
package order
import (
"context"
"time"
"hfb_sys/backend/internal/model"
@@ -9,9 +10,9 @@ import (
"gorm.io/gorm"
)
func (r *Repository) AdminClose(adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
func (r *Repository) AdminClose(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
var refund *refundAction
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
assets, err := r.lockOrderAssets(tx, orderID)
if err != nil {
return err
@@ -91,12 +92,12 @@ func (r *Repository) AdminClose(adminID uint64, orderID uint64, req AdminActionR
if err != nil {
return err
}
r.startRefundBestEffort(refund)
r.startRefundBestEffort(ctx, refund)
return nil
}
func (r *Repository) AdminMarkAbnormal(adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
return r.db.Transaction(func(tx *gorm.DB) error {
func (r *Repository) AdminMarkAbnormal(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
assets, err := r.lockOrderAssets(tx, orderID)
if err != nil {
return err
@@ -162,9 +163,10 @@ func (r *Repository) AdminMarkAbnormal(adminID uint64, orderID uint64, req Admin
}
// AdminRefund 触发后台人工退款,退款由 payment 模块走渠道原路退回。
func (r *Repository) AdminRefund(orderID uint64) (*RefundStatusDTO, error) {
func (r *Repository) AdminRefund(ctx context.Context, orderID uint64) (*RefundStatusDTO, error) {
var order model.RentalOrder
if err := r.db.First(&order, orderID).Error; err != nil {
db := r.db.WithContext(ctx)
if err := db.First(&order, orderID).Error; err != nil {
return nil, err
}
if order.RefundStatus == refundStatusRefunded {
@@ -177,12 +179,12 @@ func (r *Repository) AdminRefund(orderID uint64) (*RefundStatusDTO, error) {
if totalCent <= 0 {
return nil, ErrInvalidCheckoutAmount
}
status, err := r.refundFunc(orderID, totalCent, refundBizAdmin, "后台人工退款")
status, err := r.refundFunc(ctx, orderID, totalCent, refundBizAdmin, "后台人工退款")
if err != nil {
return nil, err
}
// 重新读取订单,拿到 payment 模块更新后的退款字段。
if err := r.db.First(&order, orderID).Error; err != nil {
if err := db.First(&order, orderID).Error; err != nil {
return nil, err
}
dto := r.buildRefundStatusDTO(&order)
@@ -193,9 +195,9 @@ func (r *Repository) AdminRefund(orderID uint64) (*RefundStatusDTO, error) {
}
// AdminRefundStatus 查询订单退款状态。
func (r *Repository) AdminRefundStatus(orderID uint64) (*RefundStatusDTO, error) {
func (r *Repository) AdminRefundStatus(ctx context.Context, orderID uint64) (*RefundStatusDTO, error) {
var order model.RentalOrder
if err := r.db.First(&order, orderID).Error; err != nil {
if err := r.db.WithContext(ctx).First(&order, orderID).Error; err != nil {
return nil, err
}
return r.buildRefundStatusDTO(&order), nil
+15 -14
View File
@@ -1,6 +1,7 @@
package order
import (
"context"
"time"
"hfb_sys/backend/internal/model"
@@ -10,9 +11,9 @@ import (
"gorm.io/gorm/clause"
)
func (r *Repository) SubmitCheckout(userID uint64, orderID uint64, req SubmitCheckoutRequest) (*HandoffRecordDTO, error) {
func (r *Repository) SubmitCheckout(ctx context.Context, userID uint64, orderID uint64, req SubmitCheckoutRequest) (*HandoffRecordDTO, error) {
var recordID uint64
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
return err
@@ -70,16 +71,16 @@ func (r *Repository) SubmitCheckout(userID uint64, orderID uint64, req SubmitChe
if err != nil {
return nil, err
}
return r.findHandoffRecord(recordID)
return r.findHandoffRecord(ctx, recordID)
}
func (r *Repository) ConfirmReturn(userID uint64, orderID uint64) error {
return r.ConfirmCheckout(userID, orderID)
func (r *Repository) ConfirmReturn(ctx context.Context, userID uint64, orderID uint64) error {
return r.ConfirmCheckout(ctx, userID, orderID)
}
func (r *Repository) ConfirmCheckout(userID uint64, orderID uint64) error {
func (r *Repository) ConfirmCheckout(ctx context.Context, userID uint64, orderID uint64) error {
var refund *refundAction
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
return err
@@ -112,13 +113,13 @@ func (r *Repository) ConfirmCheckout(userID uint64, orderID uint64) error {
if err != nil {
return err
}
r.startRefundBestEffort(refund)
r.startRefundBestEffort(ctx, refund)
return nil
}
func (r *Repository) CounterCheckout(userID uint64, orderID uint64, req CounterCheckoutRequest) (*CheckoutDTO, error) {
func (r *Repository) CounterCheckout(ctx context.Context, userID uint64, orderID uint64, req CounterCheckoutRequest) (*CheckoutDTO, error) {
var checkoutID uint64
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
return err
@@ -181,7 +182,7 @@ func (r *Repository) CounterCheckout(userID uint64, orderID uint64, req CounterC
if err != nil {
return nil, err
}
checkout, err := r.findCheckout(checkoutID)
checkout, err := r.findCheckout(ctx, checkoutID)
if err != nil {
return nil, err
}
@@ -194,9 +195,9 @@ func (r *Repository) CounterCheckout(userID uint64, orderID uint64, req CounterC
return &dto, nil
}
func (r *Repository) AcceptCheckout(userID uint64, orderID uint64) error {
func (r *Repository) AcceptCheckout(ctx context.Context, userID uint64, orderID uint64) error {
var refund *refundAction
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
return err
@@ -224,6 +225,6 @@ func (r *Repository) AcceptCheckout(userID uint64, orderID uint64) error {
if err != nil {
return err
}
r.startRefundBestEffort(refund)
r.startRefundBestEffort(ctx, refund)
return nil
}
+22 -21
View File
@@ -1,6 +1,7 @@
package order
import (
"context"
"errors"
"net/http"
"strconv"
@@ -30,7 +31,7 @@ func (h *Handler) Create(c *gin.Context) {
response.BadRequest(c, "订单信息不完整")
return
}
item, err := h.service.Create(userID, req)
item, err := h.service.Create(c.Request.Context(), userID, req)
if err != nil {
writeOrderError(c, err)
return
@@ -44,7 +45,7 @@ func (h *Handler) List(c *gin.Context) {
response.Unauthorized(c, "缺少用户上下文")
return
}
items, err := h.service.ListForUser(userID)
items, err := h.service.ListForUser(c.Request.Context(), userID)
if err != nil {
writeOrderError(c, err)
return
@@ -54,7 +55,7 @@ func (h *Handler) List(c *gin.Context) {
func (h *Handler) AdminList(c *gin.Context) {
page, pageSize := parsePagination(c)
result, err := h.service.ListAdmin(page, pageSize)
result, err := h.service.ListAdmin(c.Request.Context(), page, pageSize)
if err != nil {
writeOrderError(c, err)
return
@@ -67,7 +68,7 @@ func (h *Handler) AdminDetail(c *gin.Context) {
if !ok {
return
}
item, err := h.service.FindAdmin(id)
item, err := h.service.FindAdmin(c.Request.Context(), id)
if err != nil {
writeOrderError(c, err)
return
@@ -80,7 +81,7 @@ func (h *Handler) AdminHandoffRecords(c *gin.Context) {
if !ok {
return
}
items, err := h.service.HandoffRecordsAdmin(id)
items, err := h.service.HandoffRecordsAdmin(c.Request.Context(), id)
if err != nil {
writeOrderError(c, err)
return
@@ -101,7 +102,7 @@ func (h *Handler) AdminRefund(c *gin.Context) {
if !ok {
return
}
item, err := h.service.AdminRefund(orderID)
item, err := h.service.AdminRefund(c.Request.Context(), orderID)
if err != nil {
writeOrderError(c, err)
return
@@ -114,7 +115,7 @@ func (h *Handler) AdminRefundStatus(c *gin.Context) {
if !ok {
return
}
item, err := h.service.AdminRefundStatus(orderID)
item, err := h.service.AdminRefundStatus(c.Request.Context(), orderID)
if err != nil {
writeOrderError(c, err)
return
@@ -122,7 +123,7 @@ func (h *Handler) AdminRefundStatus(c *gin.Context) {
response.OK(c, item)
}
func (h *Handler) adminAction(c *gin.Context, fn func(uint64, uint64, AdminActionRequest, AuditMeta) error, okData gin.H) {
func (h *Handler) adminAction(c *gin.Context, fn func(context.Context, uint64, uint64, AdminActionRequest, AuditMeta) error, okData gin.H) {
adminID, ok := currentAdminID(c)
if !ok {
response.Unauthorized(c, "缺少管理员上下文")
@@ -137,7 +138,7 @@ func (h *Handler) adminAction(c *gin.Context, fn func(uint64, uint64, AdminActio
response.BadRequest(c, "操作原因不能为空")
return
}
if err := fn(adminID, id, req, auditMeta(c)); err != nil {
if err := fn(c.Request.Context(), adminID, id, req, auditMeta(c)); err != nil {
writeOrderError(c, err)
return
}
@@ -162,7 +163,7 @@ func (h *Handler) Detail(c *gin.Context) {
if !ok {
return
}
item, err := h.service.FindForUser(userID, id)
item, err := h.service.FindForUser(c.Request.Context(), userID, id)
if err != nil {
writeOrderError(c, err)
return
@@ -180,7 +181,7 @@ func (h *Handler) Cancel(c *gin.Context) {
if !ok {
return
}
if err := h.service.Cancel(userID, id); err != nil {
if err := h.service.Cancel(c.Request.Context(), userID, id); err != nil {
writeOrderError(c, err)
return
}
@@ -197,7 +198,7 @@ func (h *Handler) Pay(c *gin.Context) {
if !ok {
return
}
if err := h.service.Pay(userID, id); err != nil {
if err := h.service.Pay(c.Request.Context(), userID, id); err != nil {
writeOrderError(c, err)
return
}
@@ -219,7 +220,7 @@ func (h *Handler) SubmitHandoff(c *gin.Context) {
response.BadRequest(c, "交接说明不能为空")
return
}
record, err := h.service.SubmitHandoff(userID, id, req)
record, err := h.service.SubmitHandoff(c.Request.Context(), userID, id, req)
if err != nil {
writeOrderError(c, err)
return
@@ -237,7 +238,7 @@ func (h *Handler) ConfirmReceive(c *gin.Context) {
if !ok {
return
}
if err := h.service.ConfirmReceive(userID, id); err != nil {
if err := h.service.ConfirmReceive(c.Request.Context(), userID, id); err != nil {
writeOrderError(c, err)
return
}
@@ -254,7 +255,7 @@ func (h *Handler) HandoffRecords(c *gin.Context) {
if !ok {
return
}
items, err := h.service.HandoffRecords(userID, id)
items, err := h.service.HandoffRecords(c.Request.Context(), userID, id)
if err != nil {
writeOrderError(c, err)
return
@@ -277,7 +278,7 @@ func (h *Handler) SubmitReturn(c *gin.Context) {
response.BadRequest(c, "归还说明不能为空")
return
}
record, err := h.service.SubmitReturn(userID, id, req)
record, err := h.service.SubmitReturn(c.Request.Context(), userID, id, req)
if err != nil {
writeOrderError(c, err)
return
@@ -295,7 +296,7 @@ func (h *Handler) ConfirmReturn(c *gin.Context) {
if !ok {
return
}
if err := h.service.ConfirmReturn(userID, id); err != nil {
if err := h.service.ConfirmReturn(c.Request.Context(), userID, id); err != nil {
writeOrderError(c, err)
return
}
@@ -317,7 +318,7 @@ func (h *Handler) SubmitCheckout(c *gin.Context) {
response.BadRequest(c, "结账说明不能为空")
return
}
record, err := h.service.SubmitCheckout(userID, id, req)
record, err := h.service.SubmitCheckout(c.Request.Context(), userID, id, req)
if err != nil {
writeOrderError(c, err)
return
@@ -335,7 +336,7 @@ func (h *Handler) ConfirmCheckout(c *gin.Context) {
if !ok {
return
}
if err := h.service.ConfirmCheckout(userID, id); err != nil {
if err := h.service.ConfirmCheckout(c.Request.Context(), userID, id); err != nil {
writeOrderError(c, err)
return
}
@@ -357,7 +358,7 @@ func (h *Handler) CounterCheckout(c *gin.Context) {
response.BadRequest(c, "结账修正原因不能为空")
return
}
checkout, err := h.service.CounterCheckout(userID, id, req)
checkout, err := h.service.CounterCheckout(c.Request.Context(), userID, id, req)
if err != nil {
writeOrderError(c, err)
return
@@ -375,7 +376,7 @@ func (h *Handler) AcceptCheckout(c *gin.Context) {
if !ok {
return
}
if err := h.service.AcceptCheckout(userID, id); err != nil {
if err := h.service.AcceptCheckout(c.Request.Context(), userID, id); err != nil {
writeOrderError(c, err)
return
}
+17 -13
View File
@@ -1,6 +1,8 @@
package order
import (
"context"
"hfb_sys/backend/internal/model"
"hfb_sys/backend/internal/modules/notification"
@@ -8,9 +10,9 @@ import (
"gorm.io/gorm/clause"
)
func (r *Repository) SubmitHandoff(userID uint64, orderID uint64, req SubmitHandoffRequest) (*HandoffRecordDTO, error) {
func (r *Repository) SubmitHandoff(ctx context.Context, userID uint64, orderID uint64, req SubmitHandoffRequest) (*HandoffRecordDTO, error) {
var recordID uint64
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
return err
@@ -52,16 +54,17 @@ func (r *Repository) SubmitHandoff(userID uint64, orderID uint64, req SubmitHand
if err != nil {
return nil, err
}
return r.findHandoffRecord(recordID)
return r.findHandoffRecord(ctx, recordID)
}
func (r *Repository) HandoffRecords(userID uint64, orderID uint64) ([]HandoffRecordDTO, error) {
func (r *Repository) HandoffRecords(ctx context.Context, userID uint64, orderID uint64) ([]HandoffRecordDTO, error) {
var order model.RentalOrder
if err := r.db.Where("id = ? AND (renter_id = ? OR owner_id = ?)", orderID, userID, userID).First(&order).Error; err != nil {
db := r.db.WithContext(ctx)
if err := db.Where("id = ? AND (renter_id = ? OR owner_id = ?)", orderID, userID, userID).First(&order).Error; err != nil {
return nil, err
}
var records []model.HandoffRecord
if err := r.db.Where("order_id = ?", orderID).Order("id ASC").Find(&records).Error; err != nil {
if err := db.Where("order_id = ?", orderID).Order("id ASC").Find(&records).Error; err != nil {
return nil, err
}
items := make([]HandoffRecordDTO, 0, len(records))
@@ -71,17 +74,18 @@ func (r *Repository) HandoffRecords(userID uint64, orderID uint64) ([]HandoffRec
return items, nil
}
func (r *Repository) SubmitReturn(userID uint64, orderID uint64, req SubmitReturnRequest) (*HandoffRecordDTO, error) {
return r.SubmitCheckout(userID, orderID, SubmitCheckoutRequest{Content: req.Content})
func (r *Repository) SubmitReturn(ctx context.Context, userID uint64, orderID uint64, req SubmitReturnRequest) (*HandoffRecordDTO, error) {
return r.SubmitCheckout(ctx, userID, orderID, SubmitCheckoutRequest{Content: req.Content})
}
func (r *Repository) HandoffRecordsAdmin(orderID uint64) ([]HandoffRecordDTO, error) {
func (r *Repository) HandoffRecordsAdmin(ctx context.Context, orderID uint64) ([]HandoffRecordDTO, error) {
var order model.RentalOrder
if err := r.db.First(&order, orderID).Error; err != nil {
db := r.db.WithContext(ctx)
if err := db.First(&order, orderID).Error; err != nil {
return nil, err
}
var records []model.HandoffRecord
if err := r.db.Where("order_id = ?", orderID).Order("id ASC").Find(&records).Error; err != nil {
if err := db.Where("order_id = ?", orderID).Order("id ASC").Find(&records).Error; err != nil {
return nil, err
}
items := make([]HandoffRecordDTO, 0, len(records))
@@ -91,9 +95,9 @@ func (r *Repository) HandoffRecordsAdmin(orderID uint64) ([]HandoffRecordDTO, er
return items, nil
}
func (r *Repository) findHandoffRecord(id uint64) (*HandoffRecordDTO, error) {
func (r *Repository) findHandoffRecord(ctx context.Context, id uint64) (*HandoffRecordDTO, error) {
var record model.HandoffRecord
if err := r.db.First(&record, id).Error; err != nil {
if err := r.db.WithContext(ctx).First(&record, id).Error; err != nil {
return nil, err
}
dto := toHandoffDTO(record)
+12 -11
View File
@@ -1,6 +1,7 @@
package order
import (
"context"
"time"
"hfb_sys/backend/internal/model"
@@ -11,9 +12,9 @@ import (
"gorm.io/gorm/clause"
)
func (r *Repository) Create(renterID uint64, req CreateRequest) (*OrderDTO, error) {
func (r *Repository) Create(ctx context.Context, renterID uint64, req CreateRequest) (*OrderDTO, error) {
var createdID uint64
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var listing model.RentalListing
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&listing, req.ListingID).Error; err != nil {
return err
@@ -88,7 +89,7 @@ func (r *Repository) Create(renterID uint64, req CreateRequest) (*OrderDTO, erro
if err != nil {
return nil, err
}
return r.FindForUser(renterID, createdID)
return r.FindForUser(ctx, renterID, createdID)
}
func (r *Repository) depositAmountsForOrder(tx *gorm.DB, renterID uint64, originalDepositCent int64) (int64, int64, error) {
@@ -127,14 +128,14 @@ func calculateDepositWaiver(originalDepositCent int64, quotaCent int64, usedCent
}
// Pay 保留旧接口兼容,但真实付款必须走 payment 模块的渠道支付入口。
func (r *Repository) Pay(userID uint64, orderID uint64) error {
func (r *Repository) Pay(ctx context.Context, userID uint64, orderID uint64) error {
return ErrChannelPaymentRequired
}
// ConfirmPaidFromChannel 在乐刷确认支付后推进订单状态;租客资金不进入站内钱包。
func (r *Repository) ConfirmPaidFromChannel(orderID uint64, providerBizNo string) error {
func (r *Repository) ConfirmPaidFromChannel(ctx context.Context, orderID uint64, providerBizNo string) error {
var newConvID uint64
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
assets, err := r.lockOrderAssets(tx, orderID)
if err != nil {
return err
@@ -200,9 +201,9 @@ func (r *Repository) ConfirmPaidFromChannel(orderID uint64, providerBizNo string
return nil
}
func (r *Repository) Cancel(userID uint64, orderID uint64) error {
func (r *Repository) Cancel(ctx context.Context, userID uint64, orderID uint64) error {
var refund *refundAction
err := r.db.Transaction(func(tx *gorm.DB) error {
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Where("id = ? AND renter_id = ?", orderID, userID).
@@ -261,12 +262,12 @@ func (r *Repository) Cancel(userID uint64, orderID uint64) error {
if err != nil {
return err
}
r.startRefundBestEffort(refund)
r.startRefundBestEffort(ctx, refund)
return nil
}
func (r *Repository) ConfirmReceive(userID uint64, orderID uint64) error {
return r.db.Transaction(func(tx *gorm.DB) error {
func (r *Repository) ConfirmReceive(ctx context.Context, userID uint64, orderID uint64) error {
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var order model.RentalOrder
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
return err
+31 -26
View File
@@ -1,6 +1,7 @@
package order
import (
"context"
"strconv"
"time"
@@ -9,9 +10,10 @@ import (
"gorm.io/gorm"
)
func (r *Repository) ListForUser(userID uint64) ([]OrderDTO, error) {
func (r *Repository) ListForUser(ctx context.Context, userID uint64) ([]OrderDTO, error) {
var rows []orderRow
err := r.baseQuery().
db := r.db.WithContext(ctx)
err := r.baseQuery(ctx).
Where("o.renter_id = ? OR o.owner_id = ?", userID, userID).
Order("o.id DESC").
Scan(&rows).Error
@@ -19,27 +21,28 @@ func (r *Repository) ListForUser(userID uint64) ([]OrderDTO, error) {
return nil, err
}
items := make([]OrderDTO, 0, len(rows))
paymentTimeoutMinutes := pendingPaymentTimeoutMinutes(r.db)
paymentTimeoutMinutes := pendingPaymentTimeoutMinutes(db)
for _, row := range rows {
dto := row.toDTOForUser(userID)
applyPaymentDeadline(&dto, row.RentalOrder, paymentTimeoutMinutes)
if shouldAttachCheckout(row.Status) {
dto.Checkout = r.latestCheckoutDTOForUser(row.ID, userID, row.RentalOrder)
dto.Checkout = r.latestCheckoutDTOForUser(ctx, row.ID, userID, row.RentalOrder)
}
items = append(items, dto)
}
return items, nil
}
func (r *Repository) ListAdmin(page, pageSize int) (*PaginatedResult, error) {
func (r *Repository) ListAdmin(ctx context.Context, page, pageSize int) (*PaginatedResult, error) {
var total int64
if err := r.adminQuery().Count(&total).Error; err != nil {
db := r.db.WithContext(ctx)
if err := r.adminQuery(ctx).Count(&total).Error; err != nil {
return nil, err
}
offset := (page - 1) * pageSize
var rows []orderRow
err := r.adminQuery().
err := r.adminQuery(ctx).
Order("o.id DESC").
Limit(pageSize).
Offset(offset).
@@ -49,7 +52,7 @@ func (r *Repository) ListAdmin(page, pageSize int) (*PaginatedResult, error) {
}
items := make([]OrderDTO, 0, len(rows))
paymentTimeoutMinutes := pendingPaymentTimeoutMinutes(r.db)
paymentTimeoutMinutes := pendingPaymentTimeoutMinutes(db)
for _, row := range rows {
dto := row.toAdminDTO()
applyPaymentDeadline(&dto, row.RentalOrder, paymentTimeoutMinutes)
@@ -64,27 +67,29 @@ func (r *Repository) ListAdmin(page, pageSize int) (*PaginatedResult, error) {
}, nil
}
func (r *Repository) FindAdmin(orderID uint64) (*OrderDTO, error) {
func (r *Repository) FindAdmin(ctx context.Context, orderID uint64) (*OrderDTO, error) {
var row orderRow
if err := r.adminQuery().Where("o.id = ?", orderID).First(&row).Error; err != nil {
db := r.db.WithContext(ctx)
if err := r.adminQuery(ctx).Where("o.id = ?", orderID).First(&row).Error; err != nil {
return nil, err
}
dto := row.toAdminDTO()
applyPaymentDeadline(&dto, row.RentalOrder, pendingPaymentTimeoutMinutes(r.db))
dto.Checkout = r.latestCheckoutAdminDTO(orderID)
applyPaymentDeadline(&dto, row.RentalOrder, pendingPaymentTimeoutMinutes(db))
dto.Checkout = r.latestCheckoutAdminDTO(ctx, orderID)
return &dto, nil
}
func (r *Repository) FindForUser(userID uint64, orderID uint64) (*OrderDTO, error) {
func (r *Repository) FindForUser(ctx context.Context, userID uint64, orderID uint64) (*OrderDTO, error) {
var row orderRow
if err := r.baseQuery().
db := r.db.WithContext(ctx)
if err := r.baseQuery(ctx).
Where("o.id = ? AND (o.renter_id = ? OR o.owner_id = ?)", orderID, userID, userID).
First(&row).Error; err != nil {
return nil, err
}
dto := row.toDTOForUser(userID)
applyPaymentDeadline(&dto, row.RentalOrder, pendingPaymentTimeoutMinutes(r.db))
dto.Checkout = r.latestCheckoutDTOForUser(orderID, userID, row.RentalOrder)
applyPaymentDeadline(&dto, row.RentalOrder, pendingPaymentTimeoutMinutes(db))
dto.Checkout = r.latestCheckoutDTOForUser(ctx, orderID, userID, row.RentalOrder)
return &dto, nil
}
@@ -109,9 +114,9 @@ func applyPaymentDeadline(dto *OrderDTO, order model.RentalOrder, timeoutMinutes
dto.PaymentDeadlineAt = &deadline
}
func (r *Repository) latestCheckoutAdminDTO(orderID uint64) *CheckoutDTO {
func (r *Repository) latestCheckoutAdminDTO(ctx context.Context, orderID uint64) *CheckoutDTO {
var checkout model.OrderCheckout
if err := r.db.Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != nil {
if err := r.db.WithContext(ctx).Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != nil {
return nil
}
dto := toCheckoutAdminDTO(checkout)
@@ -127,32 +132,32 @@ func shouldAttachCheckout(status string) bool {
}
}
func (r *Repository) latestCheckoutDTOForUser(orderID uint64, userID uint64, order model.RentalOrder) *CheckoutDTO {
func (r *Repository) latestCheckoutDTOForUser(ctx context.Context, orderID uint64, userID uint64, order model.RentalOrder) *CheckoutDTO {
var checkout model.OrderCheckout
if err := r.db.Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != nil {
if err := r.db.WithContext(ctx).Where("order_id = ?", orderID).Order("id DESC").First(&checkout).Error; err != nil {
return nil
}
dto := toCheckoutDTOForUser(checkout, userID, order)
return &dto
}
func (r *Repository) findCheckout(id uint64) (*model.OrderCheckout, error) {
func (r *Repository) findCheckout(ctx context.Context, id uint64) (*model.OrderCheckout, error) {
var checkout model.OrderCheckout
if err := r.db.First(&checkout, id).Error; err != nil {
if err := r.db.WithContext(ctx).First(&checkout, id).Error; err != nil {
return nil, err
}
return &checkout, nil
}
func (r *Repository) baseQuery() *gorm.DB {
return r.db.Table("rental_orders AS o").
func (r *Repository) baseQuery(ctx context.Context) *gorm.DB {
return r.db.WithContext(ctx).Table("rental_orders AS o").
Select("o.*, l.listing_no, a.title, a.server_region, a.login_platform").
Joins("JOIN rental_listings AS l ON l.id = o.listing_id").
Joins("JOIN game_accounts AS a ON a.id = o.account_id")
}
func (r *Repository) adminQuery() *gorm.DB {
return r.db.Table("rental_orders AS o").
func (r *Repository) adminQuery(ctx context.Context) *gorm.DB {
return r.db.WithContext(ctx).Table("rental_orders AS o").
Select("o.*, l.listing_no, a.title, a.server_region, a.login_platform, owner.phone AS owner_phone, renter.phone AS renter_phone").
Joins("JOIN rental_listings AS l ON l.id = o.listing_id").
Joins("JOIN game_accounts AS a ON a.id = o.account_id").
+3 -2
View File
@@ -1,6 +1,7 @@
package order
import (
"context"
"log"
"hfb_sys/backend/internal/model"
@@ -24,11 +25,11 @@ func (r *Repository) prepareRefund(order *model.RentalOrder, amountCent int64, b
}, nil
}
func (r *Repository) startRefundBestEffort(action *refundAction) {
func (r *Repository) startRefundBestEffort(ctx context.Context, action *refundAction) {
if action == nil || r.refundFunc == nil {
return
}
if _, err := r.refundFunc(action.OrderID, action.RefundAmountCent, action.BizType, action.Remark); err != nil {
if _, err := r.refundFunc(ctx, action.OrderID, action.RefundAmountCent, action.BizType, action.Remark); err != nil {
log.Printf("[order] start refund failed order_id=%d biz_type=%s amount_cent=%d err=%v", action.OrderID, action.BizType, action.RefundAmountCent, err)
}
}
+3 -1
View File
@@ -1,13 +1,15 @@
package order
import (
"context"
"hfb_sys/backend/internal/modules/chat"
"gorm.io/gorm"
)
// RefundFunc 由 payment 模块注入,避免 order 与 payment 形成循环依赖。
type RefundFunc func(orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error)
type RefundFunc func(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string, remark string) (status string, err error)
type refundAction struct {
OrderID uint64
RefundAmountCent int64
@@ -102,7 +102,7 @@ func TestRepositoryCreateOrderValidatesListing(t *testing.T) {
}
db.Create(&listing)
_, err := repo.Create(renter.ID, CreateRequest{ListingID: listing.ID})
_, err := repo.Create(t.Context(), renter.ID, CreateRequest{ListingID: listing.ID})
if err != tc.wantErr {
t.Fatalf("error = %v, want %v", err, tc.wantErr)
@@ -144,7 +144,7 @@ func TestRepositoryCreateOrderRejectsOwnListing(t *testing.T) {
db.Create(&listing)
// 号主尝试租自己的商品
_, err := repo.Create(owner.ID, CreateRequest{ListingID: listing.ID})
_, err := repo.Create(t.Context(), owner.ID, CreateRequest{ListingID: listing.ID})
if err != ErrCannotRentOwnListing {
t.Fatalf("error = %v, want ErrCannotRentOwnListing", err)
+46 -43
View File
@@ -1,6 +1,9 @@
package order
import "errors"
import (
"context"
"errors"
)
var (
ErrDependencyUnavailable = errors.New("dependency unavailable")
@@ -32,179 +35,179 @@ func NewService(repo *Repository) *Service {
return &Service{repo: repo}
}
func (s *Service) Create(userID uint64, req CreateRequest) (*OrderDTO, error) {
func (s *Service) Create(ctx context.Context, userID uint64, req CreateRequest) (*OrderDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if req.ListingID == 0 {
return nil, ErrInvalidRentHours
}
return s.repo.Create(userID, req)
return s.repo.Create(ctx, userID, req)
}
func (s *Service) Cancel(userID uint64, orderID uint64) error {
func (s *Service) Cancel(ctx context.Context, userID uint64, orderID uint64) error {
if s.repo == nil {
return ErrDependencyUnavailable
}
return s.repo.Cancel(userID, orderID)
return s.repo.Cancel(ctx, userID, orderID)
}
func (s *Service) Pay(userID uint64, orderID uint64) error {
func (s *Service) Pay(ctx context.Context, userID uint64, orderID uint64) error {
if s.repo == nil {
return ErrDependencyUnavailable
}
if orderID == 0 {
return ErrOrderCannotPay
}
return s.repo.Pay(userID, orderID)
return s.repo.Pay(ctx, userID, orderID)
}
func (s *Service) SubmitHandoff(userID uint64, orderID uint64, req SubmitHandoffRequest) (*HandoffRecordDTO, error) {
func (s *Service) SubmitHandoff(ctx context.Context, userID uint64, orderID uint64, req SubmitHandoffRequest) (*HandoffRecordDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 || req.Content == "" {
return nil, ErrOrderCannotHandoff
}
return s.repo.SubmitHandoff(userID, orderID, req)
return s.repo.SubmitHandoff(ctx, userID, orderID, req)
}
func (s *Service) ConfirmReceive(userID uint64, orderID uint64) error {
func (s *Service) ConfirmReceive(ctx context.Context, userID uint64, orderID uint64) error {
if s.repo == nil {
return ErrDependencyUnavailable
}
return s.repo.ConfirmReceive(userID, orderID)
return s.repo.ConfirmReceive(ctx, userID, orderID)
}
func (s *Service) HandoffRecords(userID uint64, orderID uint64) ([]HandoffRecordDTO, error) {
func (s *Service) HandoffRecords(ctx context.Context, userID uint64, orderID uint64) ([]HandoffRecordDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.HandoffRecords(userID, orderID)
return s.repo.HandoffRecords(ctx, userID, orderID)
}
func (s *Service) SubmitReturn(userID uint64, orderID uint64, req SubmitReturnRequest) (*HandoffRecordDTO, error) {
func (s *Service) SubmitReturn(ctx context.Context, userID uint64, orderID uint64, req SubmitReturnRequest) (*HandoffRecordDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 || req.Content == "" {
return nil, ErrOrderCannotReturn
}
return s.repo.SubmitReturn(userID, orderID, req)
return s.repo.SubmitReturn(ctx, userID, orderID, req)
}
func (s *Service) SubmitCheckout(userID uint64, orderID uint64, req SubmitCheckoutRequest) (*HandoffRecordDTO, error) {
func (s *Service) SubmitCheckout(ctx context.Context, userID uint64, orderID uint64, req SubmitCheckoutRequest) (*HandoffRecordDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 || req.Content == "" {
return nil, ErrCheckoutCannotSubmit
}
return s.repo.SubmitCheckout(userID, orderID, req)
return s.repo.SubmitCheckout(ctx, userID, orderID, req)
}
func (s *Service) ConfirmReturn(userID uint64, orderID uint64) error {
func (s *Service) ConfirmReturn(ctx context.Context, userID uint64, orderID uint64) error {
if s.repo == nil {
return ErrDependencyUnavailable
}
return s.repo.ConfirmReturn(userID, orderID)
return s.repo.ConfirmReturn(ctx, userID, orderID)
}
func (s *Service) ConfirmCheckout(userID uint64, orderID uint64) error {
func (s *Service) ConfirmCheckout(ctx context.Context, userID uint64, orderID uint64) error {
if s.repo == nil {
return ErrDependencyUnavailable
}
return s.repo.ConfirmCheckout(userID, orderID)
return s.repo.ConfirmCheckout(ctx, userID, orderID)
}
func (s *Service) CounterCheckout(userID uint64, orderID uint64, req CounterCheckoutRequest) (*CheckoutDTO, error) {
func (s *Service) CounterCheckout(ctx context.Context, userID uint64, orderID uint64, req CounterCheckoutRequest) (*CheckoutDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 || req.Reason == "" {
return nil, ErrCheckoutCannotCounter
}
return s.repo.CounterCheckout(userID, orderID, req)
return s.repo.CounterCheckout(ctx, userID, orderID, req)
}
func (s *Service) AcceptCheckout(userID uint64, orderID uint64) error {
func (s *Service) AcceptCheckout(ctx context.Context, userID uint64, orderID uint64) error {
if s.repo == nil {
return ErrDependencyUnavailable
}
return s.repo.AcceptCheckout(userID, orderID)
return s.repo.AcceptCheckout(ctx, userID, orderID)
}
func (s *Service) ListForUser(userID uint64) ([]OrderDTO, error) {
func (s *Service) ListForUser(ctx context.Context, userID uint64) ([]OrderDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.ListForUser(userID)
return s.repo.ListForUser(ctx, userID)
}
func (s *Service) ListAdmin(page, pageSize int) (*PaginatedResult, error) {
func (s *Service) ListAdmin(ctx context.Context, page, pageSize int) (*PaginatedResult, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.ListAdmin(page, pageSize)
return s.repo.ListAdmin(ctx, page, pageSize)
}
func (s *Service) FindAdmin(orderID uint64) (*OrderDTO, error) {
func (s *Service) FindAdmin(ctx context.Context, orderID uint64) (*OrderDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.FindAdmin(orderID)
return s.repo.FindAdmin(ctx, orderID)
}
func (s *Service) HandoffRecordsAdmin(orderID uint64) ([]HandoffRecordDTO, error) {
func (s *Service) HandoffRecordsAdmin(ctx context.Context, orderID uint64) ([]HandoffRecordDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.HandoffRecordsAdmin(orderID)
return s.repo.HandoffRecordsAdmin(ctx, orderID)
}
func (s *Service) AdminClose(adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
func (s *Service) AdminClose(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
if s.repo == nil {
return ErrDependencyUnavailable
}
if orderID == 0 || req.Reason == "" {
return ErrOrderCannotComplete
}
return s.repo.AdminClose(adminID, orderID, req, meta)
return s.repo.AdminClose(ctx, adminID, orderID, req, meta)
}
func (s *Service) AdminMarkAbnormal(adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
func (s *Service) AdminMarkAbnormal(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
if s.repo == nil {
return ErrDependencyUnavailable
}
if orderID == 0 || req.Reason == "" {
return ErrOrderCannotComplete
}
return s.repo.AdminMarkAbnormal(adminID, orderID, req, meta)
return s.repo.AdminMarkAbnormal(ctx, adminID, orderID, req, meta)
}
func (s *Service) AdminRefund(orderID uint64) (*RefundStatusDTO, error) {
func (s *Service) AdminRefund(ctx context.Context, orderID uint64) (*RefundStatusDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 {
return nil, ErrOrderCannotComplete
}
return s.repo.AdminRefund(orderID)
return s.repo.AdminRefund(ctx, orderID)
}
func (s *Service) AdminRefundStatus(orderID uint64) (*RefundStatusDTO, error) {
func (s *Service) AdminRefundStatus(ctx context.Context, orderID uint64) (*RefundStatusDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
if orderID == 0 {
return nil, ErrOrderCannotComplete
}
return s.repo.AdminRefundStatus(orderID)
return s.repo.AdminRefundStatus(ctx, orderID)
}
func (s *Service) FindForUser(userID uint64, orderID uint64) (*OrderDTO, error) {
func (s *Service) FindForUser(ctx context.Context, userID uint64, orderID uint64) (*OrderDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.FindForUser(userID, orderID)
return s.repo.FindForUser(ctx, userID, orderID)
}
+11 -11
View File
@@ -8,7 +8,7 @@ import (
// TestServiceDependencyChecks 测试所有 Service 方法的依赖检查
func TestServiceCreateWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
_, err := svc.Create(1, CreateRequest{ListingID: 100})
_, err := svc.Create(t.Context(), 1, CreateRequest{ListingID: 100})
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("Create() error = %v, want ErrDependencyUnavailable", err)
}
@@ -16,7 +16,7 @@ func TestServiceCreateWithNilRepo(t *testing.T) {
func TestServiceCreateWithZeroListingID(t *testing.T) {
svc := &Service{repo: &Repository{}}
_, err := svc.Create(1, CreateRequest{ListingID: 0})
_, err := svc.Create(t.Context(), 1, CreateRequest{ListingID: 0})
if !errors.Is(err, ErrInvalidRentHours) {
t.Fatalf("Create() error = %v, want ErrInvalidRentHours", err)
}
@@ -24,7 +24,7 @@ func TestServiceCreateWithZeroListingID(t *testing.T) {
func TestServiceCancelWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
err := svc.Cancel(1, 100)
err := svc.Cancel(t.Context(), 1, 100)
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("Cancel() error = %v, want ErrDependencyUnavailable", err)
}
@@ -32,7 +32,7 @@ func TestServiceCancelWithNilRepo(t *testing.T) {
func TestServicePayWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
err := svc.Pay(1, 100)
err := svc.Pay(t.Context(), 1, 100)
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("Pay() error = %v, want ErrDependencyUnavailable", err)
}
@@ -40,7 +40,7 @@ func TestServicePayWithNilRepo(t *testing.T) {
func TestServicePayWithZeroOrderID(t *testing.T) {
svc := &Service{repo: &Repository{}}
err := svc.Pay(1, 0)
err := svc.Pay(t.Context(), 1, 0)
if !errors.Is(err, ErrOrderCannotPay) {
t.Fatalf("Pay() error = %v, want ErrOrderCannotPay", err)
}
@@ -48,7 +48,7 @@ func TestServicePayWithZeroOrderID(t *testing.T) {
func TestServiceSubmitHandoffWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
_, err := svc.SubmitHandoff(1, 100, SubmitHandoffRequest{Content: "test"})
_, err := svc.SubmitHandoff(t.Context(), 1, 100, SubmitHandoffRequest{Content: "test"})
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("SubmitHandoff() error = %v, want ErrDependencyUnavailable", err)
}
@@ -56,7 +56,7 @@ func TestServiceSubmitHandoffWithNilRepo(t *testing.T) {
func TestServiceSubmitHandoffWithEmptyContent(t *testing.T) {
svc := &Service{repo: &Repository{}}
_, err := svc.SubmitHandoff(1, 100, SubmitHandoffRequest{Content: ""})
_, err := svc.SubmitHandoff(t.Context(), 1, 100, SubmitHandoffRequest{Content: ""})
if !errors.Is(err, ErrOrderCannotHandoff) {
t.Fatalf("SubmitHandoff() error = %v, want ErrOrderCannotHandoff", err)
}
@@ -64,7 +64,7 @@ func TestServiceSubmitHandoffWithEmptyContent(t *testing.T) {
func TestServiceConfirmReceiveWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
err := svc.ConfirmReceive(1, 100)
err := svc.ConfirmReceive(t.Context(), 1, 100)
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("ConfirmReceive() error = %v, want ErrDependencyUnavailable", err)
}
@@ -72,7 +72,7 @@ func TestServiceConfirmReceiveWithNilRepo(t *testing.T) {
func TestServiceSubmitReturnWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
_, err := svc.SubmitReturn(1, 100, SubmitReturnRequest{Content: "test"})
_, err := svc.SubmitReturn(t.Context(), 1, 100, SubmitReturnRequest{Content: "test"})
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("SubmitReturn() error = %v, want ErrDependencyUnavailable", err)
}
@@ -80,7 +80,7 @@ func TestServiceSubmitReturnWithNilRepo(t *testing.T) {
func TestServiceSubmitReturnWithEmptyContent(t *testing.T) {
svc := &Service{repo: &Repository{}}
_, err := svc.SubmitReturn(1, 100, SubmitReturnRequest{Content: ""})
_, err := svc.SubmitReturn(t.Context(), 1, 100, SubmitReturnRequest{Content: ""})
if !errors.Is(err, ErrOrderCannotReturn) {
t.Fatalf("SubmitReturn() error = %v, want ErrOrderCannotReturn", err)
}
@@ -88,7 +88,7 @@ func TestServiceSubmitReturnWithEmptyContent(t *testing.T) {
func TestServiceSubmitCheckoutWithNilRepo(t *testing.T) {
svc := &Service{repo: nil}
_, err := svc.SubmitCheckout(1, 100, SubmitCheckoutRequest{Content: "test"})
_, err := svc.SubmitCheckout(t.Context(), 1, 100, SubmitCheckoutRequest{Content: "test"})
if !errors.Is(err, ErrDependencyUnavailable) {
t.Fatalf("SubmitCheckout() error = %v, want ErrDependencyUnavailable", err)
}