初始化项目

This commit is contained in:
2025-12-10 09:02:41 +08:00
parent 3a40846865
commit 3c55577df8
19 changed files with 2653 additions and 10 deletions

439
service/order.go Normal file
View File

@@ -0,0 +1,439 @@
package service
import (
"context"
"errors"
"fmt"
"math/rand"
"strconv"
"time"
"github.com/gogf/gf/v2/util/gconv"
"go.mongodb.org/mongo-driver/bson"
"order/dao"
"order/model/dto"
"order/model/entity"
)
// OrderService 订单服务
type OrderService struct {
orderDao *dao.OrderDao
}
// NewOrderService 创建订单服务实例
func NewOrderService(orderDao *dao.OrderDao) *OrderService {
return &OrderService{
orderDao: orderDao,
}
}
// CreateOrder 创建订单
func (s *OrderService) CreateOrder(ctx context.Context, req *dto.CreateOrderReq) (*dto.CreateOrderResp, error) {
// 1. 参数验证
if req.TenantID == "" || req.UserID == "" || req.Subject == "" {
return nil, errors.New("必填参数不能为空")
}
if len(req.OrderItems) == 0 {
return nil, errors.New("订单商品不能为空")
}
// 2. 计算订单金额
totalAmount := int64(0)
for i := range req.OrderItems {
item := &req.OrderItems[i]
if item.Price <= 0 || item.Quantity <= 0 {
return nil, fmt.Errorf("商品价格或数量无效: %s", item.ProductName)
}
item.TotalPrice = item.Price * int64(item.Quantity)
totalAmount += item.TotalPrice
}
if totalAmount <= 0 {
return nil, errors.New("订单总金额必须大于0")
}
// 3. 生成订单号
orderNo := s.generateOrderNo(req.TenantID)
// 4. 设置订单过期时间30分钟后
expiredAt := time.Now().Add(30 * time.Minute)
// 5. 创建待支付订单
order := &entity.OrderPending{
OrderBase: entity.OrderBase{
TenantID: req.TenantID,
OrderNo: orderNo,
UserID: req.UserID,
TotalAmount: totalAmount,
PayAmount: totalAmount, // 暂时没有优惠,实付金额等于总金额
OrderType: req.OrderType,
Subject: req.Subject,
Description: req.Description,
ExpiredAt: &expiredAt,
},
OrderItems: gconv.Slice[*entity.OrderItem](req.OrderItems),
ShippingInfo: (*entity.ShippingInfo)(&req.ShippingInfo),
PayInfo: entity.PayInfo{},
}
// 6. 保存订单
if err := s.orderDao.CreatePendingOrder(ctx, order); err != nil {
return nil, fmt.Errorf("创建订单失败: %w", err)
}
// 7. 返回结果
resp := &dto.CreateOrderResp{
OrderNo: orderNo,
TotalAmount: totalAmount,
PayAmount: totalAmount,
ExpiredAt: expiredAt.Format(time.RFC3339),
}
return resp, nil
}
// generateOrderNo 生成订单号
func (s *OrderService) generateOrderNo(tenantID string) string {
timestamp := time.Now().Format("20060102150405")
random := rand.Intn(10000)
return fmt.Sprintf("%s%s%04d", tenantID, timestamp, random)
}
// QueryOrder 查询订单详情
func (s *OrderService) QueryOrder(ctx context.Context, req *dto.QueryOrderReq) (*dto.QueryOrderResp, error) {
// 1. 参数验证
if req.TenantID == "" || req.OrderNo == "" {
return nil, errors.New("必填参数不能为空")
}
// 2. 查询订单
order, status, err := s.orderDao.GetOrderByNo(ctx, req.TenantID, req.OrderNo)
if err != nil {
return nil, fmt.Errorf("获取订单失败: %w", err)
}
if order == nil {
return nil, errors.New("订单不存在")
}
// 3. 构建响应
var resp dto.QueryOrderResp
switch status {
case entity.OrderStatusPending:
if pendingOrder, ok := order.(*entity.OrderPending); ok {
resp.Order = s.convertPendingOrderToDetail(pendingOrder)
}
case entity.OrderStatusPaid:
if paidOrder, ok := order.(*entity.OrderPaid); ok {
resp.Order = s.convertPaidOrderToDetail(paidOrder)
}
case entity.OrderStatusShipped:
if shippedOrder, ok := order.(*entity.OrderShipped); ok {
resp.Order = s.convertShippedOrderToDetail(shippedOrder)
}
case entity.OrderStatusCompleted:
if completedOrder, ok := order.(*entity.OrderCompleted); ok {
resp.Order = s.convertCompletedOrderToDetail(completedOrder)
}
default:
return nil, fmt.Errorf("不支持的订单状态: %s", status)
}
return &resp, nil
}
// convertPendingOrderToDetail 转换待支付订单为详情
func (s *OrderService) convertPendingOrderToDetail(order *entity.OrderPending) dto.OrderDetail {
return dto.OrderDetail{
ID: order.ID.Hex(),
TenantID: order.TenantID,
OrderNo: order.OrderNo,
UserID: order.UserID,
TotalAmount: order.TotalAmount,
PayAmount: order.PayAmount,
Status: string(entity.OrderStatusPending),
PayMethod: order.PayMethod,
PayStatus: "unpaid",
OrderType: order.OrderType,
Subject: order.Subject,
Description: order.Description,
OrderItems: s.convertOrderItems(order.OrderItems),
ShippingInfo: dto.ShippingInfo{
Consignee: order.ShippingInfo.Consignee,
Phone: order.ShippingInfo.Phone,
Province: order.ShippingInfo.Province,
City: order.ShippingInfo.City,
District: order.ShippingInfo.District,
Address: order.ShippingInfo.Address,
PostalCode: order.ShippingInfo.PostalCode,
},
PayInfo: dto.PayInfo{
OutTradeNo: order.PayInfo.OutTradeNo,
PrepayID: order.PayInfo.PrepayID,
QRCode: order.PayInfo.QRCode,
PayURL: order.PayInfo.PayURL,
},
CreatedAt: order.CreatedAt,
UpdatedAt: order.UpdatedAt,
ExpiredAt: order.ExpiredAt,
}
}
// convertPaidOrderToDetail 转换已支付订单为详情
func (s *OrderService) convertPaidOrderToDetail(order *entity.OrderPaid) dto.OrderDetail {
return dto.OrderDetail{
ID: order.ID.Hex(),
TenantID: order.TenantID,
OrderNo: order.OrderNo,
UserID: order.UserID,
TotalAmount: order.TotalAmount,
PayAmount: order.PayAmount,
Status: string(entity.OrderStatusPaid),
PayMethod: order.PayMethod,
PayStatus: "paid",
OrderType: order.OrderType,
Subject: order.Subject,
Description: order.Description,
OrderItems: s.convertOrderItems(order.OrderItems),
ShippingInfo: dto.ShippingInfo{
Consignee: order.ShippingInfo.Consignee,
Phone: order.ShippingInfo.Phone,
Province: order.ShippingInfo.Province,
City: order.ShippingInfo.City,
District: order.ShippingInfo.District,
Address: order.ShippingInfo.Address,
PostalCode: order.ShippingInfo.PostalCode,
},
PayInfo: dto.PayInfo{
TransactionID: order.TransactionID,
OutTradeNo: order.OrderNo,
},
CreatedAt: order.CreatedAt,
UpdatedAt: order.UpdatedAt,
PaidAt: &order.PaidAt,
}
}
// convertShippedOrderToDetail 转换已发货订单为详情
func (s *OrderService) convertShippedOrderToDetail(order *entity.OrderShipped) dto.OrderDetail {
return dto.OrderDetail{
ID: order.ID.Hex(),
TenantID: order.TenantID,
OrderNo: order.OrderNo,
UserID: order.UserID,
TotalAmount: order.TotalAmount,
PayAmount: order.PayAmount,
Status: string(entity.OrderStatusShipped),
PayMethod: order.PayMethod,
PayStatus: "paid",
OrderType: order.OrderType,
Subject: order.Subject,
Description: order.Description,
OrderItems: s.convertOrderItems(order.OrderItems),
ShippingInfo: dto.ShippingInfo{
Consignee: order.ShippingInfo.Consignee,
Phone: order.ShippingInfo.Phone,
Province: order.ShippingInfo.Province,
City: order.ShippingInfo.City,
District: order.ShippingInfo.District,
Address: order.ShippingInfo.Address,
PostalCode: order.ShippingInfo.PostalCode,
},
CreatedAt: order.CreatedAt,
UpdatedAt: order.UpdatedAt,
PaidAt: &order.PaidAt,
}
}
// convertCompletedOrderToDetail 转换已完成订单为详情
func (s *OrderService) convertCompletedOrderToDetail(order *entity.OrderCompleted) dto.OrderDetail {
return dto.OrderDetail{
ID: order.ID.Hex(),
TenantID: order.TenantID,
OrderNo: order.OrderNo,
UserID: order.UserID,
TotalAmount: order.TotalAmount,
PayAmount: order.PayAmount,
Status: string(entity.OrderStatusCompleted),
PayMethod: order.PayMethod,
PayStatus: "paid",
OrderType: order.OrderType,
Subject: order.Subject,
Description: order.Description,
OrderItems: s.convertOrderItems(order.OrderItems),
ShippingInfo: dto.ShippingInfo{
Consignee: order.ShippingInfo.Consignee,
Phone: order.ShippingInfo.Phone,
Province: order.ShippingInfo.Province,
City: order.ShippingInfo.City,
District: order.ShippingInfo.District,
Address: order.ShippingInfo.Address,
PostalCode: order.ShippingInfo.PostalCode,
},
CreatedAt: order.CreatedAt,
UpdatedAt: order.UpdatedAt,
PaidAt: &order.PaidAt,
}
}
// convertOrderItems 转换订单商品项
func (s *OrderService) convertOrderItems(items []*entity.OrderItem) []dto.OrderItem {
var result []dto.OrderItem
for _, item := range items {
result = append(result, dto.OrderItem{
ProductID: item.ProductID,
ProductName: item.ProductName,
Price: item.Price,
Quantity: item.Quantity,
TotalPrice: item.TotalPrice,
ImageURL: item.ImageURL,
})
}
return result
}
// CancelOrder 取消订单
func (s *OrderService) CancelOrder(ctx context.Context, req *dto.CancelOrderReq) (*dto.CancelOrderResp, error) {
// 1. 参数验证
if req.TenantID == "" || req.OrderNo == "" {
return nil, errors.New("必填参数不能为空")
}
// 2. 查询订单
order, status, err := s.orderDao.GetOrderByNo(ctx, req.TenantID, req.OrderNo)
if err != nil {
return nil, fmt.Errorf("获取订单失败: %w", err)
}
if order == nil {
return nil, errors.New("订单不存在")
}
// 3. 检查订单状态(只有待支付订单可以取消)
if status != entity.OrderStatusPending {
return nil, fmt.Errorf("订单状态不正确,当前状态: %s", status)
}
// 4. 将订单移动到已取消状态
updateData := bson.M{
"cancel_reason": req.Reason,
}
if err := s.orderDao.MoveOrderToStatus(ctx, entity.OrderStatusPending, entity.OrderStatusCancelled, req.TenantID, req.OrderNo, updateData); err != nil {
return nil, fmt.Errorf("取消订单失败: %w", err)
}
// 5. 返回结果
resp := &dto.CancelOrderResp{
OrderNo: req.OrderNo,
Status: string(entity.OrderStatusCancelled),
}
return resp, nil
}
// ListOrders 查询订单列表
func (s *OrderService) ListOrders(ctx context.Context, req *dto.ListOrdersReq) (*dto.ListOrdersResp, error) {
// 1. 参数验证
if req.TenantID == "" {
return nil, errors.New("租户ID不能为空")
}
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 || req.PageSize > 100 {
req.PageSize = 20
}
// 2. 根据状态查询订单列表
var status entity.OrderStatus
if req.Status != "" {
status = entity.OrderStatus(req.Status)
} else {
// 默认查询所有状态
status = entity.OrderStatusPending
}
orders, total, err := s.orderDao.ListOrdersByStatus(ctx, status, req.TenantID, req.UserID, req.Page, req.PageSize)
if err != nil {
return nil, fmt.Errorf("查询订单列表失败: %w", err)
}
// 3. 转换订单列表
var orderSummaries []dto.OrderSummary
switch status {
case entity.OrderStatusPending:
if pendingOrders, ok := orders.([]entity.OrderPending); ok {
for _, order := range pendingOrders {
orderSummaries = append(orderSummaries, dto.OrderSummary{
ID: order.ID.Hex(),
OrderNo: order.OrderNo,
TotalAmount: order.TotalAmount,
PayAmount: order.PayAmount,
Status: string(entity.OrderStatusPending),
Subject: order.Subject,
CreatedAt: order.CreatedAt,
})
}
}
case entity.OrderStatusPaid:
if paidOrders, ok := orders.([]entity.OrderPaid); ok {
for _, order := range paidOrders {
orderSummaries = append(orderSummaries, dto.OrderSummary{
ID: order.ID.Hex(),
OrderNo: order.OrderNo,
TotalAmount: order.TotalAmount,
PayAmount: order.PayAmount,
Status: string(entity.OrderStatusPaid),
Subject: order.Subject,
CreatedAt: order.CreatedAt,
PaidAt: &order.PaidAt,
})
}
}
// 其他状态类似处理...
}
// 4. 返回结果
resp := &dto.ListOrdersResp{
Orders: orderSummaries,
Total: total,
Page: req.Page,
PageSize: req.PageSize,
}
return resp, nil
}
// ProcessExpiredOrders 处理过期订单
func (s *OrderService) ProcessExpiredOrders(ctx context.Context, tenantID string) error {
// 获取过期的待支付订单
expiredOrders, err := s.orderDao.GetExpiredPendingOrders(ctx, tenantID)
if err != nil {
return fmt.Errorf("获取过期订单失败: %w", err)
}
// 批量取消过期订单
for _, order := range expiredOrders {
updateData := bson.M{
"cancel_reason": "订单超时自动取消",
}
if err := s.orderDao.MoveOrderToStatus(ctx, entity.OrderStatusPending, entity.OrderStatusCancelled, tenantID, order.OrderNo, updateData); err != nil {
// 记录错误但继续处理其他订单
fmt.Printf("取消过期订单失败: %s, 错误: %v\n", order.OrderNo, err)
}
}
return nil
}
// UpdatePayInfo 更新支付信息
func (s *OrderService) UpdatePayInfo(ctx context.Context, tenantID, orderNo string, payInfo entity.PayInfo) error {
return s.orderDao.UpdatePayInfo(ctx, tenantID, orderNo, payInfo)
}

