2025-11-17 14:11:46 +08:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-11-28 15:18:10 +08:00
|
|
|
|
"context"
|
2025-11-17 14:11:46 +08:00
|
|
|
|
"dianshang/internal/model"
|
|
|
|
|
|
"dianshang/internal/repository"
|
|
|
|
|
|
"errors"
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// CouponService 优惠券服务
|
|
|
|
|
|
type CouponService struct {
|
|
|
|
|
|
couponRepo *repository.CouponRepository
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NewCouponService 创建优惠券服务
|
|
|
|
|
|
func NewCouponService(couponRepo *repository.CouponRepository) *CouponService {
|
|
|
|
|
|
return &CouponService{
|
|
|
|
|
|
couponRepo: couponRepo,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetAvailableCoupons 获取可用优惠券列表
|
|
|
|
|
|
func (s *CouponService) GetAvailableCoupons() ([]model.Coupon, error) {
|
|
|
|
|
|
return s.couponRepo.GetAvailableCoupons()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetAvailableCouponsWithUserStatus 获取可用优惠券列表(包含用户已领取状态)
|
|
|
|
|
|
func (s *CouponService) GetAvailableCouponsWithUserStatus(userID uint) ([]map[string]interface{}, error) {
|
|
|
|
|
|
// 获取所有可用优惠券
|
|
|
|
|
|
coupons, err := s.couponRepo.GetAvailableCoupons()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var result []map[string]interface{}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果用户已登录,检查每个优惠券的领取状态
|
|
|
|
|
|
for _, coupon := range coupons {
|
|
|
|
|
|
couponData := map[string]interface{}{
|
|
|
|
|
|
"id": coupon.ID,
|
|
|
|
|
|
"name": coupon.Name,
|
|
|
|
|
|
"type": coupon.Type,
|
|
|
|
|
|
"value": coupon.Value,
|
|
|
|
|
|
"min_amount": coupon.MinAmount,
|
|
|
|
|
|
"description": coupon.Description,
|
|
|
|
|
|
"start_time": coupon.StartTime,
|
|
|
|
|
|
"end_time": coupon.EndTime,
|
|
|
|
|
|
"total_count": coupon.TotalCount,
|
|
|
|
|
|
"used_count": coupon.UsedCount,
|
|
|
|
|
|
"is_received": false, // 默认未领取
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果用户已登录,检查是否已领取
|
|
|
|
|
|
if userID > 0 {
|
|
|
|
|
|
exists, err := s.couponRepo.CheckUserCouponExists(userID, coupon.ID)
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
couponData["is_received"] = exists
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
result = append(result, couponData)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetUserCoupons 获取用户优惠券
|
|
|
|
|
|
func (s *CouponService) GetUserCoupons(userID uint, status int) ([]model.UserCoupon, error) {
|
|
|
|
|
|
return s.couponRepo.GetUserCoupons(userID, status)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ReceiveCoupon 领取优惠券
|
|
|
|
|
|
func (s *CouponService) ReceiveCoupon(userID, couponID uint) error {
|
|
|
|
|
|
// 检查优惠券是否存在且有效
|
|
|
|
|
|
coupon, err := s.couponRepo.GetByID(couponID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.New("优惠券不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否在有效期内
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
if now.Before(coupon.StartTime) || now.After(coupon.EndTime) {
|
|
|
|
|
|
return errors.New("优惠券不在有效期内")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否还有库存
|
|
|
|
|
|
if coupon.TotalCount > 0 && coupon.UsedCount >= coupon.TotalCount {
|
|
|
|
|
|
return errors.New("优惠券已被领完")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查用户是否已经领取过
|
|
|
|
|
|
exists, err := s.couponRepo.CheckUserCouponExists(userID, couponID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
if exists {
|
|
|
|
|
|
return errors.New("您已经领取过该优惠券")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建用户优惠券记录
|
|
|
|
|
|
userCoupon := &model.UserCoupon{
|
|
|
|
|
|
UserID: userID,
|
|
|
|
|
|
CouponID: couponID,
|
|
|
|
|
|
Status: 0, // 未使用
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return s.couponRepo.CreateUserCoupon(userCoupon)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UseCoupon 使用优惠券
|
|
|
|
|
|
func (s *CouponService) UseCoupon(userID, userCouponID, orderID uint) error {
|
|
|
|
|
|
// 获取用户优惠券
|
|
|
|
|
|
userCoupon, err := s.couponRepo.GetUserCouponByID(userCouponID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.New("优惠券不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否属于该用户
|
|
|
|
|
|
if userCoupon.UserID != userID {
|
|
|
|
|
|
return errors.New("无权使用该优惠券")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已使用
|
|
|
|
|
|
if userCoupon.Status != 0 {
|
|
|
|
|
|
return errors.New("优惠券已使用或已过期")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查优惠券是否在有效期内
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
if now.Before(userCoupon.Coupon.StartTime) || now.After(userCoupon.Coupon.EndTime) {
|
|
|
|
|
|
return errors.New("优惠券不在有效期内")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新优惠券状态为已使用
|
|
|
|
|
|
return s.couponRepo.UseCoupon(userCouponID, orderID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ValidateCoupon 验证优惠券是否可用
|
|
|
|
|
|
func (s *CouponService) ValidateCoupon(userID, userCouponID uint, orderAmount float64) (*model.UserCoupon, float64, error) {
|
|
|
|
|
|
// 获取用户优惠券
|
|
|
|
|
|
userCoupon, err := s.couponRepo.GetUserCouponByID(userCouponID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, 0, errors.New("优惠券不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否属于该用户
|
|
|
|
|
|
if userCoupon.UserID != userID {
|
|
|
|
|
|
return nil, 0, errors.New("无权使用该优惠券")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已使用
|
|
|
|
|
|
if userCoupon.Status != 0 {
|
|
|
|
|
|
return nil, 0, errors.New("优惠券已使用或已过期")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查优惠券是否在有效期内
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
if now.Before(userCoupon.Coupon.StartTime) || now.After(userCoupon.Coupon.EndTime) {
|
|
|
|
|
|
return nil, 0, errors.New("优惠券不在有效期内")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查最低消费金额
|
|
|
|
|
|
minAmount := float64(userCoupon.Coupon.MinAmount) / 100 // 分转元
|
|
|
|
|
|
if orderAmount < minAmount {
|
|
|
|
|
|
return nil, 0, errors.New(fmt.Sprintf("订单金额不满足优惠券使用条件,最低需要%.2f元", minAmount))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算优惠金额
|
|
|
|
|
|
var discountAmount float64
|
|
|
|
|
|
switch userCoupon.Coupon.Type {
|
|
|
|
|
|
case 1: // 满减券
|
|
|
|
|
|
discountAmount = float64(userCoupon.Coupon.Value) / 100 // 分转元
|
|
|
|
|
|
case 2: // 折扣券
|
|
|
|
|
|
discountRate := float64(userCoupon.Coupon.Value) / 100 // 85 -> 0.85
|
|
|
|
|
|
discountAmount = orderAmount * (1 - discountRate)
|
|
|
|
|
|
case 3: // 免邮券
|
|
|
|
|
|
discountAmount = 0 // 免邮券的优惠金额在运费中体现
|
|
|
|
|
|
default:
|
|
|
|
|
|
return nil, 0, errors.New("不支持的优惠券类型")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 确保优惠金额不超过订单金额
|
|
|
|
|
|
if discountAmount > orderAmount {
|
|
|
|
|
|
discountAmount = orderAmount
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return userCoupon, discountAmount, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetAvailableCouponsForOrder 获取订单可用的优惠券
|
|
|
|
|
|
func (s *CouponService) GetAvailableCouponsForOrder(userID uint, orderAmount float64) ([]model.UserCoupon, error) {
|
|
|
|
|
|
// 获取用户未使用的优惠券
|
|
|
|
|
|
userCoupons, err := s.couponRepo.GetUserCoupons(userID, 1) // 1表示未使用(API状态值)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var availableCoupons []model.UserCoupon
|
|
|
|
|
|
now := time.Now()
|
|
|
|
|
|
|
|
|
|
|
|
for _, userCoupon := range userCoupons {
|
|
|
|
|
|
// 严格检查优惠券状态:必须是未使用状态(0)且没有关联订单
|
|
|
|
|
|
if userCoupon.Status != 0 || userCoupon.OrderID != nil {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否在有效期内
|
|
|
|
|
|
if now.Before(userCoupon.Coupon.StartTime) || now.After(userCoupon.Coupon.EndTime) {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查优惠券模板是否可用
|
|
|
|
|
|
if userCoupon.Coupon.Status != 1 {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查最低消费金额
|
|
|
|
|
|
minAmount := float64(userCoupon.Coupon.MinAmount) / 100 // 分转元
|
|
|
|
|
|
if orderAmount >= minAmount {
|
|
|
|
|
|
availableCoupons = append(availableCoupons, userCoupon)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return availableCoupons, nil
|
|
|
|
|
|
}
|
2025-11-28 15:18:10 +08:00
|
|
|
|
|
|
|
|
|
|
// ==================== 管理端方法 ====================
|
|
|
|
|
|
|
|
|
|
|
|
// GetCouponListForAdmin 获取优惠券列表(管理端)
|
|
|
|
|
|
func (s *CouponService) GetCouponListForAdmin(page, pageSize int, status, couponType, keyword string) ([]model.Coupon, int64, error) {
|
|
|
|
|
|
return s.couponRepo.GetCouponListForAdmin(page, pageSize, status, couponType, keyword)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetCouponDetailForAdmin 获取优惠券详情(管理端)
|
|
|
|
|
|
func (s *CouponService) GetCouponDetailForAdmin(couponID uint) (*model.Coupon, error) {
|
|
|
|
|
|
coupon, err := s.couponRepo.GetByID(couponID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, errors.New("优惠券不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取优惠券的使用统计(注意:领取数和使用数不在Coupon表中,而是在UserCoupon表中统计)
|
|
|
|
|
|
// 这里仅作为查询,不修改coupon对象
|
|
|
|
|
|
_, _, _ = s.couponRepo.GetCouponUsageStats(couponID)
|
|
|
|
|
|
|
|
|
|
|
|
return coupon, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CreateCoupon 创建优惠券
|
|
|
|
|
|
func (s *CouponService) CreateCoupon(req interface{}, adminID uint) (*model.Coupon, error) {
|
|
|
|
|
|
// 类型断言
|
|
|
|
|
|
type CreateCouponRequest struct {
|
|
|
|
|
|
Name string
|
|
|
|
|
|
Type int
|
|
|
|
|
|
Value int64
|
|
|
|
|
|
MinAmount int64
|
|
|
|
|
|
Description string
|
|
|
|
|
|
StartTime time.Time
|
|
|
|
|
|
EndTime time.Time
|
|
|
|
|
|
TotalCount int
|
|
|
|
|
|
Status int
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reqData, ok := req.(*CreateCouponRequest)
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
return nil, errors.New("无效的请求参数")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证时间
|
|
|
|
|
|
if reqData.EndTime.Before(reqData.StartTime) {
|
|
|
|
|
|
return nil, errors.New("结束时间不能早于开始时间")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证优惠券类型和值
|
|
|
|
|
|
switch reqData.Type {
|
|
|
|
|
|
case 1: // 满减券,value是金额(分)
|
|
|
|
|
|
if reqData.Value <= 0 {
|
|
|
|
|
|
return nil, errors.New("满减券优惠金额必须大于0")
|
|
|
|
|
|
}
|
|
|
|
|
|
case 2: // 折扣券,value是折扣(85表示8.5折)
|
|
|
|
|
|
if reqData.Value <= 0 || reqData.Value > 100 {
|
|
|
|
|
|
return nil, errors.New("折扣券折扣必须在0-100之间")
|
|
|
|
|
|
}
|
|
|
|
|
|
case 3: // 免邮券
|
|
|
|
|
|
// 免邮券不需要验证value
|
|
|
|
|
|
default:
|
|
|
|
|
|
return nil, errors.New("不支持的优惠券类型")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
coupon := &model.Coupon{
|
|
|
|
|
|
Name: reqData.Name,
|
|
|
|
|
|
Type: uint8(reqData.Type),
|
|
|
|
|
|
Value: uint(reqData.Value),
|
|
|
|
|
|
MinAmount: uint(reqData.MinAmount),
|
|
|
|
|
|
Description: reqData.Description,
|
|
|
|
|
|
StartTime: reqData.StartTime,
|
|
|
|
|
|
EndTime: reqData.EndTime,
|
|
|
|
|
|
TotalCount: uint(reqData.TotalCount),
|
|
|
|
|
|
UsedCount: 0,
|
|
|
|
|
|
Status: uint8(reqData.Status),
|
|
|
|
|
|
CreatedAt: time.Now(),
|
|
|
|
|
|
UpdatedAt: time.Now(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err := s.couponRepo.Create(coupon)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("创建优惠券失败: %v", err)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return coupon, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateCoupon 更新优惠券
|
|
|
|
|
|
func (s *CouponService) UpdateCoupon(couponID uint, req interface{}) error {
|
|
|
|
|
|
// 检查优惠券是否存在
|
|
|
|
|
|
_, err := s.couponRepo.GetByID(couponID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.New("优惠券不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 类型断言
|
|
|
|
|
|
type UpdateCouponRequest struct {
|
|
|
|
|
|
Name string
|
|
|
|
|
|
Type int
|
|
|
|
|
|
Value int64
|
|
|
|
|
|
MinAmount int64
|
|
|
|
|
|
Description string
|
|
|
|
|
|
StartTime time.Time
|
|
|
|
|
|
EndTime time.Time
|
|
|
|
|
|
TotalCount int
|
|
|
|
|
|
Status int
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
reqData, ok := req.(*UpdateCouponRequest)
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
return errors.New("无效的请求参数")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证时间
|
|
|
|
|
|
if !reqData.EndTime.IsZero() && !reqData.StartTime.IsZero() {
|
|
|
|
|
|
if reqData.EndTime.Before(reqData.StartTime) {
|
|
|
|
|
|
return errors.New("结束时间不能早于开始时间")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
updates := make(map[string]interface{})
|
|
|
|
|
|
if reqData.Name != "" {
|
|
|
|
|
|
updates["name"] = reqData.Name
|
|
|
|
|
|
}
|
|
|
|
|
|
if reqData.Type > 0 {
|
|
|
|
|
|
updates["type"] = reqData.Type
|
|
|
|
|
|
}
|
|
|
|
|
|
if reqData.Value > 0 {
|
|
|
|
|
|
updates["value"] = reqData.Value
|
|
|
|
|
|
}
|
|
|
|
|
|
if reqData.MinAmount >= 0 {
|
|
|
|
|
|
updates["min_amount"] = reqData.MinAmount
|
|
|
|
|
|
}
|
|
|
|
|
|
if reqData.Description != "" {
|
|
|
|
|
|
updates["description"] = reqData.Description
|
|
|
|
|
|
}
|
|
|
|
|
|
if !reqData.StartTime.IsZero() {
|
|
|
|
|
|
updates["start_time"] = reqData.StartTime
|
|
|
|
|
|
}
|
|
|
|
|
|
if !reqData.EndTime.IsZero() {
|
|
|
|
|
|
updates["end_time"] = reqData.EndTime
|
|
|
|
|
|
}
|
|
|
|
|
|
if reqData.TotalCount >= 0 {
|
|
|
|
|
|
updates["total_count"] = reqData.TotalCount
|
|
|
|
|
|
}
|
|
|
|
|
|
if reqData.Status >= 0 {
|
|
|
|
|
|
updates["status"] = reqData.Status
|
|
|
|
|
|
}
|
|
|
|
|
|
updates["updated_at"] = time.Now()
|
|
|
|
|
|
|
|
|
|
|
|
return s.couponRepo.Update(couponID, updates)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DeleteCoupon 删除优惠券
|
|
|
|
|
|
func (s *CouponService) DeleteCoupon(couponID uint) error {
|
|
|
|
|
|
// 检查是否有用户已领取
|
|
|
|
|
|
hasUsers, err := s.couponRepo.CheckCouponHasUsers(couponID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
if hasUsers {
|
|
|
|
|
|
return errors.New("该优惠券已被用户领取,无法删除")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return s.couponRepo.Delete(couponID)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// UpdateCouponStatus 更新优惠券状态
|
|
|
|
|
|
func (s *CouponService) UpdateCouponStatus(couponID uint, status int) error {
|
|
|
|
|
|
_, err := s.couponRepo.GetByID(couponID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return errors.New("优惠券不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
updates := map[string]interface{}{
|
|
|
|
|
|
"status": status,
|
|
|
|
|
|
"updated_at": time.Now(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return s.couponRepo.Update(couponID, updates)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// BatchDeleteCoupons 批量删除优惠券
|
|
|
|
|
|
func (s *CouponService) BatchDeleteCoupons(couponIDs []uint) error {
|
|
|
|
|
|
// 检查每个优惠券是否可以删除
|
|
|
|
|
|
for _, id := range couponIDs {
|
|
|
|
|
|
hasUsers, err := s.couponRepo.CheckCouponHasUsers(id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
if hasUsers {
|
|
|
|
|
|
return fmt.Errorf("优惠券ID %d 已被用户领取,无法删除", id)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return s.couponRepo.BatchDelete(couponIDs)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetCouponStatistics 获取优惠券统计
|
|
|
|
|
|
func (s *CouponService) GetCouponStatistics(startTime, endTime time.Time) (map[string]interface{}, error) {
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
|
|
// 获取总优惠券数
|
|
|
|
|
|
totalCoupons, err := s.couponRepo.CountTotalCoupons(ctx)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取启用的优惠券数
|
|
|
|
|
|
activeCoupons, err := s.couponRepo.CountActiveCoupons(ctx)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取总领取数
|
|
|
|
|
|
totalReceived, err := s.couponRepo.CountTotalReceived(ctx, startTime, endTime)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取总使用数
|
|
|
|
|
|
totalUsed, err := s.couponRepo.CountTotalUsed(ctx, startTime, endTime)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取各类型优惠券统计
|
|
|
|
|
|
typeStats, err := s.couponRepo.GetCouponTypeStats(ctx)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取热门优惠券
|
|
|
|
|
|
topCoupons, err := s.couponRepo.GetTopCoupons(ctx, 10)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算使用率
|
|
|
|
|
|
useRate := 0.0
|
|
|
|
|
|
if totalReceived > 0 {
|
|
|
|
|
|
useRate = float64(totalUsed) / float64(totalReceived) * 100
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
stats := map[string]interface{}{
|
|
|
|
|
|
"total_coupons": totalCoupons,
|
|
|
|
|
|
"active_coupons": activeCoupons,
|
|
|
|
|
|
"total_received": totalReceived,
|
|
|
|
|
|
"total_used": totalUsed,
|
|
|
|
|
|
"use_rate": fmt.Sprintf("%.2f%%", useRate),
|
|
|
|
|
|
"type_stats": typeStats,
|
|
|
|
|
|
"top_coupons": topCoupons,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return stats, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetUserCouponListForAdmin 获取用户优惠券列表(管理端)
|
|
|
|
|
|
func (s *CouponService) GetUserCouponListForAdmin(userID uint, page, pageSize int) ([]model.UserCoupon, int64, error) {
|
|
|
|
|
|
return s.couponRepo.GetUserCouponListForAdmin(userID, page, pageSize)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// DistributeCoupon 发放优惠券
|
|
|
|
|
|
func (s *CouponService) DistributeCoupon(couponID uint, userIDs []uint, distributeAll bool, quantity int, adminID uint) (map[string]interface{}, error) {
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
|
|
|
|
// 验证优惠券是否存在
|
|
|
|
|
|
coupon, err := s.couponRepo.GetByID(couponID)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, errors.New("优惠券不存在")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查优惠券状态
|
|
|
|
|
|
if coupon.Status != 1 {
|
|
|
|
|
|
return nil, errors.New("优惠券已禁用,无法发放")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果是全员发放,获取所有用户ID
|
|
|
|
|
|
var targetUserIDs []uint
|
|
|
|
|
|
distributeType := "single"
|
|
|
|
|
|
if distributeAll {
|
|
|
|
|
|
distributeType = "all"
|
|
|
|
|
|
// TODO: 从用户表获取所有用户ID
|
|
|
|
|
|
// targetUserIDs, err = s.userRepo.GetAllUserIDs()
|
|
|
|
|
|
// 暂时返回错误,需要注入 userRepo
|
|
|
|
|
|
return nil, errors.New("全员发放功能暂未实现")
|
|
|
|
|
|
} else {
|
|
|
|
|
|
targetUserIDs = userIDs
|
|
|
|
|
|
if len(targetUserIDs) > 1 {
|
|
|
|
|
|
distributeType = "batch"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 记录发放结果
|
|
|
|
|
|
successCount := 0
|
|
|
|
|
|
failCount := 0
|
|
|
|
|
|
|
|
|
|
|
|
// 给每个用户发放优惠券
|
|
|
|
|
|
for _, userID := range targetUserIDs {
|
|
|
|
|
|
for i := 0; i < quantity; i++ {
|
|
|
|
|
|
userCoupon := &model.UserCoupon{
|
|
|
|
|
|
UserID: userID,
|
|
|
|
|
|
CouponID: couponID,
|
|
|
|
|
|
Status: 0, // 未使用
|
|
|
|
|
|
CreatedAt: time.Now(),
|
|
|
|
|
|
UpdatedAt: time.Now(),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
err := s.couponRepo.CreateUserCoupon(userCoupon)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
failCount++
|
|
|
|
|
|
} else {
|
|
|
|
|
|
successCount++
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: 记录发放历史到数据库
|
|
|
|
|
|
// 需要创建 coupon_distribute_history 表
|
|
|
|
|
|
_ = ctx
|
|
|
|
|
|
_ = adminID
|
|
|
|
|
|
|
|
|
|
|
|
result := map[string]interface{}{
|
|
|
|
|
|
"total_count": len(targetUserIDs) * quantity,
|
|
|
|
|
|
"success_count": successCount,
|
|
|
|
|
|
"fail_count": failCount,
|
|
|
|
|
|
"distribute_type": distributeType,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetDistributeHistory 获取发放历史
|
|
|
|
|
|
func (s *CouponService) GetDistributeHistory(page, pageSize int) ([]map[string]interface{}, int64, error) {
|
|
|
|
|
|
return s.couponRepo.GetDistributeHistory(page, pageSize)
|
|
|
|
|
|
}
|