1581 lines
50 KiB
Go
1581 lines
50 KiB
Go
package service
|
||
|
||
import (
|
||
"dianshang/internal/model"
|
||
"dianshang/internal/repository"
|
||
"dianshang/pkg/utils"
|
||
"errors"
|
||
"fmt"
|
||
"log"
|
||
"time"
|
||
)
|
||
|
||
// OrderService 订单服务
|
||
type OrderService struct {
|
||
orderRepo *repository.OrderRepository
|
||
productRepo *repository.ProductRepository
|
||
userRepo *repository.UserRepository
|
||
couponRepo *repository.CouponRepository
|
||
}
|
||
|
||
// NewOrderService 创建订单服务
|
||
func NewOrderService(orderRepo *repository.OrderRepository, productRepo *repository.ProductRepository, userRepo *repository.UserRepository, couponRepo *repository.CouponRepository) *OrderService {
|
||
return &OrderService{
|
||
orderRepo: orderRepo,
|
||
productRepo: productRepo,
|
||
userRepo: userRepo,
|
||
couponRepo: couponRepo,
|
||
}
|
||
}
|
||
|
||
// CreateOrderRequest 创建订单请求
|
||
type CreateOrderRequest struct {
|
||
AddressID uint `json:"address_id" binding:"required"`
|
||
Items []CreateOrderItemRequest `json:"items" binding:"required,min=1"`
|
||
Remark string `json:"remark"`
|
||
CouponID *uint `json:"coupon_id,omitempty"`
|
||
}
|
||
|
||
// CreateOrderItemRequest 创建订单项请求
|
||
type CreateOrderItemRequest struct {
|
||
ProductID uint `json:"product_id" binding:"required"`
|
||
SpecID uint `json:"spec_id"`
|
||
SKUID uint `json:"sku_id"`
|
||
Quantity int `json:"quantity" binding:"required,min=1"`
|
||
SpecName string `json:"spec_name"`
|
||
SpecValue string `json:"spec_value"`
|
||
SpecInfo interface{} `json:"spec_info"` // 改为interface{}以支持数组和对象
|
||
}
|
||
|
||
// MergeOrdersRequest 合并订单请求
|
||
type MergeOrdersRequest struct {
|
||
OrderIDs []string `json:"order_ids" binding:"required,min=2"`
|
||
}
|
||
|
||
// CreateOrder 创建订单
|
||
func (s *OrderService) CreateOrder(userID uint, req *CreateOrderRequest) (*model.Order, error) {
|
||
// 检查用户是否存在
|
||
_, err := s.userRepo.GetByID(userID)
|
||
if err != nil {
|
||
return nil, errors.New("用户不存在")
|
||
}
|
||
|
||
// 检查收货地址是否存在
|
||
address, err := s.userRepo.GetAddressByID(req.AddressID)
|
||
if err != nil || address.UserID != userID {
|
||
return nil, errors.New("收货地址不存在")
|
||
}
|
||
|
||
// 创建订单
|
||
order := &model.Order{
|
||
OrderNo: utils.GenerateOrderNo(),
|
||
UserID: userID,
|
||
Status: 1, // 待付款
|
||
PayMethod: "wechat", // 微信支付
|
||
ReceiverAddress: address.ProvinceName + address.CityName + address.DistrictName + address.DetailAddress,
|
||
ReceiverName: address.Name,
|
||
ReceiverPhone: address.Phone,
|
||
Remark: req.Remark,
|
||
CreatedAt: time.Now(),
|
||
UpdatedAt: time.Now(),
|
||
}
|
||
|
||
var totalAmount float64
|
||
var orderItems []model.OrderItem
|
||
|
||
// 处理订单项
|
||
for _, item := range req.Items {
|
||
// 检查产品是否存在
|
||
product, err := s.productRepo.GetByID(item.ProductID)
|
||
if err != nil {
|
||
return nil, errors.New("产品不存在")
|
||
}
|
||
|
||
// 检查库存和获取价格(优先检查SKU库存和价格)
|
||
var actualPrice float64
|
||
if item.SKUID > 0 {
|
||
// 如果有SKU,检查SKU库存并使用SKU价格
|
||
sku, err := s.productRepo.GetSKUByID(item.SKUID)
|
||
if err != nil {
|
||
return nil, errors.New("SKU不存在")
|
||
}
|
||
if sku.Stock < item.Quantity {
|
||
return nil, errors.New("SKU库存不足: " + product.Name)
|
||
}
|
||
actualPrice = sku.Price
|
||
} else {
|
||
// 如果没有SKU,检查商品总库存并使用商品价格
|
||
if product.Stock < item.Quantity {
|
||
return nil, errors.New("产品库存不足: " + product.Name)
|
||
}
|
||
actualPrice = product.Price
|
||
}
|
||
|
||
// 计算金额
|
||
itemAmount := actualPrice * float64(item.Quantity)
|
||
totalAmount += itemAmount
|
||
|
||
// 创建订单项
|
||
var specID *uint
|
||
var skuID *uint
|
||
if item.SpecID > 0 {
|
||
specID = &item.SpecID
|
||
}
|
||
if item.SKUID > 0 {
|
||
skuID = &item.SKUID
|
||
}
|
||
|
||
// 处理SpecInfo类型转换
|
||
var specInfo model.JSONMap
|
||
if item.SpecInfo != nil {
|
||
switch v := item.SpecInfo.(type) {
|
||
case map[string]interface{}:
|
||
specInfo = model.JSONMap(v)
|
||
case []interface{}:
|
||
// 如果是数组,转换为map格式
|
||
specInfo = make(model.JSONMap)
|
||
for _, spec := range v {
|
||
if specObj, ok := spec.(map[string]interface{}); ok {
|
||
if title, hasTitle := specObj["specTitle"]; hasTitle {
|
||
if value, hasValue := specObj["specValue"]; hasValue {
|
||
if titleStr, ok := title.(string); ok {
|
||
specInfo[titleStr] = value
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
default:
|
||
specInfo = make(model.JSONMap)
|
||
}
|
||
}
|
||
|
||
orderItem := model.OrderItem{
|
||
ProductID: item.ProductID,
|
||
SpecID: specID,
|
||
SKUID: skuID,
|
||
Quantity: item.Quantity,
|
||
Price: actualPrice,
|
||
TotalPrice: itemAmount,
|
||
ProductName: product.Name,
|
||
ProductImage: product.MainImage,
|
||
SpecName: item.SpecName,
|
||
SpecValue: item.SpecValue,
|
||
SpecInfo: specInfo,
|
||
}
|
||
orderItems = append(orderItems, orderItem)
|
||
}
|
||
|
||
order.TotalAmount = totalAmount
|
||
order.ShippingFee = 0 // 免运费
|
||
|
||
// 处理优惠券
|
||
var couponAmount float64
|
||
if req.CouponID != nil && *req.CouponID > 0 {
|
||
log.Printf("🎫 [优惠券验证] 开始处理优惠券 - 用户ID: %d, 接收到的优惠券ID: %d, 订单总金额: %.2f", userID, *req.CouponID, totalAmount)
|
||
|
||
// 获取用户优惠券
|
||
userCoupon, err := s.couponRepo.GetUserCouponByID(*req.CouponID)
|
||
if err != nil {
|
||
log.Printf("❌ [优惠券验证] 获取用户优惠券失败 - 用户ID: %d, 优惠券ID: %d, 错误: %v", userID, *req.CouponID, err)
|
||
return nil, errors.New("用户优惠券不存在")
|
||
}
|
||
|
||
log.Printf("✅ [优惠券验证] 成功获取用户优惠券 - 用户优惠券ID: %d, 优惠券模板ID: %d, 优惠券名称: %s, 状态: %d, 用户ID: %d",
|
||
userCoupon.ID, userCoupon.CouponID, userCoupon.Coupon.Name, userCoupon.Status, userCoupon.UserID)
|
||
|
||
// 验证用户优惠券是否可用
|
||
if err := s.validateUserCoupon(userCoupon, userID, totalAmount); err != nil {
|
||
log.Printf("❌ [优惠券验证] 用户优惠券验证失败 - 用户ID: %d, 优惠券ID: %d, 错误: %v", userID, *req.CouponID, err)
|
||
return nil, err
|
||
}
|
||
|
||
// 计算优惠金额
|
||
couponAmount = s.calculateCouponDiscount(&userCoupon.Coupon, totalAmount)
|
||
log.Printf("💰 [优惠券验证] 优惠金额计算完成 - 原价: %.2f, 优惠金额: %.2f, 实付金额: %.2f", totalAmount, couponAmount, totalAmount-couponAmount)
|
||
|
||
order.CouponID = req.CouponID
|
||
order.CouponAmount = couponAmount
|
||
} else {
|
||
log.Printf("ℹ️ [优惠券验证] 未使用优惠券 - 用户ID: %d", userID)
|
||
}
|
||
|
||
order.PayAmount = totalAmount - couponAmount
|
||
|
||
// 保存订单
|
||
if err := s.orderRepo.Create(order); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 保存订单项
|
||
for i := range orderItems {
|
||
orderItems[i].OrderID = order.ID
|
||
if err := s.orderRepo.CreateOrderItem(&orderItems[i]); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
|
||
// 预扣库存(防止超卖)
|
||
for i, item := range req.Items {
|
||
var err error
|
||
if item.SKUID > 0 {
|
||
// 如果有SKU,同时扣减SKU库存和商品库存
|
||
err = s.productRepo.DeductStockWithSKU(item.ProductID, &item.SKUID, item.Quantity)
|
||
} else {
|
||
// 如果没有SKU,只扣减商品库存
|
||
err = s.productRepo.ReserveStock(item.ProductID, item.Quantity)
|
||
}
|
||
|
||
if err != nil {
|
||
// 如果预扣失败,需要恢复之前已预扣的库存
|
||
for j := 0; j < i; j++ {
|
||
prevItem := req.Items[j]
|
||
if prevItem.SKUID > 0 {
|
||
s.productRepo.RestoreStockWithSKU(prevItem.ProductID, &prevItem.SKUID, prevItem.Quantity)
|
||
} else {
|
||
s.productRepo.RestoreStock(prevItem.ProductID, prevItem.Quantity)
|
||
}
|
||
}
|
||
return nil, errors.New("库存预扣失败: " + err.Error())
|
||
}
|
||
}
|
||
|
||
// 清空购物车中的相关商品
|
||
for _, item := range req.Items {
|
||
s.orderRepo.RemoveFromCart(userID, item.ProductID)
|
||
}
|
||
|
||
// 如果使用了优惠券,标记为已使用
|
||
if req.CouponID != nil && *req.CouponID > 0 {
|
||
userCoupon, err := s.couponRepo.GetUserCouponByID(*req.CouponID)
|
||
if err != nil {
|
||
return nil, errors.New("用户优惠券不存在")
|
||
}
|
||
|
||
if err := s.couponRepo.UseCoupon(userCoupon.ID, order.ID); err != nil {
|
||
return nil, errors.New("使用优惠券失败")
|
||
}
|
||
}
|
||
|
||
return order, nil
|
||
}
|
||
|
||
// validateCoupon 验证优惠券是否可用
|
||
func (s *OrderService) validateCoupon(coupon *model.Coupon, userID uint, orderAmount float64) error {
|
||
// 检查优惠券状态
|
||
if coupon.Status != 1 {
|
||
return errors.New("优惠券不可用")
|
||
}
|
||
|
||
// 检查有效期
|
||
now := time.Now()
|
||
if coupon.StartTime.After(now) {
|
||
return errors.New("优惠券尚未生效")
|
||
}
|
||
if coupon.EndTime.Before(now) {
|
||
return errors.New("优惠券已过期")
|
||
}
|
||
|
||
// 检查最低消费金额 (MinAmount是分,需要转换为元)
|
||
minAmount := float64(coupon.MinAmount) / 100
|
||
if orderAmount < minAmount {
|
||
return fmt.Errorf("订单金额不满足优惠券使用条件,最低需要%.2f元", minAmount)
|
||
}
|
||
|
||
// 检查用户是否拥有此优惠券且未使用
|
||
userCoupon, err := s.couponRepo.GetUserCouponByID(coupon.ID)
|
||
if err != nil {
|
||
return errors.New("您没有此优惠券")
|
||
}
|
||
|
||
if userCoupon.Status != 0 {
|
||
return errors.New("优惠券已使用或已过期")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// validateUserCoupon 验证用户优惠券是否可用
|
||
func (s *OrderService) validateUserCoupon(userCoupon *model.UserCoupon, userID uint, orderAmount float64) error {
|
||
log.Printf("🔍 [优惠券验证] 开始详细验证 - 用户ID: %d, 用户优惠券ID: %d", userID, userCoupon.ID)
|
||
|
||
// 首先检查优惠券是否属于当前用户
|
||
log.Printf("👤 [优惠券验证] 检查优惠券所有权 - 当前用户ID: %d, 优惠券所属用户ID: %d", userID, userCoupon.UserID)
|
||
if userCoupon.UserID != userID {
|
||
log.Printf("❌ [优惠券验证] 优惠券不属于当前用户 - 当前用户: %d, 优惠券所属用户: %d", userID, userCoupon.UserID)
|
||
return errors.New("无权使用该优惠券")
|
||
}
|
||
|
||
// 检查用户优惠券状态
|
||
log.Printf("📋 [优惠券验证] 检查用户优惠券状态 - 状态: %d (0=未使用, 1=已使用, 2=已过期)", userCoupon.Status)
|
||
if userCoupon.Status != 0 {
|
||
log.Printf("❌ [优惠券验证] 用户优惠券状态不可用 - 状态: %d", userCoupon.Status)
|
||
return errors.New("优惠券已使用或已过期")
|
||
}
|
||
|
||
// 检查优惠券状态
|
||
log.Printf("📋 [优惠券验证] 检查优惠券模板状态 - 状态: %d (1=可用)", userCoupon.Coupon.Status)
|
||
if userCoupon.Coupon.Status != 1 {
|
||
log.Printf("❌ [优惠券验证] 优惠券模板状态不可用 - 状态: %d", userCoupon.Coupon.Status)
|
||
return errors.New("优惠券不可用")
|
||
}
|
||
|
||
// 检查有效期
|
||
now := time.Now()
|
||
log.Printf("⏰ [优惠券验证] 检查有效期 - 当前时间: %s, 开始时间: %s, 结束时间: %s",
|
||
now.Format("2006-01-02 15:04:05"),
|
||
userCoupon.Coupon.StartTime.Format("2006-01-02 15:04:05"),
|
||
userCoupon.Coupon.EndTime.Format("2006-01-02 15:04:05"))
|
||
if userCoupon.Coupon.StartTime.After(now) {
|
||
log.Printf("❌ [优惠券验证] 优惠券尚未生效")
|
||
return errors.New("优惠券尚未生效")
|
||
}
|
||
if userCoupon.Coupon.EndTime.Before(now) {
|
||
log.Printf("❌ [优惠券验证] 优惠券已过期")
|
||
return errors.New("优惠券已过期")
|
||
}
|
||
|
||
// 检查最低消费金额 (MinAmount是分,需要转换为元)
|
||
minAmount := float64(userCoupon.Coupon.MinAmount) / 100
|
||
log.Printf("💰 [优惠券验证] 检查最低消费金额 - 订单金额: %.2f元, 最低要求: %.2f元", orderAmount, minAmount)
|
||
if orderAmount < minAmount {
|
||
log.Printf("❌ [优惠券验证] 订单金额不满足最低消费要求")
|
||
return fmt.Errorf("订单金额不满足优惠券使用条件,最低需要%.2f元", minAmount)
|
||
}
|
||
|
||
log.Printf("✅ [优惠券验证] 所有验证通过 - 用户ID: %d, 用户优惠券ID: %d", userID, userCoupon.ID)
|
||
return nil
|
||
}
|
||
|
||
// calculateCouponDiscount 计算优惠券折扣金额
|
||
func (s *OrderService) calculateCouponDiscount(coupon *model.Coupon, orderAmount float64) float64 {
|
||
switch coupon.Type {
|
||
case 1: // 固定金额 (Value是分,需要转换为元)
|
||
discountAmount := float64(coupon.Value)
|
||
if discountAmount > orderAmount {
|
||
return orderAmount
|
||
}
|
||
return discountAmount
|
||
case 2: // 百分比折扣 (Value是折扣,如85表示8.5折)
|
||
discountRate := float64(coupon.Value) / 100 // 85 -> 0.85
|
||
discount := orderAmount * (1 - discountRate) // 计算优惠金额
|
||
return discount
|
||
case 3: // 免运费
|
||
// 这里可以返回运费金额,目前运费为0,所以返回0
|
||
return 0
|
||
default:
|
||
return 0
|
||
}
|
||
}
|
||
|
||
// GetUserOrders 获取用户订单列表
|
||
func (s *OrderService) GetUserOrders(userID uint, status, page, pageSize int) ([]model.Order, *utils.Pagination, error) {
|
||
if page <= 0 {
|
||
page = 1
|
||
}
|
||
if pageSize <= 0 || pageSize > 100 {
|
||
pageSize = 20
|
||
}
|
||
|
||
offset := (page - 1) * pageSize
|
||
orders, total, err := s.orderRepo.GetUserOrders(userID, status, offset, pageSize)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
|
||
pagination := utils.NewPagination(page, pageSize)
|
||
pagination.Total = total
|
||
return orders, pagination, nil
|
||
}
|
||
|
||
// GetUserOrderStatistics 获取用户订单统计
|
||
func (s *OrderService) GetUserOrderStatistics(userID uint) (map[string]interface{}, error) {
|
||
stats, err := s.orderRepo.GetUserOrderStatistics(userID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return stats, nil
|
||
}
|
||
|
||
// GetOrderDetail 获取订单详情
|
||
func (s *OrderService) GetOrderDetail(userID uint, orderID uint) (*model.Order, error) {
|
||
order, err := s.orderRepo.GetByID(orderID)
|
||
if err != nil {
|
||
return nil, errors.New("订单不存在")
|
||
}
|
||
|
||
// 检查订单是否属于当前用户
|
||
if order.UserID != userID {
|
||
return nil, errors.New("无权访问该订单")
|
||
}
|
||
|
||
return order, nil
|
||
}
|
||
|
||
// GetOrderDetailByOrderNo 通过订单号获取订单详情(支持订单号或微信订单号)
|
||
func (s *OrderService) GetOrderDetailByOrderNo(userID uint, orderNo string) (*model.Order, error) {
|
||
var order *model.Order
|
||
var err error
|
||
|
||
// 先尝试使用订单号查询
|
||
order, err = s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
// 如果订单号查询失败,尝试使用微信订单号查询
|
||
order, err = s.orderRepo.GetOrderByWechatOutTradeNo(orderNo)
|
||
if err != nil {
|
||
return nil, errors.New("订单不存在")
|
||
}
|
||
}
|
||
|
||
// 检查订单是否属于当前用户
|
||
if order.UserID != userID {
|
||
return nil, errors.New("无权访问该订单")
|
||
}
|
||
|
||
return order, nil
|
||
}
|
||
|
||
// GetOrderByOrderNo 通过订单号获取订单信息(不验证用户权限,用于支付回调等场景)
|
||
func (s *OrderService) GetOrderByOrderNo(orderNo string) (*model.Order, error) {
|
||
order, err := s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
return nil, errors.New("订单不存在")
|
||
}
|
||
return order, nil
|
||
}
|
||
|
||
// PayOrder 支付订单
|
||
func (s *OrderService) PayOrder(userID uint, orderNo string) error {
|
||
order, err := s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
// 检查订单是否属于当前用户
|
||
if order.UserID != userID {
|
||
return errors.New("无权操作该订单")
|
||
}
|
||
|
||
// 检查订单状态
|
||
if order.Status != 1 {
|
||
return errors.New("订单状态不正确")
|
||
}
|
||
|
||
// 库存已在订单创建时预扣,支付成功时无需再次扣减
|
||
fmt.Printf("💰 [PayOrder] 支付成功,库存已在订单创建时预扣 - 订单号: %s\n", orderNo)
|
||
|
||
// 获取订单项以更新销量
|
||
orderItems, err := s.orderRepo.GetOrderItems(order.ID)
|
||
if err != nil {
|
||
fmt.Printf("❌ [PayOrder] 获取订单项失败: %v\n", err)
|
||
return errors.New("获取订单项失败")
|
||
}
|
||
|
||
// 支付成功时更新销量
|
||
for _, item := range orderItems {
|
||
err := s.productRepo.UpdateSales(item.ProductID, item.Quantity)
|
||
if err != nil {
|
||
fmt.Printf("❌ [PayOrder] 更新销量失败 - 产品ID: %d, 数量: %d, 错误: %v\n", item.ProductID, item.Quantity, err)
|
||
// 销量更新失败不影响支付成功,只记录日志
|
||
} else {
|
||
fmt.Printf("✅ [PayOrder] 支付成功,更新销量 - 产品ID: %d, 数量: %d\n", item.ProductID, item.Quantity)
|
||
}
|
||
}
|
||
|
||
// 模拟支付成功,更新订单状态
|
||
updates := map[string]interface{}{
|
||
"status": 2, // 待发货
|
||
"pay_status": 1, // 已支付
|
||
"pay_time": time.Now(),
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
// 添加调试日志
|
||
fmt.Printf("🔄 [PayOrder] 开始更新订单 %s,用户ID: %d\n", orderNo, userID)
|
||
fmt.Printf("🔄 [PayOrder] 更新数据: %+v\n", updates)
|
||
|
||
err = s.orderRepo.UpdateByOrderNo(orderNo, updates)
|
||
if err != nil {
|
||
fmt.Printf("❌ [PayOrder] 数据库更新失败: %v\n", err)
|
||
// 如果订单更新失败,库存已在创建时预扣,无需额外处理
|
||
return err
|
||
}
|
||
|
||
fmt.Printf("✅ [PayOrder] 数据库更新成功\n")
|
||
return nil
|
||
}
|
||
|
||
// GetPendingOrdersCount 获取待处理订单数量
|
||
func (s *OrderService) GetPendingOrdersCount() (int64, error) {
|
||
return s.orderRepo.GetPendingOrdersCount()
|
||
}
|
||
|
||
// GetRegionStatistics 获取地区统计
|
||
func (s *OrderService) GetRegionStatistics(startDate, endDate string) ([]map[string]interface{}, error) {
|
||
// 简化实现,返回基础地区统计数据
|
||
var results []map[string]interface{}
|
||
|
||
// 模拟地区数据
|
||
regions := []string{"北京", "上海", "广州", "深圳", "杭州", "南京", "成都", "武汉"}
|
||
for i, region := range regions {
|
||
results = append(results, map[string]interface{}{
|
||
"region": region,
|
||
"order_count": 100 - i*8,
|
||
"order_amount": float64(10000 - i*800),
|
||
})
|
||
}
|
||
|
||
return results, nil
|
||
}
|
||
|
||
// GetPaymentMethodStatistics 获取支付方式统计
|
||
func (s *OrderService) GetPaymentMethodStatistics(startDate, endDate string) ([]map[string]interface{}, error) {
|
||
// 简化实现,返回基础支付方式统计数据
|
||
var results []map[string]interface{}
|
||
|
||
// 模拟支付方式数据
|
||
paymentMethods := []map[string]interface{}{
|
||
{"method": "微信支付", "count": 150, "amount": 15000.0},
|
||
{"method": "支付宝", "count": 120, "amount": 12000.0},
|
||
{"method": "银行卡", "count": 80, "amount": 8000.0},
|
||
{"method": "余额支付", "count": 50, "amount": 5000.0},
|
||
}
|
||
|
||
results = paymentMethods
|
||
return results, nil
|
||
}
|
||
|
||
// GetRefundStatistics 获取退款统计
|
||
func (s *OrderService) GetRefundStatistics(startDate, endDate string) (map[string]interface{}, error) {
|
||
// 简化实现,返回基础退款统计数据
|
||
result := map[string]interface{}{
|
||
"total_refund_count": 25,
|
||
"total_refund_amount": 2500.0,
|
||
"refund_rate": 0.05, // 5%退款率
|
||
"avg_refund_amount": 100.0,
|
||
"refund_reasons": []map[string]interface{}{
|
||
{"reason": "商品质量问题", "count": 10},
|
||
{"reason": "不喜欢", "count": 8},
|
||
{"reason": "尺寸不合适", "count": 5},
|
||
{"reason": "其他", "count": 2},
|
||
},
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// RefundOrderDuplicate 申请退款(重复方法,已移除)
|
||
func (s *OrderService) RefundOrderDuplicate(userID uint, orderNo string) error {
|
||
log.Printf("[服务层] 开始处理退款申请: userID=%d, orderNo=%s", userID, orderNo)
|
||
|
||
// 获取订单信息
|
||
order, err := s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
log.Printf("[服务层] 退款申请失败: 订单不存在, orderNo=%s, error=%v", orderNo, err)
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
log.Printf("[服务层] 找到订单: ID=%d, UserID=%d, Status=%d, CreatedAt=%v",
|
||
order.ID, order.UserID, order.Status, order.CreatedAt)
|
||
|
||
// 验证用户权限
|
||
if order.UserID != userID {
|
||
log.Printf("[服务层] 退款申请失败: 订单不属于当前用户, orderUserID=%d, requestUserID=%d", order.UserID, userID)
|
||
return errors.New("无权操作此订单")
|
||
}
|
||
|
||
// 验证订单状态是否允许退款(已付款状态)
|
||
if order.Status != 2 && order.Status != 3 && order.Status != 4 {
|
||
log.Printf("[服务层] 退款申请失败: 订单状态不允许退款, currentStatus=%d", order.Status)
|
||
return errors.New("订单状态不允许退款")
|
||
}
|
||
|
||
// 获取订单项以恢复库存和减少销量
|
||
orderItems, err := s.orderRepo.GetOrderItems(order.ID)
|
||
if err != nil {
|
||
log.Printf("[服务层] 获取订单项失败: %v", err)
|
||
return errors.New("获取订单项失败")
|
||
}
|
||
|
||
// 恢复库存
|
||
for _, item := range orderItems {
|
||
err := s.productRepo.RestoreStock(item.ProductID, item.Quantity)
|
||
if err != nil {
|
||
log.Printf("❌ [RefundOrder] 恢复库存失败 - 产品ID: %d, 数量: %d, 错误: %v", item.ProductID, item.Quantity, err)
|
||
} else {
|
||
log.Printf("✅ [RefundOrder] 成功恢复库存 - 产品ID: %d, 数量: %d", item.ProductID, item.Quantity)
|
||
}
|
||
}
|
||
|
||
// 如果订单已完成(状态为4),需要减少销量
|
||
if order.Status == 4 {
|
||
for _, item := range orderItems {
|
||
err := s.productRepo.UpdateSales(item.ProductID, -item.Quantity) // 负数表示减少
|
||
if err != nil {
|
||
log.Printf("❌ [RefundOrder] 减少销量失败 - 产品ID: %d, 数量: %d, 错误: %v", item.ProductID, item.Quantity, err)
|
||
} else {
|
||
log.Printf("✅ [RefundOrder] 成功减少销量 - 产品ID: %d, 数量: %d", item.ProductID, item.Quantity)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新订单状态为退款中(8)
|
||
log.Printf("[服务层] 更新订单状态为退款中: 原状态=%d", order.Status)
|
||
|
||
updates := map[string]interface{}{
|
||
"status": 8, // 退款中状态
|
||
"refund_time": time.Now(),
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
// 保存订单更新
|
||
if err := s.orderRepo.UpdateByOrderNo(orderNo, updates); err != nil {
|
||
log.Printf("[服务层] 更新订单状态失败: %v", err)
|
||
return err
|
||
}
|
||
|
||
log.Printf("[服务层] 退款申请处理成功: orderNo=%s", orderNo)
|
||
return nil
|
||
}
|
||
|
||
// CancelOrder 取消订单
|
||
func (s *OrderService) CancelOrder(userID uint, orderNo string) error {
|
||
order, err := s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
// 检查订单是否属于当前用户
|
||
if order.UserID != userID {
|
||
return errors.New("无权操作该订单")
|
||
}
|
||
|
||
// 检查订单状态(只有待付款的订单可以取消)
|
||
if order.Status != 1 {
|
||
return errors.New("订单状态不正确,无法取消")
|
||
}
|
||
|
||
// 恢复库存
|
||
items, err := s.orderRepo.GetOrderItems(order.ID)
|
||
if err == nil {
|
||
for _, item := range items {
|
||
var err error
|
||
if item.SKUID != nil && *item.SKUID > 0 {
|
||
// 如果有SKU,同时恢复SKU库存和商品库存
|
||
err = s.productRepo.RestoreStockWithSKU(item.ProductID, item.SKUID, item.Quantity)
|
||
} else {
|
||
// 如果没有SKU,只恢复商品库存
|
||
err = s.productRepo.RestoreStock(item.ProductID, item.Quantity)
|
||
}
|
||
|
||
if err != nil {
|
||
log.Printf("恢复库存失败 - 商品ID: %d, SKU ID: %v, 数量: %d, 错误: %v", item.ProductID, item.SKUID, item.Quantity, err)
|
||
}
|
||
}
|
||
|
||
// 取消订单时只需恢复库存,无需减少销量(因为只有待付款订单才能取消,销量尚未增加)
|
||
}
|
||
|
||
// 恢复优惠券(如果订单使用了优惠券)
|
||
if order.CouponID != nil && *order.CouponID > 0 {
|
||
// 根据订单ID查找对应的用户优惠券
|
||
userCoupon, err := s.couponRepo.GetUserCouponByOrderID(order.ID)
|
||
if err != nil {
|
||
log.Printf("查找用户优惠券失败: orderID=%d, couponID=%d, error=%v", order.ID, *order.CouponID, err)
|
||
} else {
|
||
if err := s.couponRepo.RestoreCoupon(userCoupon.ID); err != nil {
|
||
log.Printf("恢复优惠券失败: userCouponID=%d, orderID=%d, error=%v", userCoupon.ID, order.ID, err)
|
||
} else {
|
||
log.Printf("优惠券恢复成功: userCouponID=%d, orderID=%d", userCoupon.ID, order.ID)
|
||
}
|
||
}
|
||
// 不阻止订单取消,只记录日志
|
||
}
|
||
|
||
// 更新订单状态
|
||
updates := map[string]interface{}{
|
||
"status": 6, // 已取消
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
return s.orderRepo.UpdateByOrderNo(orderNo, updates)
|
||
}
|
||
|
||
// RemindShip 提醒发货
|
||
func (s *OrderService) RemindShip(userID uint, orderNo string) error {
|
||
log.Printf("[服务层] ===== 提醒发货服务开始 =====")
|
||
log.Printf("[服务层] 提醒发货参数: userID=%d, orderNo=%s", userID, orderNo)
|
||
|
||
order, err := s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
log.Printf("[服务层] 提醒发货失败: 订单不存在, orderNo=%s, error=%v", orderNo, err)
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
log.Printf("[服务层] 找到订单: ID=%d, UserID=%d, Status=%d, CreatedAt=%v",
|
||
order.ID, order.UserID, order.Status, order.CreatedAt)
|
||
|
||
// 检查订单是否属于当前用户
|
||
if order.UserID != userID {
|
||
log.Printf("[服务层] 提醒发货失败: 订单不属于当前用户, orderUserID=%d, requestUserID=%d", order.UserID, userID)
|
||
return errors.New("无权操作该订单")
|
||
}
|
||
|
||
// 检查订单状态(只有已付款/待发货的订单可以提醒发货)
|
||
if order.Status != 2 && order.Status != 3 {
|
||
log.Printf("[服务层] 提醒发货失败: 订单状态不正确, currentStatus=%d, 期望状态=2或3", order.Status)
|
||
return errors.New("订单状态不正确,无法提醒发货")
|
||
}
|
||
|
||
// 检查订单创建时间,如果是刚创建的订单(10分钟内),提示用户稍后再试
|
||
if time.Since(order.CreatedAt) < 10*time.Minute {
|
||
log.Printf("[服务层] 提醒发货失败: 订单创建时间过短, 创建时间=%v, 当前时间=%v, 需等待=%v",
|
||
order.CreatedAt, time.Now(), 10*time.Minute-time.Since(order.CreatedAt))
|
||
return errors.New("订单刚创建,请10分钟后再提醒发货")
|
||
}
|
||
|
||
// 这里可以添加实际的提醒逻辑,比如发送通知给商家
|
||
// 1. 发送短信通知商家
|
||
// 2. 发送邮件通知商家
|
||
// 3. 在商家后台系统中创建提醒记录
|
||
// 4. 推送消息到商家APP
|
||
|
||
log.Printf("[服务层] 🔔 提醒发货成功: 用户 %d 提醒发货订单 %s", userID, orderNo)
|
||
log.Printf("[服务层] 📦 商家将收到发货提醒通知")
|
||
|
||
return nil
|
||
}
|
||
|
||
// RefundOrderLegacy 申请退款(旧方法)
|
||
func (s *OrderService) RefundOrderLegacy(userID uint, orderNo string) error {
|
||
// 此方法已被移除,使用上面的RefundOrder方法
|
||
return nil
|
||
|
||
// 获取订单信息
|
||
order, err := s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
log.Printf("[服务层] 退款申请失败: 订单不存在, orderNo=%s, error=%v", orderNo, err)
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
log.Printf("[服务层] 找到订单: ID=%d, UserID=%d, Status=%d, CreatedAt=%v",
|
||
order.ID, order.UserID, order.Status, order.CreatedAt)
|
||
|
||
// 验证用户权限
|
||
if order.UserID != userID {
|
||
log.Printf("[服务层] 退款申请失败: 订单不属于当前用户, orderUserID=%d, requestUserID=%d", order.UserID, userID)
|
||
return errors.New("无权操作此订单")
|
||
}
|
||
|
||
// 验证订单状态是否允许退款(已付款状态)
|
||
if order.Status != 2 && order.Status != 3 {
|
||
log.Printf("[服务层] 退款申请失败: 订单状态不允许退款, currentStatus=%d", order.Status)
|
||
return errors.New("订单状态不允许退款,只有已付款待发货的订单可以申请退款")
|
||
}
|
||
|
||
// 更新订单状态为已退款(9)
|
||
log.Printf("[服务层] 更新订单状态为已退款: 原状态=%d", order.Status)
|
||
|
||
updates := map[string]interface{}{
|
||
"status": 9, // 已退款状态
|
||
"refund_time": time.Now(),
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
// 保存订单更新
|
||
if err := s.orderRepo.UpdateByOrderNo(orderNo, updates); err != nil {
|
||
log.Printf("[服务层] 更新订单状态失败: %v", err)
|
||
return err
|
||
}
|
||
|
||
log.Printf("[服务层] 退款申请处理成功: orderNo=%s", orderNo)
|
||
return nil
|
||
}
|
||
|
||
// ConfirmReceive 确认收货
|
||
func (s *OrderService) ConfirmReceive(userID uint, orderNo string) error {
|
||
order, err := s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
// 检查订单是否属于当前用户
|
||
if order.UserID != userID {
|
||
return errors.New("无权操作该订单")
|
||
}
|
||
|
||
// 检查订单状态(只有已发货(4)或待收货(5)的订单可以确认收货)
|
||
if order.Status != 4 && order.Status != 5 {
|
||
return errors.New("订单状态不正确")
|
||
}
|
||
|
||
fmt.Printf("✅ [ConfirmReceive] 确认收货成功 - 订单号: %s (销量已在支付时更新)\n", orderNo)
|
||
|
||
// 更新订单状态
|
||
updates := map[string]interface{}{
|
||
"status": 6, // 已完成
|
||
"receive_time": time.Now(),
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
return s.orderRepo.UpdateByOrderNo(orderNo, updates)
|
||
}
|
||
|
||
// GetOrderList 获取订单列表(管理员)
|
||
func (s *OrderService) GetOrderList(page, pageSize int, conditions map[string]interface{}) ([]model.Order, *utils.Pagination, error) {
|
||
if page <= 0 {
|
||
page = 1
|
||
}
|
||
if pageSize <= 0 || pageSize > 100 {
|
||
pageSize = 20
|
||
}
|
||
|
||
offset := (page - 1) * pageSize
|
||
orders, total, err := s.orderRepo.GetList(offset, pageSize, conditions)
|
||
if err != nil {
|
||
return nil, nil, err
|
||
}
|
||
|
||
pagination := utils.NewPagination(page, pageSize)
|
||
pagination.Total = total
|
||
return orders, pagination, nil
|
||
}
|
||
|
||
// ShipOrder 发货(管理员)
|
||
func (s *OrderService) ShipOrder(orderNo, logisticsCompany, logisticsNo string) error {
|
||
order, err := s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
// 检查订单状态(只有待发货的订单可以发货)
|
||
if order.Status != 2 && order.Status != 3 {
|
||
return errors.New("订单状态不正确")
|
||
}
|
||
|
||
// 更新订单状态
|
||
updates := map[string]interface{}{
|
||
"status": 4, // 已发货
|
||
"logistics_company": logisticsCompany,
|
||
"logistics_no": logisticsNo,
|
||
"shipped_at": time.Now(),
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
return s.orderRepo.UpdateByOrderNo(orderNo, updates)
|
||
}
|
||
|
||
// ShipOrderByID 通过订单ID发货
|
||
func (s *OrderService) ShipOrderByID(orderID uint, logisticsCompany, logisticsNo string) error {
|
||
order, err := s.orderRepo.GetByID(orderID)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
// 检查订单状态(只有待发货的订单可以发货)
|
||
if order.Status != 2 && order.Status != 3 {
|
||
return errors.New("订单状态不正确")
|
||
}
|
||
|
||
// 更新订单状态
|
||
updates := map[string]interface{}{
|
||
"status": 4, // 已发货
|
||
"logistics_company": logisticsCompany,
|
||
"logistics_no": logisticsNo,
|
||
"shipped_at": time.Now(),
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
return s.orderRepo.Update(orderID, updates)
|
||
}
|
||
|
||
|
||
|
||
// MergeOrders 合并订单
|
||
func (s *OrderService) MergeOrders(userID uint, req *MergeOrdersRequest) (*model.Order, error) {
|
||
// 1. 验证订单数量
|
||
if len(req.OrderIDs) < 2 {
|
||
return nil, errors.New("至少需要选择2个订单进行合并")
|
||
}
|
||
|
||
// 2. 获取所有要合并的订单
|
||
var orders []model.Order
|
||
var orderIDs []uint
|
||
|
||
for _, orderIDStr := range req.OrderIDs {
|
||
orderID := uint(0)
|
||
if _, err := fmt.Sscanf(orderIDStr, "%d", &orderID); err != nil {
|
||
return nil, errors.New("订单ID格式错误")
|
||
}
|
||
orderIDs = append(orderIDs, orderID)
|
||
}
|
||
|
||
// 获取订单详情
|
||
for _, orderID := range orderIDs {
|
||
order, err := s.orderRepo.GetByID(orderID)
|
||
if err != nil {
|
||
return nil, errors.New("订单不存在")
|
||
}
|
||
|
||
// 验证订单所有权
|
||
if order.UserID != userID {
|
||
return nil, errors.New("无权限操作此订单")
|
||
}
|
||
|
||
orders = append(orders, *order)
|
||
}
|
||
|
||
// 3. 验证订单合并条件
|
||
if err := s.validateOrdersForMerge(orders); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 4. 创建合并后的新订单
|
||
mergedOrder, err := s.createMergedOrder(orders)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 5. 删除原订单
|
||
for _, orderID := range orderIDs {
|
||
if err := s.orderRepo.Delete(orderID); err != nil {
|
||
// 如果删除失败,需要回滚新创建的订单
|
||
s.orderRepo.Delete(mergedOrder.ID)
|
||
return nil, errors.New("合并订单失败,请重试")
|
||
}
|
||
}
|
||
|
||
return mergedOrder, nil
|
||
}
|
||
|
||
// validateOrdersForMerge 验证订单是否可以合并
|
||
func (s *OrderService) validateOrdersForMerge(orders []model.Order) error {
|
||
if len(orders) < 2 {
|
||
return errors.New("至少需要2个订单才能合并")
|
||
}
|
||
|
||
firstOrder := orders[0]
|
||
|
||
for _, order := range orders {
|
||
// 检查订单状态:只能合并未支付的订单
|
||
if order.Status != 1 || order.PayStatus != 0 {
|
||
return errors.New("只能合并未支付的订单")
|
||
}
|
||
|
||
// 检查用户ID是否一致
|
||
if order.UserID != firstOrder.UserID {
|
||
return errors.New("只能合并同一用户的订单")
|
||
}
|
||
|
||
// 检查收货地址是否一致
|
||
if order.ReceiverName != firstOrder.ReceiverName ||
|
||
order.ReceiverPhone != firstOrder.ReceiverPhone ||
|
||
order.ReceiverAddress != firstOrder.ReceiverAddress {
|
||
return errors.New("只能合并相同收货地址的订单")
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// createMergedOrder 创建合并后的订单
|
||
func (s *OrderService) createMergedOrder(orders []model.Order) (*model.Order, error) {
|
||
firstOrder := orders[0]
|
||
|
||
// 创建新订单
|
||
mergedOrder := &model.Order{
|
||
OrderNo: utils.GenerateOrderNo(),
|
||
UserID: firstOrder.UserID,
|
||
StoreID: firstOrder.StoreID,
|
||
Status: 1, // 待付款
|
||
PayStatus: 0, // 未支付
|
||
ReceiverName: firstOrder.ReceiverName,
|
||
ReceiverPhone: firstOrder.ReceiverPhone,
|
||
ReceiverAddress: firstOrder.ReceiverAddress,
|
||
CreatedAt: time.Now(),
|
||
UpdatedAt: time.Now(),
|
||
}
|
||
|
||
// 合并所有订单项
|
||
var allOrderItems []model.OrderItem
|
||
var totalAmount float64
|
||
var totalDiscountAmount float64
|
||
var totalCouponAmount float64
|
||
var totalShippingFee float64
|
||
|
||
for _, order := range orders {
|
||
// 累加金额
|
||
totalAmount += order.TotalAmount
|
||
totalDiscountAmount += order.DiscountAmount
|
||
totalCouponAmount += order.CouponAmount
|
||
totalShippingFee += order.ShippingFee
|
||
|
||
// 收集订单项
|
||
for _, item := range order.OrderItems {
|
||
newItem := model.OrderItem{
|
||
OrderID: mergedOrder.ID,
|
||
ProductID: item.ProductID,
|
||
SKUID: item.SKUID,
|
||
Quantity: item.Quantity,
|
||
Price: item.Price,
|
||
TotalPrice: item.TotalPrice,
|
||
ProductName: item.ProductName,
|
||
ProductImage: item.ProductImage,
|
||
SpecInfo: item.SpecInfo,
|
||
CreatedAt: time.Now(),
|
||
UpdatedAt: time.Now(),
|
||
}
|
||
allOrderItems = append(allOrderItems, newItem)
|
||
}
|
||
}
|
||
|
||
// 设置合并后的金额
|
||
mergedOrder.TotalAmount = totalAmount
|
||
mergedOrder.PayAmount = totalAmount - totalDiscountAmount - totalCouponAmount + totalShippingFee
|
||
mergedOrder.DiscountAmount = totalDiscountAmount
|
||
mergedOrder.CouponAmount = totalCouponAmount
|
||
mergedOrder.ShippingFee = totalShippingFee
|
||
mergedOrder.OrderItems = allOrderItems
|
||
|
||
// 保存到数据库
|
||
if err := s.orderRepo.Create(mergedOrder); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return mergedOrder, nil
|
||
}
|
||
|
||
// GetOrderListForAdmin 获取订单列表(管理员专用)
|
||
func (s *OrderService) GetOrderListForAdmin(page, pageSize int, conditions map[string]interface{}) ([]model.Order, *utils.Pagination, error) {
|
||
return s.GetOrderList(page, pageSize, conditions)
|
||
}
|
||
|
||
// GetOrderDetailForAdmin 获取订单详情(管理员专用)
|
||
func (s *OrderService) GetOrderDetailForAdmin(orderID uint) (*model.Order, error) {
|
||
order, err := s.orderRepo.GetByID(orderID)
|
||
if err != nil {
|
||
return nil, errors.New("订单不存在")
|
||
}
|
||
return order, nil
|
||
}
|
||
|
||
// UpdateOrderStatusByAdmin 更新订单状态(管理员专用)
|
||
func (s *OrderService) UpdateOrderStatusByAdmin(orderID uint, status int, adminRemark string) error {
|
||
order, err := s.orderRepo.GetByID(orderID)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
// 验证状态转换的合法性
|
||
if !s.isValidStatusTransition(order.Status, status) {
|
||
return errors.New("无效的状态转换")
|
||
}
|
||
|
||
updates := map[string]interface{}{
|
||
"status": status,
|
||
"admin_remark": adminRemark,
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
// 根据状态设置相应的时间字段
|
||
switch status {
|
||
case 2: // 已付款
|
||
updates["pay_time"] = time.Now()
|
||
case 4: // 已发货
|
||
updates["ship_time"] = time.Now()
|
||
case 6: // 已完成
|
||
updates["receive_time"] = time.Now()
|
||
case 7: // 已取消
|
||
updates["cancel_time"] = time.Now()
|
||
}
|
||
|
||
return s.orderRepo.Update(orderID, updates)
|
||
}
|
||
|
||
// RefundOrder 退款订单(管理员专用)
|
||
func (s *OrderService) RefundOrder(orderID uint, refundAmount float64, refundReason string, adminID uint) error {
|
||
order, err := s.orderRepo.GetByID(orderID)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
log.Printf("🔍 [RefundOrder] 订单状态检查 - 订单ID: %d, 当前状态: %d, 状态名称: %s", orderID, order.Status, s.getOrderStatusName(order.Status))
|
||
|
||
// 检查订单状态
|
||
if order.Status != 2 && order.Status != 3 && order.Status != 4 && order.Status != 5 {
|
||
log.Printf("❌ [RefundOrder] 订单状态不允许退款 - 订单ID: %d, 状态: %d", orderID, order.Status)
|
||
return errors.New("订单状态不允许退款")
|
||
}
|
||
|
||
log.Printf("✅ [RefundOrder] 订单状态检查通过 - 订单ID: %d, 状态: %d", orderID, order.Status)
|
||
|
||
// 检查退款金额
|
||
if refundAmount <= 0 || refundAmount > order.PayAmount {
|
||
return errors.New("退款金额无效")
|
||
}
|
||
|
||
// 获取订单商品项
|
||
orderItems, err := s.orderRepo.GetOrderItems(order.ID)
|
||
if err != nil {
|
||
log.Printf("获取订单商品项失败: %v", err)
|
||
return errors.New("获取订单商品项失败")
|
||
}
|
||
|
||
// 恢复库存
|
||
for _, item := range orderItems {
|
||
err := s.productRepo.RestoreStock(item.ProductID, item.Quantity)
|
||
if err != nil {
|
||
log.Printf("恢复库存失败 - 商品ID: %d, 数量: %d, 错误: %v", item.ProductID, item.Quantity, err)
|
||
}
|
||
}
|
||
|
||
// 如果订单已支付,需要减少销量(因为销量在支付时就增加了)
|
||
if order.Status >= 2 && order.PayStatus == 1 {
|
||
for _, item := range orderItems {
|
||
err := s.productRepo.UpdateSales(item.ProductID, -item.Quantity)
|
||
if err != nil {
|
||
log.Printf("❌ [RefundOrder] 减少销量失败 - 商品ID: %d, 数量: %d, 错误: %v", item.ProductID, item.Quantity, err)
|
||
} else {
|
||
log.Printf("✅ [RefundOrder] 退款成功,减少销量 - 商品ID: %d, 数量: %d", item.ProductID, item.Quantity)
|
||
}
|
||
}
|
||
}
|
||
|
||
updates := map[string]interface{}{
|
||
"status": 9, // 已退款
|
||
"refund_amount": refundAmount,
|
||
"refund_reason": refundReason,
|
||
"refund_time": time.Now(),
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
log.Printf("🔄 [RefundOrder] 准备更新订单 - 订单ID: %d", orderID)
|
||
log.Printf("🔄 [RefundOrder] 更新字段: %+v", updates)
|
||
|
||
err = s.orderRepo.Update(orderID, updates)
|
||
if err != nil {
|
||
log.Printf("❌ [RefundOrder] 数据库更新失败 - 订单ID: %d, 错误: %v", orderID, err)
|
||
return err
|
||
}
|
||
|
||
log.Printf("✅ [RefundOrder] 数据库更新成功 - 订单ID: %d", orderID)
|
||
return nil
|
||
}
|
||
|
||
// CancelOrderByAdmin 取消订单(管理员专用)
|
||
func (s *OrderService) CancelOrderByAdmin(orderID uint, cancelReason string, adminID uint) error {
|
||
order, err := s.orderRepo.GetByID(orderID)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
// 检查订单状态
|
||
if order.Status == 6 || order.Status == 7 || order.Status == 9 {
|
||
return errors.New("订单已完成、已取消或已退款,无法取消")
|
||
}
|
||
|
||
// 恢复库存
|
||
items, err := s.orderRepo.GetOrderItems(order.ID)
|
||
if err == nil {
|
||
for _, item := range items {
|
||
if item.SKUID != nil && *item.SKUID > 0 {
|
||
// 如果有SKU,同时恢复SKU库存和商品库存
|
||
err := s.productRepo.RestoreStockWithSKU(item.ProductID, item.SKUID, item.Quantity)
|
||
if err != nil {
|
||
log.Printf("管理员取消订单时恢复SKU库存失败 - 商品ID: %d, SKU ID: %d, 数量: %d, 错误: %v", item.ProductID, *item.SKUID, item.Quantity, err)
|
||
}
|
||
} else {
|
||
// 如果没有SKU,只恢复商品库存
|
||
s.productRepo.UpdateStock(item.ProductID, int(item.Quantity))
|
||
}
|
||
}
|
||
}
|
||
|
||
// 恢复优惠券(如果订单使用了优惠券)
|
||
if order.CouponID != nil && *order.CouponID > 0 {
|
||
if err := s.couponRepo.RestoreCoupon(*order.CouponID); err != nil {
|
||
log.Printf("管理员取消订单时恢复优惠券失败: orderID=%d, couponID=%d, error=%v", orderID, *order.CouponID, err)
|
||
// 不阻止订单取消,只记录日志
|
||
}
|
||
}
|
||
|
||
updates := map[string]interface{}{
|
||
"status": 7, // 已取消
|
||
"cancel_reason": cancelReason,
|
||
"cancel_time": time.Now(),
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
return s.orderRepo.Update(orderID, updates)
|
||
}
|
||
|
||
// GetOrderStatisticsWithDateRange 获取订单统计(带日期范围)
|
||
func (s *OrderService) GetOrderStatisticsWithDateRange(startDate, endDate string) (map[string]interface{}, error) {
|
||
// 暂时使用基础统计方法,后续可以扩展支持日期范围
|
||
return s.orderRepo.GetOrderStatistics()
|
||
}
|
||
|
||
// GetDailyOrderStatisticsWithDateRange 获取每日订单统计(带日期范围)
|
||
func (s *OrderService) GetDailyOrderStatisticsWithDateRange(startDate, endDate string) ([]map[string]interface{}, error) {
|
||
// 解析日期范围,计算天数
|
||
start, err := time.Parse("2006-01-02", startDate)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("invalid start date format: %v", err)
|
||
}
|
||
|
||
end, err := time.Parse("2006-01-02", endDate)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("invalid end date format: %v", err)
|
||
}
|
||
|
||
// 计算天数差
|
||
days := int(end.Sub(start).Hours()/24) + 1
|
||
if days <= 0 {
|
||
days = 1
|
||
}
|
||
|
||
// 使用基础统计方法,但确保天数正确
|
||
return s.orderRepo.GetDailyOrderStatistics(days)
|
||
}
|
||
|
||
// isValidStatusTransition 检查状态转换是否合法
|
||
func (s *OrderService) isValidStatusTransition(currentStatus, newStatus int) bool {
|
||
// 定义合法的状态转换
|
||
validTransitions := map[int][]int{
|
||
1: {2, 7}, // 未付款 -> 已付款, 已取消
|
||
2: {3, 7, 8, 9}, // 已付款 -> 待发货, 已取消, 退货中, 已退款
|
||
3: {4, 7, 8, 9}, // 待发货 -> 已发货, 已取消, 退货中, 已退款
|
||
4: {5, 8, 9}, // 已发货 -> 待收货, 退货中, 已退款
|
||
5: {6, 8, 9}, // 待收货 -> 已完成, 退货中, 已退款
|
||
6: {8, 9}, // 已完成 -> 退货中, 已退款
|
||
7: {}, // 已取消 -> 无法转换到其他状态
|
||
8: {9}, // 退货中 -> 已退款
|
||
9: {}, // 已退款 -> 无法转换到其他状态
|
||
}
|
||
|
||
allowedStatuses, exists := validTransitions[currentStatus]
|
||
if !exists {
|
||
return false
|
||
}
|
||
|
||
for _, status := range allowedStatuses {
|
||
if status == newStatus {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// GetLogisticsInfo 获取物流信息
|
||
func (s *OrderService) GetLogisticsInfo(orderNo string) (map[string]interface{}, error) {
|
||
order, err := s.orderRepo.GetByOrderNo(orderNo)
|
||
if err != nil {
|
||
return nil, errors.New("订单不存在")
|
||
}
|
||
|
||
if order.LogisticsCompany == "" || order.LogisticsNo == "" {
|
||
return nil, errors.New("订单尚未发货")
|
||
}
|
||
|
||
// 构建物流信息
|
||
logisticsInfo := map[string]interface{}{
|
||
"order_no": order.OrderNo,
|
||
"express_company": order.LogisticsCompany,
|
||
"express_no": order.LogisticsNo,
|
||
"receiver_name": order.ReceiverName,
|
||
"receiver_phone": order.ReceiverPhone,
|
||
"receiver_address": order.ReceiverAddress,
|
||
"ship_time": order.ShippedAt,
|
||
"status": order.Status,
|
||
"status_name": s.getOrderStatusName(order.Status),
|
||
}
|
||
|
||
// 这里可以集成第三方物流API获取实时物流信息
|
||
// 暂时返回基本信息
|
||
logisticsInfo["tracks"] = []map[string]interface{}{
|
||
{
|
||
"time": order.ShippedAt,
|
||
"description": "商品已发货",
|
||
"location": "发货地",
|
||
},
|
||
}
|
||
|
||
return logisticsInfo, nil
|
||
}
|
||
|
||
// UpdateOrderStatus 更新订单状态
|
||
func (s *OrderService) UpdateOrderStatus(orderID uint, status int, adminID uint) error {
|
||
order, err := s.orderRepo.GetByID(orderID)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
// 验证状态转换的合法性
|
||
if !s.isValidStatusTransition(order.Status, status) {
|
||
return errors.New("无效的状态转换")
|
||
}
|
||
|
||
updates := map[string]interface{}{
|
||
"status": status,
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
// 根据状态设置相应的时间字段
|
||
switch status {
|
||
case 2: // 已付款
|
||
updates["pay_time"] = time.Now()
|
||
updates["pay_status"] = 1
|
||
case 4: // 已发货
|
||
updates["shipped_at"] = time.Now()
|
||
case 6: // 已完成
|
||
updates["receive_time"] = time.Now()
|
||
case 7: // 已取消
|
||
updates["cancel_time"] = time.Now()
|
||
case 8: // 退货中
|
||
// 退货中状态不需要特殊的时间字段,由退款记录表管理
|
||
case 9: // 已退款
|
||
updates["refund_time"] = time.Now()
|
||
}
|
||
|
||
err = s.orderRepo.Update(orderID, updates)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 处理状态变更后的业务逻辑
|
||
return s.handleOrderStatusChange(order, status, adminID)
|
||
}
|
||
|
||
// handleOrderStatusChange 处理订单状态变更后的业务逻辑
|
||
func (s *OrderService) handleOrderStatusChange(order *model.Order, newStatus int, adminID uint) error {
|
||
switch newStatus {
|
||
case 8: // 退货中
|
||
// 当订单状态变为退货中时,可以在这里添加额外的业务逻辑
|
||
// 例如:通知相关人员、记录日志等
|
||
log.Printf("订单 %s 状态变更为退货中", order.OrderNo)
|
||
|
||
case 9: // 已退款
|
||
// 当订单状态变为已退款时,处理库存恢复等业务逻辑
|
||
log.Printf("订单 %s 状态变更为已退款,开始处理库存恢复", order.OrderNo)
|
||
|
||
// 获取订单项
|
||
orderItems, err := s.orderRepo.GetOrderItems(order.ID)
|
||
if err != nil {
|
||
log.Printf("获取订单项失败: %v", err)
|
||
return err
|
||
}
|
||
|
||
// 恢复库存(如果订单已支付)
|
||
if order.Status >= 2 && order.PayStatus == 1 {
|
||
for _, item := range orderItems {
|
||
// 恢复库存
|
||
err := s.productRepo.UpdateStock(item.ProductID, item.Quantity)
|
||
if err != nil {
|
||
log.Printf("恢复库存失败 - 商品ID: %d, 数量: %d, 错误: %v", item.ProductID, item.Quantity, err)
|
||
} else {
|
||
log.Printf("库存恢复成功 - 商品ID: %d, 数量: %d", item.ProductID, item.Quantity)
|
||
}
|
||
|
||
// 减少销量
|
||
err = s.productRepo.UpdateSales(item.ProductID, -item.Quantity)
|
||
if err != nil {
|
||
log.Printf("减少销量失败 - 商品ID: %d, 数量: %d, 错误: %v", item.ProductID, item.Quantity, err)
|
||
} else {
|
||
log.Printf("销量减少成功 - 商品ID: %d, 数量: %d", item.ProductID, item.Quantity)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// BatchUpdateOrderStatus 批量更新订单状态
|
||
func (s *OrderService) BatchUpdateOrderStatus(orderIDs []uint, status int, adminID uint) error {
|
||
for _, orderID := range orderIDs {
|
||
if err := s.UpdateOrderStatus(orderID, status, adminID); err != nil {
|
||
return fmt.Errorf("更新订单 %d 状态失败: %v", orderID, err)
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ProcessRefund 处理退款
|
||
func (s *OrderService) ProcessRefund(orderID uint, refundAmount float64, refundReason string, adminID uint) error {
|
||
order, err := s.orderRepo.GetByID(orderID)
|
||
if err != nil {
|
||
return errors.New("订单不存在")
|
||
}
|
||
|
||
// 检查订单状态
|
||
if order.Status != 2 && order.Status != 3 && order.Status != 4 && order.Status != 5 {
|
||
return errors.New("订单状态不允许退款")
|
||
}
|
||
|
||
// 检查退款金额
|
||
if refundAmount <= 0 || refundAmount > order.PayAmount {
|
||
return errors.New("退款金额无效")
|
||
}
|
||
|
||
// 获取订单商品项
|
||
orderItems, err := s.orderRepo.GetOrderItems(order.ID)
|
||
if err != nil {
|
||
return errors.New("获取订单商品项失败")
|
||
}
|
||
|
||
// 恢复库存
|
||
for _, item := range orderItems {
|
||
if err := s.productRepo.RestoreStock(item.ProductID, item.Quantity); err != nil {
|
||
log.Printf("恢复库存失败 - 商品ID: %d, 数量: %d, 错误: %v", item.ProductID, item.Quantity, err)
|
||
}
|
||
}
|
||
|
||
// 减少销量
|
||
if order.Status >= 2 && order.PayStatus == 1 {
|
||
for _, item := range orderItems {
|
||
if err := s.productRepo.UpdateSales(item.ProductID, -item.Quantity); err != nil {
|
||
log.Printf("减少销量失败 - 商品ID: %d, 数量: %d, 错误: %v", item.ProductID, item.Quantity, err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新订单状态
|
||
updates := map[string]interface{}{
|
||
"status": 9, // 已退款
|
||
"refund_amount": refundAmount,
|
||
"refund_time": time.Now(),
|
||
"cancel_reason": refundReason,
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
return s.orderRepo.Update(orderID, updates)
|
||
}
|
||
|
||
// GetOrderStatistics 获取订单统计
|
||
func (s *OrderService) GetOrderStatistics() (map[string]interface{}, error) {
|
||
return s.orderRepo.GetOrderStatistics()
|
||
}
|
||
|
||
// GetDailyOrderStatistics 获取每日订单统计
|
||
func (s *OrderService) GetDailyOrderStatistics(days int) ([]map[string]interface{}, error) {
|
||
return s.orderRepo.GetDailyOrderStatistics(days)
|
||
}
|
||
|
||
// GetOrdersByDateRange 根据日期范围获取订单
|
||
func (s *OrderService) GetOrdersByDateRange(startDate, endDate string, status int, offset, limit int) ([]model.Order, int64, error) {
|
||
conditions := make(map[string]interface{})
|
||
|
||
if startDate != "" {
|
||
conditions["start_date"] = startDate
|
||
}
|
||
if endDate != "" {
|
||
conditions["end_date"] = endDate
|
||
}
|
||
if status > 0 {
|
||
conditions["status"] = status
|
||
}
|
||
|
||
return s.orderRepo.GetList(offset, limit, conditions)
|
||
}
|
||
|
||
// ExportOrdersToExcel 导出订单到Excel
|
||
func (s *OrderService) ExportOrdersToExcel(conditions map[string]interface{}) ([]map[string]interface{}, error) {
|
||
// 获取所有符合条件的订单
|
||
orders, _, err := s.orderRepo.GetList(0, 10000, conditions) // 限制最大导出数量
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var exportData []map[string]interface{}
|
||
for _, order := range orders {
|
||
data := map[string]interface{}{
|
||
"订单号": order.OrderNo,
|
||
"用户ID": order.UserID,
|
||
"店铺ID": order.StoreID,
|
||
"订单状态": s.getOrderStatusName(order.Status),
|
||
"订单金额": order.TotalAmount,
|
||
"实付金额": order.PayAmount,
|
||
"优惠金额": order.DiscountAmount,
|
||
"运费": order.ShippingFee,
|
||
"收货人": order.ReceiverName,
|
||
"收货电话": order.ReceiverPhone,
|
||
"收货地址": order.ReceiverAddress,
|
||
"快递公司": order.LogisticsCompany,
|
||
"快递单号": order.LogisticsNo,
|
||
"支付方式": order.PayMethod,
|
||
"支付时间": s.formatTime(order.PayTime),
|
||
"发货时间": s.formatTime(order.ShippedAt),
|
||
"收货时间": s.formatTime(order.ReceiveTime),
|
||
"取消时间": s.formatTime(order.CancelTime),
|
||
"取消原因": order.CancelReason,
|
||
"退款金额": order.RefundAmount,
|
||
"退款时间": s.formatTime(order.RefundTime),
|
||
"备注": order.Remark,
|
||
"创建时间": order.CreatedAt.Format("2006-01-02 15:04:05"),
|
||
"更新时间": order.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||
}
|
||
exportData = append(exportData, data)
|
||
}
|
||
|
||
return exportData, nil
|
||
}
|
||
|
||
// GetOrderTrends 获取订单趋势
|
||
func (s *OrderService) GetOrderTrends(days int) (map[string]interface{}, error) {
|
||
dailyStats, err := s.GetDailyOrderStatistics(days)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var dates []string
|
||
var orderCounts []int
|
||
var amounts []float64
|
||
|
||
for _, stat := range dailyStats {
|
||
if date, ok := stat["date"].(string); ok {
|
||
dates = append(dates, date)
|
||
}
|
||
if count, ok := stat["order_count"].(int64); ok {
|
||
orderCounts = append(orderCounts, int(count))
|
||
}
|
||
if amount, ok := stat["total_amount"].(float64); ok {
|
||
amounts = append(amounts, amount)
|
||
}
|
||
}
|
||
|
||
return map[string]interface{}{
|
||
"dates": dates,
|
||
"order_counts": orderCounts,
|
||
"amounts": amounts,
|
||
}, nil
|
||
}
|
||
|
||
|
||
|
||
// getOrderStatusName 获取订单状态名称
|
||
func (s *OrderService) getOrderStatusName(status int) string {
|
||
statusNames := map[int]string{
|
||
1: "未付款",
|
||
2: "待发货", // 统一为待发货
|
||
3: "待发货", // 统一为待发货
|
||
4: "已发货",
|
||
5: "待收货",
|
||
6: "已完成",
|
||
7: "已取消",
|
||
8: "退货中",
|
||
9: "已退款",
|
||
}
|
||
|
||
if name, exists := statusNames[status]; exists {
|
||
return name
|
||
}
|
||
return "未知状态"
|
||
}
|
||
|
||
// formatTime 格式化时间
|
||
func (s *OrderService) formatTime(t *time.Time) string {
|
||
if t == nil {
|
||
return ""
|
||
}
|
||
return t.Format("2006-01-02 15:04:05")
|
||
} |