151 lines
3.8 KiB
Go
151 lines
3.8 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"affiliate_dash/internal/model"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type OrderService struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewOrderService(db *gorm.DB) *OrderService {
|
|
return &OrderService{db: db}
|
|
}
|
|
|
|
type OrderListQuery struct {
|
|
Page int
|
|
Size int
|
|
Status string
|
|
DistributorID *uint
|
|
}
|
|
|
|
type CreateOrderInput struct {
|
|
SkinID uint
|
|
DistributorID uint
|
|
BuyerName string
|
|
Remark string
|
|
}
|
|
|
|
func (s *OrderService) List(q OrderListQuery) ([]model.Order, int64, error) {
|
|
if q.Page < 1 {
|
|
q.Page = 1
|
|
}
|
|
if q.Size < 1 || q.Size > 100 {
|
|
q.Size = 20
|
|
}
|
|
tx := s.db.Model(&model.Order{})
|
|
if q.Status != "" {
|
|
tx = tx.Where("status = ?", q.Status)
|
|
}
|
|
if q.DistributorID != nil {
|
|
tx = tx.Where("distributor_id = ?", *q.DistributorID)
|
|
}
|
|
var total int64
|
|
if err := tx.Count(&total).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
var list []model.Order
|
|
err := tx.Preload("Skin").Preload("Distributor").
|
|
Order("id DESC").
|
|
Offset((q.Page - 1) * q.Size).Limit(q.Size).
|
|
Find(&list).Error
|
|
return list, total, err
|
|
}
|
|
|
|
func (s *OrderService) Create(in CreateOrderInput) (*model.Order, error) {
|
|
var skin model.Skin
|
|
if err := s.db.First(&skin, in.SkinID).Error; err != nil {
|
|
return nil, errors.New("皮肤不存在")
|
|
}
|
|
if skin.Status != 1 {
|
|
return nil, errors.New("皮肤已下架")
|
|
}
|
|
if skin.Stock == 0 {
|
|
return nil, errors.New("库存不足")
|
|
}
|
|
|
|
order := &model.Order{
|
|
OrderNo: generateOrderNo(),
|
|
SkinID: in.SkinID,
|
|
DistributorID: in.DistributorID,
|
|
BuyerName: in.BuyerName,
|
|
Amount: skin.Price,
|
|
CommissionAmt: skin.Price * skin.Commission,
|
|
Status: model.OrderStatusPending,
|
|
Remark: in.Remark,
|
|
}
|
|
|
|
err := s.db.Transaction(func(tx *gorm.DB) error {
|
|
if skin.Stock > 0 {
|
|
res := tx.Model(&model.Skin{}).
|
|
Where("id = ? AND stock > 0", skin.ID).
|
|
Update("stock", gorm.Expr("stock - 1"))
|
|
if res.Error != nil {
|
|
return res.Error
|
|
}
|
|
if res.RowsAffected == 0 {
|
|
return errors.New("库存不足")
|
|
}
|
|
}
|
|
return tx.Create(order).Error
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return order, nil
|
|
}
|
|
|
|
func (s *OrderService) UpdateStatus(id uint, status string) error {
|
|
allowed := map[string]bool{
|
|
model.OrderStatusPending: true,
|
|
model.OrderStatusPaid: true,
|
|
model.OrderStatusDelivered: true,
|
|
model.OrderStatusCancelled: true,
|
|
}
|
|
if !allowed[status] {
|
|
return errors.New("无效的订单状态")
|
|
}
|
|
res := s.db.Model(&model.Order{}).Where("id = ?", id).Update("status", status)
|
|
if res.Error != nil {
|
|
return res.Error
|
|
}
|
|
if res.RowsAffected == 0 {
|
|
return errors.New("订单不存在")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type DashboardStats struct {
|
|
SkinCount int64 `json:"skin_count"`
|
|
DistributorCount int64 `json:"distributor_count"`
|
|
OrderCount int64 `json:"order_count"`
|
|
TotalSales float64 `json:"total_sales"`
|
|
TotalCommission float64 `json:"total_commission"`
|
|
PendingOrderCount int64 `json:"pending_order_count"`
|
|
}
|
|
|
|
func (s *OrderService) Dashboard() (*DashboardStats, error) {
|
|
stats := &DashboardStats{}
|
|
s.db.Model(&model.Skin{}).Count(&stats.SkinCount)
|
|
s.db.Model(&model.User{}).Where("role = ?", model.RoleDistributor).Count(&stats.DistributorCount)
|
|
s.db.Model(&model.Order{}).Count(&stats.OrderCount)
|
|
s.db.Model(&model.Order{}).Where("status = ?", model.OrderStatusPending).Count(&stats.PendingOrderCount)
|
|
s.db.Model(&model.Order{}).
|
|
Where("status IN ?", []string{model.OrderStatusPaid, model.OrderStatusDelivered}).
|
|
Select("COALESCE(SUM(amount),0)").Scan(&stats.TotalSales)
|
|
s.db.Model(&model.Order{}).
|
|
Where("status IN ?", []string{model.OrderStatusPaid, model.OrderStatusDelivered}).
|
|
Select("COALESCE(SUM(commission_amt),0)").Scan(&stats.TotalCommission)
|
|
return stats, nil
|
|
}
|
|
|
|
func generateOrderNo() string {
|
|
return fmt.Sprintf("O%s%04d", time.Now().Format("20060102150405"), time.Now().Nanosecond()%10000)
|
|
}
|