From 0d1f3c3aa8a9d3c2ed7f4ef1900724a56e48da5c Mon Sep 17 00:00:00 2001 From: chenlin Date: Fri, 29 Nov 2024 09:40:33 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E4=BC=98=E5=8C=96=E9=9B=B6=E5=94=AE?= =?UTF-8?q?=E4=BC=98=E6=83=A0=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD=EF=BC=9B?= =?UTF-8?q?=202=E3=80=81=E7=9F=AD=E4=BF=A1=E6=A0=87=E7=AD=BEgo2switch?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E4=BF=AE=E6=94=B9=E4=B8=BAgo2ns;=203?= =?UTF-8?q?=E3=80=81=E4=BC=98=E5=8C=96=E6=9F=A5=E8=AF=A2=E9=9B=B6=E5=94=AE?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E6=94=AF=E4=BB=98=E7=8A=B6=E6=80=81=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=EF=BC=8C=E6=96=B0=E5=A2=9Epay=5Finit=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=9F=A5=E8=AF=A2=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/admin/apis/market/marketing.go | 57 ++++ app/admin/apis/usermanage/user.go | 2 +- app/admin/models/coupon.go | 1 + app/admin/models/erp_order.go | 2 +- app/admin/models/game_card.go | 4 +- app/admin/models/marketing.go | 409 ++++++++++++++++++++++++++--- app/admin/models/share_card.go | 6 +- app/admin/models/tools/utils.go | 23 ++ app/admin/models/user.go | 2 +- test/greentown_sms_test.go | 2 +- 10 files changed, 461 insertions(+), 47 deletions(-) diff --git a/app/admin/apis/market/marketing.go b/app/admin/apis/market/marketing.go index cb4f782..11873bc 100644 --- a/app/admin/apis/market/marketing.go +++ b/app/admin/apis/market/marketing.go @@ -2,12 +2,15 @@ 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 优惠券列表 @@ -150,6 +153,60 @@ func ErpMarketingCouponDelete(c *gin.Context) { // @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, "参数错误:优惠券id为空") + return + } + + // 检查是否有正在进行的任务 + taskList, err := model.GetTaskProgress(req.ErpCouponId) + if err != nil { + app.Error(c, http.StatusInternalServerError, err, err.Error()) + return + } + + var waitSendTaskList []model.CouponIssuanceTask + 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("erp_coupon_id in ?", req.ErpCouponId). + Updates(map[string]interface{}{ + "remark": req.Remark, + "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 diff --git a/app/admin/apis/usermanage/user.go b/app/admin/apis/usermanage/user.go index bcfc16e..ca942d4 100644 --- a/app/admin/apis/usermanage/user.go +++ b/app/admin/apis/usermanage/user.go @@ -840,7 +840,7 @@ func GroupSendMessage(c *gin.Context) { fmt.Println("tels:", tels) fmt.Println("Message:", groupMessageTemplate.Message) - //groupMessageTemplate.Message = "【go2switch】温馨提示:您的会员即将过期,请在过期之前将卡归还到门店,如有问题联系客服" + //groupMessageTemplate.Message = "【go2ns】温馨提示:您的会员即将过期,请在过期之前将卡归还到门店,如有问题联系客服" err = models.GtSendMessage(strings.Split(tels, ","), groupMessageTemplate.Message) if err != nil { logger.Errorf("SmsSend err:", logger.Field("err", err)) diff --git a/app/admin/models/coupon.go b/app/admin/models/coupon.go index fbcb97f..e5497cf 100644 --- a/app/admin/models/coupon.go +++ b/app/admin/models/coupon.go @@ -26,6 +26,7 @@ type Coupon struct { MemberLevel uint32 `json:"member_level"` // 会员等级 1-用户 2-会员 CategoryNumber string `json:"category_number"` // 可以使用该优惠券的商品分类,如果为空则表示没限制 CommodityNumber string `json:"commodity_number"` // 可以使用该优惠券的商品编号,如果为空则表示没限制 + ErpCouponId uint32 `json:"erp_coupon_id"` // 零售业务推送优惠券ID IsDraw bool `json:"is_draw" gorm:"-"` // } diff --git a/app/admin/models/erp_order.go b/app/admin/models/erp_order.go index 1f20769..4bbac66 100644 --- a/app/admin/models/erp_order.go +++ b/app/admin/models/erp_order.go @@ -2514,7 +2514,7 @@ func QueryErpOrderPayStatus(billSn string) (*ErpOrderPayResp, error) { // 查询待支付订单 var orderRecordInfo ErpOrderRecord - err = orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status in (?)", billSn, []string{Paying, PayOk}). + err = orm.Eloquent.Table("erp_order_record").Where("bill_sn = ? and status in (?)", billSn, []string{PayInit, Paying, PayOk}). Find(&orderRecordInfo).Error if err != nil { logger.Error("未查询到订单:", logger.Field("err", err)) diff --git a/app/admin/models/game_card.go b/app/admin/models/game_card.go index 307a66e..8d50e3c 100644 --- a/app/admin/models/game_card.go +++ b/app/admin/models/game_card.go @@ -1458,7 +1458,7 @@ func MemberExpirationReminder() { // return //} // - //content := "【go2switch】温馨提示:您的会员即将过期,请在过期之前将卡归还到门店,如有问题联系客服" + //content := "【go2ns】温馨提示:您的会员即将过期,请在过期之前将卡归还到门店,如有问题联系客服" //for i, _ := range users { // if users[i].Tel == "" { // continue @@ -1546,7 +1546,7 @@ func ExpireMemberSMSSend() { func ExpireMemberSMSSendDay(day uint32, nowTime time.Time) { smsSend := &ExpireMemberSmsSend{ - Message: fmt.Sprintf("【go2switch】您的租卡会员已过期%d天,卡带未归还产生滞纳金%d元,请及时续费会员或归还卡带,以避免对您造成不必要的损失。", day, day*2), + Message: fmt.Sprintf("【go2ns】您的租卡会员已过期%d天,卡带未归还产生滞纳金%d元,请及时续费会员或归还卡带,以避免对您造成不必要的损失。", day, day*2), SendTime: nowTime, Tel: "", Status: 1, diff --git a/app/admin/models/marketing.go b/app/admin/models/marketing.go index 034ec97..1292531 100644 --- a/app/admin/models/marketing.go +++ b/app/admin/models/marketing.go @@ -3,8 +3,10 @@ package models import ( "errors" "fmt" + utils "go-admin/app/admin/models/tools" orm "go-admin/common/global" "go-admin/logger" + "log" "time" ) @@ -13,24 +15,41 @@ const ( ErpCouponSending = 2 // 发放中 ErpCouponInProgress = 3 // 进行中 ErpCouponFinished = 4 // 已结束 + ErpCouponSendFailed = 5 // 发放失败 + + TaskInProgress = "in_progress" // 执行中 + TaskCompleted = "completed" // 已完成 + TaskFailed = "failed" // 发送失败 ) +// 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"` // 错误信息,若任务失败时记录 +} + // ErpCoupon 营销管理-优惠券 type ErpCoupon struct { Model - Name string `json:"name" gorm:"index"` // 优惠券名称 - Remark string `json:"remark"` // 名称备注 - Content string `json:"content"` // 优惠内容 - CategoryNumber string `json:"category_number"` // 可以使用该优惠券的商品分类,如果为空则表示没限制 - ActiveDate uint32 `json:"active_date"` // 有效期(天) - Amount float64 `json:"amount"` // 金额(元) - UserType uint32 `json:"user_type"` // 领取人限制:1-所有人 2-未付费用户 3-已付费用户 4-尊享会员 - Limit uint32 `json:"limit"` // 优惠券叠加限制 0-不限制;1-仅限原价购买时使用 - State uint32 `json:"state" gorm:"index"` // 当前状态 1-未开始;2-发放中;3-进行中;4-已结束 - SendCount uint32 `json:"send_count"` // 已发放 - UsedCount uint32 `json:"used_count"` // 已使用 - TotalPayAmount float64 `json:"total_pay_amount"` // 支付金额(元) - PerCustomerAmount float64 `json:"per_customer_amount"` // 客单价(元) + 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-仅限原价购买时使用 + 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"` // 短信提示内容 } // ErpMarketingCouponListReq 优惠券列表入参 @@ -55,42 +74,46 @@ type ErpMarketingCouponListResp struct { // ErpMarketingCouponCreateReq 新增优惠券入参 type ErpMarketingCouponCreateReq struct { - Name string `json:"name" validate:"required"` // 优惠券名称 - Remark string `json:"remark"` // 名称备注 - Content string `json:"content"` // 优惠内容/使用说明 - CategoryNumber string `json:"category_number" validate:"required"` // 可以使用该优惠券的商品分类编号,如果为空则表示没限制 - ActiveDate uint32 `json:"active_date" validate:"required"` // 有效期(天) - Amount float64 `json:"amount" validate:"required"` // 金额(元) - UserType uint32 `json:"user_type" validate:"required"` // 领取人限制:1-所有人 2-未付费用户 3-已付费用户 4-尊享会员 - Limit uint32 `json:"limit"` // 优惠券叠加限制 0-不限制;1-仅限原价购买时使用 + 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-仅限原价购买时使用 } // ErpMarketingCouponEditReq 编辑优惠券入参 type ErpMarketingCouponEditReq struct { - ErpCouponId uint32 `json:"erp_coupon_id" validate:"required"` // 优惠券id - Name string `json:"name" validate:"required"` // 优惠券名称 - Remark string `json:"remark"` // 名称备注 - Content string `json:"content"` // 优惠内容/使用说明 - CategoryNumber string `json:"category_number" validate:"required"` // 可以使用该优惠券的商品分类,如果为空则表示没限制 - ActiveDate uint32 `json:"active_date" validate:"required"` // 有效期(天) - Amount float64 `json:"amount" validate:"required"` // 金额(元) - UserType uint32 `json:"user_type" validate:"required"` // 领取人限制:1-所有人 2-未付费用户 3-已付费用户 4-尊享会员 - Limit uint32 `json:"limit"` // 优惠券叠加限制 0-不限制;1-仅限原价购买时使用 + 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-仅限原价购买时使用 } // ErpMarketingCouponDeleteReq 删除优惠券入参 type ErpMarketingCouponDeleteReq struct { - ErpCouponId uint32 `json:"erp_coupon_id" binding:"required"` // 优惠券id + ErpCouponId uint32 `json:"erp_coupon_id" validate:"required"` // 优惠券id } // ErpMarketingCouponStartReq 启动优惠券发放 type ErpMarketingCouponStartReq struct { - ErpCouponId uint32 `json:"erp_coupon_id" binding:"required"` // 优惠券id + ErpCouponId []uint32 `json:"erp_coupon_id" validate:"required"` // 优惠券id + Remark string `json:"remark" validate:"required"` // 活动名称备注 + SmsContent string `json:"sms_content" validate:"required"` // 短信提示内容 } // ErpMarketingCouponDataReq 优惠券数据入参 type ErpMarketingCouponDataReq struct { - ErpCouponId uint32 `json:"erp_coupon_id" binding:"required"` // 优惠券id + ErpCouponId uint32 `json:"erp_coupon_id" validate:"required"` // 优惠券id } // ErpMarketingCouponDataResp 优惠券数据出参 @@ -160,7 +183,8 @@ func CreateErpMarketingCoupon(req *ErpMarketingCouponCreateReq) error { erpCoupon := &ErpCoupon{ Name: req.Name, Remark: req.Remark, - Content: req.Content, + Describe: req.Describe, + Rule: req.Rule, CategoryNumber: req.CategoryNumber, ActiveDate: req.ActiveDate, Amount: req.Amount, @@ -175,6 +199,22 @@ func CreateErpMarketingCoupon(req *ErpMarketingCouponCreateReq) error { return err } + coupon := &Coupon{ + Name: req.Name, + Describe: req.Describe, + Rule: req.Rule, + CouponType: "deduction", + Value: req.Amount, + CategoryNumber: req.CategoryNumber, + ErpCouponId: erpCoupon.ID, + } + + err = orm.Eloquent.Create(coupon).Error + if err != nil { + logger.Error("create coupon order err:", logger.Field("err", err)) + return err + } + return nil } @@ -184,7 +224,7 @@ func EditErpMarketingCoupon(req *ErpMarketingCouponEditReq) 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)) + logger.Error("query erp_coupon err:", logger.Field("err", err)) return err } @@ -196,20 +236,52 @@ func EditErpMarketingCoupon(req *ErpMarketingCouponEditReq) error { // 1-更新优惠券信息 erpCoupon.Name = req.Name erpCoupon.Remark = req.Remark - erpCoupon.Content = req.Content + erpCoupon.Rule = req.Rule + erpCoupon.Describe = req.Describe erpCoupon.CategoryNumber = req.CategoryNumber erpCoupon.ActiveDate = req.ActiveDate erpCoupon.Amount = req.Amount erpCoupon.UserType = req.UserType erpCoupon.Limit = req.Limit - err = orm.Eloquent.Model(&ErpCoupon{}).Where("id = ?", req.ErpCouponId). + begin := orm.Eloquent.Begin() + err = begin.Model(&ErpCoupon{}).Where("id = ?", req.ErpCouponId). Omit("created_at").Save(erpCoupon).Error if err != nil { + begin.Rollback() logger.Error("update erp_coupon err:", logger.Field("err", err)) return err } + // 查询订单信息 + 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 + coupon.Value = req.Amount + + 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 + } + return nil } @@ -235,11 +307,272 @@ func DeleteErpMarketingCoupon(req *ErpMarketingCouponDeleteReq) error { } // 删除优惠订单 - err = orm.Eloquent.Delete(erpCoupon).Error + begin := orm.Eloquent.Begin() + err = begin.Delete(erpCoupon).Error if err != nil { + begin.Rollback() logger.Error("erp_coupon delete err:", logger.Field("err", err)) return err } + 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 + } + } + + // 处理任务,从上次中断的用户ID开始 + for { + // 查询用户ID大于 lastUserID 的用户 + users, err := getUsersAfterID(lastUserID) + if err != nil || len(users) == 0 { + log.Println("没有更多用户,任务结束") + break + } + + // 发放优惠券 + err = issueCouponsToUsers(users, req.ErpCouponId) + if err != nil { + log.Printf("发放优惠券失败: %v", err) + return err + } + + // 更新任务进度:记录处理到的最后一个用户ID + lastUserID = users[len(users)-1].ID + //err = updateTaskProgress(req.ErpCouponId, lastUserID, err.Error()) + //if err != nil { + // log.Printf("更新任务进度失败: %v", err) + // break + //} + } + + // 任务完成,更新状态 + err = markTaskAsCompleted(req.ErpCouponId) + 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 + if err != nil { + return nil, err + } + + return task, nil +} + +func getUsersAfterID(lastUserID uint32) ([]UserInfo, error) { + var users []UserInfo + err := orm.Eloquent.Model(&UserInfo{}).Where("id > ? ", lastUserID). + Order("id asc").Limit(100).Find(&users).Error + if err != nil { + return nil, err + } + 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 +} + +func markTaskAsCompleted(erpCouponId []uint32) error { + 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 + } + + 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 + } + 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 + } + return false + default: + // 未知类型,不发放 + return false + } +} + +func issueCouponsToUsers(users []UserInfo, erpCouponId []uint32) error { + 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)) + return err + } + + for _, user := range users { + // 根据条件发放优惠券 + if isEligibleForCoupon(user, erpCoupon[0]) { + // 发放优惠券 + var coupons []Coupon + err = orm.Eloquent.Table("coupon").Where("erp_coupon_id in ?", erpCouponId).First(&coupons).Error + if err != nil { + logger.Error("query coupon err:", logger.Field("err", err)) + return err + } + + for _, coupon := range coupons { + couponCode, _ := utils.GenerateRandomNumber19() + userCoupon := &UserCoupon{ + Uid: user.Uid, + CouponId: coupon.ID, + CouponType: coupon.CouponType, + Value: coupon.Value, + State: 1, + ActiveStart: time.Now(), + ActiveEnd: time.Now().AddDate(0, 0, 7), + RedeemCode: "", + CategoryNumber: coupon.CategoryNumber, + Code: couponCode, + } + + 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) + } + return err + } + } + + // 发送短信或者消息订阅通知 + err = GtSendMessage([]string{user.Tel}, erpCoupon[0].SmsContent) + if err != nil { + logger.Error(err.Error()) + } + } + } return nil } diff --git a/app/admin/models/share_card.go b/app/admin/models/share_card.go index 17e38e1..2980f0c 100644 --- a/app/admin/models/share_card.go +++ b/app/admin/models/share_card.go @@ -2267,7 +2267,7 @@ func TakeExpressStatePush(pushReq *ExpressStatePushReq) { // if userInfo.Tel == "" { // return // } - // err = SmsSend(userInfo.Tel, "【go2switch】温馨提示:您的收回卡已签收,请及时检查卡带确认功能正常,如有问题,请于签收后48小时内通过小程序发起异常反馈。") + // err = SmsSend(userInfo.Tel, "【go2ns】温馨提示:您的收回卡已签收,请及时检查卡带确认功能正常,如有问题,请于签收后48小时内通过小程序发起异常反馈。") // if err != nil { // logger.Errorf("SmsSend err:",logger.Field("err",err)) // return @@ -2303,7 +2303,7 @@ func TakeExpressStatePush(pushReq *ExpressStatePushReq) { if userInfo.Tel == "" { return } - err = GtSendMessage([]string{userInfo.Tel}, "【go2switch】温馨提示:您的收回卡已签收,请及时检查卡带确认功能正常,如有问题,请于签收后48小时内通过小程序发起异常反馈。") + err = GtSendMessage([]string{userInfo.Tel}, "【go2ns】温馨提示:您的收回卡已签收,请及时检查卡带确认功能正常,如有问题,请于签收后48小时内通过小程序发起异常反馈。") if err != nil { logger.Errorf("SmsSend err:", logger.Field("err", err)) return @@ -2344,7 +2344,7 @@ func TakeExpressStatePush(pushReq *ExpressStatePushReq) { if userInfo.Tel == "" { return } - err = GtSendMessage([]string{userInfo.Tel}, "【go2switch】温馨提示:您的借卡已签收,请及时检查卡带确认功能正常,如有问题,请于签收后48小时内通过小程序发起异常反馈。") + err = GtSendMessage([]string{userInfo.Tel}, "【go2ns】温馨提示:您的借卡已签收,请及时检查卡带确认功能正常,如有问题,请于签收后48小时内通过小程序发起异常反馈。") if err != nil { logger.Errorf("SmsSend err:", logger.Field("err", err)) return diff --git a/app/admin/models/tools/utils.go b/app/admin/models/tools/utils.go index 3dfb1e3..1f018cc 100644 --- a/app/admin/models/tools/utils.go +++ b/app/admin/models/tools/utils.go @@ -1,8 +1,10 @@ package tools import ( + "crypto/rand" "fmt" "github.com/holdno/snowFlakeByGo" + "math/big" mathrand "math/rand" "time" @@ -105,3 +107,24 @@ func RandomLenNum(length int) string { } return str } + +func GenerateRandomNumber19() (string, error) { + // Define the maximum number for a single digit (0-9) + const digits = "0123456789" + const length = 19 + + // To store the generated number + number := make([]byte, length) + + for i := 0; i < length; i++ { + // Generate a random index from 0 to 9 + index, err := rand.Int(rand.Reader, big.NewInt(int64(len(digits)))) + if err != nil { + return "", err + } + // Convert the index to a byte and add it to the number + number[i] = digits[index.Int64()] + } + + return string(number), nil +} diff --git a/app/admin/models/user.go b/app/admin/models/user.go index db59ca5..0b1c81d 100644 --- a/app/admin/models/user.go +++ b/app/admin/models/user.go @@ -3539,7 +3539,7 @@ func SendMessageMemberRenewal() { } } if len(tels) > 0 { - message := "【go2switch】提醒:您的租卡会员时长仅剩余一个月,现在续费最高立减200元!赶快进入小程序领取优惠吧~" + message := "【go2ns】提醒:您的租卡会员时长仅剩余一个月,现在续费最高立减200元!赶快进入小程序领取优惠吧~" err = GtSendMessage(tels, message) if err != nil { logger.Errorf("SmsSend err:", logger.Field("err", err)) diff --git a/test/greentown_sms_test.go b/test/greentown_sms_test.go index 8622236..6a82c27 100644 --- a/test/greentown_sms_test.go +++ b/test/greentown_sms_test.go @@ -10,7 +10,7 @@ func TestSend(t *testing.T) { tel := "13714071204,17727927738" //tel := "17602181899" - models.SmsSend(tel, "【go2switch】温馨提示:您的会员即将过期,请在过期之前将卡归还到门店,如有问题联系客服") + models.SmsSend(tel, "【go2ns】温馨提示:您的会员即将过期,请在过期之前将卡归还到门店,如有问题联系客服") } func TestGtSendMessage(t *testing.T) {