继续补齐核心模块 Context 超时控制
This commit is contained in:
@@ -37,7 +37,7 @@ func (h *Handler) AdminList(c *gin.Context) {
|
||||
filter := c.DefaultQuery("filter", "all")
|
||||
page, pageSize := parsePagination(c)
|
||||
principal := Principal{Type: "admin", ID: adminID}
|
||||
result, err := h.service.ListConversationsWithFilter(principal, page, pageSize, filter)
|
||||
result, err := h.service.ListConversationsWithFilter(c.Request.Context(), principal, page, pageSize, filter)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -73,7 +73,7 @@ func (h *Handler) OrderConversation(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
item, err := h.service.FindOrderConversation(userID, orderID)
|
||||
item, err := h.service.FindOrderConversation(c.Request.Context(), userID, orderID)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -87,7 +87,7 @@ func (h *Handler) EnsureSupportConversation(c *gin.Context) {
|
||||
response.Unauthorized(c, "缺少用户上下文")
|
||||
return
|
||||
}
|
||||
item, err := h.service.EnsureSupportConversation(userID)
|
||||
item, err := h.service.EnsureSupportConversation(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -165,7 +165,7 @@ func (h *Handler) AdminTransfer(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
principal := Principal{Type: "admin", ID: adminID}
|
||||
if err := h.service.TransferConversation(principal, id, req); err != nil {
|
||||
if err := h.service.TransferConversation(c.Request.Context(), principal, id, req); err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
}
|
||||
@@ -173,7 +173,7 @@ func (h *Handler) AdminTransfer(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *Handler) AdminSupportAdmins(c *gin.Context) {
|
||||
admins, err := h.service.GetAvailableSupportAdmins()
|
||||
admins, err := h.service.GetAvailableSupportAdmins(c.Request.Context())
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -197,7 +197,7 @@ func (h *Handler) AdminUpdateRemark(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
principal := Principal{Type: "admin", ID: adminID}
|
||||
if err := h.service.UpdateRemark(principal, id, req); err != nil {
|
||||
if err := h.service.UpdateRemark(c.Request.Context(), principal, id, req); err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
}
|
||||
@@ -210,7 +210,7 @@ func (h *Handler) AdminListQuickReplies(c *gin.Context) {
|
||||
response.Unauthorized(c, "缺少管理员上下文")
|
||||
return
|
||||
}
|
||||
replies, err := h.service.ListQuickReplies(adminID)
|
||||
replies, err := h.service.ListQuickReplies(c.Request.Context(), adminID)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -229,7 +229,7 @@ func (h *Handler) AdminCreateQuickReply(c *gin.Context) {
|
||||
response.BadRequest(c, "标题和内容不能为空")
|
||||
return
|
||||
}
|
||||
reply, err := h.service.CreateQuickReply(adminID, req)
|
||||
reply, err := h.service.CreateQuickReply(c.Request.Context(), adminID, req)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -252,7 +252,7 @@ func (h *Handler) AdminUpdateQuickReply(c *gin.Context) {
|
||||
response.BadRequest(c, "请求参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateQuickReply(adminID, id, req); err != nil {
|
||||
if err := h.service.UpdateQuickReply(c.Request.Context(), adminID, id, req); err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
}
|
||||
@@ -269,7 +269,7 @@ func (h *Handler) AdminDeleteQuickReply(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteQuickReply(adminID, id); err != nil {
|
||||
if err := h.service.DeleteQuickReply(c.Request.Context(), adminID, id); err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
}
|
||||
@@ -277,7 +277,7 @@ func (h *Handler) AdminDeleteQuickReply(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *Handler) AdminGetAutoWelcome(c *gin.Context) {
|
||||
message := h.service.GetAutoWelcomeMessage()
|
||||
message := h.service.GetAutoWelcomeMessage(c.Request.Context())
|
||||
response.OK(c, gin.H{"message": message})
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ func (h *Handler) AdminUpdateAutoWelcome(c *gin.Context) {
|
||||
response.BadRequest(c, "话术内容不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateAutoWelcomeMessage(req.Message); err != nil {
|
||||
if err := h.service.UpdateAutoWelcomeMessage(c.Request.Context(), req.Message); err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func (h *Handler) AdminUpdateAutoWelcome(c *gin.Context) {
|
||||
|
||||
func (h *Handler) list(c *gin.Context, principal Principal) {
|
||||
page, pageSize := parsePagination(c)
|
||||
result, err := h.service.ListConversations(principal, page, pageSize)
|
||||
result, err := h.service.ListConversations(c.Request.Context(), principal, page, pageSize)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -311,7 +311,7 @@ func (h *Handler) detail(c *gin.Context, principal Principal) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
item, err := h.service.FindConversation(principal, id)
|
||||
item, err := h.service.FindConversation(c.Request.Context(), principal, id)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -325,7 +325,7 @@ func (h *Handler) messages(c *gin.Context, principal Principal) {
|
||||
return
|
||||
}
|
||||
page, pageSize := parsePagination(c)
|
||||
result, err := h.service.Messages(principal, id, page, pageSize)
|
||||
result, err := h.service.Messages(c.Request.Context(), principal, id, page, pageSize)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -343,7 +343,7 @@ func (h *Handler) send(c *gin.Context, principal Principal) {
|
||||
response.BadRequest(c, "消息格式不正确")
|
||||
return
|
||||
}
|
||||
message, err := h.service.SendMessage(principal, id, req)
|
||||
message, err := h.service.SendMessage(c.Request.Context(), principal, id, req)
|
||||
if err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
@@ -356,7 +356,7 @@ func (h *Handler) markRead(c *gin.Context, principal Principal) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if err := h.service.MarkRead(principal, id); err != nil {
|
||||
if err := h.service.MarkRead(c.Request.Context(), principal, id); err != nil {
|
||||
writeChatError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -118,10 +119,11 @@ func (r *Repository) NotifyNewConversation(conversationID uint64) {
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Repository) ListConversations(principal Principal, page, pageSize int) (*PaginatedResult, error) {
|
||||
func (r *Repository) ListConversations(ctx context.Context, principal Principal, page, pageSize int) (*PaginatedResult, error) {
|
||||
page, pageSize = normalizePagination(page, pageSize)
|
||||
db := r.db.WithContext(ctx)
|
||||
var total int64
|
||||
countDB := r.db.Table("chat_conversations AS c").
|
||||
countDB := db.Table("chat_conversations AS c").
|
||||
Joins("JOIN chat_participants AS cp ON cp.conversation_id = c.id").
|
||||
Where("cp.participant_type = ? AND cp.participant_id = ?", principal.Type, principal.ID)
|
||||
if err := countDB.Count(&total).Error; err != nil {
|
||||
@@ -130,7 +132,7 @@ func (r *Repository) ListConversations(principal Principal, page, pageSize int)
|
||||
|
||||
var rows []conversationRow
|
||||
offset := (page - 1) * pageSize
|
||||
err := r.conversationQuery(principal).
|
||||
err := r.conversationQuery(ctx, principal).
|
||||
Order("COALESCE(c.last_message_at, c.created_at) DESC, c.id DESC").
|
||||
Offset(offset).
|
||||
Limit(pageSize).
|
||||
@@ -145,17 +147,18 @@ func (r *Repository) ListConversations(principal Principal, page, pageSize int)
|
||||
return &PaginatedResult{Items: items, Total: total, Page: page, PageSize: pageSize}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) FindConversation(principal Principal, id uint64) (*ConversationDTO, error) {
|
||||
func (r *Repository) FindConversation(ctx context.Context, principal Principal, id uint64) (*ConversationDTO, error) {
|
||||
db := r.db.WithContext(ctx)
|
||||
// 管理员可以查看任意会话,无需是 participant
|
||||
if principal.Type == "admin" {
|
||||
var conversation model.ChatConversation
|
||||
if err := r.db.First(&conversation, id).Error; err != nil {
|
||||
if err := db.First(&conversation, id).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrConversationNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
participants, err := r.participants(conversation.ID)
|
||||
participants, err := r.participants(ctx, conversation.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -179,14 +182,14 @@ func (r *Repository) FindConversation(principal Principal, id uint64) (*Conversa
|
||||
|
||||
// 普通用户需要是 participant
|
||||
var row conversationRow
|
||||
err := r.conversationQuery(principal).Where("c.id = ?", id).First(&row).Error
|
||||
err := r.conversationQuery(ctx, principal).Where("c.id = ?", id).First(&row).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrConversationNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
participants, err := r.participants(row.ID)
|
||||
participants, err := r.participants(ctx, row.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -194,17 +197,17 @@ func (r *Repository) FindConversation(principal Principal, id uint64) (*Conversa
|
||||
return &dto, nil
|
||||
}
|
||||
|
||||
func (r *Repository) FindOrderConversation(userID uint64, orderID uint64) (*ConversationDTO, error) {
|
||||
func (r *Repository) FindOrderConversation(ctx context.Context, userID uint64, orderID uint64) (*ConversationDTO, error) {
|
||||
var row conversationRow
|
||||
principal := Principal{Type: "user", ID: userID}
|
||||
err := r.conversationQuery(principal).Where("c.order_id = ?", orderID).First(&row).Error
|
||||
err := r.conversationQuery(ctx, principal).Where("c.order_id = ?", orderID).First(&row).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrConversationNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
participants, err := r.participants(row.ID)
|
||||
participants, err := r.participants(ctx, row.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -212,9 +215,9 @@ func (r *Repository) FindOrderConversation(userID uint64, orderID uint64) (*Conv
|
||||
return &dto, nil
|
||||
}
|
||||
|
||||
func (r *Repository) EnsureSupportConversation(userID uint64) (*ConversationDTO, error) {
|
||||
func (r *Repository) EnsureSupportConversation(ctx context.Context, userID uint64) (*ConversationDTO, error) {
|
||||
var conversationID uint64
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var existing model.ChatConversation
|
||||
err := tx.Table("chat_conversations AS c").
|
||||
Select("c.*").
|
||||
@@ -288,7 +291,7 @@ func (r *Repository) EnsureSupportConversation(userID uint64) (*ConversationDTO,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item, err := r.FindConversation(Principal{Type: "user", ID: userID}, conversationID)
|
||||
item, err := r.FindConversation(ctx, Principal{Type: "user", ID: userID}, conversationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -296,18 +299,19 @@ func (r *Repository) EnsureSupportConversation(userID uint64) (*ConversationDTO,
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (r *Repository) Messages(principal Principal, conversationID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
func (r *Repository) Messages(ctx context.Context, principal Principal, conversationID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
page, pageSize = normalizePagination(page, pageSize)
|
||||
db := r.db.WithContext(ctx)
|
||||
|
||||
// 管理员可以查看任意会话的消息,普通用户需要是 participant
|
||||
if principal.Type != "admin" {
|
||||
if _, err := r.findParticipant(r.db, principal, conversationID, false); err != nil {
|
||||
if _, err := r.findParticipant(db, principal, conversationID, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// 管理员需要验证会话存在
|
||||
var count int64
|
||||
if err := r.db.Model(&model.ChatConversation{}).Where("id = ?", conversationID).Count(&count).Error; err != nil {
|
||||
if err := db.Model(&model.ChatConversation{}).Where("id = ?", conversationID).Count(&count).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if count == 0 {
|
||||
@@ -316,28 +320,28 @@ func (r *Repository) Messages(principal Principal, conversationID uint64, page,
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := r.db.Model(&model.ChatMessage{}).Where("conversation_id = ?", conversationID).Count(&total).Error; err != nil {
|
||||
if err := db.Model(&model.ChatMessage{}).Where("conversation_id = ?", conversationID).Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
var rows []model.ChatMessage
|
||||
if err := r.db.Where("conversation_id = ?", conversationID).
|
||||
if err := db.Where("conversation_id = ?", conversationID).
|
||||
Order("id ASC").
|
||||
Offset(offset).
|
||||
Limit(pageSize).
|
||||
Find(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items, err := r.toMessageDTOs(principal, rows)
|
||||
items, err := r.toMessageDTOs(ctx, principal, rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PaginatedResult{Items: items, Total: total, Page: page, PageSize: pageSize}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) SendMessage(principal Principal, conversationID uint64, req SendMessageRequest) (*MessageDTO, error) {
|
||||
func (r *Repository) SendMessage(ctx context.Context, principal Principal, conversationID uint64, req SendMessageRequest) (*MessageDTO, error) {
|
||||
var messageID uint64
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var conversation model.ChatConversation
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&conversation, conversationID).Error; err != nil {
|
||||
return err
|
||||
@@ -409,10 +413,10 @@ func (r *Repository) SendMessage(principal Principal, conversationID uint64, req
|
||||
return nil, err
|
||||
}
|
||||
var message model.ChatMessage
|
||||
if err := r.db.First(&message, messageID).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).First(&message, messageID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items, err := r.toMessageDTOs(principal, []model.ChatMessage{message})
|
||||
items, err := r.toMessageDTOs(ctx, principal, []model.ChatMessage{message})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -442,8 +446,8 @@ func (r *Repository) SendMessage(principal Principal, conversationID uint64, req
|
||||
return &items[0], nil
|
||||
}
|
||||
|
||||
func (r *Repository) MarkRead(principal Principal, conversationID uint64) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
func (r *Repository) MarkRead(ctx context.Context, principal Principal, conversationID uint64) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// 管理员可以不是 participant,直接返回成功
|
||||
if principal.Type == "admin" {
|
||||
// 尝试查找 participant 记录,如果有就更新
|
||||
@@ -471,8 +475,8 @@ func (r *Repository) MarkRead(principal Principal, conversationID uint64) error
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Repository) conversationQuery(principal Principal) *gorm.DB {
|
||||
return r.db.Table("chat_conversations AS c").
|
||||
func (r *Repository) conversationQuery(ctx context.Context, principal Principal) *gorm.DB {
|
||||
return r.db.WithContext(ctx).Table("chat_conversations AS c").
|
||||
Select(`c.id, c.order_id, c.type, c.title, c.status, c.last_message_id,
|
||||
c.last_message_preview, c.last_message_at, c.created_at, c.updated_at, cp.role,
|
||||
(
|
||||
@@ -503,12 +507,12 @@ func (r *Repository) findParticipant(tx *gorm.DB, principal Principal, conversat
|
||||
return &participant, nil
|
||||
}
|
||||
|
||||
func (r *Repository) participants(conversationID uint64) ([]ParticipantDTO, error) {
|
||||
func (r *Repository) participants(ctx context.Context, conversationID uint64) ([]ParticipantDTO, error) {
|
||||
var rows []model.ChatParticipant
|
||||
if err := r.db.Where("conversation_id = ?", conversationID).Order("id ASC").Find(&rows).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("conversation_id = ?", conversationID).Order("id ASC").Find(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userNames, userAvatars, adminNames, err := r.participantNames(rows)
|
||||
userNames, userAvatars, adminNames, err := r.participantNames(ctx, rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -538,7 +542,7 @@ func (r *Repository) participants(conversationID uint64) ([]ParticipantDTO, erro
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (r *Repository) toMessageDTOs(principal Principal, rows []model.ChatMessage) ([]MessageDTO, error) {
|
||||
func (r *Repository) toMessageDTOs(ctx context.Context, principal Principal, rows []model.ChatMessage) ([]MessageDTO, error) {
|
||||
userIDs := make([]uint64, 0)
|
||||
adminIDs := make([]uint64, 0)
|
||||
for _, row := range rows {
|
||||
@@ -549,11 +553,11 @@ func (r *Repository) toMessageDTOs(principal Principal, rows []model.ChatMessage
|
||||
adminIDs = append(adminIDs, row.SenderID)
|
||||
}
|
||||
}
|
||||
userNames, userAvatars, err := r.userNames(userIDs)
|
||||
userNames, userAvatars, err := r.userNames(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
adminNames, err := r.adminNames(adminIDs)
|
||||
adminNames, err := r.adminNames(ctx, adminIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -590,7 +594,7 @@ func (r *Repository) toMessageDTOs(principal Principal, rows []model.ChatMessage
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (r *Repository) participantNames(rows []model.ChatParticipant) (map[uint64]string, map[uint64]string, map[uint64]string, error) {
|
||||
func (r *Repository) participantNames(ctx context.Context, rows []model.ChatParticipant) (map[uint64]string, map[uint64]string, map[uint64]string, error) {
|
||||
userIDs := make([]uint64, 0)
|
||||
adminIDs := make([]uint64, 0)
|
||||
for _, row := range rows {
|
||||
@@ -601,25 +605,25 @@ func (r *Repository) participantNames(rows []model.ChatParticipant) (map[uint64]
|
||||
adminIDs = append(adminIDs, row.ParticipantID)
|
||||
}
|
||||
}
|
||||
userNames, userAvatars, err := r.userNames(userIDs)
|
||||
userNames, userAvatars, err := r.userNames(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
adminNames, err := r.adminNames(adminIDs)
|
||||
adminNames, err := r.adminNames(ctx, adminIDs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return userNames, userAvatars, adminNames, nil
|
||||
}
|
||||
|
||||
func (r *Repository) userNames(ids []uint64) (map[uint64]string, map[uint64]string, error) {
|
||||
func (r *Repository) userNames(ctx context.Context, ids []uint64) (map[uint64]string, map[uint64]string, error) {
|
||||
names := map[uint64]string{}
|
||||
avatars := map[uint64]string{}
|
||||
if len(ids) == 0 {
|
||||
return names, avatars, nil
|
||||
}
|
||||
var users []model.User
|
||||
if err := r.db.Where("id IN ?", uniqueIDs(ids)).Find(&users).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("id IN ?", uniqueIDs(ids)).Find(&users).Error; err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, user := range users {
|
||||
@@ -633,13 +637,13 @@ func (r *Repository) userNames(ids []uint64) (map[uint64]string, map[uint64]stri
|
||||
return names, avatars, nil
|
||||
}
|
||||
|
||||
func (r *Repository) adminNames(ids []uint64) (map[uint64]string, error) {
|
||||
func (r *Repository) adminNames(ctx context.Context, ids []uint64) (map[uint64]string, error) {
|
||||
names := map[uint64]string{}
|
||||
if len(ids) == 0 {
|
||||
return names, nil
|
||||
}
|
||||
var admins []model.AdminUser
|
||||
if err := r.db.Where("id IN ?", uniqueIDs(ids)).Find(&admins).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("id IN ?", uniqueIDs(ids)).Find(&admins).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, admin := range admins {
|
||||
@@ -802,8 +806,8 @@ func messagePreview(content string, attachments []string) string {
|
||||
}
|
||||
|
||||
// TransferConversation 转接会话给其他客服
|
||||
func (r *Repository) TransferConversation(principal Principal, conversationID uint64, toAdminID uint64) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
func (r *Repository) TransferConversation(ctx context.Context, principal Principal, conversationID uint64, toAdminID uint64) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// 验证当前操作者是会话参与者
|
||||
if _, err := r.findParticipant(tx, principal, conversationID, false); err != nil {
|
||||
return err
|
||||
@@ -855,7 +859,8 @@ func (r *Repository) TransferConversation(principal Principal, conversationID ui
|
||||
}
|
||||
|
||||
// GetAvailableSupportAdmins 获取可用客服列表及其会话数
|
||||
func (r *Repository) GetAvailableSupportAdmins() ([]SupportAdminDTO, error) {
|
||||
func (r *Repository) GetAvailableSupportAdmins(ctx context.Context) ([]SupportAdminDTO, error) {
|
||||
db := r.db.WithContext(ctx)
|
||||
// 仅展示客服角色管理员,超级管理员即使有 chat:view 权限也不作为客服候选。
|
||||
type adminRow struct {
|
||||
ID uint64
|
||||
@@ -863,7 +868,7 @@ func (r *Repository) GetAvailableSupportAdmins() ([]SupportAdminDTO, error) {
|
||||
SupportStatus string
|
||||
}
|
||||
var admins []adminRow
|
||||
err := r.db.Table("admin_users AS au").
|
||||
err := db.Table("admin_users AS au").
|
||||
Select("au.id, COALESCE(NULLIF(au.nickname, ''), au.username) AS nickname, au.support_status").
|
||||
Joins("JOIN admin_user_roles AS aur ON aur.admin_user_id = au.id").
|
||||
Joins("JOIN roles AS r ON r.id = aur.role_id").
|
||||
@@ -885,7 +890,7 @@ func (r *Repository) GetAvailableSupportAdmins() ([]SupportAdminDTO, error) {
|
||||
adminIDs[i] = a.ID
|
||||
}
|
||||
if len(adminIDs) > 0 {
|
||||
r.db.Table("chat_participants").
|
||||
db.Table("chat_participants").
|
||||
Select("participant_id AS admin_id, COUNT(*) AS count").
|
||||
Where("participant_type = ? AND role = ? AND participant_id IN ?", "admin", "support", adminIDs).
|
||||
Group("participant_id").
|
||||
@@ -909,19 +914,20 @@ func (r *Repository) GetAvailableSupportAdmins() ([]SupportAdminDTO, error) {
|
||||
}
|
||||
|
||||
// ListConversationsWithFilter 支持筛选的会话列表
|
||||
func (r *Repository) ListConversationsWithFilter(principal Principal, page, pageSize int, filter string) (*PaginatedResult, error) {
|
||||
func (r *Repository) ListConversationsWithFilter(ctx context.Context, principal Principal, page, pageSize int, filter string) (*PaginatedResult, error) {
|
||||
page, pageSize = normalizePagination(page, pageSize)
|
||||
db := r.db.WithContext(ctx)
|
||||
|
||||
// 管理员在"全部"模式下直接查询所有会话
|
||||
if principal.Type == "admin" && filter == "all" {
|
||||
var total int64
|
||||
if err := r.db.Model(&model.ChatConversation{}).Count(&total).Error; err != nil {
|
||||
if err := db.Model(&model.ChatConversation{}).Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var conversations []model.ChatConversation
|
||||
offset := (page - 1) * pageSize
|
||||
if err := r.db.Order("COALESCE(last_message_at, created_at) DESC, id DESC").
|
||||
if err := db.Order("COALESCE(last_message_at, created_at) DESC, id DESC").
|
||||
Offset(offset).
|
||||
Limit(pageSize).
|
||||
Find(&conversations).Error; err != nil {
|
||||
@@ -930,7 +936,7 @@ func (r *Repository) ListConversationsWithFilter(principal Principal, page, page
|
||||
|
||||
items := make([]ConversationDTO, 0, len(conversations))
|
||||
for _, conv := range conversations {
|
||||
participants, err := r.participants(conv.ID)
|
||||
participants, err := r.participants(ctx, conv.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -955,7 +961,7 @@ func (r *Repository) ListConversationsWithFilter(principal Principal, page, page
|
||||
|
||||
// 其他情况使用原有逻辑
|
||||
var total int64
|
||||
countDB := r.db.Table("chat_conversations AS c").
|
||||
countDB := db.Table("chat_conversations AS c").
|
||||
Joins("JOIN chat_participants AS cp ON cp.conversation_id = c.id")
|
||||
|
||||
switch filter {
|
||||
@@ -965,7 +971,7 @@ func (r *Repository) ListConversationsWithFilter(principal Principal, page, page
|
||||
case "unassigned":
|
||||
// 未分配客服的会话
|
||||
countDB = countDB.Where("c.id NOT IN (?)",
|
||||
r.db.Table("chat_participants").Select("conversation_id").Where("participant_type = ? AND role = ?", "admin", "support"))
|
||||
db.Table("chat_participants").Select("conversation_id").Where("participant_type = ? AND role = ?", "admin", "support"))
|
||||
default:
|
||||
// 普通用户的全部会话
|
||||
countDB = countDB.Where("cp.participant_type = ? AND cp.participant_id = ?", principal.Type, principal.ID)
|
||||
@@ -978,13 +984,13 @@ func (r *Repository) ListConversationsWithFilter(principal Principal, page, page
|
||||
var rows []conversationRow
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
queryDB := r.conversationQuery(principal)
|
||||
queryDB := r.conversationQuery(ctx, principal)
|
||||
switch filter {
|
||||
case "mine":
|
||||
queryDB = queryDB.Where("cp.participant_type = ? AND cp.participant_id = ?", principal.Type, principal.ID)
|
||||
case "unassigned":
|
||||
queryDB = queryDB.Where("c.id NOT IN (?)",
|
||||
r.db.Table("chat_participants").Select("conversation_id").Where("participant_type = ? AND role = ?", "admin", "support"))
|
||||
db.Table("chat_participants").Select("conversation_id").Where("participant_type = ? AND role = ?", "admin", "support"))
|
||||
default:
|
||||
// 普通用户的全部会话
|
||||
queryDB = queryDB.Where("cp.participant_type = ? AND cp.participant_id = ?", principal.Type, principal.ID)
|
||||
@@ -1001,7 +1007,7 @@ func (r *Repository) ListConversationsWithFilter(principal Principal, page, page
|
||||
|
||||
items := make([]ConversationDTO, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
participants, err := r.participants(row.ID)
|
||||
participants, err := r.participants(ctx, row.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1048,16 +1054,16 @@ func encodeStringList(items []string) datatypes.JSON {
|
||||
}
|
||||
|
||||
// UpdateRemark 更新会话备注
|
||||
func (r *Repository) UpdateRemark(principal Principal, conversationID uint64, remark string) error {
|
||||
return r.db.Model(&model.ChatParticipant{}).
|
||||
func (r *Repository) UpdateRemark(ctx context.Context, principal Principal, conversationID uint64, remark string) error {
|
||||
return r.db.WithContext(ctx).Model(&model.ChatParticipant{}).
|
||||
Where("conversation_id = ? AND participant_type = ? AND participant_id = ?", conversationID, principal.Type, principal.ID).
|
||||
Update("remark", remark).Error
|
||||
}
|
||||
|
||||
// ListQuickReplies 获取快捷回复列表(个人 + 全局)
|
||||
func (r *Repository) ListQuickReplies(adminID uint64) ([]QuickReplyDTO, error) {
|
||||
func (r *Repository) ListQuickReplies(ctx context.Context, adminID uint64) ([]QuickReplyDTO, error) {
|
||||
var replies []model.ChatQuickReply
|
||||
err := r.db.Where("admin_user_id = ? OR admin_user_id = 0", adminID).
|
||||
err := r.db.WithContext(ctx).Where("admin_user_id = ? OR admin_user_id = 0", adminID).
|
||||
Order("admin_user_id DESC, sort_order ASC, id ASC").
|
||||
Find(&replies).Error
|
||||
if err != nil {
|
||||
@@ -1078,7 +1084,7 @@ func (r *Repository) ListQuickReplies(adminID uint64) ([]QuickReplyDTO, error) {
|
||||
}
|
||||
|
||||
// CreateQuickReply 创建快捷回复
|
||||
func (r *Repository) CreateQuickReply(adminID uint64, req CreateQuickReplyRequest) (*QuickReplyDTO, error) {
|
||||
func (r *Repository) CreateQuickReply(ctx context.Context, adminID uint64, req CreateQuickReplyRequest) (*QuickReplyDTO, error) {
|
||||
ownerID := adminID
|
||||
if req.IsGlobal {
|
||||
ownerID = 0
|
||||
@@ -1089,7 +1095,7 @@ func (r *Repository) CreateQuickReply(adminID uint64, req CreateQuickReplyReques
|
||||
Content: req.Content,
|
||||
SortOrder: req.SortOrder,
|
||||
}
|
||||
if err := r.db.Create(&reply).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Create(&reply).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &QuickReplyDTO{
|
||||
@@ -1103,8 +1109,8 @@ func (r *Repository) CreateQuickReply(adminID uint64, req CreateQuickReplyReques
|
||||
}
|
||||
|
||||
// UpdateQuickReply 更新快捷回复
|
||||
func (r *Repository) UpdateQuickReply(adminID uint64, replyID uint64, req UpdateQuickReplyRequest) error {
|
||||
query := r.db.Model(&model.ChatQuickReply{}).Where("id = ? AND (admin_user_id = ? OR admin_user_id = 0)", replyID, adminID)
|
||||
func (r *Repository) UpdateQuickReply(ctx context.Context, adminID uint64, replyID uint64, req UpdateQuickReplyRequest) error {
|
||||
query := r.db.WithContext(ctx).Model(&model.ChatQuickReply{}).Where("id = ? AND (admin_user_id = ? OR admin_user_id = 0)", replyID, adminID)
|
||||
updates := map[string]interface{}{}
|
||||
if req.Title != "" {
|
||||
updates["title"] = req.Title
|
||||
@@ -1122,23 +1128,23 @@ func (r *Repository) UpdateQuickReply(adminID uint64, replyID uint64, req Update
|
||||
}
|
||||
|
||||
// DeleteQuickReply 删除快捷回复
|
||||
func (r *Repository) DeleteQuickReply(adminID uint64, replyID uint64) error {
|
||||
return r.db.Where("id = ? AND admin_user_id = ?", replyID, adminID).
|
||||
func (r *Repository) DeleteQuickReply(ctx context.Context, adminID uint64, replyID uint64) error {
|
||||
return r.db.WithContext(ctx).Where("id = ? AND admin_user_id = ?", replyID, adminID).
|
||||
Delete(&model.ChatQuickReply{}).Error
|
||||
}
|
||||
|
||||
// GetAutoWelcomeMessage 获取建群自动话术
|
||||
func (r *Repository) GetAutoWelcomeMessage() string {
|
||||
func (r *Repository) GetAutoWelcomeMessage(ctx context.Context) string {
|
||||
var cfg model.SystemConfig
|
||||
if err := r.db.Where("`key` = ?", "chat.auto_welcome_message").First(&cfg).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("`key` = ?", "chat.auto_welcome_message").First(&cfg).Error; err != nil {
|
||||
return "欢迎加入订单群聊!如有任何问题,请随时沟通。"
|
||||
}
|
||||
return cfg.Value
|
||||
}
|
||||
|
||||
// UpdateAutoWelcomeMessage 更新建群自动话术
|
||||
func (r *Repository) UpdateAutoWelcomeMessage(message string) error {
|
||||
return r.db.Model(&model.SystemConfig{}).
|
||||
func (r *Repository) UpdateAutoWelcomeMessage(ctx context.Context, message string) error {
|
||||
return r.db.WithContext(ctx).Model(&model.SystemConfig{}).
|
||||
Where("`key` = ?", "chat.auto_welcome_message").
|
||||
Update("value", message).Error
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -21,45 +22,45 @@ func NewService(repo *Repository) *Service {
|
||||
return &Service{repo: repo}
|
||||
}
|
||||
|
||||
func (s *Service) ListConversations(principal Principal, page, pageSize int) (*PaginatedResult, error) {
|
||||
func (s *Service) ListConversations(ctx context.Context, principal Principal, page, pageSize int) (*PaginatedResult, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListConversations(principal, page, pageSize)
|
||||
return s.repo.ListConversations(ctx, principal, page, pageSize)
|
||||
}
|
||||
|
||||
func (s *Service) FindConversation(principal Principal, id uint64) (*ConversationDTO, error) {
|
||||
func (s *Service) FindConversation(ctx context.Context, principal Principal, id uint64) (*ConversationDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindConversation(principal, id)
|
||||
return s.repo.FindConversation(ctx, principal, id)
|
||||
}
|
||||
|
||||
func (s *Service) FindOrderConversation(userID uint64, orderID uint64) (*ConversationDTO, error) {
|
||||
func (s *Service) FindOrderConversation(ctx context.Context, userID uint64, orderID uint64) (*ConversationDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindOrderConversation(userID, orderID)
|
||||
return s.repo.FindOrderConversation(ctx, userID, orderID)
|
||||
}
|
||||
|
||||
func (s *Service) EnsureSupportConversation(userID uint64) (*ConversationDTO, error) {
|
||||
func (s *Service) EnsureSupportConversation(ctx context.Context, userID uint64) (*ConversationDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if userID == 0 {
|
||||
return nil, ErrPermissionDenied
|
||||
}
|
||||
return s.repo.EnsureSupportConversation(userID)
|
||||
return s.repo.EnsureSupportConversation(ctx, userID)
|
||||
}
|
||||
|
||||
func (s *Service) Messages(principal Principal, conversationID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
func (s *Service) Messages(ctx context.Context, principal Principal, conversationID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.Messages(principal, conversationID, page, pageSize)
|
||||
return s.repo.Messages(ctx, principal, conversationID, page, pageSize)
|
||||
}
|
||||
|
||||
func (s *Service) SendMessage(principal Principal, conversationID uint64, req SendMessageRequest) (*MessageDTO, error) {
|
||||
func (s *Service) SendMessage(ctx context.Context, principal Principal, conversationID uint64, req SendMessageRequest) (*MessageDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
@@ -75,7 +76,7 @@ func (s *Service) SendMessage(principal Principal, conversationID uint64, req Se
|
||||
if !ok {
|
||||
return nil, ErrInvalidMessage
|
||||
}
|
||||
return s.repo.SendMessage(principal, conversationID, req)
|
||||
return s.repo.SendMessage(ctx, principal, conversationID, req)
|
||||
}
|
||||
|
||||
func normalizeAttachmentURLS(items []string) ([]string, bool) {
|
||||
@@ -112,52 +113,52 @@ func normalizeAttachmentURLS(items []string) ([]string, bool) {
|
||||
return result, true
|
||||
}
|
||||
|
||||
func (s *Service) MarkRead(principal Principal, conversationID uint64) error {
|
||||
func (s *Service) MarkRead(ctx context.Context, principal Principal, conversationID uint64) error {
|
||||
if s.repo == nil {
|
||||
return ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.MarkRead(principal, conversationID)
|
||||
return s.repo.MarkRead(ctx, principal, conversationID)
|
||||
}
|
||||
|
||||
func (s *Service) TransferConversation(principal Principal, conversationID uint64, req TransferRequest) error {
|
||||
func (s *Service) TransferConversation(ctx context.Context, principal Principal, conversationID uint64, req TransferRequest) error {
|
||||
if s.repo == nil {
|
||||
return ErrDependencyUnavailable
|
||||
}
|
||||
if conversationID == 0 || req.ToAdminID == 0 {
|
||||
return ErrInvalidMessage
|
||||
}
|
||||
return s.repo.TransferConversation(principal, conversationID, req.ToAdminID)
|
||||
return s.repo.TransferConversation(ctx, principal, conversationID, req.ToAdminID)
|
||||
}
|
||||
|
||||
func (s *Service) GetAvailableSupportAdmins() ([]SupportAdminDTO, error) {
|
||||
func (s *Service) GetAvailableSupportAdmins(ctx context.Context) ([]SupportAdminDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.GetAvailableSupportAdmins()
|
||||
return s.repo.GetAvailableSupportAdmins(ctx)
|
||||
}
|
||||
|
||||
func (s *Service) ListConversationsWithFilter(principal Principal, page, pageSize int, filter string) (*PaginatedResult, error) {
|
||||
func (s *Service) ListConversationsWithFilter(ctx context.Context, principal Principal, page, pageSize int, filter string) (*PaginatedResult, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListConversationsWithFilter(principal, page, pageSize, filter)
|
||||
return s.repo.ListConversationsWithFilter(ctx, principal, page, pageSize, filter)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateRemark(principal Principal, conversationID uint64, req UpdateRemarkRequest) error {
|
||||
func (s *Service) UpdateRemark(ctx context.Context, principal Principal, conversationID uint64, req UpdateRemarkRequest) error {
|
||||
if s.repo == nil {
|
||||
return ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.UpdateRemark(principal, conversationID, req.Remark)
|
||||
return s.repo.UpdateRemark(ctx, principal, conversationID, req.Remark)
|
||||
}
|
||||
|
||||
func (s *Service) ListQuickReplies(adminID uint64) ([]QuickReplyDTO, error) {
|
||||
func (s *Service) ListQuickReplies(ctx context.Context, adminID uint64) ([]QuickReplyDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListQuickReplies(adminID)
|
||||
return s.repo.ListQuickReplies(ctx, adminID)
|
||||
}
|
||||
|
||||
func (s *Service) CreateQuickReply(adminID uint64, req CreateQuickReplyRequest) (*QuickReplyDTO, error) {
|
||||
func (s *Service) CreateQuickReply(ctx context.Context, adminID uint64, req CreateQuickReplyRequest) (*QuickReplyDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
@@ -166,33 +167,33 @@ func (s *Service) CreateQuickReply(adminID uint64, req CreateQuickReplyRequest)
|
||||
if req.Title == "" || req.Content == "" {
|
||||
return nil, ErrInvalidMessage
|
||||
}
|
||||
return s.repo.CreateQuickReply(adminID, req)
|
||||
return s.repo.CreateQuickReply(ctx, adminID, req)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateQuickReply(adminID uint64, replyID uint64, req UpdateQuickReplyRequest) error {
|
||||
func (s *Service) UpdateQuickReply(ctx context.Context, adminID uint64, replyID uint64, req UpdateQuickReplyRequest) error {
|
||||
if s.repo == nil {
|
||||
return ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.UpdateQuickReply(adminID, replyID, req)
|
||||
return s.repo.UpdateQuickReply(ctx, adminID, replyID, req)
|
||||
}
|
||||
|
||||
func (s *Service) DeleteQuickReply(adminID uint64, replyID uint64) error {
|
||||
func (s *Service) DeleteQuickReply(ctx context.Context, adminID uint64, replyID uint64) error {
|
||||
if s.repo == nil {
|
||||
return ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.DeleteQuickReply(adminID, replyID)
|
||||
return s.repo.DeleteQuickReply(ctx, adminID, replyID)
|
||||
}
|
||||
|
||||
func (s *Service) GetAutoWelcomeMessage() string {
|
||||
func (s *Service) GetAutoWelcomeMessage(ctx context.Context) string {
|
||||
if s.repo == nil {
|
||||
return ""
|
||||
}
|
||||
return s.repo.GetAutoWelcomeMessage()
|
||||
return s.repo.GetAutoWelcomeMessage(ctx)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateAutoWelcomeMessage(message string) error {
|
||||
func (s *Service) UpdateAutoWelcomeMessage(ctx context.Context, message string) error {
|
||||
if s.repo == nil {
|
||||
return ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.UpdateAutoWelcomeMessage(message)
|
||||
return s.repo.UpdateAutoWelcomeMessage(ctx, message)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func (h *Handler) Create(c *gin.Context) {
|
||||
response.BadRequest(c, "申诉信息不完整")
|
||||
return
|
||||
}
|
||||
item, err := h.service.Create(userID, orderID, req)
|
||||
item, err := h.service.Create(c.Request.Context(), userID, orderID, req)
|
||||
if err != nil {
|
||||
writeDisputeError(c, err)
|
||||
return
|
||||
@@ -49,7 +49,7 @@ func (h *Handler) List(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
page, pageSize := parsePagination(c)
|
||||
result, err := h.service.ListForUser(userID, page, pageSize)
|
||||
result, err := h.service.ListForUser(c.Request.Context(), userID, page, pageSize)
|
||||
if err != nil {
|
||||
writeDisputeError(c, err)
|
||||
return
|
||||
@@ -67,7 +67,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 {
|
||||
writeDisputeError(c, err)
|
||||
return
|
||||
@@ -77,7 +77,7 @@ func (h *Handler) Detail(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 {
|
||||
writeDisputeError(c, err)
|
||||
return
|
||||
@@ -100,7 +100,7 @@ func (h *Handler) AdminArbitrate(c *gin.Context) {
|
||||
response.BadRequest(c, "仲裁结果和备注不能为空")
|
||||
return
|
||||
}
|
||||
item, err := h.service.Arbitrate(adminID, id, req, auditMeta(c))
|
||||
item, err := h.service.Arbitrate(c.Request.Context(), adminID, id, req, auditMeta(c))
|
||||
if err != nil {
|
||||
writeDisputeError(c, err)
|
||||
return
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package dispute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -23,7 +24,7 @@ type Repository struct {
|
||||
}
|
||||
|
||||
// RefundFunc 由 payment 模块注入,避免 dispute 与 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
|
||||
@@ -40,9 +41,9 @@ func (r *Repository) SetRefundFunc(fn RefundFunc) {
|
||||
r.refundFunc = fn
|
||||
}
|
||||
|
||||
func (r *Repository) Create(userID uint64, orderID uint64, req CreateRequest) (*DisputeDTO, error) {
|
||||
func (r *Repository) Create(ctx context.Context, userID uint64, orderID uint64, req CreateRequest) (*DisputeDTO, 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 order model.RentalOrder
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
|
||||
return err
|
||||
@@ -140,18 +141,19 @@ func (r *Repository) Create(userID uint64, orderID uint64, req CreateRequest) (*
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return r.FindForUser(userID, createdID)
|
||||
return r.FindForUser(ctx, userID, createdID)
|
||||
}
|
||||
|
||||
func (r *Repository) ListForUser(userID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
conditions := r.db.Model(&model.Dispute{}).Where("initiator_id = ? OR target_user_id = ?", userID, userID)
|
||||
func (r *Repository) ListForUser(ctx context.Context, userID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
db := r.db.WithContext(ctx)
|
||||
conditions := db.Model(&model.Dispute{}).Where("initiator_id = ? OR target_user_id = ?", userID, userID)
|
||||
var total int64
|
||||
if err := conditions.Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
var rows []disputeRow
|
||||
err := r.baseQuery().
|
||||
err := r.baseQuery(ctx).
|
||||
Where("d.initiator_id = ? OR d.target_user_id = ?", userID, userID).
|
||||
Order("d.id DESC").
|
||||
Offset(offset).Limit(pageSize).
|
||||
@@ -162,9 +164,9 @@ func (r *Repository) ListForUser(userID uint64, page, pageSize int) (*PaginatedR
|
||||
return &PaginatedResult{Items: toDTOs(rows), Total: total, Page: page, PageSize: pageSize}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) FindForUser(userID uint64, id uint64) (*DisputeDTO, error) {
|
||||
func (r *Repository) FindForUser(ctx context.Context, userID uint64, id uint64) (*DisputeDTO, error) {
|
||||
var row disputeRow
|
||||
if err := r.baseQuery().
|
||||
if err := r.baseQuery(ctx).
|
||||
Where("d.id = ? AND (d.initiator_id = ? OR d.target_user_id = ?)", id, userID, userID).
|
||||
First(&row).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -173,23 +175,23 @@ func (r *Repository) FindForUser(userID uint64, id uint64) (*DisputeDTO, error)
|
||||
return &dto, 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.db.Model(&model.Dispute{}).Count(&total).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Model(&model.Dispute{}).Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
var rows []disputeRow
|
||||
err := r.baseQuery().Order("d.id DESC").Offset(offset).Limit(pageSize).Scan(&rows).Error
|
||||
err := r.baseQuery(ctx).Order("d.id DESC").Offset(offset).Limit(pageSize).Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PaginatedResult{Items: toDTOs(rows), Total: total, Page: page, PageSize: pageSize}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) Arbitrate(adminID uint64, id uint64, req ArbitrateRequest, meta AuditMeta) (*DisputeDTO, error) {
|
||||
func (r *Repository) Arbitrate(ctx context.Context, adminID uint64, id uint64, req ArbitrateRequest, meta AuditMeta) (*DisputeDTO, 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 row model.Dispute
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&row, id).Error; err != nil {
|
||||
return err
|
||||
@@ -328,9 +330,9 @@ func (r *Repository) Arbitrate(adminID uint64, id uint64, req ArbitrateRequest,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.startRefundBestEffort(refund)
|
||||
r.startRefundBestEffort(ctx, refund)
|
||||
var row disputeRow
|
||||
if err := r.baseQuery().Where("d.id = ?", id).First(&row).Error; err != nil {
|
||||
if err := r.baseQuery(ctx).Where("d.id = ?", id).First(&row).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dto := row.toDTO()
|
||||
@@ -442,11 +444,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
|
||||
}
|
||||
_, _ = r.refundFunc(action.OrderID, action.RefundAmountCent, action.BizType, action.Remark)
|
||||
_, _ = r.refundFunc(ctx, action.OrderID, action.RefundAmountCent, action.BizType, action.Remark)
|
||||
}
|
||||
|
||||
func renterFrozenBalance(tx *gorm.DB, renterID uint64) (int64, error) {
|
||||
@@ -463,8 +465,8 @@ func renterFrozenBalance(tx *gorm.DB, renterID uint64) (int64, error) {
|
||||
return account.FrozenBalanceCent, nil
|
||||
}
|
||||
|
||||
func (r *Repository) baseQuery() *gorm.DB {
|
||||
return r.db.Table("disputes AS d").
|
||||
func (r *Repository) baseQuery(ctx context.Context) *gorm.DB {
|
||||
return r.db.WithContext(ctx).Table("disputes AS d").
|
||||
Select("d.*, o.order_no, l.listing_no, a.title").
|
||||
Joins("JOIN rental_orders AS o ON o.id = d.order_id").
|
||||
Joins("JOIN rental_listings AS l ON l.id = o.listing_id").
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package dispute
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDependencyUnavailable = errors.New("dependency unavailable")
|
||||
@@ -18,43 +21,43 @@ func NewService(repo *Repository) *Service {
|
||||
return &Service{repo: repo}
|
||||
}
|
||||
|
||||
func (s *Service) Create(userID uint64, orderID uint64, req CreateRequest) (*DisputeDTO, error) {
|
||||
func (s *Service) Create(ctx context.Context, userID uint64, orderID uint64, req CreateRequest) (*DisputeDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if orderID == 0 || req.Type == "" || req.Description == "" {
|
||||
return nil, ErrInvalidDispute
|
||||
}
|
||||
return s.repo.Create(userID, orderID, req)
|
||||
return s.repo.Create(ctx, userID, orderID, req)
|
||||
}
|
||||
|
||||
func (s *Service) ListForUser(userID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
func (s *Service) ListForUser(ctx context.Context, userID uint64, page, pageSize int) (*PaginatedResult, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListForUser(userID, page, pageSize)
|
||||
return s.repo.ListForUser(ctx, userID, page, pageSize)
|
||||
}
|
||||
|
||||
func (s *Service) FindForUser(userID uint64, id uint64) (*DisputeDTO, error) {
|
||||
func (s *Service) FindForUser(ctx context.Context, userID uint64, id uint64) (*DisputeDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindForUser(userID, id)
|
||||
return s.repo.FindForUser(ctx, userID, id)
|
||||
}
|
||||
|
||||
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) Arbitrate(adminID uint64, id uint64, req ArbitrateRequest, meta AuditMeta) (*DisputeDTO, error) {
|
||||
func (s *Service) Arbitrate(ctx context.Context, adminID uint64, id uint64, req ArbitrateRequest, meta AuditMeta) (*DisputeDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if id == 0 || req.Result == "" || req.Remark == "" {
|
||||
return nil, ErrInvalidDispute
|
||||
}
|
||||
return s.repo.Arbitrate(adminID, id, req, meta)
|
||||
return s.repo.Arbitrate(ctx, adminID, id, req, meta)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package listing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
@@ -117,7 +118,7 @@ func (h *Handler) SubmitReview(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *Handler) ListPendingReview(c *gin.Context) {
|
||||
items, err := h.service.ListPendingReview()
|
||||
items, err := h.service.ListPendingReview(c.Request.Context())
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -130,7 +131,7 @@ func (h *Handler) ListAdmin(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
result, err := h.service.ListAdmin(query)
|
||||
result, err := h.service.ListAdmin(c.Request.Context(), query)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -143,7 +144,7 @@ func (h *Handler) FindAdmin(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
item, err := h.service.FindAdmin(id)
|
||||
item, err := h.service.FindAdmin(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -159,7 +160,7 @@ func (h *Handler) AdminMarkAbnormal(c *gin.Context) {
|
||||
h.adminAction(c, h.service.AdminMarkAbnormal)
|
||||
}
|
||||
|
||||
func (h *Handler) adminAction(c *gin.Context, fn func(uint64, uint64, AdminActionRequest, AuditMeta) (*ListingDTO, error)) {
|
||||
func (h *Handler) adminAction(c *gin.Context, fn func(context.Context, uint64, uint64, AdminActionRequest, AuditMeta) (*ListingDTO, error)) {
|
||||
adminID, ok := currentAdminID(c)
|
||||
if !ok {
|
||||
response.Unauthorized(c, "缺少管理员上下文")
|
||||
@@ -174,7 +175,7 @@ func (h *Handler) adminAction(c *gin.Context, fn func(uint64, uint64, AdminActio
|
||||
response.BadRequest(c, "操作原因不能为空")
|
||||
return
|
||||
}
|
||||
item, err := fn(adminID, id, req, auditMeta(c))
|
||||
item, err := fn(c.Request.Context(), adminID, id, req, auditMeta(c))
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -195,7 +196,7 @@ func (h *Handler) Approve(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
item, err := h.service.Approve(id)
|
||||
item, err := h.service.Approve(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -218,7 +219,7 @@ func (h *Handler) AdjustReviewPrice(c *gin.Context) {
|
||||
response.BadRequest(c, "调价参数不正确")
|
||||
return
|
||||
}
|
||||
item, err := h.service.AdjustReviewPrice(adminID, id, req, auditMeta(c))
|
||||
item, err := h.service.AdjustReviewPrice(c.Request.Context(), adminID, id, req, auditMeta(c))
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -236,7 +237,7 @@ func (h *Handler) Reject(c *gin.Context) {
|
||||
response.BadRequest(c, "审核拒绝原因不能为空")
|
||||
return
|
||||
}
|
||||
item, err := h.service.Reject(id, req)
|
||||
item, err := h.service.Reject(c.Request.Context(), id, req)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -254,7 +255,7 @@ func (h *Handler) Offline(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
item, err := h.service.Offline(ownerID, id)
|
||||
item, err := h.service.Offline(c.Request.Context(), ownerID, id)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -263,7 +264,7 @@ func (h *Handler) Offline(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *Handler) ListPublic(c *gin.Context) {
|
||||
items, err := h.service.ListPublic(parsePublicListQuery(c))
|
||||
items, err := h.service.ListPublic(c.Request.Context(), parsePublicListQuery(c))
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -276,7 +277,7 @@ func (h *Handler) FindPublic(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
item, err := h.service.FindPublic(id)
|
||||
item, err := h.service.FindPublic(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -289,7 +290,7 @@ func (h *Handler) Cover(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
key, err := h.service.FindPublicCoverKey(id)
|
||||
key, err := h.service.FindPublicCoverKey(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -307,7 +308,7 @@ func (h *Handler) Screenshot(c *gin.Context) {
|
||||
response.BadRequest(c, "截图序号不正确")
|
||||
return
|
||||
}
|
||||
key, err := h.service.FindPublicScreenshotKey(id, index)
|
||||
key, err := h.service.FindPublicScreenshotKey(c.Request.Context(), id, index)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -350,7 +351,7 @@ func (h *Handler) ListMine(c *gin.Context) {
|
||||
response.Unauthorized(c, "缺少用户上下文")
|
||||
return
|
||||
}
|
||||
items, err := h.service.ListMine(ownerID)
|
||||
items, err := h.service.ListMine(c.Request.Context(), ownerID)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
@@ -368,7 +369,7 @@ func (h *Handler) FindMine(c *gin.Context) {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
item, err := h.service.FindMine(ownerID, id)
|
||||
item, err := h.service.FindMine(c.Request.Context(), ownerID, id)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package listing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -47,9 +48,9 @@ func initialPublishState(reviewRequired bool) (string, string, *time.Time) {
|
||||
return "published", "approved", &now
|
||||
}
|
||||
|
||||
func (r *Repository) Create(ownerID uint64, req CreateRequest, reviewRequired bool) (*ListingDTO, error) {
|
||||
func (r *Repository) Create(ctx context.Context, ownerID uint64, req CreateRequest, reviewRequired bool) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
listingNo, err := r.nextListingNo(tx, time.Now())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -107,9 +108,9 @@ type externalUploadCreate struct {
|
||||
ParsedPayload []byte
|
||||
}
|
||||
|
||||
func (r *Repository) CreateFromExternalUpload(upload externalUploadCreate, req CreateRequest) (*ListingDTO, error) {
|
||||
func (r *Repository) CreateFromExternalUpload(ctx context.Context, upload externalUploadCreate, req CreateRequest) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
listingNo, err := r.nextListingNo(tx, time.Now())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -182,9 +183,9 @@ func (r *Repository) CreateFromExternalUpload(upload externalUploadCreate, req C
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) Update(ownerID uint64, listingID uint64, req UpdateRequest, reviewRequired bool) (*ListingDTO, error) {
|
||||
func (r *Repository) Update(ctx context.Context, ownerID uint64, listingID uint64, req UpdateRequest, reviewRequired bool) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -230,9 +231,9 @@ func (r *Repository) Update(ownerID uint64, listingID uint64, req UpdateRequest,
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) SubmitReview(ownerID uint64, listingID uint64, reviewRequired bool) (*ListingDTO, error) {
|
||||
func (r *Repository) SubmitReview(ctx context.Context, ownerID uint64, listingID uint64, reviewRequired bool) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -258,9 +259,9 @@ func (r *Repository) SubmitReview(ownerID uint64, listingID uint64, reviewRequir
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) ListPendingReview() ([]ListingDTO, error) {
|
||||
func (r *Repository) ListPendingReview(ctx context.Context) ([]ListingDTO, error) {
|
||||
var rows []listingRow
|
||||
err := r.baseQuery().
|
||||
err := r.baseQuery(ctx).
|
||||
Where("l.review_status = ? AND l.status <> ?", "pending", "offline").
|
||||
Order("l.updated_at ASC, l.id ASC").
|
||||
Limit(200).
|
||||
@@ -271,7 +272,7 @@ func (r *Repository) ListPendingReview() ([]ListingDTO, error) {
|
||||
return rowsToDTO(rows), nil
|
||||
}
|
||||
|
||||
func (r *Repository) ListAdmin(query AdminListQuery) (*AdminListResult, error) {
|
||||
func (r *Repository) ListAdmin(ctx context.Context, query AdminListQuery) (*AdminListResult, error) {
|
||||
page := query.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
@@ -287,13 +288,13 @@ func (r *Repository) ListAdmin(query AdminListQuery) (*AdminListResult, error) {
|
||||
pageSize = 100
|
||||
}
|
||||
|
||||
countDB := r.applyAdminListFilters(r.db.Table("rental_listings AS l"), query)
|
||||
countDB := r.applyAdminListFilters(r.db.WithContext(ctx).Table("rental_listings AS l"), query)
|
||||
var total int64
|
||||
if err := countDB.Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
db := r.applyAdminListFilters(r.baseQuery(), query)
|
||||
db := r.applyAdminListFilters(r.baseQuery(ctx), query)
|
||||
offset := (page - 1) * pageSize
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
@@ -386,21 +387,21 @@ func (r *Repository) ensureUploadOwnerUser(tx *gorm.DB, admin *model.AdminUser)
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (r *Repository) FindAdmin(listingID uint64) (*ListingDTO, error) {
|
||||
return r.findDTO("l.id = ?", listingID)
|
||||
func (r *Repository) FindAdmin(ctx context.Context, listingID uint64) (*ListingDTO, error) {
|
||||
return r.findDTO(ctx, "l.id = ?", listingID)
|
||||
}
|
||||
|
||||
func (r *Repository) AdminOffline(adminID uint64, listingID uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
return r.adminUpdateStatus(adminID, listingID, req, meta, "offline", "offline", "listing.admin_offline", "商品已被后台下架", "你的租号商品已被后台下架,请查看原因后处理。")
|
||||
func (r *Repository) AdminOffline(ctx context.Context, adminID uint64, listingID uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
return r.adminUpdateStatus(ctx, adminID, listingID, req, meta, "offline", "offline", "listing.admin_offline", "商品已被后台下架", "你的租号商品已被后台下架,请查看原因后处理。")
|
||||
}
|
||||
|
||||
func (r *Repository) AdminMarkAbnormal(adminID uint64, listingID uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
return r.adminUpdateStatus(adminID, listingID, req, meta, "abnormal", "abnormal", "listing.mark_abnormal", "商品已被标记异常", "你的租号商品已被后台标记异常,请联系客服处理。")
|
||||
func (r *Repository) AdminMarkAbnormal(ctx context.Context, adminID uint64, listingID uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
return r.adminUpdateStatus(ctx, adminID, listingID, req, meta, "abnormal", "abnormal", "listing.mark_abnormal", "商品已被标记异常", "你的租号商品已被后台标记异常,请联系客服处理。")
|
||||
}
|
||||
|
||||
func (r *Repository) adminUpdateStatus(adminID uint64, listingID uint64, req AdminActionRequest, meta AuditMeta, listingStatus string, accountStatus string, action string, title string, content string) (*ListingDTO, error) {
|
||||
func (r *Repository) adminUpdateStatus(ctx context.Context, adminID uint64, listingID uint64, req AdminActionRequest, meta AuditMeta, listingStatus string, accountStatus string, action string, title string, content string) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
listing, account, err := r.findForReviewUpdate(tx, listingID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -453,9 +454,9 @@ func (r *Repository) adminUpdateStatus(adminID uint64, listingID uint64, req Adm
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) Approve(listingID uint64) (*ListingDTO, error) {
|
||||
func (r *Repository) Approve(ctx context.Context, listingID uint64) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
listing, account, err := r.findForReviewUpdate(tx, listingID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -492,9 +493,9 @@ func (r *Repository) Approve(listingID uint64) (*ListingDTO, error) {
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) AdjustReviewPrice(adminID uint64, listingID uint64, req AdminPriceAdjustRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
func (r *Repository) AdjustReviewPrice(ctx context.Context, adminID uint64, listingID uint64, req AdminPriceAdjustRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
listing, account, err := r.findForReviewUpdate(tx, listingID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -583,9 +584,9 @@ func (r *Repository) AdjustReviewPrice(adminID uint64, listingID uint64, req Adm
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) Reject(listingID uint64, req ReviewRequest) (*ListingDTO, error) {
|
||||
func (r *Repository) Reject(ctx context.Context, listingID uint64, req ReviewRequest) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
listing, account, err := r.findForReviewUpdate(tx, listingID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -621,9 +622,9 @@ func (r *Repository) Reject(listingID uint64, req ReviewRequest) (*ListingDTO, e
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) Offline(ownerID uint64, listingID uint64) (*ListingDTO, error) {
|
||||
func (r *Repository) Offline(ctx context.Context, ownerID uint64, listingID uint64) (*ListingDTO, error) {
|
||||
var dto *ListingDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
listing, account, err := r.findOwnedForUpdate(tx, ownerID, listingID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -648,14 +649,14 @@ func (r *Repository) Offline(ownerID uint64, listingID uint64) (*ListingDTO, err
|
||||
return dto, err
|
||||
}
|
||||
|
||||
func (r *Repository) ListPublic(query PublicListQuery) (*PublicListResult, error) {
|
||||
func (r *Repository) ListPublic(ctx context.Context, query PublicListQuery) (*PublicListResult, error) {
|
||||
page, pageSize := normalizedPublicPage(query)
|
||||
if canListPublicWithSQL(query) {
|
||||
return r.listPublicPage(query, page, pageSize)
|
||||
return r.listPublicPage(ctx, query, page, pageSize)
|
||||
}
|
||||
|
||||
var rows []listingRow
|
||||
err := r.baseQuery().
|
||||
err := r.baseQuery(ctx).
|
||||
Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false).
|
||||
Order("l.published_at DESC, l.id DESC").
|
||||
Scan(&rows).Error
|
||||
@@ -694,9 +695,9 @@ func (r *Repository) ListPublic(query PublicListQuery) (*PublicListResult, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Repository) listPublicPage(query PublicListQuery, page int, pageSize int) (*PublicListResult, error) {
|
||||
func (r *Repository) listPublicPage(ctx context.Context, query PublicListQuery, page int, pageSize int) (*PublicListResult, error) {
|
||||
var total int64
|
||||
if err := r.db.Table("rental_listings AS l").
|
||||
if err := r.db.WithContext(ctx).Table("rental_listings AS l").
|
||||
Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false).
|
||||
Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -704,7 +705,7 @@ func (r *Repository) listPublicPage(query PublicListQuery, page int, pageSize in
|
||||
|
||||
var rows []listingRow
|
||||
offset := (page - 1) * pageSize
|
||||
err := applyPublicSQLSort(r.baseQuery(), query.Sort).
|
||||
err := applyPublicSQLSort(r.baseQuery(ctx), query.Sort).
|
||||
Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false).
|
||||
Limit(pageSize).
|
||||
Offset(offset).
|
||||
@@ -712,7 +713,7 @@ func (r *Repository) listPublicPage(query PublicListQuery, page int, pageSize in
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
zoneCounts, err := r.publicZoneCountsCached()
|
||||
zoneCounts, err := r.publicZoneCountsCached(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -786,7 +787,7 @@ func applyPublicSQLSort(db *gorm.DB, sortKey string) *gorm.DB {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) publicZoneCountsCached() (map[string]int64, error) {
|
||||
func (r *Repository) publicZoneCountsCached(ctx context.Context) (map[string]int64, error) {
|
||||
now := time.Now()
|
||||
r.publicZoneCountsMu.Lock()
|
||||
defer r.publicZoneCountsMu.Unlock()
|
||||
@@ -795,7 +796,7 @@ func (r *Repository) publicZoneCountsCached() (map[string]int64, error) {
|
||||
}
|
||||
|
||||
var rows []publicZoneRow
|
||||
err := r.db.Table("rental_listings AS l").
|
||||
err := r.db.WithContext(ctx).Table("rental_listings AS l").
|
||||
Select("a.login_platform, a.haf_coin_amount, a.asset_summary").
|
||||
Joins("JOIN game_accounts AS a ON a.id = l.account_id").
|
||||
Where("l.status = ? AND l.review_status = ? AND l.in_transaction = ?", "published", "approved", false).
|
||||
@@ -844,9 +845,9 @@ func copyPublicZoneCounts(counts map[string]int64) map[string]int64 {
|
||||
return copied
|
||||
}
|
||||
|
||||
func (r *Repository) ListMine(ownerID uint64) ([]ListingDTO, error) {
|
||||
func (r *Repository) ListMine(ctx context.Context, ownerID uint64) ([]ListingDTO, error) {
|
||||
var rows []listingRow
|
||||
err := r.baseQuery().
|
||||
err := r.baseQuery(ctx).
|
||||
Where("l.owner_id = ?", ownerID).
|
||||
Order("l.id DESC").
|
||||
Scan(&rows).Error
|
||||
@@ -1232,8 +1233,8 @@ func timeRangeCoversHour(start int, end int, hour int) bool {
|
||||
return hour >= start || hour <= end
|
||||
}
|
||||
|
||||
func (r *Repository) FindPublic(id uint64) (*ListingDTO, error) {
|
||||
dto, err := r.findDTO("l.id = ? AND l.status = ? AND l.review_status = ? AND l.in_transaction = ?", id, "published", "approved", false)
|
||||
func (r *Repository) FindPublic(ctx context.Context, id uint64) (*ListingDTO, error) {
|
||||
dto, err := r.findDTO(ctx, "l.id = ? AND l.status = ? AND l.review_status = ? AND l.in_transaction = ?", id, "published", "approved", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1241,15 +1242,15 @@ func (r *Repository) FindPublic(id uint64) (*ListingDTO, error) {
|
||||
return dto, nil
|
||||
}
|
||||
|
||||
func (r *Repository) FindPublicCoverKey(id uint64) (string, error) {
|
||||
return r.FindPublicScreenshotKey(id, 0)
|
||||
func (r *Repository) FindPublicCoverKey(ctx context.Context, id uint64) (string, error) {
|
||||
return r.FindPublicScreenshotKey(ctx, id, 0)
|
||||
}
|
||||
|
||||
func (r *Repository) FindPublicScreenshotKey(id uint64, index int) (string, error) {
|
||||
func (r *Repository) FindPublicScreenshotKey(ctx context.Context, id uint64, index int) (string, error) {
|
||||
if index < 0 {
|
||||
return "", gorm.ErrRecordNotFound
|
||||
}
|
||||
dto, err := r.findDTO("l.id = ? AND l.status = ? AND l.review_status = ? AND l.in_transaction = ?", id, "published", "approved", false)
|
||||
dto, err := r.findDTO(ctx, "l.id = ? AND l.status = ? AND l.review_status = ? AND l.in_transaction = ?", id, "published", "approved", false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -1262,8 +1263,8 @@ func (r *Repository) FindPublicScreenshotKey(id uint64, index int) (string, erro
|
||||
return "", gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (r *Repository) FindMine(ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
dto, err := r.findDTO("l.id = ? AND l.owner_id = ?", id, ownerID)
|
||||
func (r *Repository) FindMine(ctx context.Context, ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
dto, err := r.findDTO(ctx, "l.id = ? AND l.owner_id = ?", id, ownerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1283,9 +1284,9 @@ func (r *Repository) findOwnedForUpdate(tx *gorm.DB, ownerID uint64, listingID u
|
||||
return &listing, &account, nil
|
||||
}
|
||||
|
||||
func (r *Repository) findDTO(where string, args ...any) (*ListingDTO, error) {
|
||||
func (r *Repository) findDTO(ctx context.Context, where string, args ...any) (*ListingDTO, error) {
|
||||
var row listingRow
|
||||
err := r.baseQuery().
|
||||
err := r.baseQuery(ctx).
|
||||
Where(where, args...).
|
||||
First(&row).Error
|
||||
if err != nil {
|
||||
@@ -1295,8 +1296,8 @@ func (r *Repository) findDTO(where string, args ...any) (*ListingDTO, error) {
|
||||
return &dto, nil
|
||||
}
|
||||
|
||||
func (r *Repository) baseQuery() *gorm.DB {
|
||||
return r.db.Table("rental_listings AS l").
|
||||
func (r *Repository) baseQuery(ctx context.Context) *gorm.DB {
|
||||
return r.db.WithContext(ctx).Table("rental_listings AS l").
|
||||
Select(`l.*, a.title, a.description, a.game_name, a.server_region, a.login_platform, a.rank_level,
|
||||
a.haf_coin_amount, a.asset_summary, a.screenshot_urls, COALESCE(u.phone, '') AS owner_phone, COALESCE(u.nickname, '') AS owner_nickname`).
|
||||
Joins("JOIN game_accounts AS a ON a.id = l.account_id").
|
||||
|
||||
@@ -96,7 +96,7 @@ func (s *Service) Create(ctx context.Context, ownerID uint64, req CreateRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.Create(ownerID, req, reviewRequired)
|
||||
return s.repo.Create(ctx, ownerID, req, reviewRequired)
|
||||
}
|
||||
|
||||
func (s *Service) ImportExternalUpload(ctx context.Context, req ExternalUploadRequest, meta ExternalUploadMeta) (*ExternalUploadResponse, error) {
|
||||
@@ -132,7 +132,7 @@ func (s *Service) ImportExternalUpload(ctx context.Context, req ExternalUploadRe
|
||||
continue
|
||||
}
|
||||
parsedPayload, _ := json.Marshal(item)
|
||||
dto, err := s.repo.CreateFromExternalUpload(externalUploadCreate{
|
||||
dto, err := s.repo.CreateFromExternalUpload(ctx, externalUploadCreate{
|
||||
UploaderName: uploaderName,
|
||||
ClientUploadTime: clientUploadTime,
|
||||
ClientIP: meta.IP,
|
||||
@@ -187,7 +187,7 @@ func (s *Service) Update(ctx context.Context, ownerID uint64, id uint64, req Upd
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.Update(ownerID, id, req, reviewRequired)
|
||||
return s.repo.Update(ctx, ownerID, id, req, reviewRequired)
|
||||
}
|
||||
|
||||
func (s *Service) SubmitReview(ctx context.Context, ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
@@ -198,124 +198,124 @@ func (s *Service) SubmitReview(ctx context.Context, ownerID uint64, id uint64) (
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.repo.SubmitReview(ownerID, id, reviewRequired)
|
||||
return s.repo.SubmitReview(ctx, ownerID, id, reviewRequired)
|
||||
}
|
||||
|
||||
func (s *Service) ListPendingReview() ([]ListingDTO, error) {
|
||||
func (s *Service) ListPendingReview(ctx context.Context) ([]ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListPendingReview()
|
||||
return s.repo.ListPendingReview(ctx)
|
||||
}
|
||||
|
||||
func (s *Service) ListAdmin(query AdminListQuery) (*AdminListResult, error) {
|
||||
func (s *Service) ListAdmin(ctx context.Context, query AdminListQuery) (*AdminListResult, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListAdmin(query)
|
||||
return s.repo.ListAdmin(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) FindAdmin(id uint64) (*ListingDTO, error) {
|
||||
func (s *Service) FindAdmin(ctx context.Context, id uint64) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindAdmin(id)
|
||||
return s.repo.FindAdmin(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) AdminOffline(adminID uint64, id uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
func (s *Service) AdminOffline(ctx context.Context, adminID uint64, id uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if req.Reason == "" {
|
||||
return nil, ErrInvalidInput
|
||||
}
|
||||
return s.repo.AdminOffline(adminID, id, req, meta)
|
||||
return s.repo.AdminOffline(ctx, adminID, id, req, meta)
|
||||
}
|
||||
|
||||
func (s *Service) AdminMarkAbnormal(adminID uint64, id uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
func (s *Service) AdminMarkAbnormal(ctx context.Context, adminID uint64, id uint64, req AdminActionRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if req.Reason == "" {
|
||||
return nil, ErrInvalidInput
|
||||
}
|
||||
return s.repo.AdminMarkAbnormal(adminID, id, req, meta)
|
||||
return s.repo.AdminMarkAbnormal(ctx, adminID, id, req, meta)
|
||||
}
|
||||
|
||||
func (s *Service) Approve(id uint64) (*ListingDTO, error) {
|
||||
func (s *Service) Approve(ctx context.Context, id uint64) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.Approve(id)
|
||||
return s.repo.Approve(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) AdjustReviewPrice(adminID uint64, id uint64, req AdminPriceAdjustRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
func (s *Service) AdjustReviewPrice(ctx context.Context, adminID uint64, id uint64, req AdminPriceAdjustRequest, meta AuditMeta) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if req.BuyerRatio <= 0 && req.BuyerTotalPriceCent <= 0 {
|
||||
return nil, ErrInvalidPrice
|
||||
}
|
||||
return s.repo.AdjustReviewPrice(adminID, id, req, meta)
|
||||
return s.repo.AdjustReviewPrice(ctx, adminID, id, req, meta)
|
||||
}
|
||||
|
||||
func (s *Service) Reject(id uint64, req ReviewRequest) (*ListingDTO, error) {
|
||||
func (s *Service) Reject(ctx context.Context, id uint64, req ReviewRequest) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
if req.Reason == "" {
|
||||
return nil, ErrInvalidInput
|
||||
}
|
||||
return s.repo.Reject(id, req)
|
||||
return s.repo.Reject(ctx, id, req)
|
||||
}
|
||||
|
||||
func (s *Service) Offline(ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
func (s *Service) Offline(ctx context.Context, ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.Offline(ownerID, id)
|
||||
return s.repo.Offline(ctx, ownerID, id)
|
||||
}
|
||||
|
||||
func (s *Service) ListPublic(query PublicListQuery) (*PublicListResult, error) {
|
||||
func (s *Service) ListPublic(ctx context.Context, query PublicListQuery) (*PublicListResult, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListPublic(query)
|
||||
return s.repo.ListPublic(ctx, query)
|
||||
}
|
||||
|
||||
func (s *Service) ListMine(ownerID uint64) ([]ListingDTO, error) {
|
||||
func (s *Service) ListMine(ctx context.Context, ownerID uint64) ([]ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.ListMine(ownerID)
|
||||
return s.repo.ListMine(ctx, ownerID)
|
||||
}
|
||||
|
||||
func (s *Service) FindPublic(id uint64) (*ListingDTO, error) {
|
||||
func (s *Service) FindPublic(ctx context.Context, id uint64) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindPublic(id)
|
||||
return s.repo.FindPublic(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) FindPublicCoverKey(id uint64) (string, error) {
|
||||
func (s *Service) FindPublicCoverKey(ctx context.Context, id uint64) (string, error) {
|
||||
if s.repo == nil {
|
||||
return "", ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindPublicCoverKey(id)
|
||||
return s.repo.FindPublicCoverKey(ctx, id)
|
||||
}
|
||||
|
||||
func (s *Service) FindPublicScreenshotKey(id uint64, index int) (string, error) {
|
||||
func (s *Service) FindPublicScreenshotKey(ctx context.Context, id uint64, index int) (string, error) {
|
||||
if s.repo == nil {
|
||||
return "", ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindPublicScreenshotKey(id, index)
|
||||
return s.repo.FindPublicScreenshotKey(ctx, id, index)
|
||||
}
|
||||
|
||||
func (s *Service) FindMine(ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
func (s *Service) FindMine(ctx context.Context, ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindMine(ownerID, id)
|
||||
return s.repo.FindMine(ctx, ownerID, id)
|
||||
}
|
||||
|
||||
type publishRules struct {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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").
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -69,22 +69,22 @@ func (c runtimePaymentConfig) isMockMode() bool {
|
||||
return c.Provider == "mock"
|
||||
}
|
||||
|
||||
func (r *Repository) defaultRuntimeConfig() (*runtimePaymentConfig, error) {
|
||||
func (r *Repository) defaultRuntimeConfig(ctx context.Context) (*runtimePaymentConfig, error) {
|
||||
if r.configRepo == nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
dto, err := r.configRepo.FindDefaultAny(true)
|
||||
dto, err := r.configRepo.FindDefaultAny(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return runtimeConfigFromDTO(dto), nil
|
||||
}
|
||||
|
||||
func (r *Repository) runtimeConfigForPayment(payment *model.PaymentOrder) (*runtimePaymentConfig, error) {
|
||||
func (r *Repository) runtimeConfigForPayment(ctx context.Context, payment *model.PaymentOrder) (*runtimePaymentConfig, error) {
|
||||
provider := firstNonEmpty(payment.Provider, "mock")
|
||||
merchantID := payment.MerchantID
|
||||
if r.configRepo != nil && merchantID != "" {
|
||||
dto, err := r.configRepo.FindByProviderMerchant(provider, merchantID, true)
|
||||
dto, err := r.configRepo.FindByProviderMerchant(ctx, provider, merchantID, true)
|
||||
if err == nil {
|
||||
return runtimeConfigFromDTO(dto), nil
|
||||
}
|
||||
@@ -94,7 +94,7 @@ func (r *Repository) runtimeConfigForPayment(payment *model.PaymentOrder) (*runt
|
||||
}
|
||||
if provider != "leshua" {
|
||||
if provider == "lakala" && r.configRepo != nil {
|
||||
dto, err := r.configRepo.FindDefaultByProvider(provider, true)
|
||||
dto, err := r.configRepo.FindDefaultByProvider(ctx, provider, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -108,7 +108,7 @@ func (r *Repository) runtimeConfigForPayment(payment *model.PaymentOrder) (*runt
|
||||
if r.configRepo == nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
dto, err := r.configRepo.FindDefaultByProvider(provider, true)
|
||||
dto, err := r.configRepo.FindDefaultByProvider(ctx, provider, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -136,7 +136,7 @@ func runtimeConfigFromDTO(dto *paymentconfig.ConfigDTO) *runtimePaymentConfig {
|
||||
}
|
||||
|
||||
func (r *Repository) Start(ctx context.Context, userID uint64, orderID uint64, req StartPaymentRequest, clientIP string) (*PaymentDTO, error) {
|
||||
defaultConfig, err := r.defaultRuntimeConfig()
|
||||
defaultConfig, err := r.defaultRuntimeConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
@@ -144,12 +144,12 @@ func (r *Repository) Start(ctx context.Context, userID uint64, orderID uint64, r
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(payment)
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(ctx, payment)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
if payment.Status == "paid" {
|
||||
r.recordConfigUsage(runtimeConfig, payment)
|
||||
r.recordConfigUsage(ctx, runtimeConfig, payment)
|
||||
dto := toDTO(*payment)
|
||||
return &dto, nil
|
||||
}
|
||||
@@ -166,12 +166,12 @@ func (r *Repository) Start(ctx context.Context, userID uint64, orderID uint64, r
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.recordConfigUsage(runtimeConfig, latest)
|
||||
r.recordConfigUsage(ctx, runtimeConfig, latest)
|
||||
dto := toDTO(*latest)
|
||||
return &dto, nil
|
||||
}
|
||||
if payment.Status == "paying" && (payment.TDCode != "" || payment.JSPayURL != "" || payment.JSPayInfo != "") {
|
||||
r.recordConfigUsage(runtimeConfig, payment)
|
||||
r.recordConfigUsage(ctx, runtimeConfig, payment)
|
||||
dto := toDTO(*payment)
|
||||
return &dto, nil
|
||||
}
|
||||
@@ -221,7 +221,7 @@ func (r *Repository) Start(ctx context.Context, userID uint64, orderID uint64, r
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.recordConfigUsage(runtimeConfig, latest)
|
||||
r.recordConfigUsage(ctx, runtimeConfig, latest)
|
||||
log.Printf("[payment] payment result order_id=%d order_no=%s payment_id=%d provider=%s amount_cent=%d status=%s provider_order_id=%s",
|
||||
orderID, orderRow.OrderNo, latest.ID, runtimeConfig.Provider, latest.AmountCent, latest.Status, latest.ProviderOrderID)
|
||||
dto := toDTO(*latest)
|
||||
@@ -233,7 +233,7 @@ func (r *Repository) StartWalletRecharge(ctx context.Context, userID uint64, req
|
||||
if userID == 0 || amountCent < moneyCent(MinWalletRechargeAmount) {
|
||||
return nil, ErrPaymentCannotStart
|
||||
}
|
||||
runtimeConfig, err := r.defaultRuntimeConfig()
|
||||
runtimeConfig, err := r.defaultRuntimeConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
@@ -254,7 +254,7 @@ func (r *Repository) StartWalletRecharge(ctx context.Context, userID uint64, req
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.recordConfigUsage(runtimeConfig, latest)
|
||||
r.recordConfigUsage(ctx, runtimeConfig, latest)
|
||||
dto := toDTO(*latest)
|
||||
return &dto, nil
|
||||
}
|
||||
@@ -303,7 +303,7 @@ func (r *Repository) StartWalletRecharge(ctx context.Context, userID uint64, req
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.recordConfigUsage(runtimeConfig, latest)
|
||||
r.recordConfigUsage(ctx, runtimeConfig, latest)
|
||||
log.Printf("[payment] wallet recharge result user_id=%d payment_id=%d provider=%s amount_cent=%d status=%s provider_order_id=%s",
|
||||
userID, latest.ID, runtimeConfig.Provider, latest.AmountCent, latest.Status, latest.ProviderOrderID)
|
||||
dto := toDTO(*latest)
|
||||
@@ -318,7 +318,7 @@ func (r *Repository) QueryWalletRecharge(ctx context.Context, userID uint64, pay
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(&payment)
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(ctx, &payment)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
@@ -352,7 +352,7 @@ func (r *Repository) Query(ctx context.Context, userID uint64, orderID uint64) (
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(&payment)
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(ctx, &payment)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
@@ -392,7 +392,7 @@ func (r *Repository) HandleNotify(ctx context.Context, provider string, params m
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(payment)
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(ctx, payment)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
@@ -423,7 +423,7 @@ func (r *Repository) StartRefund(ctx context.Context, orderID uint64, refundAmou
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(&originalPayment)
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(ctx, &originalPayment)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
@@ -471,7 +471,7 @@ func (r *Repository) StartRefund(ctx context.Context, orderID uint64, refundAmou
|
||||
if err := r.db.WithContext(ctx).Create(&refundOrder).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.recordConfigUsage(runtimeConfig, &refundOrder)
|
||||
r.recordConfigUsage(ctx, runtimeConfig, &refundOrder)
|
||||
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)
|
||||
}
|
||||
@@ -484,7 +484,7 @@ func (r *Repository) StartRefund(ctx context.Context, orderID uint64, refundAmou
|
||||
}
|
||||
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)
|
||||
r.recordConfigUsage(ctx, runtimeConfig, &refundOrder)
|
||||
if err := r.markOrderRefunding(ctx, orderID, refundAmountCent); err != nil {
|
||||
log.Printf("[payment] mark order refunding failed order_id=%d err=%v", orderID, err)
|
||||
}
|
||||
@@ -560,7 +560,7 @@ func (r *Repository) QueryRefundStatus(ctx context.Context, orderID uint64) (*Re
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(&payment)
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(ctx, &payment)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
@@ -656,7 +656,7 @@ func (r *Repository) HandleRefundNotify(ctx context.Context, provider string, pa
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(payment)
|
||||
runtimeConfig, err := r.runtimeConfigForPayment(ctx, payment)
|
||||
if err != nil {
|
||||
return nil, ErrPaymentUnavailable
|
||||
}
|
||||
@@ -947,7 +947,7 @@ func (r *Repository) confirmPaid(ctx context.Context, payment *model.PaymentOrde
|
||||
if r.orderRepo == nil {
|
||||
return ErrDependencyUnavailable
|
||||
}
|
||||
if err := r.orderRepo.ConfirmPaidFromChannel(payment.OrderID, firstNonEmpty(payment.ProviderOrderID, payment.PaymentNo)); err != nil {
|
||||
if err := r.orderRepo.ConfirmPaidFromChannel(ctx, payment.OrderID, firstNonEmpty(payment.ProviderOrderID, payment.PaymentNo)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -1041,11 +1041,11 @@ func (r *Repository) verifyNotify(ctx context.Context, payment *model.PaymentOrd
|
||||
return verify, nil
|
||||
}
|
||||
|
||||
func (r *Repository) recordConfigUsage(runtimeConfig *runtimePaymentConfig, payment *model.PaymentOrder) {
|
||||
func (r *Repository) recordConfigUsage(ctx context.Context, runtimeConfig *runtimePaymentConfig, payment *model.PaymentOrder) {
|
||||
if r.configRepo == nil || runtimeConfig == nil || payment == nil || runtimeConfig.ID == 0 {
|
||||
return
|
||||
}
|
||||
if err := r.configRepo.RecordUsage(runtimeConfig.ID, payment.ID, runtimeConfig.Provider, runtimeConfig.MerchantID, payment.AmountCent, payment.BizType); err != nil {
|
||||
if err := r.configRepo.RecordUsage(ctx, runtimeConfig.ID, payment.ID, runtimeConfig.Provider, runtimeConfig.MerchantID, payment.AmountCent, payment.BizType); err != nil {
|
||||
log.Printf("[payment] record config usage failed config_id=%d payment_id=%d err=%v", runtimeConfig.ID, payment.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func (h *Handler) List(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.service.List(query)
|
||||
resp, err := h.service.List(c.Request.Context(), query)
|
||||
if err != nil {
|
||||
response.Error(c, http.StatusInternalServerError, "internal_error", "查询失败")
|
||||
return
|
||||
@@ -62,7 +62,7 @@ func (h *Handler) Get(c *gin.Context) {
|
||||
|
||||
includeSecret := c.Query("include_secret") == "true"
|
||||
|
||||
config, err := h.service.Get(id, includeSecret)
|
||||
config, err := h.service.Get(c.Request.Context(), id, includeSecret)
|
||||
if err == ErrConfigNotFound {
|
||||
response.NotFound(c, "配置不存在")
|
||||
return
|
||||
@@ -87,7 +87,7 @@ func (h *Handler) ExportBackup(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
backup, err := h.service.ExportBackup(adminID, auditMeta(c))
|
||||
backup, err := h.service.ExportBackup(c.Request.Context(), adminID, auditMeta(c))
|
||||
if err == ErrDecryptionFailed {
|
||||
response.Error(c, http.StatusInternalServerError, "decrypt_failed", "密钥解密失败")
|
||||
return
|
||||
@@ -128,7 +128,7 @@ func (h *Handler) ImportBackup(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.service.ImportBackup(backup, adminID, auditMeta(c))
|
||||
result, err := h.service.ImportBackup(c.Request.Context(), backup, adminID, auditMeta(c))
|
||||
if err == ErrInvalidBackup || err == ErrEmptyBackup || err == ErrInvalidProvider ||
|
||||
err == ErrNameRequired || err == ErrMerchantIDRequired || err == ErrGatewayURLRequired ||
|
||||
err == ErrSignKeyRequired || err == ErrNotifyKeyRequired || err == ErrNotifyURLRequired ||
|
||||
@@ -168,7 +168,7 @@ func (h *Handler) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.service.Create(req, adminID, auditMeta(c))
|
||||
config, err := h.service.Create(c.Request.Context(), req, adminID, auditMeta(c))
|
||||
if err == ErrNameRequired || err == ErrMerchantIDRequired || err == ErrGatewayURLRequired ||
|
||||
err == ErrSignKeyRequired || err == ErrNotifyKeyRequired || err == ErrInvalidProvider ||
|
||||
err == ErrNotifyURLRequired || err == ErrInvalidSignType || err == ErrAppIDRequired ||
|
||||
@@ -210,7 +210,7 @@ func (h *Handler) Update(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.service.Update(id, req, adminID, auditMeta(c))
|
||||
config, err := h.service.Update(c.Request.Context(), id, req, adminID, auditMeta(c))
|
||||
if err == ErrConfigNotFound {
|
||||
response.NotFound(c, "配置不存在")
|
||||
return
|
||||
@@ -246,7 +246,7 @@ func (h *Handler) Delete(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
err = h.service.Delete(id, adminID, auditMeta(c))
|
||||
err = h.service.Delete(c.Request.Context(), id, adminID, auditMeta(c))
|
||||
if err == ErrConfigNotFound {
|
||||
response.NotFound(c, "配置不存在")
|
||||
return
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package paymentconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"hfb_sys/backend/internal/auditlog"
|
||||
@@ -26,11 +27,11 @@ func NewRepository(db *gorm.DB, encryptor Encryptor) *Repository {
|
||||
}
|
||||
|
||||
// List 获取配置列表
|
||||
func (r *Repository) List(query ListQuery) ([]ConfigDTO, int64, error) {
|
||||
func (r *Repository) List(ctx context.Context, query ListQuery) ([]ConfigDTO, int64, error) {
|
||||
var items []model.PaymentMerchantConfig
|
||||
var total int64
|
||||
|
||||
db := r.db.Model(&model.PaymentMerchantConfig{})
|
||||
db := r.db.WithContext(ctx).Model(&model.PaymentMerchantConfig{})
|
||||
|
||||
// 过滤条件
|
||||
if query.Provider != "" {
|
||||
@@ -79,9 +80,9 @@ func (r *Repository) List(query ListQuery) ([]ConfigDTO, int64, error) {
|
||||
}
|
||||
|
||||
// FindByID 根据 ID 查询配置
|
||||
func (r *Repository) FindByID(id uint64, includeSecret bool) (*ConfigDTO, error) {
|
||||
func (r *Repository) FindByID(ctx context.Context, id uint64, includeSecret bool) (*ConfigDTO, error) {
|
||||
var item model.PaymentMerchantConfig
|
||||
if err := r.db.Where("id = ?", id).First(&item).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("id = ?", id).First(&item).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrConfigNotFound
|
||||
}
|
||||
@@ -95,9 +96,9 @@ func (r *Repository) FindByID(id uint64, includeSecret bool) (*ConfigDTO, error)
|
||||
}
|
||||
|
||||
// FindDefault 查询默认配置
|
||||
func (r *Repository) FindDefault(provider string) (*model.PaymentMerchantConfig, error) {
|
||||
func (r *Repository) FindDefault(ctx context.Context, provider string) (*model.PaymentMerchantConfig, error) {
|
||||
var item model.PaymentMerchantConfig
|
||||
if err := r.db.Where("provider = ? AND is_default = ? AND status = ?", provider, true, "active").First(&item).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("provider = ? AND is_default = ? AND status = ?", provider, true, "active").First(&item).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrNoActiveConfigFound
|
||||
}
|
||||
@@ -107,9 +108,9 @@ func (r *Repository) FindDefault(provider string) (*model.PaymentMerchantConfig,
|
||||
}
|
||||
|
||||
// FindDefaultAny 查询任意服务商的默认启用配置。
|
||||
func (r *Repository) FindDefaultAny(includeSecret bool) (*ConfigDTO, error) {
|
||||
func (r *Repository) FindDefaultAny(ctx context.Context, includeSecret bool) (*ConfigDTO, error) {
|
||||
var item model.PaymentMerchantConfig
|
||||
if err := r.db.Where("status = ?", "active").Order("is_default DESC, id DESC").First(&item).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("status = ?", "active").Order("is_default DESC, id DESC").First(&item).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrNoActiveConfigFound
|
||||
}
|
||||
@@ -123,8 +124,8 @@ func (r *Repository) FindDefaultAny(includeSecret bool) (*ConfigDTO, error) {
|
||||
}
|
||||
|
||||
// FindDefaultByProvider 查询指定服务商的默认启用配置。
|
||||
func (r *Repository) FindDefaultByProvider(provider string, includeSecret bool) (*ConfigDTO, error) {
|
||||
item, err := r.FindDefault(provider)
|
||||
func (r *Repository) FindDefaultByProvider(ctx context.Context, provider string, includeSecret bool) (*ConfigDTO, error) {
|
||||
item, err := r.FindDefault(ctx, provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -136,9 +137,9 @@ func (r *Repository) FindDefaultByProvider(provider string, includeSecret bool)
|
||||
}
|
||||
|
||||
// FindByProviderMerchant 根据服务商和商户号查配置,用于历史支付单继续使用原商户密钥。
|
||||
func (r *Repository) FindByProviderMerchant(provider string, merchantID string, includeSecret bool) (*ConfigDTO, error) {
|
||||
func (r *Repository) FindByProviderMerchant(ctx context.Context, provider string, merchantID string, includeSecret bool) (*ConfigDTO, error) {
|
||||
var item model.PaymentMerchantConfig
|
||||
if err := r.db.Where("provider = ? AND merchant_id = ?", provider, merchantID).
|
||||
if err := r.db.WithContext(ctx).Where("provider = ? AND merchant_id = ?", provider, merchantID).
|
||||
Order("status = 'active' DESC, is_default DESC, id DESC").
|
||||
First(&item).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -154,9 +155,9 @@ func (r *Repository) FindByProviderMerchant(provider string, merchantID string,
|
||||
}
|
||||
|
||||
// ExportBackup 导出所有支付配置备份,包含解密后的密钥。
|
||||
func (r *Repository) ExportBackup(actorID uint64, meta AuditMeta) (*ExportBackup, error) {
|
||||
func (r *Repository) ExportBackup(ctx context.Context, actorID uint64, meta AuditMeta) (*ExportBackup, error) {
|
||||
var backup *ExportBackup
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var items []model.PaymentMerchantConfig
|
||||
if err := tx.Order("is_default DESC, id DESC").Find(&items).Error; err != nil {
|
||||
return err
|
||||
@@ -193,7 +194,7 @@ func (r *Repository) ExportBackup(actorID uint64, meta AuditMeta) (*ExportBackup
|
||||
}
|
||||
|
||||
// ImportBackup 导入支付配置备份,按 provider + merchant_id 更新或新增。
|
||||
func (r *Repository) ImportBackup(backup ExportBackup, actorID uint64, meta AuditMeta) (*ImportBackupResult, error) {
|
||||
func (r *Repository) ImportBackup(ctx context.Context, backup ExportBackup, actorID uint64, meta AuditMeta) (*ImportBackupResult, error) {
|
||||
if backup.Type != "payment_config_backup" || backup.Version <= 0 {
|
||||
return nil, ErrInvalidBackup
|
||||
}
|
||||
@@ -204,7 +205,7 @@ func (r *Repository) ImportBackup(backup ExportBackup, actorID uint64, meta Audi
|
||||
activeKey := backupActiveKey(backup.Configs)
|
||||
result := &ImportBackupResult{Total: len(backup.Configs)}
|
||||
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if activeKey != "" {
|
||||
if err := deactivateOtherConfigs(tx, 0, actorID); err != nil {
|
||||
return err
|
||||
@@ -299,23 +300,23 @@ func (r *Repository) ImportBackup(backup ExportBackup, actorID uint64, meta Audi
|
||||
}
|
||||
|
||||
// FindActiveByProvider 查询提供商的所有激活配置
|
||||
func (r *Repository) FindActiveByProvider(provider string) ([]model.PaymentMerchantConfig, error) {
|
||||
func (r *Repository) FindActiveByProvider(ctx context.Context, provider string) ([]model.PaymentMerchantConfig, error) {
|
||||
var items []model.PaymentMerchantConfig
|
||||
if err := r.db.Where("provider = ? AND status = ?", provider, "active").Order("is_default DESC, id DESC").Find(&items).Error; err != nil {
|
||||
if err := r.db.WithContext(ctx).Where("provider = ? AND status = ?", provider, "active").Order("is_default DESC, id DESC").Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// Create 创建配置
|
||||
func (r *Repository) Create(req CreateRequest, actorID uint64, meta AuditMeta) (*ConfigDTO, error) {
|
||||
func (r *Repository) Create(ctx context.Context, req CreateRequest, actorID uint64, meta AuditMeta) (*ConfigDTO, error) {
|
||||
// 验证必填字段
|
||||
if err := r.validateCreateRequest(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dto ConfigDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
// 加密密钥
|
||||
encryptedSignKey, err := r.encryptor.Encrypt(req.SignKey)
|
||||
if err != nil {
|
||||
@@ -388,7 +389,7 @@ func (r *Repository) Create(req CreateRequest, actorID uint64, meta AuditMeta) (
|
||||
}
|
||||
|
||||
// Update 更新配置
|
||||
func (r *Repository) Update(id uint64, req UpdateRequest, actorID uint64, meta AuditMeta) (*ConfigDTO, error) {
|
||||
func (r *Repository) Update(ctx context.Context, id uint64, req UpdateRequest, actorID uint64, meta AuditMeta) (*ConfigDTO, error) {
|
||||
if req.SignType != nil && *req.SignType != "" && !isValidSignType(*req.SignType) {
|
||||
return nil, ErrInvalidSignType
|
||||
}
|
||||
@@ -400,7 +401,7 @@ func (r *Repository) Update(id uint64, req UpdateRequest, actorID uint64, meta A
|
||||
}
|
||||
|
||||
var dto ConfigDTO
|
||||
err := r.db.Transaction(func(tx *gorm.DB) error {
|
||||
err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var item model.PaymentMerchantConfig
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", id).First(&item).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -507,8 +508,8 @@ func (r *Repository) Update(id uint64, req UpdateRequest, actorID uint64, meta A
|
||||
}
|
||||
|
||||
// Delete 删除配置
|
||||
func (r *Repository) Delete(id uint64, actorID uint64, meta AuditMeta) error {
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
func (r *Repository) Delete(ctx context.Context, id uint64, actorID uint64, meta AuditMeta) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var item model.PaymentMerchantConfig
|
||||
if err := tx.Where("id = ?", id).First(&item).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
@@ -540,9 +541,9 @@ func (r *Repository) Delete(id uint64, actorID uint64, meta AuditMeta) error {
|
||||
}
|
||||
|
||||
// IncrementUsage 增加使用统计
|
||||
func (r *Repository) IncrementUsage(id uint64, amountCent int64) error {
|
||||
func (r *Repository) IncrementUsage(ctx context.Context, id uint64, amountCent int64) error {
|
||||
now := time.Now()
|
||||
return r.db.Model(&model.PaymentMerchantConfig{}).Where("id = ?", id).Updates(map[string]any{
|
||||
return r.db.WithContext(ctx).Model(&model.PaymentMerchantConfig{}).Where("id = ?", id).Updates(map[string]any{
|
||||
"total_transactions": gorm.Expr("total_transactions + ?", 1),
|
||||
"total_amount_cent": gorm.Expr("total_amount_cent + ?", amountCent),
|
||||
"last_used_at": now,
|
||||
@@ -550,11 +551,11 @@ func (r *Repository) IncrementUsage(id uint64, amountCent int64) error {
|
||||
}
|
||||
|
||||
// RecordUsage 记录支付配置命中情况,同一个支付单只记录一次。
|
||||
func (r *Repository) RecordUsage(configID uint64, paymentOrderID uint64, provider string, merchantID string, amountCent int64, bizType string) error {
|
||||
func (r *Repository) RecordUsage(ctx context.Context, configID uint64, paymentOrderID uint64, provider string, merchantID string, amountCent int64, bizType string) error {
|
||||
if configID == 0 || paymentOrderID == 0 {
|
||||
return nil
|
||||
}
|
||||
return r.db.Transaction(func(tx *gorm.DB) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var existing model.PaymentConfigUsageLog
|
||||
err := tx.Where("payment_order_id = ?", paymentOrderID).First(&existing).Error
|
||||
if err == nil {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package paymentconfig
|
||||
|
||||
import "context"
|
||||
|
||||
type Service struct {
|
||||
repo *Repository
|
||||
}
|
||||
@@ -9,8 +11,8 @@ func NewService(repo *Repository) *Service {
|
||||
}
|
||||
|
||||
// List 获取配置列表
|
||||
func (s *Service) List(query ListQuery) (*ListResponse, error) {
|
||||
items, total, err := s.repo.List(query)
|
||||
func (s *Service) List(ctx context.Context, query ListQuery) (*ListResponse, error) {
|
||||
items, total, err := s.repo.List(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -33,38 +35,38 @@ func (s *Service) List(query ListQuery) (*ListResponse, error) {
|
||||
}
|
||||
|
||||
// Get 获取单个配置
|
||||
func (s *Service) Get(id uint64, includeSecret bool) (*ConfigDTO, error) {
|
||||
return s.repo.FindByID(id, includeSecret)
|
||||
func (s *Service) Get(ctx context.Context, id uint64, includeSecret bool) (*ConfigDTO, error) {
|
||||
return s.repo.FindByID(ctx, id, includeSecret)
|
||||
}
|
||||
|
||||
// ExportBackup 导出支付配置备份。
|
||||
func (s *Service) ExportBackup(actorID uint64, meta AuditMeta) (*ExportBackup, error) {
|
||||
return s.repo.ExportBackup(actorID, meta)
|
||||
func (s *Service) ExportBackup(ctx context.Context, actorID uint64, meta AuditMeta) (*ExportBackup, error) {
|
||||
return s.repo.ExportBackup(ctx, actorID, meta)
|
||||
}
|
||||
|
||||
// ImportBackup 导入支付配置备份。
|
||||
func (s *Service) ImportBackup(backup ExportBackup, actorID uint64, meta AuditMeta) (*ImportBackupResult, error) {
|
||||
return s.repo.ImportBackup(backup, actorID, meta)
|
||||
func (s *Service) ImportBackup(ctx context.Context, backup ExportBackup, actorID uint64, meta AuditMeta) (*ImportBackupResult, error) {
|
||||
return s.repo.ImportBackup(ctx, backup, actorID, meta)
|
||||
}
|
||||
|
||||
// Create 创建配置
|
||||
func (s *Service) Create(req CreateRequest, actorID uint64, meta AuditMeta) (*ConfigDTO, error) {
|
||||
return s.repo.Create(req, actorID, meta)
|
||||
func (s *Service) Create(ctx context.Context, req CreateRequest, actorID uint64, meta AuditMeta) (*ConfigDTO, error) {
|
||||
return s.repo.Create(ctx, req, actorID, meta)
|
||||
}
|
||||
|
||||
// Update 更新配置
|
||||
func (s *Service) Update(id uint64, req UpdateRequest, actorID uint64, meta AuditMeta) (*ConfigDTO, error) {
|
||||
return s.repo.Update(id, req, actorID, meta)
|
||||
func (s *Service) Update(ctx context.Context, id uint64, req UpdateRequest, actorID uint64, meta AuditMeta) (*ConfigDTO, error) {
|
||||
return s.repo.Update(ctx, id, req, actorID, meta)
|
||||
}
|
||||
|
||||
// Delete 删除配置
|
||||
func (s *Service) Delete(id uint64, actorID uint64, meta AuditMeta) error {
|
||||
return s.repo.Delete(id, actorID, meta)
|
||||
func (s *Service) Delete(ctx context.Context, id uint64, actorID uint64, meta AuditMeta) error {
|
||||
return s.repo.Delete(ctx, id, actorID, meta)
|
||||
}
|
||||
|
||||
// GetDefaultConfig 获取默认配置(用于支付模块调用)
|
||||
func (s *Service) GetDefaultConfig(provider string) (*ConfigDTO, error) {
|
||||
config, err := s.repo.FindDefault(provider)
|
||||
func (s *Service) GetDefaultConfig(ctx context.Context, provider string) (*ConfigDTO, error) {
|
||||
config, err := s.repo.FindDefault(ctx, provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user