web
This commit is contained in:
466
server/internal/handler/admin_coupon.go
Normal file
466
server/internal/handler/admin_coupon.go
Normal file
@@ -0,0 +1,466 @@
|
||||
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"`
|
||||
}
|
||||
@@ -109,6 +109,25 @@ func (h *AdminProductHandler) UpdateProduct(c *gin.Context) {
|
||||
response.BadRequest(c, "请求参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 处理 category_id 字段:转换为 JSONUintSlice 类型
|
||||
if categoryIDRaw, ok := updates["category_id"]; ok {
|
||||
switch v := categoryIDRaw.(type) {
|
||||
case []interface{}:
|
||||
var categoryIDs []uint
|
||||
for _, item := range v {
|
||||
switch id := item.(type) {
|
||||
case float64:
|
||||
categoryIDs = append(categoryIDs, uint(id))
|
||||
case int:
|
||||
categoryIDs = append(categoryIDs, uint(id))
|
||||
}
|
||||
}
|
||||
updates["category_id"] = model.JSONUintSlice(categoryIDs)
|
||||
case []uint:
|
||||
updates["category_id"] = model.JSONUintSlice(v)
|
||||
}
|
||||
}
|
||||
|
||||
// 商品更新时间会自动设置
|
||||
|
||||
@@ -212,6 +231,7 @@ func (h *AdminProductHandler) CreateCategory(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 创建分类
|
||||
if err := h.productService.CreateCategory(&category); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
@@ -235,6 +255,40 @@ func (h *AdminProductHandler) UpdateCategory(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 处理 platform 字段:转换为 JSONSlice 类型
|
||||
if platformRaw, ok := updates["platform"]; ok {
|
||||
switch v := platformRaw.(type) {
|
||||
case []interface{}:
|
||||
// 前端传来的是数组
|
||||
var platforms []string
|
||||
for _, item := range v {
|
||||
if str, ok := item.(string); ok {
|
||||
platforms = append(platforms, str)
|
||||
}
|
||||
}
|
||||
updates["platform"] = model.JSONSlice(platforms)
|
||||
case string:
|
||||
// 前端传来的是单个字符串,转为数组
|
||||
updates["platform"] = model.JSONSlice([]string{v})
|
||||
default:
|
||||
// 其他类型,删除该字段
|
||||
delete(updates, "platform")
|
||||
}
|
||||
}
|
||||
|
||||
// 删除只读字段,避免更新时出错
|
||||
readonlyFields := []string{"id", "created_at", "updated_at", "children", "hasChildren", "level"}
|
||||
for _, field := range readonlyFields {
|
||||
delete(updates, field)
|
||||
}
|
||||
|
||||
// 删除不存在的字段
|
||||
nonExistFields := []string{"is_show", "keywords"}
|
||||
for _, field := range nonExistFields {
|
||||
delete(updates, field)
|
||||
}
|
||||
|
||||
// 更新分类基本信息
|
||||
if err := h.productService.UpdateCategory(uint(id), updates); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
|
||||
@@ -36,9 +36,16 @@ func (h *CartHandler) GetCart(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 计算购物车统计信息
|
||||
count, _ := h.cartService.GetCartCount(userID.(uint))
|
||||
total, _ := h.cartService.GetCartTotal(userID.(uint))
|
||||
// 优化: 在一次查询结果基础上计算统计信息,避免重复查询
|
||||
var count int
|
||||
var total float64
|
||||
for _, item := range cart {
|
||||
count += item.Quantity
|
||||
if item.Product.ID != 0 {
|
||||
// 将价格从分转换为元
|
||||
total += (float64(item.Product.Price) / 100) * float64(item.Quantity)
|
||||
}
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"items": cart,
|
||||
|
||||
@@ -137,6 +137,22 @@ func (h *CommentHandler) GetCommentStats(c *gin.Context) {
|
||||
response.Success(c, stats)
|
||||
}
|
||||
|
||||
// GetHighRatingComments 获取高分评论(用于首页展示)
|
||||
func (h *CommentHandler) GetHighRatingComments(c *gin.Context) {
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "6"))
|
||||
|
||||
comments, err := h.commentService.GetHighRatingComments(limit)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
result := h.convertToResponseList(comments)
|
||||
|
||||
response.Success(c, result)
|
||||
}
|
||||
|
||||
// GetCommentDetail 获取评论详情
|
||||
func (h *CommentHandler) GetCommentDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
|
||||
@@ -57,14 +57,14 @@ func (h *FrontendHandler) GetHomeData(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 获取推荐商品
|
||||
recommendProducts, _, err := h.productService.GetProductList(1, 10, 0, "", 0, 0, "default", "desc")
|
||||
recommendProducts, _, err := h.productService.GetProductList(1, 10, 0, "", 0, 0, nil, "default", "desc")
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, "获取推荐商品失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 获取热门商品
|
||||
hotProducts, _, err := h.productService.GetProductList(1, 10, 0, "", 0, 0, "sales", "desc")
|
||||
hotProducts, _, err := h.productService.GetProductList(1, 10, 0, "", 0, 0, nil, "sales", "desc")
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, "获取热门商品失败: "+err.Error())
|
||||
return
|
||||
@@ -91,7 +91,7 @@ func (h *FrontendHandler) GetProductsRecommend(c *gin.Context) {
|
||||
page := utils.StringToInt(c.DefaultQuery("page", "1"))
|
||||
pageSize := utils.StringToInt(c.DefaultQuery("page_size", "10"))
|
||||
|
||||
products, pagination, err := h.productService.GetProductList(page, pageSize, 0, "", 0, 0, "default", "desc")
|
||||
products, pagination, err := h.productService.GetProductList(page, pageSize, 0, "", 0, 0, nil, "default", "desc")
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
@@ -106,7 +106,7 @@ func (h *FrontendHandler) GetProductsHot(c *gin.Context) {
|
||||
page := utils.StringToInt(c.DefaultQuery("page", "1"))
|
||||
pageSize := utils.StringToInt(c.DefaultQuery("page_size", "10"))
|
||||
|
||||
products, pagination, err := h.productService.GetProductList(page, pageSize, 0, "", 0, 0, "sales", "desc")
|
||||
products, pagination, err := h.productService.GetProductList(page, pageSize, 0, "", 0, 0, nil, "sales", "desc")
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
@@ -474,7 +474,13 @@ func (h *FrontendHandler) convertProductToFrontend(product *model.Product) model
|
||||
SpuStockQuantity: product.Stock,
|
||||
SoldNum: product.Sales,
|
||||
IsPutOnSale: 1,
|
||||
CategoryIds: []string{strconv.Itoa(int(product.CategoryID))},
|
||||
CategoryIds: func() []string {
|
||||
ids := make([]string, len(product.CategoryID))
|
||||
for i, id := range product.CategoryID {
|
||||
ids[i] = strconv.Itoa(int(id))
|
||||
}
|
||||
return ids
|
||||
}(),
|
||||
SpecList: specList,
|
||||
SkuList: skuList,
|
||||
SpuTagList: spuTagList,
|
||||
|
||||
243
server/internal/handler/livestream.go
Normal file
243
server/internal/handler/livestream.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"dianshang/internal/model"
|
||||
"dianshang/internal/service"
|
||||
"dianshang/pkg/response"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type LiveStreamHandler struct {
|
||||
liveStreamService service.LiveStreamService
|
||||
}
|
||||
|
||||
func NewLiveStreamHandler(liveStreamService service.LiveStreamService) *LiveStreamHandler {
|
||||
return &LiveStreamHandler{
|
||||
liveStreamService: liveStreamService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetLiveStreamList 获取投流源列表
|
||||
func (h *LiveStreamHandler) GetLiveStreamList(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
title := c.Query("title")
|
||||
platform := c.Query("platform")
|
||||
|
||||
var status *int
|
||||
if statusStr := c.Query("status"); statusStr != "" {
|
||||
statusVal, err := strconv.Atoi(statusStr)
|
||||
if err == nil {
|
||||
status = &statusVal
|
||||
}
|
||||
}
|
||||
|
||||
streams, total, err := h.liveStreamService.GetLiveStreamList(page, pageSize, title, platform, status)
|
||||
if err != nil {
|
||||
response.Error(c, response.ERROR)
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"list": streams,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"size": pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
// GetLiveStreamDetail 获取投流源详情
|
||||
func (h *LiveStreamHandler) GetLiveStreamDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "投流源ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
stream, err := h.liveStreamService.GetLiveStreamByID(uint(id))
|
||||
if err != nil {
|
||||
response.Error(c, response.ERROR)
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, stream)
|
||||
}
|
||||
|
||||
// GetActiveLiveStreams 获取启用的投流源(前台接口)
|
||||
func (h *LiveStreamHandler) GetActiveLiveStreams(c *gin.Context) {
|
||||
streams, err := h.liveStreamService.GetActiveLiveStreams()
|
||||
if err != nil {
|
||||
response.Error(c, response.ERROR)
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, streams)
|
||||
}
|
||||
|
||||
// CreateLiveStream 创建投流源
|
||||
func (h *LiveStreamHandler) CreateLiveStream(c *gin.Context) {
|
||||
var req struct {
|
||||
Title string `json:"title" binding:"required"`
|
||||
Platform string `json:"platform" binding:"required"`
|
||||
StreamURL string `json:"stream_url" binding:"required"`
|
||||
CoverImage string `json:"cover_image"`
|
||||
Description string `json:"description"`
|
||||
Status int `json:"status"`
|
||||
Sort int `json:"sort"`
|
||||
StartTime *time.Time `json:"start_time"`
|
||||
EndTime *time.Time `json:"end_time"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
stream := &model.LiveStream{
|
||||
Title: req.Title,
|
||||
Platform: req.Platform,
|
||||
StreamURL: req.StreamURL,
|
||||
CoverImage: req.CoverImage,
|
||||
Description: req.Description,
|
||||
Status: req.Status,
|
||||
Sort: req.Sort,
|
||||
StartTime: req.StartTime,
|
||||
EndTime: req.EndTime,
|
||||
}
|
||||
|
||||
if err := h.liveStreamService.CreateLiveStream(stream); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, stream)
|
||||
}
|
||||
|
||||
// UpdateLiveStream 更新投流源
|
||||
func (h *LiveStreamHandler) UpdateLiveStream(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "投流源ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Title string `json:"title"`
|
||||
Platform string `json:"platform"`
|
||||
StreamURL string `json:"stream_url"`
|
||||
CoverImage string `json:"cover_image"`
|
||||
Description string `json:"description"`
|
||||
Status int `json:"status"`
|
||||
Sort int `json:"sort"`
|
||||
StartTime *time.Time `json:"start_time"`
|
||||
EndTime *time.Time `json:"end_time"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
stream := &model.LiveStream{
|
||||
Title: req.Title,
|
||||
Platform: req.Platform,
|
||||
StreamURL: req.StreamURL,
|
||||
CoverImage: req.CoverImage,
|
||||
Description: req.Description,
|
||||
Status: req.Status,
|
||||
Sort: req.Sort,
|
||||
StartTime: req.StartTime,
|
||||
EndTime: req.EndTime,
|
||||
}
|
||||
|
||||
if err := h.liveStreamService.UpdateLiveStream(uint(id), stream); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, stream)
|
||||
}
|
||||
|
||||
// UpdateLiveStreamStatus 更新投流源状态
|
||||
func (h *LiveStreamHandler) UpdateLiveStreamStatus(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "投流源ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Status int `json:"status" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.liveStreamService.UpdateLiveStreamStatus(uint(id), req.Status); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// DeleteLiveStream 删除投流源
|
||||
func (h *LiveStreamHandler) DeleteLiveStream(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "投流源ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.liveStreamService.DeleteLiveStream(uint(id)); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// BatchDeleteLiveStreams 批量删除投流源
|
||||
func (h *LiveStreamHandler) BatchDeleteLiveStreams(c *gin.Context) {
|
||||
var req struct {
|
||||
IDs []uint `json:"ids" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.liveStreamService.BatchDeleteLiveStreams(req.IDs); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// IncrementViewCount 增加观看次数
|
||||
func (h *LiveStreamHandler) IncrementViewCount(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "投流源ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.liveStreamService.IncrementViewCount(uint(id)); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, nil)
|
||||
}
|
||||
@@ -11,19 +11,21 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// OrderHandler 璁㈠崟澶勭悊鍣?
|
||||
// OrderHandler 订单处理器
|
||||
type OrderHandler struct {
|
||||
orderService *service.OrderService
|
||||
orderService *service.OrderService
|
||||
wechatPayService *service.WeChatPayService
|
||||
}
|
||||
|
||||
// NewOrderHandler 鍒涘缓璁㈠崟澶勭悊鍣?
|
||||
func NewOrderHandler(orderService *service.OrderService) *OrderHandler {
|
||||
// NewOrderHandler 创建订单处理器
|
||||
func NewOrderHandler(orderService *service.OrderService, wechatPayService *service.WeChatPayService) *OrderHandler {
|
||||
return &OrderHandler{
|
||||
orderService: orderService,
|
||||
orderService: orderService,
|
||||
wechatPayService: wechatPayService,
|
||||
}
|
||||
}
|
||||
|
||||
// CreateOrder 鍒涘缓璁㈠崟
|
||||
// CreateOrder 创建订单
|
||||
func (h *OrderHandler) CreateOrder(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
@@ -50,7 +52,7 @@ func (h *OrderHandler) CreateOrder(c *gin.Context) {
|
||||
response.Success(c, order)
|
||||
}
|
||||
|
||||
// GetUserOrders 鑾峰彇鐢ㄦ埛璁㈠崟鍒楄〃
|
||||
// GetUserOrders 获取用户订单列表
|
||||
func (h *OrderHandler) GetUserOrders(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
@@ -422,7 +424,7 @@ func (h *OrderHandler) formatOrderDetail(order *model.Order) *OrderDetailRespons
|
||||
}
|
||||
}
|
||||
|
||||
// PayOrder 鏀粯璁㈠崟
|
||||
// PayOrder 支付订单
|
||||
func (h *OrderHandler) PayOrder(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
@@ -431,13 +433,66 @@ func (h *OrderHandler) PayOrder(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 从URL路径参数获取订单号
|
||||
orderID := c.Param("id")
|
||||
if orderID == "" {
|
||||
orderNo := c.Param("id")
|
||||
if orderNo == "" {
|
||||
response.BadRequest(c, "订单号不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.orderService.PayOrder(userID.(uint), orderID); err != nil {
|
||||
// 解析请求体获取支付方式
|
||||
var req struct {
|
||||
PaymentMethod string `json:"payment_method"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
// 如果没有提供支付方式,默认使用微信支付
|
||||
req.PaymentMethod = "wechat"
|
||||
}
|
||||
|
||||
// 获取订单详情
|
||||
order, err := h.orderService.GetOrderByOrderNo(orderNo)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, "订单不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证订单归属
|
||||
if order.UserID != userID.(uint) {
|
||||
response.ErrorWithMessage(c, response.ERROR, "无权限操作此订单")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证订单状态
|
||||
if order.Status != 1 { // 1 = 待付款
|
||||
response.ErrorWithMessage(c, response.ERROR, "订单状态不允许支付")
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是微信支付,返回支付二维码
|
||||
if req.PaymentMethod == "wechat" {
|
||||
// 调用微信Native扫码支付
|
||||
if h.wechatPayService != nil {
|
||||
paymentResp, err := h.wechatPayService.CreateNativeOrder(c.Request.Context(), order)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, paymentResp.Data)
|
||||
return
|
||||
} else {
|
||||
// 如果没有微信支付服务,返回模拟数据
|
||||
response.Success(c, gin.H{
|
||||
"qrcode_url": "https://api.example.com/qrcode/" + orderNo,
|
||||
"order_no": orderNo,
|
||||
"amount": order.TotalAmount,
|
||||
"sandbox": true,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 其他支付方式,直接标记为已支付
|
||||
if err := h.orderService.PayOrder(userID.(uint), orderNo); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -445,7 +500,7 @@ func (h *OrderHandler) PayOrder(c *gin.Context) {
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// CancelOrder 鍙栨秷璁㈠崟
|
||||
// CancelOrder 取消订单
|
||||
func (h *OrderHandler) CancelOrder(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
@@ -534,7 +589,7 @@ func (h *OrderHandler) RefundOrder(c *gin.Context) {
|
||||
response.Success(c, gin.H{"message": "退款申请已提交"})
|
||||
}
|
||||
|
||||
// ConfirmReceive 纭鏀惰揣纭鏀惰揣
|
||||
// ConfirmReceive 确认收货
|
||||
func (h *OrderHandler) ConfirmReceive(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
@@ -558,7 +613,7 @@ func (h *OrderHandler) ConfirmReceive(c *gin.Context) {
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// GetOrderList 鑾峰彇璁㈠崟鍒楄〃锛堢鐞嗗憳锟?
|
||||
// GetOrderList 获取订单列表(支持条件查询)
|
||||
func (h *OrderHandler) GetOrderList(c *gin.Context) {
|
||||
page := utils.StringToInt(c.DefaultQuery("page", "1"))
|
||||
pageSize := utils.StringToInt(c.DefaultQuery("page_size", "20"))
|
||||
@@ -589,7 +644,7 @@ func (h *OrderHandler) GetOrderList(c *gin.Context) {
|
||||
response.Page(c, orders, pagination.Total, pagination.Page, pagination.PageSize)
|
||||
}
|
||||
|
||||
// ShipOrder 鍙戣揣锛堢鐞嗗憳锟?
|
||||
// ShipOrder 发货
|
||||
func (h *OrderHandler) ShipOrder(c *gin.Context) {
|
||||
var req struct {
|
||||
OrderNo string `json:"order_no" binding:"required"`
|
||||
@@ -609,7 +664,7 @@ func (h *OrderHandler) ShipOrder(c *gin.Context) {
|
||||
response.Success(c, nil)
|
||||
}
|
||||
|
||||
// GetOrderStatistics 鑾峰彇璁㈠崟缁熻
|
||||
// GetOrderStatistics 获取订单统计
|
||||
func (h *OrderHandler) GetOrderStatistics(c *gin.Context) {
|
||||
statistics, err := h.orderService.GetOrderStatistics()
|
||||
if err != nil {
|
||||
@@ -620,7 +675,7 @@ func (h *OrderHandler) GetOrderStatistics(c *gin.Context) {
|
||||
response.Success(c, statistics)
|
||||
}
|
||||
|
||||
// GetDailyOrderStatistics 鑾峰彇姣忔棩璁㈠崟缁熻
|
||||
// GetDailyOrderStatistics 获取每日订单统计
|
||||
func (h *OrderHandler) GetDailyOrderStatistics(c *gin.Context) {
|
||||
days := utils.StringToInt(c.DefaultQuery("days", "30"))
|
||||
|
||||
|
||||
51
server/internal/handler/order_payment_status.go
Normal file
51
server/internal/handler/order_payment_status.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"dianshang/pkg/response"
|
||||
)
|
||||
|
||||
// GetPaymentStatus 获取订单支付状态
|
||||
func (h *OrderHandler) GetPaymentStatus(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Unauthorized(c)
|
||||
return
|
||||
}
|
||||
|
||||
// 从URL路径参数获取订单号
|
||||
orderNo := c.Param("id")
|
||||
if orderNo == "" {
|
||||
response.BadRequest(c, "订单号不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取订单详情
|
||||
order, err := h.orderService.GetOrderByOrderNo(orderNo)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, "订单不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证订单归属
|
||||
if order.UserID != userID.(uint) {
|
||||
response.ErrorWithMessage(c, response.ERROR, "无权限操作此订单")
|
||||
return
|
||||
}
|
||||
|
||||
// 返回支付状态
|
||||
status := "unpaid"
|
||||
if order.Status == 2 || order.Status == 3 || order.Status == 4 || order.Status == 5 || order.Status == 6 {
|
||||
status = "paid"
|
||||
} else if order.Status == 7 {
|
||||
status = "canceled"
|
||||
} else if order.Status == 9 {
|
||||
status = "refunded"
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"status": status,
|
||||
"order_no": order.OrderNo,
|
||||
"order_status": order.Status,
|
||||
})
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package handler
|
||||
import (
|
||||
"dianshang/internal/service"
|
||||
"dianshang/pkg/response"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -169,37 +170,51 @@ func (h *PaymentHandler) CancelPayment(c *gin.Context) {
|
||||
|
||||
// PaymentNotify 支付回调通知
|
||||
func (h *PaymentHandler) PaymentNotify(c *gin.Context) {
|
||||
log.Printf("[=== 微信支付回调 ===] 收到回调请求")
|
||||
log.Printf("[回调请求] 请求方法: %s", c.Request.Method)
|
||||
log.Printf("[回调请求] 请求路径: %s", c.Request.URL.Path)
|
||||
log.Printf("[回调请求] 客户端IP: %s", c.ClientIP())
|
||||
|
||||
// 读取回调数据
|
||||
body, err := c.GetRawData()
|
||||
if err != nil {
|
||||
log.Printf("[回调错误] 读取回调数据失败: %v", err)
|
||||
response.ErrorWithMessage(c, response.ERROR, "读取回调数据失败")
|
||||
return
|
||||
}
|
||||
log.Printf("[回调数据] 数据长度: %d bytes", len(body))
|
||||
|
||||
// 获取请求头
|
||||
headers := make(map[string]string)
|
||||
for key, values := range c.Request.Header {
|
||||
if len(values) > 0 {
|
||||
headers[key] = values[0]
|
||||
log.Printf("[回调请求头] %s: %s", key, values[0])
|
||||
}
|
||||
}
|
||||
|
||||
// 处理微信支付回调
|
||||
log.Printf("[回调处理] 开始验证签名并解析数据...")
|
||||
notify, err := h.wechatPayService.HandleNotify(c.Request.Context(), body, headers)
|
||||
if err != nil {
|
||||
log.Printf("[回调错误] 处理支付回调失败: %v", err)
|
||||
response.ErrorWithMessage(c, response.ERROR, "处理支付回调失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
log.Printf("[回调数据] 事件类型: %s", notify.EventType)
|
||||
|
||||
// 根据回调类型处理
|
||||
if notify.EventType == "TRANSACTION.SUCCESS" {
|
||||
log.Printf("[支付成功] 开始处理支付成功回调...")
|
||||
// 支付成功,更新订单状态
|
||||
err = h.wechatPayService.ProcessPaymentSuccess(c.Request.Context(), notify)
|
||||
if err != nil {
|
||||
log.Printf("[回调错误] 处理支付成功回调失败: %v", err)
|
||||
response.ErrorWithMessage(c, response.ERROR, "处理支付成功回调失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[支付成功] 订单状态更新成功")
|
||||
response.Success(c, gin.H{
|
||||
"code": "SUCCESS",
|
||||
"message": "处理成功",
|
||||
@@ -207,6 +222,7 @@ func (h *PaymentHandler) PaymentNotify(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[回调处理] 非支付成功事件,仅记录")
|
||||
response.Success(c, gin.H{
|
||||
"code": "SUCCESS",
|
||||
"message": "回调已接收",
|
||||
|
||||
139
server/internal/handler/platform.go
Normal file
139
server/internal/handler/platform.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"dianshang/internal/model"
|
||||
"dianshang/internal/service"
|
||||
"dianshang/pkg/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// PlatformHandler 平台处理器
|
||||
type PlatformHandler struct {
|
||||
platformService *service.PlatformService
|
||||
}
|
||||
|
||||
// NewPlatformHandler 创建平台处理器
|
||||
func NewPlatformHandler(platformService *service.PlatformService) *PlatformHandler {
|
||||
return &PlatformHandler{
|
||||
platformService: platformService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetPlatforms 获取平台列表
|
||||
func (h *PlatformHandler) GetPlatforms(c *gin.Context) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
name := c.Query("name")
|
||||
|
||||
var status *int
|
||||
if statusStr := c.Query("status"); statusStr != "" {
|
||||
if s, err := strconv.Atoi(statusStr); err == nil {
|
||||
status = &s
|
||||
}
|
||||
}
|
||||
|
||||
platforms, pagination, err := h.platformService.GetPlatformList(page, pageSize, status, name)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if pagination != nil {
|
||||
response.Page(c, platforms, pagination.Total, pagination.Page, pagination.PageSize)
|
||||
} else {
|
||||
response.Success(c, platforms)
|
||||
}
|
||||
}
|
||||
|
||||
// GetPlatform 获取平台详情
|
||||
func (h *PlatformHandler) GetPlatform(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "无效的平台ID")
|
||||
return
|
||||
}
|
||||
|
||||
platform, err := h.platformService.GetPlatformByID(uint(id))
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, platform)
|
||||
}
|
||||
|
||||
// CreatePlatform 创建平台
|
||||
func (h *PlatformHandler) CreatePlatform(c *gin.Context) {
|
||||
var platform model.Platform
|
||||
if err := c.ShouldBindJSON(&platform); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 验证必填字段
|
||||
if platform.Code == "" {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "平台代码不能为空")
|
||||
return
|
||||
}
|
||||
if platform.Name == "" {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "平台名称不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.platformService.CreatePlatform(&platform); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, platform)
|
||||
}
|
||||
|
||||
// UpdatePlatform 更新平台
|
||||
func (h *PlatformHandler) UpdatePlatform(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "无效的平台ID")
|
||||
return
|
||||
}
|
||||
|
||||
var updates map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.platformService.UpdatePlatform(uint(id), updates); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.SuccessWithMessage(c, "平台更新成功", nil)
|
||||
}
|
||||
|
||||
// DeletePlatform 删除平台
|
||||
func (h *PlatformHandler) DeletePlatform(c *gin.Context) {
|
||||
id, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR_INVALID_PARAMS, "无效的平台ID")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.platformService.DeletePlatform(uint(id)); err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.SuccessWithMessage(c, "平台删除成功", nil)
|
||||
}
|
||||
|
||||
// GetAllActivePlatforms 获取所有启用的平台(用于下拉选择)
|
||||
func (h *PlatformHandler) GetAllActivePlatforms(c *gin.Context) {
|
||||
platforms, err := h.platformService.GetAllActivePlatforms()
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, platforms)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"dianshang/pkg/response"
|
||||
"dianshang/pkg/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -26,11 +27,35 @@ func NewProductHandler(productService *service.ProductService) *ProductHandler {
|
||||
func (h *ProductHandler) GetProductList(c *gin.Context) {
|
||||
page := utils.StringToInt(c.DefaultQuery("page", "1"))
|
||||
pageSize := utils.StringToInt(c.DefaultQuery("page_size", "20"))
|
||||
categoryID := utils.StringToUint(c.Query("category_id"))
|
||||
|
||||
// 支持 category_ids (逗号分隔)或 category_id
|
||||
var categoryID uint
|
||||
if categoryIDsStr := c.Query("category_ids"); categoryIDsStr != "" {
|
||||
// 解析第一个分类ID
|
||||
ids := strings.Split(categoryIDsStr, ",")
|
||||
if len(ids) > 0 {
|
||||
categoryID = utils.StringToUint(strings.TrimSpace(ids[0]))
|
||||
}
|
||||
} else {
|
||||
categoryID = utils.StringToUint(c.Query("category_id"))
|
||||
}
|
||||
|
||||
keyword := c.Query("keyword")
|
||||
minPrice, _ := strconv.ParseFloat(c.Query("min_price"), 64)
|
||||
maxPrice, _ := strconv.ParseFloat(c.Query("max_price"), 64)
|
||||
|
||||
// 库存筛选参数
|
||||
var inStock *bool
|
||||
if inStockStr := c.Query("in_stock"); inStockStr != "" {
|
||||
if inStockStr == "true" {
|
||||
trueVal := true
|
||||
inStock = &trueVal
|
||||
} else if inStockStr == "false" {
|
||||
falseVal := false
|
||||
inStock = &falseVal
|
||||
}
|
||||
}
|
||||
|
||||
// 处理排序参数:将前端传递的数字参数转换为后端期望的字符串参数
|
||||
sortParam := c.Query("sort")
|
||||
sortTypeParam := c.Query("sortType")
|
||||
@@ -52,7 +77,7 @@ func (h *ProductHandler) GetProductList(c *gin.Context) {
|
||||
sortType = "desc"
|
||||
}
|
||||
|
||||
products, pagination, err := h.productService.GetProductList(page, pageSize, categoryID, keyword, minPrice, maxPrice, sort, sortType)
|
||||
products, pagination, err := h.productService.GetProductList(page, pageSize, categoryID, keyword, minPrice, maxPrice, inStock, sort, sortType)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
@@ -137,7 +162,20 @@ func (h *ProductHandler) DeleteProduct(c *gin.Context) {
|
||||
|
||||
// GetCategories 鑾峰彇鍒嗙被鍒楄〃
|
||||
func (h *ProductHandler) GetCategories(c *gin.Context) {
|
||||
categories, err := h.productService.GetCategories()
|
||||
// 支持平台筛选参数:platform=web 或 platform=miniprogram
|
||||
platform := c.Query("platform")
|
||||
|
||||
var categories []model.Category
|
||||
var err error
|
||||
|
||||
if platform != "" {
|
||||
// 根据平台获取分类
|
||||
categories, err = h.productService.GetCategoriesByPlatform(platform)
|
||||
} else {
|
||||
// 获取所有分类
|
||||
categories, err = h.productService.GetCategories()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
|
||||
@@ -36,6 +36,19 @@ type LoginRequest struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
}
|
||||
|
||||
// EmailLoginRequest 邮箱登录请求结构
|
||||
type EmailLoginRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Password string `json:"password" binding:"required,min=6"`
|
||||
}
|
||||
|
||||
// EmailRegisterRequest 邮箱注册请求结构
|
||||
type EmailRegisterRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Password string `json:"password" binding:"required,min=6"`
|
||||
Nickname string `json:"nickname" binding:"required"`
|
||||
}
|
||||
|
||||
// WeChatLoginRequest 微信登录请求结构
|
||||
type WeChatLoginRequest struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
@@ -128,6 +141,56 @@ func (h *UserHandler) WeChatLogin(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// EmailLogin 邮箱登录(Web端使用)
|
||||
func (h *UserHandler) EmailLogin(c *gin.Context) {
|
||||
var req EmailLoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取客户端IP和UserAgent
|
||||
clientIP := utils.GetClientIP(
|
||||
c.ClientIP(),
|
||||
c.GetHeader("X-Forwarded-For"),
|
||||
c.GetHeader("X-Real-IP"),
|
||||
)
|
||||
userAgent := c.Request.UserAgent()
|
||||
|
||||
// 调用用户服务进行邮箱登录
|
||||
user, token, err := h.userService.EmailLogin(req.Email, req.Password, clientIP, userAgent)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"user": user,
|
||||
"token": token,
|
||||
})
|
||||
}
|
||||
|
||||
// EmailRegister 邮箱注册(Web端使用)
|
||||
func (h *UserHandler) EmailRegister(c *gin.Context) {
|
||||
var req EmailRegisterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用用户服务进行注册
|
||||
user, err := h.userService.EmailRegister(req.Email, req.Password, req.Nickname)
|
||||
if err != nil {
|
||||
response.ErrorWithMessage(c, response.ERROR, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"user_id": user.ID,
|
||||
"message": "注册成功",
|
||||
})
|
||||
}
|
||||
|
||||
// GetWeChatSession 获取微信会话信息
|
||||
func (h *UserHandler) GetWeChatSession(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
|
||||
Reference in New Issue
Block a user