Files
ai_dianshang/server/internal/handler/admin_coupon.go
2025-11-28 15:18:10 +08:00

467 lines
15 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"dianshang/internal/service"
"dianshang/pkg/logger"
"dianshang/pkg/response"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
type AdminCouponHandler struct {
couponService *service.CouponService
}
func NewAdminCouponHandler(couponService *service.CouponService) *AdminCouponHandler {
return &AdminCouponHandler{
couponService: couponService,
}
}
// GetCouponList 获取优惠券列表
// @Summary 获取优惠券列表
// @Description 管理员获取所有优惠券(分页)
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param status query int false "状态筛选1-启用 0-禁用)"
// @Param type query int false "类型筛选1-满减 2-折扣 3-免邮)"
// @Param keyword query string false "搜索关键词"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons [get]
func (h *AdminCouponHandler) GetCouponList(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
status := c.Query("status")
couponType := c.Query("type")
keyword := c.Query("keyword")
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 10
}
coupons, total, err := h.couponService.GetCouponListForAdmin(page, pageSize, status, couponType, keyword)
if err != nil {
logger.Error("获取优惠券列表失败", "error", err)
response.ErrorWithMessage(c, response.ERROR, "获取优惠券列表失败")
return
}
response.Page(c, coupons, total, page, pageSize)
}
// GetCouponDetail 获取优惠券详情
// @Summary 获取优惠券详情
// @Description 获取指定优惠券的详细信息
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param id path int true "优惠券ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons/{id} [get]
func (h *AdminCouponHandler) GetCouponDetail(c *gin.Context) {
couponIDStr := c.Param("id")
couponID, err := strconv.ParseUint(couponIDStr, 10, 32)
if err != nil {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "无效的优惠券ID")
return
}
coupon, err := h.couponService.GetCouponDetailForAdmin(uint(couponID))
if err != nil {
logger.Error("获取优惠券详情失败", "error", err, "couponID", couponID)
response.ErrorWithMessage(c, response.ERROR, err.Error())
return
}
response.Success(c, coupon)
}
// CreateCoupon 创建优惠券
// @Summary 创建优惠券
// @Description 创建新的优惠券
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param request body CreateCouponRequest true "创建优惠券请求"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons [post]
func (h *AdminCouponHandler) CreateCoupon(c *gin.Context) {
var req CreateCouponRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("绑定创建优惠券参数失败", "error", err)
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "参数错误: "+err.Error())
return
}
// 获取管理员ID
adminID, exists := c.Get("user_id")
if !exists {
response.Error(c, response.ERROR_UNAUTHORIZED)
return
}
coupon, err := h.couponService.CreateCoupon(&req, adminID.(uint))
if err != nil {
logger.Error("创建优惠券失败", "error", err)
response.ErrorWithMessage(c, response.ERROR, err.Error())
return
}
logger.Info("优惠券创建成功", "couponID", coupon.ID, "name", coupon.Name, "adminID", adminID)
response.Success(c, coupon)
}
// UpdateCoupon 更新优惠券
// @Summary 更新优惠券
// @Description 更新优惠券信息
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param id path int true "优惠券ID"
// @Param request body UpdateCouponRequest true "更新优惠券请求"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons/{id} [put]
func (h *AdminCouponHandler) UpdateCoupon(c *gin.Context) {
couponIDStr := c.Param("id")
couponID, err := strconv.ParseUint(couponIDStr, 10, 32)
if err != nil {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "无效的优惠券ID")
return
}
var req UpdateCouponRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("绑定更新优惠券参数失败", "error", err)
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "参数错误: "+err.Error())
return
}
err = h.couponService.UpdateCoupon(uint(couponID), &req)
if err != nil {
logger.Error("更新优惠券失败", "error", err, "couponID", couponID)
response.ErrorWithMessage(c, response.ERROR, err.Error())
return
}
logger.Info("优惠券更新成功", "couponID", couponID)
response.Success(c, "更新成功")
}
// DeleteCoupon 删除优惠券
// @Summary 删除优惠券
// @Description 删除指定优惠券
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param id path int true "优惠券ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons/{id} [delete]
func (h *AdminCouponHandler) DeleteCoupon(c *gin.Context) {
couponIDStr := c.Param("id")
couponID, err := strconv.ParseUint(couponIDStr, 10, 32)
if err != nil {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "无效的优惠券ID")
return
}
err = h.couponService.DeleteCoupon(uint(couponID))
if err != nil {
logger.Error("删除优惠券失败", "error", err, "couponID", couponID)
response.ErrorWithMessage(c, response.ERROR, err.Error())
return
}
logger.Info("优惠券删除成功", "couponID", couponID)
response.Success(c, "删除成功")
}
// UpdateCouponStatus 更新优惠券状态
// @Summary 更新优惠券状态
// @Description 启用或禁用优惠券
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param id path int true "优惠券ID"
// @Param request body UpdateStatusRequest true "状态更新请求"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons/{id}/status [put]
func (h *AdminCouponHandler) UpdateCouponStatus(c *gin.Context) {
couponIDStr := c.Param("id")
couponID, err := strconv.ParseUint(couponIDStr, 10, 32)
if err != nil {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "无效的优惠券ID")
return
}
var req UpdateStatusRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("绑定状态更新参数失败", "error", err)
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "参数错误: "+err.Error())
return
}
err = h.couponService.UpdateCouponStatus(uint(couponID), req.Status)
if err != nil {
logger.Error("更新优惠券状态失败", "error", err, "couponID", couponID)
response.ErrorWithMessage(c, response.ERROR, err.Error())
return
}
logger.Info("优惠券状态更新成功", "couponID", couponID, "status", req.Status)
response.Success(c, "状态更新成功")
}
// BatchDeleteCoupons 批量删除优惠券
// @Summary 批量删除优惠券
// @Description 批量删除多个优惠券
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param request body BatchDeleteRequest true "批量删除请求"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons/batch [delete]
func (h *AdminCouponHandler) BatchDeleteCoupons(c *gin.Context) {
var req BatchDeleteRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("绑定批量删除参数失败", "error", err)
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "参数错误: "+err.Error())
return
}
if len(req.IDs) == 0 {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "请选择要删除的优惠券")
return
}
err := h.couponService.BatchDeleteCoupons(req.IDs)
if err != nil {
logger.Error("批量删除优惠券失败", "error", err)
response.ErrorWithMessage(c, response.ERROR, err.Error())
return
}
logger.Info("批量删除优惠券成功", "count", len(req.IDs))
response.Success(c, "批量删除成功")
}
// GetCouponStatistics 获取优惠券统计
// @Summary 获取优惠券统计
// @Description 获取优惠券使用统计数据
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param start_date query string false "开始日期"
// @Param end_date query string false "结束日期"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons/statistics [get]
func (h *AdminCouponHandler) GetCouponStatistics(c *gin.Context) {
startDate := c.Query("start_date")
endDate := c.Query("end_date")
// 如果没有提供日期默认查询最近30天
if startDate == "" || endDate == "" {
now := time.Now()
endDate = now.Format("2006-01-02")
startDate = now.AddDate(0, 0, -30).Format("2006-01-02")
}
// 解析日期
startTime, err := time.Parse("2006-01-02", startDate)
if err != nil {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "开始日期格式错误")
return
}
endTime, err := time.Parse("2006-01-02", endDate)
if err != nil {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "结束日期格式错误")
return
}
endTime = endTime.Add(23*time.Hour + 59*time.Minute + 59*time.Second)
stats, err := h.couponService.GetCouponStatistics(startTime, endTime)
if err != nil {
logger.Error("获取优惠券统计失败", "error", err)
response.ErrorWithMessage(c, response.ERROR, "获取优惠券统计失败")
return
}
response.Success(c, stats)
}
// GetUserCouponList 获取用户优惠券列表
// @Summary 获取用户优惠券列表
// @Description 查看指定用户的优惠券领取记录
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param user_id query int true "用户ID"
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons/user-coupons [get]
func (h *AdminCouponHandler) GetUserCouponList(c *gin.Context) {
userIDStr := c.Query("user_id")
if userIDStr == "" {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "用户ID不能为空")
return
}
userID, err := strconv.ParseUint(userIDStr, 10, 32)
if err != nil {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "无效的用户ID")
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 10
}
coupons, total, err := h.couponService.GetUserCouponListForAdmin(uint(userID), page, pageSize)
if err != nil {
logger.Error("获取用户优惠券列表失败", "error", err)
response.ErrorWithMessage(c, response.ERROR, "获取用户优惠券列表失败")
return
}
response.Page(c, coupons, total, page, pageSize)
}
// DistributeCoupon 发放优惠券
// @Summary 发放优惠券
// @Description 给用户发放优惠券(单个/批量/全员)
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param request body DistributeCouponRequest true "发放请求"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons/distribute [post]
func (h *AdminCouponHandler) DistributeCoupon(c *gin.Context) {
var req DistributeCouponRequest
if err := c.ShouldBindJSON(&req); err != nil {
logger.Error("绑定发放优惠券参数失败", "error", err)
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "参数错误: "+err.Error())
return
}
// 验证参数
if !req.DistributeAll && len(req.UserIDs) == 0 {
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "请选择用户或全员发放")
return
}
// 获取管理员ID
adminID, exists := c.Get("user_id")
if !exists {
response.Error(c, response.ERROR_UNAUTHORIZED)
return
}
// 调用服务层发放优惠券
result, err := h.couponService.DistributeCoupon(req.CouponID, req.UserIDs, req.DistributeAll, req.Quantity, adminID.(uint))
if err != nil {
logger.Error("发放优惠券失败", "error", err)
response.ErrorWithMessage(c, response.ERROR, err.Error())
return
}
logger.Info("优惠券发放成功", "couponID", req.CouponID, "totalCount", result["total_count"], "successCount", result["success_count"], "adminID", adminID)
response.Success(c, result)
}
// GetDistributeHistory 获取发放历史
// @Summary 获取发放历史
// @Description 获取优惠券发放记录
// @Tags 管理员-优惠券管理
// @Accept json
// @Produce json
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Router /admin/coupons/distribute/history [get]
func (h *AdminCouponHandler) GetDistributeHistory(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
if page < 1 {
page = 1
}
if pageSize < 1 || pageSize > 100 {
pageSize = 10
}
history, total, err := h.couponService.GetDistributeHistory(page, pageSize)
if err != nil {
logger.Error("获取发放历史失败", "error", err)
response.ErrorWithMessage(c, response.ERROR, "获取发放历史失败")
return
}
response.Page(c, history, total, page, pageSize)
}
// 请求结构体
type CreateCouponRequest struct {
Name string `json:"name" binding:"required,max=100"`
Type int `json:"type" binding:"required,oneof=1 2 3"`
Value int64 `json:"value" binding:"required,min=1"`
MinAmount int64 `json:"min_amount" binding:"min=0"`
Description string `json:"description" binding:"max=500"`
StartTime time.Time `json:"start_time" binding:"required"`
EndTime time.Time `json:"end_time" binding:"required"`
TotalCount int `json:"total_count" binding:"min=0"`
Status int `json:"status" binding:"oneof=0 1"`
}
type UpdateCouponRequest struct {
Name string `json:"name" binding:"max=100"`
Type int `json:"type" binding:"oneof=1 2 3"`
Value int64 `json:"value" binding:"min=1"`
MinAmount int64 `json:"min_amount" binding:"min=0"`
Description string `json:"description" binding:"max=500"`
StartTime time.Time `json:"start_time"`
EndTime time.Time `json:"end_time"`
TotalCount int `json:"total_count" binding:"min=0"`
Status int `json:"status" binding:"oneof=0 1"`
}
type UpdateStatusRequest struct {
Status int `json:"status" binding:"required,oneof=0 1"`
}
type BatchDeleteRequest struct {
IDs []uint `json:"ids" binding:"required,min=1"`
}
type DistributeCouponRequest struct {
CouponID uint `json:"coupon_id" binding:"required"`
UserIDs []uint `json:"user_ids"`
DistributeAll bool `json:"distribute_all"`
Quantity int `json:"quantity" binding:"required,min=1,max=100"`
}