This commit is contained in:
sjk
2025-11-28 15:18:10 +08:00
parent ad4a600af9
commit 5683f35942
188 changed files with 53680 additions and 1062 deletions

View File

@@ -1,6 +1,7 @@
package service
import (
"context"
"dianshang/internal/model"
"dianshang/internal/repository"
"errors"
@@ -224,3 +225,338 @@ func (s *CouponService) GetAvailableCouponsForOrder(userID uint, orderAmount flo
return availableCoupons, nil
}
// ==================== 管理端方法 ====================
// 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)
}