Files
ai_dianshang/server/internal/service/refund.go
2025-11-17 14:11:46 +08:00

829 lines
28 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"dianshang/internal/model"
"dianshang/internal/repository"
"dianshang/pkg/logger"
"dianshang/pkg/utils"
"fmt"
"time"
)
type RefundService struct {
refundRepo *repository.RefundRepository
orderRepo *repository.OrderRepository
wechatPaySvc *WeChatPayService
}
func NewRefundService(refundRepo *repository.RefundRepository, orderRepo *repository.OrderRepository, wechatPaySvc *WeChatPayService) *RefundService {
return &RefundService{
refundRepo: refundRepo,
orderRepo: orderRepo,
wechatPaySvc: wechatPaySvc,
}
}
// CreateRefund 创建退款申请
func (s *RefundService) CreateRefund(ctx context.Context, req *CreateRefundRequest) (*model.Refund, error) {
logger.Info("开始创建退款申请",
"orderID", req.OrderID,
"refundAmount", req.RefundAmount,
"refundReason", req.RefundReason,
"userID", req.UserID)
// 1. 验证订单
order, err := s.orderRepo.GetByID(req.OrderID)
if err != nil {
logger.Error("查询订单失败", "error", err, "orderID", req.OrderID)
return nil, fmt.Errorf("订单不存在")
}
// 2. 验证订单状态
if order.Status != model.OrderStatusPaid {
return nil, fmt.Errorf("订单状态不允许退款,当前状态: %s", order.GetStatusText())
}
// 3. 验证用户权限
if order.UserID != req.UserID {
return nil, fmt.Errorf("无权限操作此订单")
}
// 4. 验证退款金额
if req.RefundAmount <= 0 {
return nil, fmt.Errorf("退款金额必须大于0")
}
// 将前端传递的元金额转换为分(数据库统一使用分存储)
refundAmountInCents := req.RefundAmount * 100
// 计算已退款金额
totalRefunded, err := s.refundRepo.GetTotalRefundedByOrderID(req.OrderID)
if err != nil {
logger.Error("查询已退款金额失败", "error", err, "orderID", req.OrderID)
return nil, fmt.Errorf("查询退款信息失败")
}
// 检查退款金额是否超过可退款金额(订单金额也需要转换为分进行比较)
orderAmountInCents := order.TotalAmount * 100
availableRefund := orderAmountInCents - totalRefunded
if refundAmountInCents > availableRefund {
return nil, fmt.Errorf("退款金额超过可退款金额,可退款: %.2f", availableRefund/100.0)
}
// 5. 生成退款记录
refund := &model.Refund{
RefundNo: utils.GenerateRefundNo(),
WechatOutRefundNo: utils.GenerateWechatOutRefundNo(),
OrderID: req.OrderID,
OrderNo: order.OrderNo, // 设置订单号
UserID: req.UserID,
RefundAmount: refundAmountInCents, // 存储为分
ActualRefundAmount: refundAmountInCents, // 设置实际退款金额,初始等于申请退款金额(分)
RefundReason: req.RefundReason,
RefundType: req.RefundType,
Status: model.RefundStatusPending,
WechatRefundStatus: "",
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
// 6. 保存退款记录
err = s.refundRepo.Create(refund)
if err != nil {
logger.Error("创建退款记录失败", "error", err)
return nil, fmt.Errorf("创建退款申请失败")
}
// 7. 更新订单状态为退款中
if order.Status != model.OrderStatusReturning {
orderUpdates := map[string]interface{}{
"status": model.OrderStatusReturning,
"refund_time": time.Now(),
"updated_at": time.Now(),
}
err = s.orderRepo.UpdateByID(order.ID, orderUpdates)
if err != nil {
logger.Error("更新订单状态为退款中失败", "error", err, "orderID", order.ID)
// 不返回错误,因为退款记录已经创建成功
} else {
logger.Info("订单状态已更新为退款中", "orderID", order.ID, "orderNo", order.OrderNo)
}
}
// 8. 创建退款日志
statusTo := model.RefundStatusPending
userID := req.UserID
err = s.createRefundLog(refund.ID, "create", nil, &statusTo, "用户申请退款", &userID)
if err != nil {
logger.Warn("创建退款日志失败", "error", err, "refundID", refund.ID)
}
logger.Info("退款申请创建成功",
"refundID", refund.ID,
"refundNo", refund.RefundNo,
"orderID", req.OrderID,
"refundAmountYuan", req.RefundAmount,
"refundAmountCents", refundAmountInCents)
return refund, nil
}
// ProcessRefund 处理退款(管理员审核通过后调用)
func (s *RefundService) ProcessRefund(ctx context.Context, refundID uint, adminID uint, adminRemark string) error {
logger.Info("开始处理退款", "refundID", refundID, "adminID", adminID)
// 1. 查询退款记录
refund, err := s.refundRepo.GetByID(refundID)
if err != nil {
logger.Error("查询退款记录失败", "error", err, "refundID", refundID)
return fmt.Errorf("退款记录不存在")
}
// 2. 验证退款状态
if refund.Status != model.RefundStatusPending {
return fmt.Errorf("退款状态不允许处理,当前状态: %s", refund.GetStatusText())
}
// 3. 查询订单信息
order, err := s.orderRepo.GetByID(refund.OrderID)
if err != nil {
logger.Error("查询订单失败", "error", err, "orderID", refund.OrderID)
return fmt.Errorf("订单不存在")
}
// 4. 更新退款状态为处理中
err = s.refundRepo.UpdateByID(refundID, map[string]interface{}{
"status": model.RefundStatusProcessing,
"admin_remark": adminRemark,
"audit_time": time.Now(),
})
if err != nil {
logger.Error("更新退款状态失败", "error", err, "refundID", refundID)
return fmt.Errorf("更新退款状态失败")
}
// 5. 创建退款日志
statusFrom := model.RefundStatusPending
statusTo := model.RefundStatusProcessing
err = s.createRefundLog(refundID, "approve", &statusFrom, &statusTo, fmt.Sprintf("管理员审核通过: %s", adminRemark), &adminID)
if err != nil {
logger.Warn("创建退款日志失败", "error", err, "refundID", refundID)
}
// 6. 调用微信退款API
wechatResp, err := s.wechatPaySvc.CreateRefund(ctx, refund, order)
if err != nil {
logger.Error("调用微信退款API失败", "error", err, "refundID", refundID)
// 更新退款状态为失败
s.refundRepo.UpdateByID(refundID, map[string]interface{}{
"status": model.RefundStatusFailed,
"admin_remark": fmt.Sprintf("微信退款失败: %v", err),
})
statusFrom := model.RefundStatusProcessing
statusTo := model.RefundStatusFailed
s.createRefundLog(refundID, "fail", &statusFrom, &statusTo, fmt.Sprintf("微信退款失败: %v", err), &adminID)
return fmt.Errorf("微信退款失败: %v", err)
}
// 7. 更新退款记录的微信信息
updates := map[string]interface{}{
"wechat_refund_id": wechatResp.Data["refund_id"],
"wechat_refund_status": wechatResp.Data["status"],
"updated_at": time.Now(),
}
// 如果微信返回了用户收款账户信息
if userAccount, ok := wechatResp.Data["user_received_account"].(string); ok && userAccount != "" {
updates["wechat_user_received_account"] = userAccount
}
// 如果微信返回了退款账户信息
if refundAccount, ok := wechatResp.Data["funds_account"].(string); ok && refundAccount != "" {
updates["wechat_refund_account"] = refundAccount
}
// 如果微信退款立即成功
if status, ok := wechatResp.Data["status"].(string); ok && status == "SUCCESS" {
updates["status"] = model.RefundStatusSuccess
if successTime, ok := wechatResp.Data["success_time"].(string); ok && successTime != "" {
if parsedTime, err := time.Parse("2006-01-02T15:04:05+08:00", successTime); err == nil {
updates["wechat_success_time"] = parsedTime
}
}
}
err = s.refundRepo.UpdateByID(refundID, updates)
if err != nil {
logger.Error("更新退款微信信息失败", "error", err, "refundID", refundID)
}
// 8. 如果退款成功,更新订单退款信息
if status, ok := wechatResp.Data["status"].(string); ok && status == "SUCCESS" {
err = s.updateOrderRefundInfo(order, refund)
if err != nil {
logger.Error("更新订单退款信息失败", "error", err, "orderID", order.ID)
}
// 创建成功日志
statusFrom := model.RefundStatusProcessing
statusTo := model.RefundStatusSuccess
s.createRefundLog(refundID, "success", &statusFrom, &statusTo, "微信退款成功", &adminID)
} else {
// 创建处理中日志
statusFrom := model.RefundStatusProcessing
statusTo := model.RefundStatusProcessing
s.createRefundLog(refundID, "processing", &statusFrom, &statusTo, "微信退款处理中", &adminID)
}
logger.Info("退款处理完成",
"refundID", refundID,
"wechatRefundID", wechatResp.Data["refund_id"],
"status", wechatResp.Data["status"])
return nil
}
// RejectRefund 拒绝退款申请
func (s *RefundService) RejectRefund(ctx context.Context, refundID uint, adminID uint, rejectReason string) error {
logger.Info("拒绝退款申请", "refundID", refundID, "adminID", adminID, "reason", rejectReason)
// 1. 查询退款记录
refund, err := s.refundRepo.GetByID(refundID)
if err != nil {
logger.Error("查询退款记录失败", "error", err, "refundID", refundID)
return fmt.Errorf("退款记录不存在")
}
// 2. 验证退款状态
if refund.Status != model.RefundStatusPending {
return fmt.Errorf("退款状态不允许拒绝,当前状态: %s", refund.GetStatusText())
}
// 3. 更新退款状态为已拒绝
err = s.refundRepo.UpdateByID(refundID, map[string]interface{}{
"status": model.RefundStatusRejected,
"reject_reason": rejectReason,
"reject_time": time.Now(),
})
if err != nil {
logger.Error("更新退款状态失败", "error", err, "refundID", refundID)
return fmt.Errorf("更新退款状态失败")
}
// 4. 创建退款日志
statusFrom := model.RefundStatusPending
statusTo := model.RefundStatusRejected
err = s.createRefundLog(refundID, "reject", &statusFrom, &statusTo, fmt.Sprintf("管理员拒绝: %s", rejectReason), &adminID)
if err != nil {
logger.Warn("创建退款日志失败", "error", err, "refundID", refundID)
}
logger.Info("退款申请已拒绝", "refundID", refundID)
return nil
}
// HandleWeChatRefundNotify 处理微信退款回调通知(解析和解密)
func (s *RefundService) HandleWeChatRefundNotify(ctx context.Context, body []byte, headers map[string]string) (*WeChatRefundNotify, error) {
logger.Info("开始处理微信退款回调通知")
if s.wechatPaySvc == nil {
return nil, fmt.Errorf("微信支付服务未初始化")
}
// 调用微信支付服务解析和解密回调数据
notify, err := s.wechatPaySvc.HandleRefundNotify(ctx, body, headers)
if err != nil {
logger.Error("解析退款回调数据失败", "error", err)
return nil, err
}
logger.Info("成功解析退款回调数据", "eventType", notify.EventType)
return notify, nil
}
// HandleRefundCallback 处理微信退款回调
func (s *RefundService) HandleRefundCallback(ctx context.Context, notify *WeChatRefundNotify) error {
logger.Info("处理微信退款回调", "eventType", notify.EventType)
if notify.DecryptedData == nil {
return fmt.Errorf("回调数据中缺少解密数据")
}
outRefundNo := notify.DecryptedData.OutRefundNo
if outRefundNo == "" {
return fmt.Errorf("回调数据中缺少退款单号")
}
// 1. 查询退款记录
refund, err := s.refundRepo.GetByWechatOutRefundNo(outRefundNo)
if err != nil {
logger.Error("根据微信退款单号查询退款记录失败", "error", err, "outRefundNo", outRefundNo)
return fmt.Errorf("退款记录不存在")
}
// 2. 根据事件类型处理不同的退款状态
var newStatus int
var logRemark string
switch notify.EventType {
case "REFUND.SUCCESS":
// 退款成功
if refund.Status == model.RefundStatusSuccess {
logger.Info("退款已经是成功状态,跳过处理", "refundID", refund.ID)
return nil
}
newStatus = model.RefundStatusSuccess
logRemark = "微信退款回调:退款成功"
case "REFUND.ABNORMAL":
// 退款异常
newStatus = model.RefundStatusFailed
logRemark = "微信退款回调:退款异常"
case "REFUND.CLOSED":
// 退款关闭
newStatus = model.RefundStatusFailed
logRemark = "微信退款回调:退款关闭"
default:
logger.Warn("未知的退款回调事件类型", "eventType", notify.EventType)
return nil
}
// 3. 更新退款状态和微信信息
updates := map[string]interface{}{
"status": newStatus,
"wechat_refund_id": notify.DecryptedData.RefundId,
"wechat_refund_status": notify.DecryptedData.RefundStatus,
"updated_at": time.Now(),
}
// 只有成功时才更新收款账户和成功时间
if notify.EventType == "REFUND.SUCCESS" {
updates["wechat_user_received_account"] = notify.DecryptedData.UserReceivedAccount
// 解析成功时间
if notify.DecryptedData.SuccessTime != "" {
if successTime, err := time.Parse("2006-01-02T15:04:05+08:00", notify.DecryptedData.SuccessTime); err == nil {
updates["wechat_success_time"] = successTime
}
}
}
err = s.refundRepo.UpdateByID(refund.ID, updates)
if err != nil {
logger.Error("更新退款状态失败", "error", err, "refundID", refund.ID)
return fmt.Errorf("更新退款状态失败")
}
// 4. 只有退款成功时才更新订单退款信息
if newStatus == model.RefundStatusSuccess {
order, err := s.orderRepo.GetByID(refund.OrderID)
if err != nil {
logger.Error("查询订单失败", "error", err, "orderID", refund.OrderID)
} else {
err = s.updateOrderRefundInfo(order, refund)
if err != nil {
logger.Error("更新订单退款信息失败", "error", err, "orderID", order.ID)
}
}
}
// 5. 创建退款日志
statusFrom := refund.Status
statusTo := newStatus
var operatorID *uint = nil
err = s.createRefundLog(refund.ID, "callback", &statusFrom, &statusTo, logRemark, operatorID)
if err != nil {
logger.Warn("创建退款日志失败", "error", err, "refundID", refund.ID)
}
logger.Info("微信退款回调处理完成", "refundID", refund.ID, "outRefundNo", outRefundNo, "newStatus", newStatus)
return nil
}
// GetRefundsByOrderID 获取订单的退款记录
func (s *RefundService) GetRefundsByOrderID(ctx context.Context, orderID uint, userID uint) ([]*model.Refund, error) {
// 验证用户权限
order, err := s.orderRepo.GetByID(orderID)
if err != nil {
return nil, fmt.Errorf("订单不存在")
}
if order.UserID != userID {
return nil, fmt.Errorf("无权限查看此订单的退款信息")
}
refunds, err := s.refundRepo.GetByOrderID(orderID)
if err != nil {
return nil, err
}
// 转换为指针切片
result := make([]*model.Refund, len(refunds))
for i := range refunds {
result[i] = &refunds[i]
}
return result, nil
}
// SyncRefundAndOrderStatus 同步退款状态和订单状态
// 这个方法用于修复退款状态已成功但订单状态未更新的问题
func (s *RefundService) SyncRefundAndOrderStatus(ctx context.Context) error {
logger.Info("开始同步退款状态和订单状态")
// 1. 查询所有状态为成功的退款记录
refunds, err := s.refundRepo.GetRefundsByStatus(model.RefundStatusSuccess)
if err != nil {
logger.Error("查询成功退款记录失败", "error", err)
return fmt.Errorf("查询成功退款记录失败: %v", err)
}
// 2. 遍历每个退款记录,检查对应的订单状态
for _, refund := range refunds {
// 获取订单信息
order, err := s.orderRepo.GetByID(refund.OrderID)
if err != nil {
logger.Error("获取订单信息失败", "error", err, "orderID", refund.OrderID)
continue
}
// 如果订单状态不是已退款,则需要更新
if order.Status != model.OrderStatusRefunded {
// 计算订单总退款金额
totalRefunded, err := s.refundRepo.GetTotalRefundedByOrderID(order.ID)
if err != nil {
logger.Error("计算订单总退款金额失败", "error", err, "orderID", order.ID)
continue
}
// 如果总退款金额大于等于订单金额,则更新订单状态为已退款
if totalRefunded >= order.TotalAmount {
updates := map[string]interface{}{
"status": model.OrderStatusRefunded,
"refunded_at": time.Now(),
"updated_at": time.Now(),
}
err = s.orderRepo.UpdateByID(order.ID, updates)
if err != nil {
logger.Error("更新订单状态为已退款失败", "error", err, "orderID", order.ID)
continue
}
logger.Info("订单状态已更新为已退款",
"orderID", order.ID,
"orderNo", order.OrderNo,
"totalAmount", order.TotalAmount,
"totalRefunded", totalRefunded)
} else if order.Status != model.OrderStatusReturning {
// 如果是部分退款且订单状态不是退款中,则更新为退款中
updates := map[string]interface{}{
"status": model.OrderStatusReturning,
"updated_at": time.Now(),
}
err = s.orderRepo.UpdateByID(order.ID, updates)
if err != nil {
logger.Error("更新订单状态为退款中失败", "error", err, "orderID", order.ID)
continue
}
logger.Info("订单状态已更新为退款中",
"orderID", order.ID,
"orderNo", order.OrderNo,
"totalAmount", order.TotalAmount,
"totalRefunded", totalRefunded)
}
}
}
logger.Info("同步退款状态和订单状态完成")
return nil
}
// GetRefundsByUserID 获取用户的退款记录
func (s *RefundService) GetRefundsByUserID(ctx context.Context, userID uint, page, pageSize int) ([]*model.Refund, int64, error) {
refunds, total, err := s.refundRepo.GetByUserID(userID, page, pageSize)
if err != nil {
return nil, 0, err
}
// 转换为指针切片
result := make([]*model.Refund, len(refunds))
for i := range refunds {
result[i] = &refunds[i]
// 检查退款状态是否为成功,但订单状态不是已退款
if refunds[i].Status == model.RefundStatusSuccess && refunds[i].Order.Status != model.OrderStatusRefunded {
// 计算订单总退款金额
totalRefunded, err := s.refundRepo.GetTotalRefundedByOrderID(refunds[i].OrderID)
if err != nil {
logger.Error("计算订单总退款金额失败", "error", err, "orderID", refunds[i].OrderID)
continue
}
// 如果总退款金额大于等于订单金额,则更新订单状态为已退款
if totalRefunded >= refunds[i].Order.TotalAmount {
updates := map[string]interface{}{
"status": model.OrderStatusRefunded,
"refunded_at": time.Now(),
"updated_at": time.Now(),
}
err = s.orderRepo.UpdateByID(refunds[i].OrderID, updates)
if err != nil {
logger.Error("更新订单状态为已退款失败", "error", err, "orderID", refunds[i].OrderID)
continue
}
// 更新当前退款记录中的订单状态
result[i].Order.Status = model.OrderStatusRefunded
logger.Info("订单状态已更新为已退款",
"orderID", refunds[i].OrderID,
"orderNo", refunds[i].OrderNo,
"totalAmount", refunds[i].Order.TotalAmount,
"totalRefunded", totalRefunded)
} else if refunds[i].Order.Status != model.OrderStatusReturning {
// 如果是部分退款且订单状态不是退款中,则更新为退款中
updates := map[string]interface{}{
"status": model.OrderStatusReturning,
"updated_at": time.Now(),
}
err = s.orderRepo.UpdateByID(refunds[i].OrderID, updates)
if err != nil {
logger.Error("更新订单状态为退款中失败", "error", err, "orderID", refunds[i].OrderID)
continue
}
// 更新当前退款记录中的订单状态
result[i].Order.Status = model.OrderStatusReturning
logger.Info("订单状态已更新为退款中",
"orderID", refunds[i].OrderID,
"orderNo", refunds[i].OrderNo,
"totalAmount", refunds[i].Order.TotalAmount,
"totalRefunded", totalRefunded)
}
}
}
return result, total, nil
}
// GetRefundByID 获取退款详情
func (s *RefundService) GetRefundByID(ctx context.Context, refundID uint, userID uint) (*model.Refund, error) {
refund, err := s.refundRepo.GetByID(refundID)
if err != nil {
return nil, fmt.Errorf("退款记录不存在")
}
// 验证用户权限
if refund.UserID != userID {
return nil, fmt.Errorf("无权限查看此退款记录")
}
return refund, nil
}
// QueryRefundStatus 查询微信退款状态
func (s *RefundService) QueryRefundStatus(ctx context.Context, refundID uint) error {
logger.Info("查询微信退款状态", "refundID", refundID)
// 1. 查询退款记录
refund, err := s.refundRepo.GetByID(refundID)
if err != nil {
logger.Error("查询退款记录失败", "error", err, "refundID", refundID)
return fmt.Errorf("退款记录不存在")
}
if refund.WechatOutRefundNo == "" {
return fmt.Errorf("退款记录没有微信退款单号")
}
// 2. 调用微信查询退款API
wechatRefund, err := s.wechatPaySvc.QueryRefund(ctx, refund.WechatOutRefundNo)
if err != nil {
logger.Error("查询微信退款状态失败", "error", err, "outRefundNo", refund.WechatOutRefundNo)
return fmt.Errorf("查询微信退款状态失败: %v", err)
}
// 3. 更新退款记录
updates := map[string]interface{}{
"wechat_refund_status": wechatRefund.WechatRefundStatus,
"wechat_user_received_account": wechatRefund.WechatUserReceivedAccount,
"wechat_refund_account": wechatRefund.WechatRefundAccount,
"updated_at": time.Now(),
}
// 如果微信退款成功,更新本地状态
if wechatRefund.WechatRefundStatus == "SUCCESS" {
// 无论当前退款状态如何,只要微信退款成功,就更新为成功状态
updates["status"] = model.RefundStatusSuccess
if wechatRefund.WechatSuccessTime != nil {
updates["wechat_success_time"] = *wechatRefund.WechatSuccessTime
}
// 更新订单退款信息
order, err := s.orderRepo.GetByID(refund.OrderID)
if err == nil {
s.updateOrderRefundInfo(order, refund)
}
// 只有当状态发生变化时才创建日志
if refund.Status != model.RefundStatusSuccess {
statusFrom := refund.Status
statusTo := model.RefundStatusSuccess
var operatorID *uint = nil
s.createRefundLog(refund.ID, "query_success", &statusFrom, &statusTo, "查询确认微信退款成功", operatorID)
}
}
err = s.refundRepo.UpdateByID(refund.ID, updates)
if err != nil {
logger.Error("更新退款状态失败", "error", err, "refundID", refundID)
return fmt.Errorf("更新退款状态失败")
}
logger.Info("退款状态查询完成", "refundID", refundID, "status", wechatRefund.WechatRefundStatus)
return nil
}
// updateOrderRefundInfo 更新订单退款信息
func (s *RefundService) updateOrderRefundInfo(order *model.Order, refund *model.Refund) error {
// 计算订单总退款金额
totalRefunded, err := s.refundRepo.GetTotalRefundedByOrderID(order.ID)
if err != nil {
return err
}
// 计算退款次数
refundCount, err := s.refundRepo.GetRefundCountByOrderID(order.ID)
if err != nil {
return err
}
updates := map[string]interface{}{
"total_refund_amount": totalRefunded,
"refund_count": refundCount,
"updated_at": time.Now(),
}
// 如果全额退款,更新订单状态为已退款
if totalRefunded >= order.TotalAmount {
updates["status"] = model.OrderStatusRefunded
updates["refunded_at"] = time.Now()
logger.Info("更新订单状态为已退款",
"orderID", order.ID,
"totalAmount", order.TotalAmount,
"totalRefunded", totalRefunded,
"refundID", refund.ID)
} else if order.Status == model.OrderStatusReturning {
// 如果是部分退款且当前状态是退款中,保持退款中状态
// 这样可以区分部分退款和全额退款的订单
updates["status"] = model.OrderStatusReturning
logger.Info("保持订单状态为退款中",
"orderID", order.ID,
"totalAmount", order.TotalAmount,
"totalRefunded", totalRefunded,
"refundID", refund.ID)
}
err = s.orderRepo.UpdateByID(order.ID, updates)
if err != nil {
logger.Error("更新订单退款信息失败", "error", err, "orderID", order.ID)
return err
}
return nil
}
// createRefundLog 创建退款日志
func (s *RefundService) createRefundLog(refundID uint, action string, statusFrom, statusTo *int, remark string, operatorID *uint) error {
log := &model.RefundLog{
RefundID: refundID,
Action: action,
StatusFrom: statusFrom,
StatusTo: statusTo,
OperatorType: "admin",
OperatorID: operatorID,
Remark: remark,
CreatedAt: time.Now(),
}
return s.refundRepo.CreateLog(log)
}
// GetPendingRefunds 获取待处理的退款申请(管理员)
func (s *RefundService) GetPendingRefunds(ctx context.Context, page, pageSize int) ([]*model.Refund, int64, error) {
logger.Info("获取待处理退款申请", "page", page, "pageSize", pageSize)
refunds, total, err := s.refundRepo.GetPendingRefunds(page, pageSize)
if err != nil {
logger.Error("获取待处理退款申请失败", "error", err)
return nil, 0, err
}
// 转换为指针切片
result := make([]*model.Refund, len(refunds))
for i := range refunds {
result[i] = &refunds[i]
}
return result, total, nil
}
// GetAllRefunds 获取所有退款记录(管理员)
func (s *RefundService) GetAllRefunds(ctx context.Context, page, pageSize int, status, userID string) ([]*model.Refund, int64, error) {
logger.Info("获取所有退款记录", "page", page, "pageSize", pageSize, "status", status, "userID", userID)
// 构建查询条件
conditions := make(map[string]interface{})
if status != "" {
conditions["status"] = status
}
if userID != "" {
conditions["user_id"] = userID
}
refunds, total, err := s.refundRepo.GetAllRefunds(page, pageSize, conditions)
if err != nil {
logger.Error("获取所有退款记录失败", "error", err)
return nil, 0, err
}
// 转换为指针切片
result := make([]*model.Refund, len(refunds))
for i := range refunds {
result[i] = &refunds[i]
}
return result, total, nil
}
// GetRefundLogs 获取退款日志(管理员)
func (s *RefundService) GetRefundLogs(ctx context.Context, refundID uint) ([]model.RefundLog, error) {
logger.Info("获取退款日志", "refundID", refundID)
logs, err := s.refundRepo.GetRefundLogsByRefundID(refundID)
if err != nil {
logger.Error("获取退款日志失败", "error", err, "refundID", refundID)
return nil, err
}
return logs, nil
}
// GetRefundDetailForAdmin 获取退款详情(管理员专用)
func (s *RefundService) GetRefundDetailForAdmin(ctx context.Context, refundID uint) (*model.Refund, error) {
logger.Info("管理员获取退款详情", "refundID", refundID)
refund, err := s.refundRepo.GetByID(refundID)
if err != nil {
logger.Error("获取退款详情失败", "error", err, "refundID", refundID)
return nil, fmt.Errorf("退款记录不存在")
}
return refund, nil
}
// GetRefundStatistics 获取退款统计数据
func (s *RefundService) GetRefundStatistics(ctx context.Context, startTime, endTime time.Time) (map[string]interface{}, error) {
logger.Info("获取退款统计数据", "startTime", startTime, "endTime", endTime)
stats, err := s.refundRepo.GetRefundStatistics(startTime, endTime)
if err != nil {
logger.Error("获取退款统计数据失败", "error", err)
return nil, err
}
// 转换数据格式以匹配前端期望的格式
result := map[string]interface{}{
"total_refunds": stats["total_count"],
"pending_refunds": stats["pending_count"],
"processing_refunds": stats["processing_count"],
"total_amount": stats["total_amount"],
"success_count": stats["success_count"],
"success_amount": stats["success_amount"],
"approved_count": stats["approved_count"],
"rejected_count": stats["rejected_count"],
"failed_count": stats["failed_count"],
}
return result, nil
}
// 请求结构体
type CreateRefundRequest struct {
OrderID uint `json:"order_id" binding:"required"`
UserID uint `json:"user_id"` // 由后端从JWT token中获取不需要前端提供
RefundAmount float64 `json:"refund_amount" binding:"required,gt=0"`
RefundReason string `json:"refund_reason" binding:"required,max=500"`
RefundType int `json:"refund_type" binding:"required,oneof=1 2"` // 1:仅退款 2:退货退款
}