初始化项目
This commit is contained in:
439
service/order.go
Normal file
439
service/order.go
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user