2024-11-26 11:04:36 +00:00
|
|
|
|
package models
|
|
|
|
|
|
|
|
|
|
import (
|
2024-11-29 10:47:58 +00:00
|
|
|
|
"bytes"
|
|
|
|
|
"encoding/json"
|
2024-11-26 11:04:36 +00:00
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2024-11-29 01:40:33 +00:00
|
|
|
|
utils "go-admin/app/admin/models/tools"
|
2024-11-26 11:04:36 +00:00
|
|
|
|
orm "go-admin/common/global"
|
|
|
|
|
"go-admin/logger"
|
2024-12-05 10:47:16 +00:00
|
|
|
|
"go-admin/tools/config"
|
2024-11-29 10:47:58 +00:00
|
|
|
|
"io/ioutil"
|
2024-11-29 01:40:33 +00:00
|
|
|
|
"log"
|
2024-11-29 10:47:58 +00:00
|
|
|
|
"net/http"
|
|
|
|
|
"net/url"
|
2024-12-05 10:47:16 +00:00
|
|
|
|
"strings"
|
2024-11-26 11:04:36 +00:00
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
ErpCouponUnStarted = 1 // 未开始
|
|
|
|
|
ErpCouponSending = 2 // 发放中
|
|
|
|
|
ErpCouponInProgress = 3 // 进行中
|
|
|
|
|
ErpCouponFinished = 4 // 已结束
|
2024-11-29 01:40:33 +00:00
|
|
|
|
ErpCouponSendFailed = 5 // 发放失败
|
|
|
|
|
|
|
|
|
|
TaskInProgress = "in_progress" // 执行中
|
|
|
|
|
TaskCompleted = "completed" // 已完成
|
|
|
|
|
TaskFailed = "failed" // 发送失败
|
2024-11-29 10:47:58 +00:00
|
|
|
|
|
|
|
|
|
AccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token"
|
|
|
|
|
WXAppID = "wx806c079463b5b56c"
|
|
|
|
|
WXSecret = "cb125688bf4e482f66e8c46062d568fc"
|
|
|
|
|
|
|
|
|
|
WxSubscribeMessage = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token="
|
|
|
|
|
TemplateId = "aP3c503T3wn-tJtpDccvxtNi7b1qjrwavFFfo-k2SAQ"
|
|
|
|
|
WxJumpUrl = "/pages/voucher/voucher" // 跳转微信小程序的页面路径
|
2024-12-05 10:47:16 +00:00
|
|
|
|
|
|
|
|
|
WXGenerateSchema = "https://api.weixin.qq.com/wxa/generatescheme?access_token="
|
|
|
|
|
|
|
|
|
|
SMSHead = "【go2ns】"
|
2024-11-26 11:04:36 +00:00
|
|
|
|
)
|
|
|
|
|
|
2024-11-29 10:47:58 +00:00
|
|
|
|
// WxAccessToken 微信access_token
|
|
|
|
|
type WxAccessToken struct {
|
|
|
|
|
Model
|
|
|
|
|
Appid string `json:"appid"` // 小程序appid
|
|
|
|
|
AccessToken string `json:"access_token"` // 获取到的凭证
|
|
|
|
|
ExpiresTime time.Time `json:"expires_time"` // 凭证有效时间
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 01:40:33 +00:00
|
|
|
|
// CouponIssuanceTask 优惠券发放任务表
|
|
|
|
|
type CouponIssuanceTask struct {
|
|
|
|
|
Model
|
|
|
|
|
ErpCouponId uint32 `json:"erp_coupon_id"` // 优惠券id
|
|
|
|
|
ErpCouponName string `json:"erp_coupon_name"` // 优惠券名称
|
|
|
|
|
Status string `json:"status"` // 任务状态:in_progress(执行中)、completed(已完成)、failed(发送失败)
|
|
|
|
|
LastUserID uint32 `json:"last_user_id"` // 上次处理的用户ID
|
|
|
|
|
ErrorMessage string `json:"error_message" gorm:"type:text"` // 错误信息,若任务失败时记录
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 11:04:36 +00:00
|
|
|
|
// ErpCoupon 营销管理-优惠券
|
|
|
|
|
type ErpCoupon struct {
|
|
|
|
|
Model
|
2024-11-29 10:47:58 +00:00
|
|
|
|
Name string `json:"name" gorm:"index"` // 优惠券名称
|
|
|
|
|
Remark string `json:"remark"` // 备注
|
|
|
|
|
Describe string `json:"describe" gorm:"type:text"` // 优惠券简介
|
|
|
|
|
Rule string `json:"rule" gorm:"type:text"` // 优惠券使用规则
|
|
|
|
|
CategoryNumber string `json:"category_number"` // 可以使用该优惠券的商品分类,如果为空则表示没限制
|
|
|
|
|
ActiveDate uint32 `json:"active_date"` // 有效期(天)
|
|
|
|
|
Amount uint32 `json:"amount"` // 金额(元)
|
|
|
|
|
UserType uint32 `json:"user_type"` // 领取人限制:1-所有人 2-未付费用户 3-已付费用户 4-尊享会员
|
|
|
|
|
Limit uint32 `json:"limit"` // 优惠券叠加限制 0-不限制;1-仅限原价购买时使用
|
|
|
|
|
StartTime *time.Time `json:"start_time"` // 启动时间
|
|
|
|
|
State uint32 `json:"state" gorm:"index"` // 当前状态 1-未开始;2-发放中;3-进行中;4-已结束;5-发放失败
|
|
|
|
|
SendCount uint32 `json:"send_count"` // 已发放
|
|
|
|
|
UsedCount uint32 `json:"used_count"` // 已使用
|
|
|
|
|
TotalPayAmount float64 `json:"total_pay_amount"` // 支付金额(元)
|
|
|
|
|
PerCustomerAmount float64 `json:"per_customer_amount"` // 客单价(元)
|
|
|
|
|
SmsContent string `json:"sms_content" gorm:"type:text"` // 短信提示内容
|
2024-11-26 11:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErpMarketingCouponListReq 优惠券列表入参
|
|
|
|
|
type ErpMarketingCouponListReq struct {
|
|
|
|
|
Name string `json:"name" gorm:"index"` // 优惠券名称
|
|
|
|
|
State uint32 `json:"state" gorm:"index"` // 当前状态 1-未开始;2-发放中;3-进行中;4-已结束
|
|
|
|
|
CreatedTimeStart string `json:"created_time_start"` // 创建开始时间
|
|
|
|
|
CreatedTimeEnd string `json:"created_time_end"` // 创建结束时间
|
|
|
|
|
PageIndex int `json:"pageIndex"` // 页码
|
|
|
|
|
PageSize int `json:"pageSize"` // 页面条数
|
|
|
|
|
IsExport uint32 `json:"is_export"` // 1-导出
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErpMarketingCouponListResp 优惠券列表出参
|
|
|
|
|
type ErpMarketingCouponListResp struct {
|
|
|
|
|
List []ErpCoupon `json:"list"`
|
|
|
|
|
Total int `json:"total"` // 总条数
|
|
|
|
|
PageIndex int `json:"pageIndex"` // 页码
|
|
|
|
|
PageSize int `json:"pageSize"` // 页面条数
|
|
|
|
|
ExportUrl string `json:"export_url"` // 导出excel路径
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErpMarketingCouponCreateReq 新增优惠券入参
|
|
|
|
|
type ErpMarketingCouponCreateReq struct {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
Name string `json:"name" validate:"required"` // 优惠券名称
|
2024-11-29 10:47:58 +00:00
|
|
|
|
Remark string `json:"remark"` // 备注
|
2024-11-29 01:40:33 +00:00
|
|
|
|
Describe string `json:"describe" gorm:"type:text"` // 优惠券简介
|
|
|
|
|
Rule string `json:"rule" gorm:"type:text"` // 优惠券使用规则
|
|
|
|
|
CategoryNumber string `json:"category_number" validate:"required"` // 可以使用该优惠券的商品分类编号,如果为空则表示没限制
|
|
|
|
|
ActiveDate uint32 `json:"active_date" validate:"required"` // 有效期(天)
|
|
|
|
|
Amount uint32 `json:"amount" validate:"required"` // 金额(元)
|
|
|
|
|
UserType uint32 `json:"user_type" validate:"required"` // 领取人限制:1-所有人 2-未付费用户 3-已付费用户 4-尊享会员
|
|
|
|
|
Limit uint32 `json:"limit"` // 优惠券叠加限制 0-不限制;1-仅限原价购买时使用
|
2024-12-06 06:26:12 +00:00
|
|
|
|
SmsContent string `json:"sms_content" validate:"required"` // 短信提示内容
|
2024-11-26 11:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErpMarketingCouponEditReq 编辑优惠券入参
|
|
|
|
|
type ErpMarketingCouponEditReq struct {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
ErpCouponId uint32 `json:"erp_coupon_id" validate:"required"` // 优惠券id
|
|
|
|
|
Name string `json:"name" validate:"required"` // 优惠券名称
|
|
|
|
|
Remark string `json:"remark"` // 名称备注
|
|
|
|
|
Describe string `json:"describe" gorm:"type:text"` // 优惠券简介
|
|
|
|
|
Rule string `json:"rule" gorm:"type:text"` // 优惠券使用规则
|
|
|
|
|
CategoryNumber string `json:"category_number" validate:"required"` // 可以使用该优惠券的商品分类,如果为空则表示没限制
|
|
|
|
|
ActiveDate uint32 `json:"active_date" validate:"required"` // 有效期(天)
|
|
|
|
|
Amount uint32 `json:"amount" validate:"required"` // 金额(元)
|
|
|
|
|
UserType uint32 `json:"user_type" validate:"required"` // 领取人限制:1-所有人 2-未付费用户 3-已付费用户 4-尊享会员
|
|
|
|
|
Limit uint32 `json:"limit"` // 优惠券叠加限制 0-不限制;1-仅限原价购买时使用
|
2024-12-06 06:26:12 +00:00
|
|
|
|
SmsContent string `json:"sms_content" validate:"required"` // 短信提示内容
|
2024-11-26 11:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-06 03:23:00 +00:00
|
|
|
|
// ErpMarketingCouponDetailReq 优惠券详情入参
|
|
|
|
|
type ErpMarketingCouponDetailReq struct {
|
|
|
|
|
ErpCouponId uint32 `json:"erp_coupon_id" validate:"required"` // 优惠券id
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 11:04:36 +00:00
|
|
|
|
// ErpMarketingCouponDeleteReq 删除优惠券入参
|
|
|
|
|
type ErpMarketingCouponDeleteReq struct {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
ErpCouponId uint32 `json:"erp_coupon_id" validate:"required"` // 优惠券id
|
2024-11-26 11:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErpMarketingCouponStartReq 启动优惠券发放
|
|
|
|
|
type ErpMarketingCouponStartReq struct {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
ErpCouponId []uint32 `json:"erp_coupon_id" validate:"required"` // 优惠券id
|
|
|
|
|
Remark string `json:"remark" validate:"required"` // 活动名称备注
|
|
|
|
|
SmsContent string `json:"sms_content" validate:"required"` // 短信提示内容
|
2024-11-26 11:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErpMarketingCouponDataReq 优惠券数据入参
|
|
|
|
|
type ErpMarketingCouponDataReq struct {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
ErpCouponId uint32 `json:"erp_coupon_id" validate:"required"` // 优惠券id
|
2024-11-26 11:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ErpMarketingCouponDataResp 优惠券数据出参
|
|
|
|
|
type ErpMarketingCouponDataResp struct {
|
2024-12-05 10:47:16 +00:00
|
|
|
|
TotalAmount float64 `json:"total_amount"` // 用券总成交额:使用该优惠券的订单付款总金额
|
|
|
|
|
TotalDiscount float64 `json:"total_discount"` // 优惠总金额:使用该优惠券优惠的总金额
|
|
|
|
|
CostEffectiveness float64 `json:"cost_effectiveness"` // 费效比:优惠总金额 / 用券总成交额
|
|
|
|
|
OrderCount int `json:"order_count"` // 订单数:使用该优惠券的付款订单数
|
|
|
|
|
UnitPrice float64 `json:"unit_price"` // 用券笔单价:用券总成交额 / 使用该优惠券的付款订单数
|
|
|
|
|
CustomerCount int `json:"customer_count"` // 用券客户数:使用该优惠券的成交客户数
|
|
|
|
|
ProductCount int `json:"product_count"` // 购买商品件数:使用该优惠券购买的商品数量
|
|
|
|
|
Products []ProductInfo `json:"products"` // 商品信息:包含每个商品的ID、名称、付款件数、付款人数等信息
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ProductInfo 商品信息结构体
|
|
|
|
|
type ProductInfo struct {
|
|
|
|
|
ProductID int `json:"product_id"` // 商品ID
|
|
|
|
|
ProductName string `json:"product_name"` // 商品名称
|
|
|
|
|
PayCount int `json:"pay_count"` // 付款件数:使用该优惠券购买该商品的付款件数
|
|
|
|
|
PayCustomer int `json:"pay_customer"` // 付款人数:购买该商品并使用优惠券的客户人数
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type GenerateSchemeReq struct {
|
|
|
|
|
Path string `json:"path" validate:"required"` // 跳转小程序页面
|
|
|
|
|
EnvVersion string `json:"env_version"` // 默认值"release"。要打开的小程序版本。正式版为"release",体验版为"trial",开发版为"develop"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type GenerateSchemeResp struct {
|
|
|
|
|
Openlink string `json:"openlink"` // 小程序跳转链接
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type WXGenerateSchemeResponse struct {
|
|
|
|
|
Errcode int `json:"errcode"`
|
|
|
|
|
Errmsg string `json:"errmsg"`
|
|
|
|
|
Openlink string `json:"openlink"`
|
2024-11-26 11:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// List 查询优惠券列表
|
|
|
|
|
func (m *ErpMarketingCouponListReq) List() (*ErpMarketingCouponListResp, error) {
|
|
|
|
|
resp := &ErpMarketingCouponListResp{
|
|
|
|
|
PageIndex: m.PageIndex,
|
|
|
|
|
PageSize: m.PageSize,
|
|
|
|
|
}
|
|
|
|
|
page := m.PageIndex - 1
|
|
|
|
|
if page < 0 {
|
|
|
|
|
page = 0
|
|
|
|
|
}
|
|
|
|
|
if m.PageSize == 0 {
|
|
|
|
|
m.PageSize = 10
|
|
|
|
|
}
|
|
|
|
|
qs := orm.Eloquent.Table("erp_coupon")
|
|
|
|
|
|
|
|
|
|
if m.Name != "" {
|
|
|
|
|
qs = qs.Where("name = ?", m.Name)
|
|
|
|
|
}
|
|
|
|
|
if m.State != 0 {
|
|
|
|
|
qs = qs.Where("state = ?", m.State)
|
|
|
|
|
}
|
|
|
|
|
if m.CreatedTimeStart != "" {
|
|
|
|
|
parse, err := time.Parse(QueryTimeFormat, m.CreatedTimeStart)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Errorf("erpPurchaseOrderList err:", err)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
qs = qs.Where("created_at > ?", parse)
|
|
|
|
|
}
|
|
|
|
|
if m.CreatedTimeEnd != "" {
|
|
|
|
|
parse, err := time.Parse(QueryTimeFormat, m.CreatedTimeEnd)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Errorf("erpPurchaseOrderList err:", err)
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
qs = qs.Where("created_at < ?", parse)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var count int64
|
|
|
|
|
err := qs.Count(&count).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("count err:", logger.Field("err", err))
|
|
|
|
|
return resp, err
|
|
|
|
|
}
|
|
|
|
|
resp.Total = int(count)
|
|
|
|
|
|
|
|
|
|
var couponList []ErpCoupon
|
|
|
|
|
err = qs.Order("id DESC").Offset(page * m.PageSize).Limit(m.PageSize).Find(&couponList).Error
|
|
|
|
|
if err != nil && err != RecordNotFound {
|
|
|
|
|
logger.Error("erp_coupon list err:", logger.Field("err", err))
|
|
|
|
|
return resp, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-23 07:30:07 +00:00
|
|
|
|
for i, v := range couponList {
|
|
|
|
|
couponList[i].Amount = v.Amount / 100
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 11:04:36 +00:00
|
|
|
|
resp.List = couponList
|
|
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CreateErpMarketingCoupon 新增优惠券
|
|
|
|
|
func CreateErpMarketingCoupon(req *ErpMarketingCouponCreateReq) error {
|
|
|
|
|
erpCoupon := &ErpCoupon{
|
|
|
|
|
Name: req.Name,
|
|
|
|
|
Remark: req.Remark,
|
2024-11-29 01:40:33 +00:00
|
|
|
|
Describe: req.Describe,
|
|
|
|
|
Rule: req.Rule,
|
2024-11-26 11:04:36 +00:00
|
|
|
|
CategoryNumber: req.CategoryNumber,
|
|
|
|
|
ActiveDate: req.ActiveDate,
|
2024-11-29 10:47:58 +00:00
|
|
|
|
Amount: req.Amount * 100,
|
2024-11-26 11:04:36 +00:00
|
|
|
|
UserType: req.UserType,
|
|
|
|
|
Limit: req.Limit,
|
|
|
|
|
State: ErpCouponUnStarted,
|
2024-12-06 06:26:12 +00:00
|
|
|
|
SmsContent: req.SmsContent,
|
2024-11-26 11:04:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := orm.Eloquent.Create(erpCoupon).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("create purchase order err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 01:40:33 +00:00
|
|
|
|
coupon := &Coupon{
|
|
|
|
|
Name: req.Name,
|
|
|
|
|
Describe: req.Describe,
|
|
|
|
|
Rule: req.Rule,
|
|
|
|
|
CouponType: "deduction",
|
2024-11-29 10:47:58 +00:00
|
|
|
|
Value: req.Amount * 100,
|
2024-11-29 01:40:33 +00:00
|
|
|
|
CategoryNumber: req.CategoryNumber,
|
2024-12-05 10:47:16 +00:00
|
|
|
|
ActivityType: 666,
|
2024-11-29 01:40:33 +00:00
|
|
|
|
ErpCouponId: erpCoupon.ID,
|
2024-12-10 10:04:41 +00:00
|
|
|
|
ActiveStart: Now(),
|
2024-11-29 10:47:58 +00:00
|
|
|
|
ActiveEnd: Now().AddDate(0, 0, int(req.ActiveDate)),
|
2024-12-05 10:47:16 +00:00
|
|
|
|
Limit: req.Limit,
|
2024-11-29 01:40:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = orm.Eloquent.Create(coupon).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("create coupon order err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 11:04:36 +00:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EditErpMarketingCoupon 编辑优惠券
|
|
|
|
|
func EditErpMarketingCoupon(req *ErpMarketingCouponEditReq) error {
|
|
|
|
|
// 查询订单信息
|
|
|
|
|
var erpCoupon ErpCoupon
|
|
|
|
|
err := orm.Eloquent.Table("erp_coupon").Where("id=?", req.ErpCouponId).Find(&erpCoupon).Error
|
|
|
|
|
if err != nil {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
logger.Error("query erp_coupon err:", logger.Field("err", err))
|
2024-11-26 11:04:36 +00:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if erpCoupon.ID == 0 {
|
|
|
|
|
logger.Error("delete err, erpCoupon ID is:", logger.Field("erpCoupon.ID", req.ErpCouponId))
|
|
|
|
|
return errors.New(fmt.Sprintf("编辑失败:未查询到优惠券id[%d]", req.ErpCouponId))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 1-更新优惠券信息
|
|
|
|
|
erpCoupon.Name = req.Name
|
|
|
|
|
erpCoupon.Remark = req.Remark
|
2024-11-29 01:40:33 +00:00
|
|
|
|
erpCoupon.Rule = req.Rule
|
|
|
|
|
erpCoupon.Describe = req.Describe
|
2024-11-26 11:04:36 +00:00
|
|
|
|
erpCoupon.CategoryNumber = req.CategoryNumber
|
|
|
|
|
erpCoupon.ActiveDate = req.ActiveDate
|
2024-12-05 10:47:16 +00:00
|
|
|
|
erpCoupon.Amount = req.Amount * 100
|
2024-11-26 11:04:36 +00:00
|
|
|
|
erpCoupon.UserType = req.UserType
|
|
|
|
|
erpCoupon.Limit = req.Limit
|
2024-12-06 06:26:12 +00:00
|
|
|
|
erpCoupon.SmsContent = req.SmsContent
|
2024-11-26 11:04:36 +00:00
|
|
|
|
|
2024-11-29 01:40:33 +00:00
|
|
|
|
begin := orm.Eloquent.Begin()
|
|
|
|
|
err = begin.Model(&ErpCoupon{}).Where("id = ?", req.ErpCouponId).
|
2024-11-26 11:04:36 +00:00
|
|
|
|
Omit("created_at").Save(erpCoupon).Error
|
|
|
|
|
if err != nil {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
begin.Rollback()
|
2024-11-26 11:04:36 +00:00
|
|
|
|
logger.Error("update erp_coupon err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 01:40:33 +00:00
|
|
|
|
// 查询订单信息
|
|
|
|
|
var coupon Coupon
|
|
|
|
|
err = orm.Eloquent.Table("coupon").Where("erp_coupon_id=?", req.ErpCouponId).Find(&coupon).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
begin.Rollback()
|
|
|
|
|
logger.Error("query coupon err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
coupon.Name = req.Name
|
|
|
|
|
coupon.Describe = req.Describe
|
|
|
|
|
coupon.Rule = req.Rule
|
2024-12-05 10:47:16 +00:00
|
|
|
|
coupon.Value = req.Amount * 100
|
2024-12-10 10:04:41 +00:00
|
|
|
|
coupon.ActiveStart = Now()
|
|
|
|
|
coupon.ActiveEnd = Now().AddDate(0, 0, int(req.ActiveDate))
|
2024-11-29 01:40:33 +00:00
|
|
|
|
|
|
|
|
|
err = begin.Model(&Coupon{}).Where("erp_coupon_id = ?", req.ErpCouponId).
|
|
|
|
|
Omit("created_at").Save(coupon).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
begin.Rollback()
|
|
|
|
|
logger.Error("update coupon err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = begin.Commit().Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
begin.Rollback()
|
|
|
|
|
logger.Error("commit update erp_coupon err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-26 11:04:36 +00:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DeleteErpMarketingCoupon 删除优惠券
|
|
|
|
|
func DeleteErpMarketingCoupon(req *ErpMarketingCouponDeleteReq) error {
|
|
|
|
|
// 查询订单信息
|
|
|
|
|
var erpCoupon ErpCoupon
|
|
|
|
|
err := orm.Eloquent.Table("erp_coupon").Where("id=?", req.ErpCouponId).Find(&erpCoupon).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("purchase order err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if erpCoupon.ID == 0 {
|
|
|
|
|
logger.Error("delete err, erpCoupon ID is:", logger.Field("erpCoupon.ID", req.ErpCouponId))
|
|
|
|
|
return errors.New(fmt.Sprintf("删除失败:未查询到优惠券id[%d]", req.ErpCouponId))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 仅未开始和已结束的订单可删除
|
|
|
|
|
if erpCoupon.State != ErpCouponUnStarted && erpCoupon.State != ErpCouponFinished {
|
|
|
|
|
logger.Error("delete err, erpCoupon.State is:", logger.Field("erpCoupon.State", erpCoupon.State))
|
|
|
|
|
return errors.New("删除失败:仅未开始和已结束的优惠券可删除")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 删除优惠订单
|
2024-11-29 01:40:33 +00:00
|
|
|
|
begin := orm.Eloquent.Begin()
|
|
|
|
|
err = begin.Delete(erpCoupon).Error
|
2024-11-26 11:04:36 +00:00
|
|
|
|
if err != nil {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
begin.Rollback()
|
2024-11-26 11:04:36 +00:00
|
|
|
|
logger.Error("erp_coupon delete err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 01:40:33 +00:00
|
|
|
|
err = begin.Table("coupon").Where("erp_coupon_id", req.ErpCouponId).Delete(&Coupon{}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
begin.Rollback()
|
|
|
|
|
logger.Error("coupon delete err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = begin.Commit().Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
begin.Rollback()
|
|
|
|
|
logger.Error("commit erp_coupon delete err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StartCouponIssuanceTask 启动发放优惠券的任务
|
|
|
|
|
func StartCouponIssuanceTask(req *ErpMarketingCouponStartReq, taskList []CouponIssuanceTask) error {
|
|
|
|
|
var erpCouponList []ErpCoupon
|
|
|
|
|
err := orm.Eloquent.Table("erp_coupon").Where("id in ?", req.ErpCouponId).Find(&erpCouponList).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("purchase order err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(taskList) < len(erpCouponList) {
|
|
|
|
|
for _, erpCoupon := range erpCouponList {
|
|
|
|
|
exitFlag := false
|
|
|
|
|
for _, task := range taskList {
|
|
|
|
|
if erpCoupon.ID == task.ErpCouponId {
|
|
|
|
|
exitFlag = true
|
|
|
|
|
break
|
|
|
|
|
} else {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !exitFlag {
|
|
|
|
|
// 新建任务
|
|
|
|
|
taskInfo := CouponIssuanceTask{
|
|
|
|
|
ErpCouponId: erpCoupon.ID,
|
|
|
|
|
ErpCouponName: erpCoupon.Name,
|
|
|
|
|
Status: TaskInProgress,
|
|
|
|
|
LastUserID: 0,
|
|
|
|
|
ErrorMessage: "",
|
|
|
|
|
}
|
|
|
|
|
err := orm.Eloquent.Create(&taskInfo).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("create CouponIssuanceTask err:", logger.Field("err", err))
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
taskList = append(taskList, taskInfo)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 遍历taskList,获取最小的lastUserID
|
|
|
|
|
var lastUserID uint32
|
|
|
|
|
for _, task := range taskList {
|
|
|
|
|
if lastUserID == 0 {
|
|
|
|
|
lastUserID = task.LastUserID
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if lastUserID > task.LastUserID {
|
|
|
|
|
lastUserID = task.LastUserID
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 10:47:58 +00:00
|
|
|
|
var nTotalCount int
|
2024-11-29 01:40:33 +00:00
|
|
|
|
// 处理任务,从上次中断的用户ID开始
|
|
|
|
|
for {
|
|
|
|
|
// 查询用户ID大于 lastUserID 的用户
|
2024-11-29 10:47:58 +00:00
|
|
|
|
users, err := getUsersAfterID(lastUserID, erpCouponList[0].UserType)
|
2024-11-29 01:40:33 +00:00
|
|
|
|
if err != nil || len(users) == 0 {
|
|
|
|
|
log.Println("没有更多用户,任务结束")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发放优惠券
|
2024-12-05 11:39:22 +00:00
|
|
|
|
count, err := issueCouponsToUsers(users, req.ErpCouponId)
|
2024-11-29 01:40:33 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("发放优惠券失败: %v", err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新任务进度:记录处理到的最后一个用户ID
|
|
|
|
|
lastUserID = users[len(users)-1].ID
|
2024-12-05 11:39:22 +00:00
|
|
|
|
nTotalCount += count
|
2024-11-29 01:40:33 +00:00
|
|
|
|
//err = updateTaskProgress(req.ErpCouponId, lastUserID, err.Error())
|
|
|
|
|
//if err != nil {
|
|
|
|
|
// log.Printf("更新任务进度失败: %v", err)
|
|
|
|
|
// break
|
|
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 任务完成,更新状态
|
2024-11-29 10:47:58 +00:00
|
|
|
|
err = markTaskAsCompleted(req.ErpCouponId, nTotalCount)
|
2024-11-29 01:40:33 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CheckUserType 校验批量选择的优惠券使用人群是否相同
|
|
|
|
|
func CheckUserType(erpCouponId []uint32) bool {
|
|
|
|
|
var erpCouponList []ErpCoupon
|
|
|
|
|
err := orm.Eloquent.Table("erp_coupon").Where("id in ?", erpCouponId).Find(&erpCouponList).Error
|
|
|
|
|
if err != nil || err == RecordNotFound {
|
|
|
|
|
logger.Error("query erp_coupon err:", logger.Field("err", err))
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var userType uint32
|
|
|
|
|
for _, erpCoupon := range erpCouponList {
|
|
|
|
|
if userType == 0 {
|
|
|
|
|
userType = erpCoupon.UserType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if userType != erpCoupon.UserType {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetTaskProgress 查询优惠券任务执行情况
|
|
|
|
|
func GetTaskProgress(erpCouponId []uint32) ([]CouponIssuanceTask, error) {
|
|
|
|
|
var task []CouponIssuanceTask
|
|
|
|
|
err := orm.Eloquent.Table("coupon_issuance_task").Where("erp_coupon_id in ?", erpCouponId).
|
|
|
|
|
Order("updated_at desc").First(&task).Error
|
2024-11-29 10:47:58 +00:00
|
|
|
|
if err != nil && err.Error() != "record not found" {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return task, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 10:47:58 +00:00
|
|
|
|
func getUsersAfterID(lastUserID, userType uint32) ([]UserInfo, error) {
|
|
|
|
|
var err error
|
2024-11-29 01:40:33 +00:00
|
|
|
|
var users []UserInfo
|
2024-12-10 10:04:41 +00:00
|
|
|
|
// 获取尊享会员信息
|
|
|
|
|
var privilegeUserList []PrivilegeMember
|
2024-11-29 10:47:58 +00:00
|
|
|
|
|
|
|
|
|
switch userType {
|
2024-12-10 06:50:15 +00:00
|
|
|
|
case 2: // 2-未付费用户:MemberLevel 不能是 (2, 3, 4, 5),不包含已过期的租卡用户
|
2024-12-10 10:04:41 +00:00
|
|
|
|
err = orm.Eloquent.Model(&UserInfo{}).Where("id > ? and member_level not in ? and member_expire = ?",
|
|
|
|
|
lastUserID, []uint32{MemberLevelGold, MemberLevelPeriod, MemberLevelPlatinum, MemberLevelBlackGold},
|
|
|
|
|
"0000-00-00 00:00:00").Order("id asc").Limit(100).Find(&users).Error
|
|
|
|
|
|
|
|
|
|
case 3: // 3-已付费用户:MemberLevel 必须是 (2, 3, 4, 5)
|
2024-11-29 10:47:58 +00:00
|
|
|
|
err = orm.Eloquent.Model(&UserInfo{}).Where("id > ? and member_level in ?", lastUserID, []uint32{
|
|
|
|
|
MemberLevelGold, MemberLevelPeriod, MemberLevelPlatinum, MemberLevelBlackGold}).
|
|
|
|
|
Order("id asc").Limit(100).Find(&users).Error
|
2024-12-10 10:04:41 +00:00
|
|
|
|
|
|
|
|
|
case 4: // 尊享会员
|
|
|
|
|
err = orm.Eloquent.Model(&PrivilegeMember{}).Where("id > ? and member_level = ?",
|
|
|
|
|
lastUserID, MemberLevelPrivilege).Order("id asc").Limit(100).Find(&privilegeUserList).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
for _, privilegeUser := range privilegeUserList {
|
|
|
|
|
user, _ := GetUserInfoByUid(privilegeUser.Uid)
|
|
|
|
|
if user.ID == 0 { // 没查到数据
|
|
|
|
|
user.Uid = privilegeUser.Uid
|
|
|
|
|
user.Tel = privilegeUser.Tel
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
user.ID = privilegeUser.ID
|
|
|
|
|
user.MemberLevel = MemberLevelPrivilege
|
|
|
|
|
users = append(users, user)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 5: // 测试用户
|
|
|
|
|
if lastUserID == 0 {
|
|
|
|
|
users, err = GetTestUserConfig()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 10:47:58 +00:00
|
|
|
|
case 1: // 1-所有人:不限制会员等级,所有人均可领取
|
2024-12-06 06:45:12 +00:00
|
|
|
|
fallthrough
|
2024-11-29 10:47:58 +00:00
|
|
|
|
default:
|
|
|
|
|
err = orm.Eloquent.Model(&UserInfo{}).Where("id > ?", lastUserID).
|
|
|
|
|
Order("id asc").Limit(100).Find(&users).Error
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 01:40:33 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-12-10 10:04:41 +00:00
|
|
|
|
|
2024-11-29 01:40:33 +00:00
|
|
|
|
return users, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func updateTaskProgress(erpCouponId []uint32, lastUserID uint32, errMsg string) error {
|
|
|
|
|
err := orm.Eloquent.Table("coupon_issuance_task").Where("erp_coupon_id in ?", erpCouponId).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"last_user_id": lastUserID,
|
|
|
|
|
"error_message": errMsg,
|
|
|
|
|
"updated_at": time.Now(),
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 10:47:58 +00:00
|
|
|
|
func markTaskAsCompleted(erpCouponId []uint32, nTotalCount int) error {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
err := orm.Eloquent.Table("coupon_issuance_task").Where("erp_coupon_id in ?", erpCouponId).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"Status": TaskCompleted,
|
|
|
|
|
"updated_at": time.Now(),
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("更新任务完成状态失败: %v", err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 10:47:58 +00:00
|
|
|
|
err = orm.Eloquent.Table("erp_coupon").Where("id in ?", erpCouponId).
|
|
|
|
|
Updates(map[string]interface{}{
|
|
|
|
|
"state": ErpCouponInProgress,
|
|
|
|
|
"send_count": nTotalCount,
|
|
|
|
|
"updated_at": time.Now(),
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("更新任务完成状态失败: %v", err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 01:40:33 +00:00
|
|
|
|
log.Println("任务完成")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 校验用户是否符合领取优惠券的条件
|
|
|
|
|
func isEligibleForCoupon(user UserInfo, erpCoupon ErpCoupon) bool {
|
|
|
|
|
switch erpCoupon.UserType {
|
|
|
|
|
case 1:
|
|
|
|
|
// 1-所有人:不限制会员等级,所有人均可领取
|
|
|
|
|
return true
|
|
|
|
|
case 2:
|
|
|
|
|
// 2-未付费用户:MemberLevel 不能是 (2, 3, 4, 5)
|
|
|
|
|
if user.MemberLevel == MemberLevelGold ||
|
|
|
|
|
user.MemberLevel == MemberLevelPeriod ||
|
|
|
|
|
user.MemberLevel == MemberLevelPlatinum ||
|
|
|
|
|
user.MemberLevel == MemberLevelBlackGold {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2024-12-10 10:04:41 +00:00
|
|
|
|
|
|
|
|
|
if IsValidPrivilegeMember(user.Uid) { // 如果是尊享会员
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 01:40:33 +00:00
|
|
|
|
return true
|
|
|
|
|
case 3:
|
|
|
|
|
// 3-已付费用户:MemberLevel 必须是 (2, 3, 4, 5)
|
|
|
|
|
if user.MemberLevel == MemberLevelGold ||
|
|
|
|
|
user.MemberLevel == MemberLevelPeriod ||
|
|
|
|
|
user.MemberLevel == MemberLevelPlatinum ||
|
|
|
|
|
user.MemberLevel == MemberLevelBlackGold {
|
|
|
|
|
return true
|
|
|
|
|
}
|
2024-12-10 10:04:41 +00:00
|
|
|
|
|
|
|
|
|
if IsValidPrivilegeMember(user.Uid) { // 如果是尊享会员
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
case 4: // 尊享会员
|
|
|
|
|
if user.MemberLevel == MemberLevelPrivilege {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if IsValidPrivilegeMember(user.Uid) { // 如果是尊享会员
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
case 5: // 测试用户
|
|
|
|
|
if user.MemberLevel == MemberLevelTest {
|
|
|
|
|
return true
|
|
|
|
|
}
|
2024-11-29 01:40:33 +00:00
|
|
|
|
return false
|
|
|
|
|
default:
|
|
|
|
|
// 未知类型,不发放
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-05 11:39:22 +00:00
|
|
|
|
func issueCouponsToUsers(users []UserInfo, erpCouponId []uint32) (int, error) {
|
2024-11-29 01:40:33 +00:00
|
|
|
|
var erpCoupon []ErpCoupon
|
|
|
|
|
err := orm.Eloquent.Table("erp_coupon").Where("id in ?", erpCouponId).Find(&erpCoupon).Error
|
|
|
|
|
if err != nil || err == RecordNotFound {
|
|
|
|
|
logger.Error("query erp_coupon err:", logger.Field("err", err))
|
2024-12-05 11:39:22 +00:00
|
|
|
|
return 0, err
|
2024-11-29 01:40:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-05 11:39:22 +00:00
|
|
|
|
var nSendCount int
|
2024-11-29 01:40:33 +00:00
|
|
|
|
for _, user := range users {
|
|
|
|
|
// 根据条件发放优惠券
|
|
|
|
|
if isEligibleForCoupon(user, erpCoupon[0]) {
|
|
|
|
|
// 发放优惠券
|
|
|
|
|
var coupons []Coupon
|
2024-12-05 10:47:16 +00:00
|
|
|
|
err = orm.Eloquent.Table("coupon").Where("erp_coupon_id in ?", erpCouponId).Find(&coupons).Error
|
2024-11-29 01:40:33 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("query coupon err:", logger.Field("err", err))
|
2024-12-05 11:39:22 +00:00
|
|
|
|
return 0, err
|
2024-11-29 01:40:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, coupon := range coupons {
|
2024-12-10 10:04:41 +00:00
|
|
|
|
duration := coupon.ActiveEnd.Sub(coupon.ActiveStart).Hours() / 24
|
2024-11-29 01:40:33 +00:00
|
|
|
|
couponCode, _ := utils.GenerateRandomNumber19()
|
|
|
|
|
userCoupon := &UserCoupon{
|
|
|
|
|
Uid: user.Uid,
|
|
|
|
|
CouponId: coupon.ID,
|
|
|
|
|
CouponType: coupon.CouponType,
|
|
|
|
|
Value: coupon.Value,
|
|
|
|
|
State: 1,
|
|
|
|
|
ActiveStart: time.Now(),
|
2024-12-10 10:04:41 +00:00
|
|
|
|
ActiveEnd: time.Now().Add(time.Duration(duration) * 24 * time.Hour),
|
2024-11-29 01:40:33 +00:00
|
|
|
|
RedeemCode: "",
|
|
|
|
|
CategoryNumber: coupon.CategoryNumber,
|
|
|
|
|
Code: couponCode,
|
2024-12-05 10:47:16 +00:00
|
|
|
|
ActivityType: coupon.ActivityType,
|
|
|
|
|
Limit: coupon.Limit,
|
2024-11-29 01:40:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = orm.Eloquent.Create(userCoupon).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("create user coupon err:", logger.Field("err", err))
|
|
|
|
|
err = updateTaskProgress(erpCouponId, user.ID, err.Error())
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("更新任务进度失败: %v", err)
|
|
|
|
|
}
|
2024-12-05 11:39:22 +00:00
|
|
|
|
return 0, err
|
2024-11-29 01:40:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 10:47:58 +00:00
|
|
|
|
// 发送订阅通知
|
|
|
|
|
var wxToken WxAccessToken
|
|
|
|
|
err = orm.Eloquent.Table("wx_access_token").Where("appid = ?", WXAppID).First(&wxToken).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("query wx_access_token err:", logger.Field("err", err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if wxToken.ID == 0 || wxToken.ExpiresTime.Before(time.Now()) {
|
|
|
|
|
CheckAccessToken()
|
|
|
|
|
|
|
|
|
|
err = orm.Eloquent.Table("wx_access_token").Where("appid = ?", WXAppID).First(&wxToken).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("query wx_access_token err:", logger.Field("err", err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toUserOpenid := user.WxOpenID
|
|
|
|
|
templateId := TemplateId
|
|
|
|
|
data := map[string]interface{}{
|
|
|
|
|
"thing1": map[string]interface{}{"value": coupons[0].Name},
|
|
|
|
|
"thing5": map[string]interface{}{"value": coupons[0].Describe},
|
|
|
|
|
"date3": map[string]interface{}{"value": coupons[0].ActiveEnd.Format("2006年01月02日")},
|
|
|
|
|
"thing4": map[string]interface{}{"value": coupons[0].Rule},
|
|
|
|
|
}
|
|
|
|
|
//developer为开发版;trial为体验版;formal为正式版;默认为正式版
|
2024-12-05 10:47:16 +00:00
|
|
|
|
miniProgramState := config.MessageConfig.MiniProgramState
|
|
|
|
|
_, err = SendMessage(wxToken.AccessToken, toUserOpenid, templateId, data, WxJumpUrl, miniProgramState)
|
2024-11-29 01:40:33 +00:00
|
|
|
|
if err != nil {
|
2024-12-05 10:47:16 +00:00
|
|
|
|
logger.Errorf("SendMessage err:", err)
|
2024-11-29 10:47:58 +00:00
|
|
|
|
// 如果订阅通知发送失败,则发送短信
|
2024-12-05 10:47:16 +00:00
|
|
|
|
err = GtSendMessage([]string{user.Tel}, ComposeSMSContent(erpCoupon[0].SmsContent))
|
2024-11-29 10:47:58 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error(err.Error())
|
|
|
|
|
}
|
2024-11-29 01:40:33 +00:00
|
|
|
|
}
|
2024-12-05 11:39:22 +00:00
|
|
|
|
|
|
|
|
|
nSendCount++
|
2024-11-29 01:40:33 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-05 11:39:22 +00:00
|
|
|
|
return nSendCount, nil
|
2024-11-26 11:04:36 +00:00
|
|
|
|
}
|
2024-11-29 10:47:58 +00:00
|
|
|
|
|
2024-12-05 10:47:16 +00:00
|
|
|
|
func ComposeSMSContent(content string) string {
|
|
|
|
|
// 1. 判断短信内容开头是否有【go2ns】,没有的话添加上
|
|
|
|
|
if !strings.HasPrefix(content, SMSHead) {
|
|
|
|
|
content = SMSHead + content
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 判断短信内容中是否有 http 开头的链接,没有的话在最后加上
|
|
|
|
|
if !strings.Contains(content, "http") {
|
|
|
|
|
content += " 详情请点击:" + config.MessageConfig.SmsUrl
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return content
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 10:47:58 +00:00
|
|
|
|
// Response 请求微信返回基础数据
|
|
|
|
|
type Response struct {
|
|
|
|
|
Errcode int `json:"errcode"`
|
|
|
|
|
Errmsg string `json:"errmsg"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AccessTokenResponse 返回给用户的数据
|
|
|
|
|
type AccessTokenResponse struct {
|
|
|
|
|
AccessToken string `json:"access_token"` // 获取到的凭证
|
|
|
|
|
ExpiresIn int64 `json:"expires_in"` // 凭证有效时间,单位:秒。目前是7200秒之内的值。
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type accessTokenResponse struct {
|
|
|
|
|
Response
|
|
|
|
|
AccessTokenResponse
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CheckAccessToken 定期更新access_token
|
|
|
|
|
func CheckAccessToken() {
|
|
|
|
|
accessToken, err := GetWxAccessToken()
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("query wx_access_token err:", logger.Field("err", err))
|
|
|
|
|
} else {
|
|
|
|
|
now := time.Now()
|
|
|
|
|
twoHoursLater := now.Add(2 * time.Hour)
|
|
|
|
|
|
|
|
|
|
// 查询access_token
|
|
|
|
|
var wxToken WxAccessToken
|
|
|
|
|
err = orm.Eloquent.Table("wx_access_token").Where("appid = ?", WXAppID).First(&wxToken).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("query wx_access_token err:", logger.Field("err", err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if wxToken.ID != 0 { // 更新
|
|
|
|
|
// 更新access_token
|
|
|
|
|
err = orm.Eloquent.Table("wx_access_token").Where("appid = ?", WXAppID).
|
|
|
|
|
Updates(map[string]interface{}{
|
2024-12-05 10:47:16 +00:00
|
|
|
|
"access_token": accessToken.AccessToken,
|
2024-11-29 10:47:58 +00:00
|
|
|
|
"expires_time": twoHoursLater,
|
|
|
|
|
}).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Errorf("更新wx_access_token失败: %v", err)
|
|
|
|
|
}
|
|
|
|
|
} else { // 插入
|
|
|
|
|
tempToken := &WxAccessToken{
|
|
|
|
|
Appid: WXAppID,
|
|
|
|
|
AccessToken: accessToken.AccessToken,
|
|
|
|
|
ExpiresTime: twoHoursLater,
|
|
|
|
|
}
|
|
|
|
|
err = orm.Eloquent.Create(tempToken).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("create wx_access_token err:", logger.Field("err", err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetWxAccessToken 获取小程序的access_token
|
|
|
|
|
func GetWxAccessToken() (lures AccessTokenResponse, err error) {
|
|
|
|
|
api, err := code2url(WXAppID, WXSecret)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res, err := http.Get(api)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer res.Body.Close()
|
|
|
|
|
|
|
|
|
|
if res.StatusCode != 200 {
|
|
|
|
|
err = errors.New("微信服务器发生错误")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var data accessTokenResponse
|
|
|
|
|
err = json.NewDecoder(res.Body).Decode(&data)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if data.Errcode != 0 {
|
|
|
|
|
err = errors.New(data.Errmsg)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lures = data.AccessTokenResponse
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 拼接 获取 session_key 的 URL
|
|
|
|
|
func code2url(appID, secret string) (string, error) {
|
|
|
|
|
url, err := url.Parse(AccessTokenUrl)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
query := url.Query()
|
|
|
|
|
|
|
|
|
|
query.Set("appid", appID)
|
|
|
|
|
query.Set("secret", secret)
|
|
|
|
|
query.Set("grant_type", "client_credential")
|
|
|
|
|
|
|
|
|
|
url.RawQuery = query.Encode()
|
|
|
|
|
|
|
|
|
|
return url.String(), nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-05 10:47:16 +00:00
|
|
|
|
// SendMessageReq 消息订阅请求参数
|
|
|
|
|
type SendMessageReq struct {
|
|
|
|
|
TemplateId string `json:"template_id"`
|
|
|
|
|
Page string `json:"page"`
|
|
|
|
|
Touser string `json:"touser"`
|
|
|
|
|
Data interface{} `json:"data"`
|
|
|
|
|
MiniprogramState string `json:"miniprogram_state"`
|
|
|
|
|
Lang string `json:"lang"`
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-29 10:47:58 +00:00
|
|
|
|
// SendMessage 发送订阅消息
|
2024-12-05 10:47:16 +00:00
|
|
|
|
func SendMessage(accessToken, toUserOpenid, templateId string, data interface{}, page, miniProgramState string) (
|
|
|
|
|
sendMessageRsp *Response, err error) {
|
2024-11-29 10:47:58 +00:00
|
|
|
|
url := fmt.Sprintf("%s%s", WxSubscribeMessage, accessToken)
|
2024-12-05 10:47:16 +00:00
|
|
|
|
|
|
|
|
|
body := SendMessageReq{
|
|
|
|
|
TemplateId: templateId,
|
|
|
|
|
Page: page,
|
|
|
|
|
Touser: toUserOpenid,
|
|
|
|
|
Data: data,
|
|
|
|
|
MiniprogramState: miniProgramState,
|
|
|
|
|
Lang: "zh_CN",
|
2024-11-29 10:47:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-05 10:47:16 +00:00
|
|
|
|
respRaw, err := HttpRequest(http.MethodPost, url, body)
|
|
|
|
|
|
|
|
|
|
rsp := Response{}
|
|
|
|
|
|
|
|
|
|
if err = json.Unmarshal(respRaw, &rsp); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if rsp.Errcode != 0 {
|
|
|
|
|
return nil, errors.New(rsp.Errmsg)
|
|
|
|
|
}
|
|
|
|
|
return &rsp, err
|
2024-11-29 10:47:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func HttpRequest(method, url string, reqBody interface{}) ([]byte, error) {
|
|
|
|
|
client := http.Client{}
|
|
|
|
|
reqBodyBytes, err := json.Marshal(reqBody)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
reqBodyReader := bytes.NewReader(reqBodyBytes)
|
|
|
|
|
req, err := http.NewRequest(method, url, reqBodyReader)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rsp, err := client.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer rsp.Body.Close()
|
|
|
|
|
if rsp.StatusCode != 200 {
|
|
|
|
|
return nil, errors.New(fmt.Sprintf("response Status err:%d", rsp.StatusCode))
|
|
|
|
|
}
|
|
|
|
|
rspBody, err := ioutil.ReadAll(rsp.Body)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return rspBody, nil
|
|
|
|
|
}
|
2024-12-05 10:47:16 +00:00
|
|
|
|
|
|
|
|
|
// GetUserCouponIdsByErpCouponId 通过erp的优惠券id查找用户的优惠券id列表
|
|
|
|
|
func GetUserCouponIdsByErpCouponId(erpCouponId uint32) ([]uint32, error) {
|
|
|
|
|
// 先查询 Coupon 表,根据 ErpCouponId 获取 coupon_id
|
|
|
|
|
var coupon Coupon
|
|
|
|
|
err := orm.Eloquent.Where("erp_coupon_id = ?", erpCouponId).First(&coupon).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
// 如果未找到对应优惠券ID,返回错误
|
|
|
|
|
return nil, fmt.Errorf("优惠券ID不存在或查询失败")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用 coupon_id 查找 UserCoupon 表中的记录,获取所有符合条件的记录
|
|
|
|
|
var userCoupons []UserCoupon
|
|
|
|
|
err = orm.Eloquent.Where("coupon_id = ?", coupon.ID).Find(&userCoupons).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
// 如果查询失败,返回错误
|
|
|
|
|
return nil, fmt.Errorf("查询 UserCoupon 记录失败")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 提取所有找到的 userCoupon 的 ID
|
|
|
|
|
var userCouponIds []uint32
|
|
|
|
|
for _, userCoupon := range userCoupons {
|
|
|
|
|
userCouponIds = append(userCouponIds, userCoupon.ID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 返回所有 UserCoupon ID
|
|
|
|
|
return userCouponIds, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WXGenerateScheme 获取小程序跳转链接
|
|
|
|
|
func WXGenerateScheme(req *GenerateSchemeReq) (*GenerateSchemeResp, error) {
|
|
|
|
|
var rsp WXGenerateSchemeResponse
|
|
|
|
|
|
|
|
|
|
var wxToken WxAccessToken
|
|
|
|
|
err := orm.Eloquent.Table("wx_access_token").Where("appid = ?", WXAppID).First(&wxToken).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("query wx_access_token err:", logger.Field("err", err))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if wxToken.ID == 0 || wxToken.ExpiresTime.Before(time.Now()) {
|
|
|
|
|
CheckAccessToken()
|
|
|
|
|
|
|
|
|
|
err = orm.Eloquent.Table("wx_access_token").Where("appid = ?", WXAppID).First(&wxToken).Error
|
|
|
|
|
if err != nil {
|
|
|
|
|
logger.Error("query wx_access_token err:", logger.Field("err", err))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
url := WXGenerateSchema + wxToken.AccessToken
|
|
|
|
|
/*
|
|
|
|
|
{
|
|
|
|
|
"jump_wxa": {
|
|
|
|
|
"path": "/pages/startPage/index",
|
|
|
|
|
"query": "",
|
|
|
|
|
"env_version": "release"
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
"expire_type": 1,
|
|
|
|
|
"expire_interval": 30
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
reqScheme := map[string]interface{}{
|
|
|
|
|
"jump_wxa": map[string]string{
|
|
|
|
|
"path": req.Path,
|
|
|
|
|
"env_version": req.EnvVersion,
|
|
|
|
|
},
|
|
|
|
|
"expire_type": 1, //小程序 URL Link 失效类型,时间戳:0,间隔天数:1
|
|
|
|
|
"expire_interval": 30, //最长间隔天数为30天。expire_type 为 1 必填
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := HttpRequest(http.MethodPost, url, reqScheme)
|
|
|
|
|
/*
|
|
|
|
|
{
|
|
|
|
|
"errcode": 0, //成功返回
|
|
|
|
|
"errmsg": "ok",
|
|
|
|
|
"url_link": "https://wxaurl.cn/pjKMfcQsULj"
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
if err = json.Unmarshal(data, &rsp); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if rsp.Errcode != 0 {
|
|
|
|
|
return nil, errors.New(fmt.Sprintf("生成小程序跳转链接失败:errcode %d,errmsg %s", rsp.Errcode, rsp.Errmsg))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resp := GenerateSchemeResp{
|
|
|
|
|
Openlink: rsp.Openlink,
|
|
|
|
|
}
|
|
|
|
|
return &resp, nil
|
|
|
|
|
}
|