2、coupon和user_coupon表增加limit字段; 3、零售开单时,如果使用优惠券,则同步更新优惠券对应的数据; 4、增加优惠券数据接口; 5、增加获取小程序跳转链接接口; 6、增加保定金自动审核的定时任务,每天早上9点30分退前一天的保证金,最多10条;
360 lines
11 KiB
Go
360 lines
11 KiB
Go
package market
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"github.com/gin-gonic/gin"
|
||
model "go-admin/app/admin/models"
|
||
orm "go-admin/common/global"
|
||
"go-admin/logger"
|
||
"go-admin/tools"
|
||
"go-admin/tools/app"
|
||
"net/http"
|
||
"time"
|
||
)
|
||
|
||
// ErpMarketingCouponList 优惠券列表
|
||
// @Summary 优惠券列表
|
||
// @Tags 营销管理,V1.4.4
|
||
// @Produce json
|
||
// @Accept json
|
||
// @Param request body models.ErpMarketingCouponListReq true "优惠券列表模型"
|
||
// @Success 200 {object} models.ErpMarketingCouponListResp
|
||
// @Router /api/v1/marketing/coupon/list [post]
|
||
func ErpMarketingCouponList(c *gin.Context) {
|
||
req := &model.ErpMarketingCouponListReq{}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
logger.Error("ShouldBindJSON err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
|
||
return
|
||
}
|
||
|
||
resp, err := req.List()
|
||
if err != nil {
|
||
logger.Error("ErpMarketingCouponList err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusInternalServerError, err, "获取失败"+err.Error())
|
||
return
|
||
}
|
||
|
||
app.OK(c, resp, "OK")
|
||
return
|
||
}
|
||
|
||
// ErpMarketingCouponCreate 新增优惠券
|
||
// @Summary 新增优惠券
|
||
// @Tags 营销管理,V1.4.4
|
||
// @Produce json
|
||
// @Accept json
|
||
// @Param request body models.ErpMarketingCouponCreateReq true "新增优惠券模型"
|
||
// @Success 200 {object} app.Response
|
||
// @Router /api/v1/marketing/coupon/create [post]
|
||
func ErpMarketingCouponCreate(c *gin.Context) {
|
||
var req = new(model.ErpMarketingCouponCreateReq)
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
logger.Error("ShouldBindJSON err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error())
|
||
return
|
||
}
|
||
|
||
err := tools.Validate(req) //必填参数校验
|
||
if err != nil {
|
||
app.Error(c, http.StatusBadRequest, err, err.Error())
|
||
return
|
||
}
|
||
|
||
if !model.IsExistingCategoryByNumber(req.CategoryNumber) {
|
||
app.Error(c, http.StatusBadRequest, errors.New("未查询到该分类编号"), "未查询到该分类编号")
|
||
return
|
||
}
|
||
|
||
err = model.CreateErpMarketingCoupon(req)
|
||
if err != nil {
|
||
logger.Error("CreateErpMarketingCoupon err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusInternalServerError, err, err.Error())
|
||
return
|
||
}
|
||
|
||
app.OK(c, nil, "新增成功")
|
||
return
|
||
}
|
||
|
||
// ErpMarketingCouponEdit 编辑优惠券
|
||
// @Summary 编辑优惠券
|
||
// @Tags 营销管理,V1.4.4
|
||
// @Produce json
|
||
// @Accept json
|
||
// @Param request body models.ErpMarketingCouponEditReq true "编辑优惠券模型"
|
||
// @Success 200 {object} app.Response
|
||
// @Router /api/v1/marketing/coupon/edit [post]
|
||
func ErpMarketingCouponEdit(c *gin.Context) {
|
||
var req = new(model.ErpMarketingCouponEditReq)
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
logger.Error("ShouldBindJSON err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error())
|
||
return
|
||
}
|
||
|
||
err := tools.Validate(req) //必填参数校验
|
||
if err != nil {
|
||
app.Error(c, http.StatusBadRequest, err, err.Error())
|
||
return
|
||
}
|
||
|
||
err = model.EditErpMarketingCoupon(req)
|
||
if err != nil {
|
||
logger.Error("EditErpMarketingCoupon err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusInternalServerError, err, err.Error())
|
||
return
|
||
}
|
||
|
||
app.OK(c, nil, "编辑成功")
|
||
return
|
||
}
|
||
|
||
// ErpMarketingCouponDelete 删除优惠券
|
||
// @Summary 删除优惠券
|
||
// @Tags 营销管理,V1.4.4
|
||
// @Produce json
|
||
// @Accept json
|
||
// @Param request body models.ErpMarketingCouponDeleteReq true "删除优惠券模型"
|
||
// @Success 200 {object} app.Response
|
||
// @Router /api/v1/marketing/coupon/delete [post]
|
||
func ErpMarketingCouponDelete(c *gin.Context) {
|
||
var req = new(model.ErpMarketingCouponDeleteReq)
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
logger.Error("ShouldBindJSON err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error())
|
||
return
|
||
}
|
||
|
||
err := tools.Validate(req) //必填参数校验
|
||
if err != nil {
|
||
app.Error(c, http.StatusBadRequest, err, err.Error())
|
||
return
|
||
}
|
||
|
||
err = model.DeleteErpMarketingCoupon(req)
|
||
if err != nil {
|
||
logger.Error("DeleteErpMarketingCoupon err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusInternalServerError, err, err.Error())
|
||
return
|
||
}
|
||
|
||
app.OK(c, nil, "删除成功")
|
||
return
|
||
}
|
||
|
||
// ErpMarketingCouponStart 启动优惠券发放
|
||
// @Summary 启动优惠券发放
|
||
// @Tags 营销管理,V1.4.4
|
||
// @Produce json
|
||
// @Accept json
|
||
// @Param request body models.ErpMarketingCouponStartReq true "启动优惠券发放模型"
|
||
// @Success 200 {object} app.Response
|
||
// @Router /api/v1/marketing/coupon/start [post]
|
||
func ErpMarketingCouponStart(c *gin.Context) {
|
||
var req = new(model.ErpMarketingCouponStartReq)
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
logger.Error("ShouldBindJSON err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error())
|
||
return
|
||
}
|
||
|
||
err := tools.Validate(req) //必填参数校验
|
||
if err != nil {
|
||
app.Error(c, http.StatusBadRequest, err, err.Error())
|
||
return
|
||
}
|
||
|
||
// 检查是否有正在进行的任务
|
||
taskList, err := model.GetTaskProgress(req.ErpCouponId)
|
||
if err != nil {
|
||
app.Error(c, http.StatusInternalServerError, err, err.Error())
|
||
return
|
||
}
|
||
|
||
var waitSendTaskList []model.CouponIssuanceTask
|
||
|
||
if len(taskList) != 0 {
|
||
for _, task := range taskList {
|
||
switch task.Status {
|
||
case model.TaskInProgress: // 执行中
|
||
app.Error(c, http.StatusInternalServerError, errors.New(fmt.Sprintf("[%s]已在执行中,请勿重复启动", task.ErpCouponName)),
|
||
fmt.Sprintf("[%s]已在执行中,请勿重复启动", task.ErpCouponName))
|
||
return
|
||
case model.TaskCompleted: // 已完成
|
||
app.Error(c, http.StatusInternalServerError, errors.New(fmt.Sprintf("[%s]已执行完成", task.ErpCouponName)),
|
||
fmt.Sprintf("[%s]已执行完成", task.ErpCouponName))
|
||
return
|
||
default:
|
||
waitSendTaskList = append(waitSendTaskList, task)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 校验批量选择的优惠券使用人群是否相同
|
||
if !model.CheckUserType(req.ErpCouponId) {
|
||
app.Error(c, http.StatusInternalServerError, errors.New("优惠券适用人群不同,不能同时启动"), "优惠券适用人群不同,不能同时启动")
|
||
}
|
||
|
||
// 更新短信和备注
|
||
err = orm.Eloquent.Table("erp_coupon").Where("id in ?", req.ErpCouponId).
|
||
Updates(map[string]interface{}{
|
||
"remark": req.Remark,
|
||
"state": model.ErpCouponSending,
|
||
"start_time": time.Now(),
|
||
"sms_content": req.SmsContent,
|
||
"updated_at": time.Now(),
|
||
}).Error
|
||
if err != nil {
|
||
logger.Errorf("更新任务完成状态失败: %v", err)
|
||
app.Error(c, http.StatusInternalServerError, err, err.Error())
|
||
}
|
||
|
||
go model.StartCouponIssuanceTask(req, waitSendTaskList)
|
||
|
||
app.OK(c, nil, "启动成功")
|
||
return
|
||
}
|
||
|
||
// ErpMarketingCouponData 优惠券数据
|
||
// @Summary 优惠券数据
|
||
// @Tags 营销管理,V1.4.4
|
||
// @Produce json
|
||
// @Accept json
|
||
// @Param request body models.ErpMarketingCouponDataReq true "优惠券数据模型"
|
||
// @Success 200 {object} models.ErpMarketingCouponDataResp
|
||
// @Router /api/v1/marketing/coupon/data [post]
|
||
func ErpMarketingCouponData(c *gin.Context) {
|
||
var req model.ErpMarketingCouponDataReq
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error())
|
||
return
|
||
}
|
||
|
||
userCouponIds, err := model.GetUserCouponIdsByErpCouponId(req.ErpCouponId)
|
||
if err != nil {
|
||
app.Error(c, http.StatusInternalServerError, err, err.Error())
|
||
return
|
||
}
|
||
|
||
// 查询使用该优惠券的订单商品
|
||
var orderCommodities []model.ErpOrderCommodity
|
||
err = orm.Eloquent.Where("coupon_id in ?", userCouponIds).
|
||
Joins("JOIN erp_order ON erp_order.id = erp_order_commodity.erp_order_id").
|
||
Where("erp_order.state = 'audited'").Find(&orderCommodities).Error
|
||
if err != nil {
|
||
app.Error(c, http.StatusInternalServerError, err, err.Error())
|
||
return
|
||
}
|
||
|
||
// 初始化统计数据
|
||
var totalAmount, totalDiscount float64
|
||
var orderCount, customerCount, productCount int
|
||
productsMap := make(map[uint32]model.ProductInfo)
|
||
customers := make(map[int]bool) // 用来去重客户
|
||
|
||
// 统计订单数据
|
||
for _, orderCommodity := range orderCommodities {
|
||
// 获取订单详情
|
||
var order model.ErpOrder
|
||
err = orm.Eloquent.First(&order, orderCommodity.ErpOrderId).Error
|
||
if err != nil {
|
||
app.Error(c, http.StatusInternalServerError, err, err.Error())
|
||
return
|
||
}
|
||
|
||
// 计算用券总成交额和总优惠金额
|
||
totalAmount += orderCommodity.ReceivedAmount
|
||
totalDiscount += orderCommodity.CouponDiscount
|
||
|
||
// 计算订单数
|
||
orderCount++
|
||
|
||
// 计算客户数
|
||
if _, exists := customers[order.Uid]; !exists {
|
||
customers[order.Uid] = true
|
||
customerCount++
|
||
}
|
||
|
||
// 计算购买商品总件数
|
||
productCount += int(orderCommodity.Count)
|
||
if _, exists := productsMap[orderCommodity.ErpCommodityId]; !exists {
|
||
productsMap[orderCommodity.ErpCommodityId] = model.ProductInfo{
|
||
ProductID: int(orderCommodity.ErpCommodityId),
|
||
ProductName: orderCommodity.ErpCommodityName,
|
||
PayCount: int(orderCommodity.Count),
|
||
PayCustomer: 1,
|
||
}
|
||
} else {
|
||
productInfo := productsMap[orderCommodity.ErpCommodityId]
|
||
productInfo.PayCount += int(orderCommodity.Count)
|
||
productInfo.PayCustomer++
|
||
productsMap[orderCommodity.ErpCommodityId] = productInfo
|
||
}
|
||
}
|
||
|
||
// 计算费效比
|
||
var costEffectiveness float64
|
||
if totalAmount > 0 {
|
||
costEffectiveness = tools.RoundFloat(totalDiscount / totalAmount)
|
||
}
|
||
|
||
// 计算用券笔单价
|
||
var unitPrice float64
|
||
if orderCount > 0 {
|
||
unitPrice = tools.RoundFloat(totalAmount / float64(orderCount))
|
||
}
|
||
|
||
// 构造返回的数据结构
|
||
resp := model.ErpMarketingCouponDataResp{
|
||
TotalAmount: totalAmount,
|
||
TotalDiscount: totalDiscount,
|
||
CostEffectiveness: costEffectiveness,
|
||
OrderCount: orderCount,
|
||
UnitPrice: unitPrice,
|
||
CustomerCount: customerCount,
|
||
ProductCount: productCount,
|
||
}
|
||
|
||
// 转换商品数据
|
||
for _, productInfo := range productsMap {
|
||
resp.Products = append(resp.Products, productInfo)
|
||
}
|
||
|
||
// 返回结果
|
||
app.OK(c, resp, "OK")
|
||
}
|
||
|
||
// GenerateScheme 获取小程序跳转链接
|
||
// @Summary 获取小程序跳转链接
|
||
// @Tags 营销管理,V1.4.4
|
||
// @Produce json
|
||
// @Accept json
|
||
// @Param request body models.GenerateSchemeReq true "获取小程序跳转链接模型"
|
||
// @Success 200 {object} models.GenerateSchemeResp
|
||
// @Router /api/v1/marketing/generateScheme [post]
|
||
func GenerateScheme(c *gin.Context) {
|
||
var req model.GenerateSchemeReq
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
app.Error(c, http.StatusBadRequest, err, "参数错误:"+err.Error())
|
||
return
|
||
}
|
||
|
||
err := tools.Validate(req) //必填参数校验
|
||
if err != nil {
|
||
app.Error(c, http.StatusBadRequest, err, err.Error())
|
||
return
|
||
}
|
||
|
||
resp, err := model.WXGenerateScheme(&req)
|
||
if err != nil {
|
||
logger.Error("WXGenerateScheme err:", logger.Field("err", err))
|
||
app.Error(c, http.StatusInternalServerError, err, err.Error())
|
||
return
|
||
}
|
||
|
||
// 返回结果
|
||
app.OK(c, resp, "OK")
|
||
}
|