387
service/payment.go Normal file
View File

@@ -0,0 +1,387 @@
package service
import (
"context"
"errors"
"fmt"
"math/rand"
"time"
"go.mongodb.org/mongo-driver/bson"
"order/dao"
"order/model/dto"
"order/model/entity"
)
// PaymentService 支付服务
type PaymentService struct {
orderDao *dao.OrderDao
paymentConfigDao *dao.PaymentConfigDao
paymentRecordDao *dao.PaymentRecordDao
refundRecordDao *dao.RefundRecordDao
}
// NewPaymentService 创建支付服务实例
func NewPaymentService(
orderDao *dao.OrderDao,
paymentConfigDao *dao.PaymentConfigDao,
paymentRecordDao *dao.PaymentRecordDao,
refundRecordDao *dao.RefundRecordDao,
) *PaymentService {
return &PaymentService{
orderDao: orderDao,
paymentConfigDao: paymentConfigDao,
paymentRecordDao: paymentRecordDao,
refundRecordDao: refundRecordDao,
}
}
// PayOrder 支付订单
func (s *PaymentService) PayOrder(ctx context.Context, req *dto.PayOrderReq) (*dto.PayOrderResp, error) {
// 1. 参数验证
if req.TenantID == "" || req.OrderNo == "" || req.PayMethod == "" || req.PayType == "" {
return nil, errors.New("必填参数不能为空")
}
// 2. 查询订单
order, status, err := s.orderDao.GetOrderByNo(ctx, req.TenantID, req.OrderNo)
if err != nil {
return nil, fmt.Errorf("获取订单失败: %w", err)
}
if order == nil {
return nil, errors.New("订单不存在")
}
// 3. 验证订单状态(只有待支付订单可以支付)
if status != entity.OrderStatusPending {
return nil, fmt.Errorf("订单状态不正确,当前状态: %s", status)
}
pendingOrder, ok := order.(*entity.OrderPending)
if !ok {
return nil, errors.New("订单类型错误")
}
// 4. 获取支付配置
paymentConfig, err := s.paymentConfigDao.GetByTenantAndMethod(ctx, req.TenantID, req.PayMethod)
if err != nil {
return nil, fmt.Errorf("获取支付配置失败: %w", err)
}
if paymentConfig == nil {
return nil, errors.New("支付配置不存在")
}
// 5. 调用第三方支付接口
payResp, err := s.callThirdPartyPayment(ctx, paymentConfig, req, pendingOrder)
if err != nil {
return nil, fmt.Errorf("调用支付接口失败: %w", err)
}
// 6. 创建支付记录
paymentRecord := &entity.PaymentRecord{
TenantID: req.TenantID,
OrderID: pendingOrder.ID,
OrderNo: req.OrderNo,
PayMethod: req.PayMethod,
PayType: req.PayType,
Amount: pendingOrder.PayAmount,
OutTradeNo: payResp.OutTradeNo,
Status: "pending",
}
if err := s.paymentRecordDao.Create(ctx, paymentRecord); err != nil {
return nil, fmt.Errorf("创建支付记录失败: %w", err)
}
// 7. 更新订单支付信息
payInfo := entity.PayInfo{
OutTradeNo: payResp.OutTradeNo,
QRCode: payResp.QRCode,
PayURL: payResp.PayURL,
PrepayID: payResp.PrepayID,
JSAPIParams: payResp.JSAPIParams,
APPParams: payResp.APPParams,
}
if err := s.orderDao.UpdatePayInfo(ctx, req.TenantID, req.OrderNo, payInfo); err != nil {
return nil, fmt.Errorf("更新订单支付信息失败: %w", err)
}
// 8. 返回支付信息
resp := &dto.PayOrderResp{
OrderNo: req.OrderNo,
QRCode: payResp.QRCode,
PayURL: payResp.PayURL,
PrepayID: payResp.PrepayID,
JSAPIParams: payResp.JSAPIParams,
APPParams: payResp.APPParams,
}
return resp, nil
}
// callThirdPartyPayment 调用第三方支付接口
func (s *PaymentService) callThirdPartyPayment(ctx context.Context, config *entity.PaymentConfig, req *dto.PayOrderReq, order *entity.OrderPending) (*PaymentResponse, error) {
// 这里应该是实际的第三方支付接口调用
// 为了演示,我们返回模拟数据
outTradeNo := s.generateOutTradeNo(req.TenantID)
payResp := &PaymentResponse{
OutTradeNo: outTradeNo,
}
// 根据支付类型返回不同的支付参数
switch req.PayType {
case "native":
// 扫码支付
payResp.QRCode = fmt.Sprintf("https://api.example.com/qrcode/%s", outTradeNo)
case "jsapi":
// JSAPI支付
payResp.PrepayID = fmt.Sprintf("wxprepay_%s", outTradeNo)
payResp.JSAPIParams = fmt.Sprintf(`{"appId":"%s","timeStamp":"%d","nonceStr":"%s","package":"prepay_id=%s","signType":"MD5","paySign":"signature"}`,
config.AppID, time.Now().Unix(), s.generateNonceStr(), payResp.PrepayID)
case "app":
// APP支付
payResp.APPParams = fmt.Sprintf(`{"appid":"%s","partnerid":"%s","prepayid":"%s","package":"Sign=WXPay","noncestr":"%s","timestamp":"%d","sign":"signature"}`,
config.AppID, config.MchID, payResp.PrepayID, s.generateNonceStr(), time.Now().Unix())
case "h5":
// H5支付
payResp.PayURL = fmt.Sprintf("https://api.example.com/h5pay/%s", outTradeNo)
}
return payResp, nil
}
// PaymentResponse 支付响应
type PaymentResponse struct {
OutTradeNo string `json:"out_trade_no"` // 商户订单号
QRCode string `json:"qrcode"` // 支付二维码
PayURL string `json:"pay_url"` // 支付链接
PrepayID string `json:"prepay_id"` // 预支付ID
JSAPIParams string `json:"jsapi_params"` // JSAPI参数
APPParams string `json:"app_params"` // APP参数
}
// generateOutTradeNo 生成商户订单号
func (s *PaymentService) generateOutTradeNo(tenantID string) string {
timestamp := time.Now().Format("20060102150405")
random := rand.Intn(10000)
return fmt.Sprintf("%s%s%04d", tenantID, timestamp, random)
}
// generateNonceStr 生成随机字符串
func (s *PaymentService) generateNonceStr() string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, 16)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}
// HandlePaymentNotify 处理支付回调
func (s *PaymentService) HandlePaymentNotify(ctx context.Context, req *PaymentNotifyReq) error {
// 1. 验证回调签名
if !s.verifyNotifySignature(req) {
return errors.New("签名验证失败")
}
// 2. 查询支付记录
paymentRecord, err := s.paymentRecordDao.GetByOrderNo(ctx, req.TenantID, req.OrderNo)
if err != nil {
return fmt.Errorf("查询支付记录失败: %w", err)
}
if paymentRecord == nil {
return errors.New("支付记录不存在")
}
// 3. 更新支付记录状态
if err := s.paymentRecordDao.UpdateStatus(ctx, paymentRecord.ID.Hex(), req.Status, req.TransactionID, req.TradeNo); err != nil {
return fmt.Errorf("更新支付记录失败: %w", err)
}
// 4. 如果支付成功,更新订单状态
if req.Status == "success" {
updateData := bson.M{
"paid_at": time.Now(),
"transaction_id": req.TransactionID,
"trade_no": req.TradeNo,
"payment_channel": req.PayMethod,
}
if err := s.orderDao.MoveOrderToStatus(ctx, entity.OrderStatusPending, entity.OrderStatusPaid, req.TenantID, req.OrderNo, updateData); err != nil {
return fmt.Errorf("更新订单状态失败: %w", err)
}
}
return nil
}
// PaymentNotifyReq 支付回调请求
type PaymentNotifyReq struct {
TenantID string `json:"tenant_id"` // 租户ID
OrderNo string `json:"order_no"` // 订单号
PayMethod string `json:"pay_method"` // 支付方式
Status string `json:"status"` // 支付状态
TransactionID string `json:"transaction_id"` // 交易号
TradeNo string `json:"trade_no"` // 交易号
Sign string `json:"sign"` // 签名
}
// verifyNotifySignature 验证回调签名
func (s *PaymentService) verifyNotifySignature(req *PaymentNotifyReq) bool {
// 这里应该是实际的签名验证逻辑
// 为了演示我们总是返回true
return true
}
// RefundOrder 退款
func (s *PaymentService) RefundOrder(ctx context.Context, req *dto.RefundOrderReq) (*dto.RefundOrderResp, error) {
// 1. 参数验证
if req.TenantID == "" || req.OrderNo == "" || req.RefundAmount <= 0 {
return nil, errors.New("必填参数不能为空")
}
// 2. 查询订单
order, status, err := s.orderDao.GetOrderByNo(ctx, req.TenantID, req.OrderNo)
if err != nil {
return nil, fmt.Errorf("获取订单失败: %w", err)
}
if order == nil {
return nil, errors.New("订单不存在")
}
// 3. 验证订单状态(只有已支付订单可以退款)
if status != entity.OrderStatusPaid {
return nil, fmt.Errorf("订单状态不正确,当前状态: %s", status)
}
paidOrder, ok := order.(*entity.OrderPaid)
if !ok {
return nil, errors.New("订单类型错误")
}
// 4. 验证退款金额
if req.RefundAmount > paidOrder.PayAmount {
return nil, errors.New("退款金额不能超过支付金额")
}
// 5. 调用第三方退款接口
refundResp, err := s.callThirdPartyRefund(ctx, req, paidOrder)
if err != nil {
return nil, fmt.Errorf("调用退款接口失败: %w", err)
}
// 6. 创建退款记录
refundRecord := &entity.RefundRecord{
TenantID: req.TenantID,
OrderID: paidOrder.ID,
OrderNo: req.OrderNo,
RefundNo: refundResp.RefundNo,
RefundAmount: req.RefundAmount,
Reason: req.Reason,
Status: "pending",
}
if err := s.refundRecordDao.Create(ctx, refundRecord); err != nil {
return nil, fmt.Errorf("创建退款记录失败: %w", err)
}
// 7. 如果是全额退款,更新订单状态
if req.RefundAmount == paidOrder.PayAmount {
updateData := bson.M{
"refund_reason": req.Reason,
}
if err := s.orderDao.MoveOrderToStatus(ctx, entity.OrderStatusPaid, entity.OrderStatusRefunded, req.TenantID, req.OrderNo, updateData); err != nil {
return nil, fmt.Errorf("更新订单状态失败: %w", err)
}
}
// 8. 返回退款结果
resp := &dto.RefundOrderResp{
RefundNo: refundResp.RefundNo,
RefundID: refundResp.RefundID,
RefundAmount: req.RefundAmount,
}
return resp, nil
}
// callThirdPartyRefund 调用第三方退款接口
func (s *PaymentService) callThirdPartyRefund(ctx context.Context, req *dto.RefundOrderReq, order *entity.OrderPaid) (*RefundResponse, error) {
// 这里应该是实际的第三方退款接口调用
// 为了演示,我们返回模拟数据
refundNo := s.generateRefundNo(req.TenantID)
refundID := fmt.Sprintf("refund_%s", refundNo)
return &RefundResponse{
RefundNo: refundNo,
RefundID: refundID,
}, nil
}
// RefundResponse 退款响应
type RefundResponse struct {
RefundNo string `json:"refund_no"` // 退款单号
RefundID string `json:"refund_id"` // 退款ID
}
// generateRefundNo 生成退款单号
func (s *PaymentService) generateRefundNo(tenantID string) string {
timestamp := time.Now().Format("20060102150405")
random := rand.Intn(10000)
return fmt.Sprintf("R%s%s%04d", tenantID, timestamp, random)
}
// HandleRefundNotify 处理退款回调
func (s *PaymentService) HandleRefundNotify(ctx context.Context, req *RefundNotifyReq) error {
// 1. 验证回调签名
if !s.verifyRefundNotifySignature(req) {
return errors.New("签名验证失败")
}
// 2. 查询退款记录
refundRecord, err := s.refundRecordDao.GetByRefundNo(ctx, req.TenantID, req.RefundNo)
if err != nil {
return fmt.Errorf("查询退款记录失败: %w", err)
}
if refundRecord == nil {
return errors.New("退款记录不存在")
}
// 3. 更新退款记录状态
if err := s.refundRecordDao.UpdateRefundStatus(ctx, refundRecord.ID.Hex(), req.Status, req.RefundID); err != nil {
return fmt.Errorf("更新退款记录失败: %w", err)
}
return nil
}
// RefundNotifyReq 退款回调请求
type RefundNotifyReq struct {
TenantID string `json:"tenant_id"` // 租户ID
RefundNo string `json:"refund_no"` // 退款单号
Status string `json:"status"` // 退款状态
RefundID string `json:"refund_id"` // 退款ID
Sign string `json:"sign"` // 签名
}
// verifyRefundNotifySignature 验证退款回调签名
func (s *PaymentService) verifyRefundNotifySignature(req *RefundNotifyReq) bool {
// 这里应该是实际的签名验证逻辑
// 为了演示我们总是返回true
return true
}

View File

@@ -0,0 +1,50 @@
package service
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/mongo"
"order/dao"
"order/model/entity"
)
var (
orderService *OrderService
paymentService *PaymentService
)
// InitServices 初始化服务
func InitServices() error {
ctx := context.Background()
// 创建订单集合映射(模拟数据)
orderCollections := make(map[entity.OrderStatus]*mongo.Collection)
// 创建DAO实例
orderDao := dao.NewOrderDao(orderCollections)
paymentConfigDao := dao.NewPaymentConfigDao(nil)
paymentRecordDao := dao.NewPaymentRecordDao(nil)
refundRecordDao := dao.NewRefundRecordDao(nil)
// 创建服务实例
orderService = NewOrderService(orderDao)
paymentService = NewPaymentService(
orderDao,
paymentConfigDao,
paymentRecordDao,
refundRecordDao,
)
g.Log().Info(ctx, "服务初始化完成")
return nil
}
// GetOrderService 获取订单服务实例
func GetOrderService() *OrderService {
return orderService
}
// GetPaymentService 获取支付服务实例
func GetPaymentService() *PaymentService {
return paymentService
}