This commit is contained in:
ziming 2025-07-01 11:52:32 +08:00
parent 3e9bb2b762
commit d091564610
8 changed files with 136 additions and 17 deletions

View File

@ -62,7 +62,7 @@ func (v *VoucherBiz) alarmText(_ context.Context, order *bo.OrderBo, errMsg stri
"</font> \n" + "</font> \n" +
"<font color='black'>" + "<font color='black'>" +
"不好了,订单发放发生异常了" + "不好了,订单发放发生异常了" +
"[<font color='red'>%s</font>]请尽快处理@相关人员。" + "[<font color='red'>%s</font>]" +
"</font>" "</font>"
return fmt.Sprintf(msg, remarks) return fmt.Sprintf(msg, remarks)

View File

@ -89,7 +89,7 @@ func (v *VoucherBiz) timeSliceQuery(ctx context.Context, startTime, endTime time
duration := 2 * time.Hour duration := 2 * time.Hour
eg := new(errgroup.Group) eg := new(errgroup.Group)
eg.SetLimit(8) eg.SetLimit(10)
for start := startTime; start.Before(endTime); start = start.Add(duration) { for start := startTime; start.Before(endTime); start = start.Add(duration) {

View File

@ -1,5 +1,7 @@
package do package do
import "time"
type WarningBudget struct { type WarningBudget struct {
StockName string // 券名称 StockName string // 券名称
StockId string // 券ID StockId string // 券ID
@ -12,3 +14,9 @@ type WarningBudget struct {
AvailableStock int64 // 可用库存 AvailableStock int64 // 可用库存
RemainingBudget int64 // 剩余预算 RemainingBudget int64 // 剩余预算
} }
type WarningBudgetLog struct {
WarningBudget *WarningBudget
Num int
LastTime time.Time
}

View File

@ -29,6 +29,10 @@ const (
ProductQueryLockKey CacheKey = "product_query_lock" ProductQueryLockKey CacheKey = "product_query_lock"
) )
var (
WarningBudgetSendIncr CacheKey = "warning_budget_send_incr"
)
var CacheKeyMap = map[CacheKey]time.Duration{ var CacheKeyMap = map[CacheKey]time.Duration{
CmbOrderLockKey: 30 * time.Second, CmbOrderLockKey: 30 * time.Second,
CmbQueryLockKey: 30 * time.Second, CmbQueryLockKey: 30 * time.Second,
@ -43,6 +47,8 @@ var CacheKeyMap = map[CacheKey]time.Duration{
ProductQueryKey: 30 * 86400 * time.Second, // 30天 ProductQueryKey: 30 * 86400 * time.Second, // 30天
ProductQueryLockKey: 30 * time.Second, ProductQueryLockKey: 30 * time.Second,
WarningBudgetSendIncr: 1 * time.Hour,
} }
type Cache struct { type Cache struct {

View File

@ -3,6 +3,7 @@ package biz
import ( import (
"sync" "sync"
"voucher/internal/biz/cmb" "voucher/internal/biz/cmb"
"voucher/internal/biz/do"
"voucher/internal/biz/mixrepos" "voucher/internal/biz/mixrepos"
"voucher/internal/biz/repo" "voucher/internal/biz/repo"
"voucher/internal/biz/wechatrepo" "voucher/internal/biz/wechatrepo"
@ -26,6 +27,7 @@ type VoucherBiz struct {
mu sync.RWMutex mu sync.RWMutex
queryMap map[string]bool queryMap map[string]bool
warningBudgeMap map[string]*do.WarningBudgetLog
} }
func NewVoucherBiz( func NewVoucherBiz(
@ -57,6 +59,7 @@ func NewVoucherBiz(
CmbMixRepo: CmbMixRepo, CmbMixRepo: CmbMixRepo,
queryMap: make(map[string]bool), queryMap: make(map[string]bool),
warningBudgeMap: make(map[string]*do.WarningBudgetLog),
} }
} }

View File

@ -9,10 +9,93 @@ import (
"time" "time"
"voucher/internal/biz/bo" "voucher/internal/biz/bo"
"voucher/internal/biz/do" "voucher/internal/biz/do"
"voucher/internal/biz/vo"
) )
func (s *VoucherBiz) WarningBudgetIncr(ctx context.Context, uid string) (int64, error) {
v := vo.WarningBudgetSendIncr.BuildCache([]string{uid})
// 增加发送计数
count, err := s.rdb.Rdb.IncrBy(ctx, v.Key, 1).Result()
if err != nil {
return 0, err
}
// 如果是第一次发送,设置 过期时间
if count == 1 {
if err = s.rdb.Rdb.Expire(ctx, v.Key, v.TTL).Err(); err != nil {
return 0, fmt.Errorf("设置过期时间失败: %v", err)
}
}
// 如果发送次数超过 6 条,限制发送
if count > 6 {
if _, err = s.rdb.Rdb.Del(ctx, v.Key).Result(); err != nil {
return 0, err
}
}
return count, nil
}
func (this *VoucherBiz) WarningBudgetGet(uid string) *do.WarningBudgetLog {
if w, ok := this.warningBudgeMap[uid]; ok {
return w
}
return nil
}
func (this *VoucherBiz) WarningBudgetSet(req *do.WarningBudget) {
this.warningBudgeMap[req.StockId] = &do.WarningBudgetLog{
WarningBudget: req,
Num: 1, // 默认1
LastTime: time.Now(),
}
}
func (this *VoucherBiz) WarningBudgetAdd(req *do.WarningBudget) *do.WarningBudgetLog {
this.mu.Lock()
defer this.mu.Unlock()
w := this.WarningBudgetGet(req.StockId)
if w == nil {
this.WarningBudgetSet(req)
} else {
w.LastTime = time.Now()
w.Num += 1
w.WarningBudget = req
}
return w
}
func (this *VoucherBiz) WarningBudgetRemove(uid string) {
this.mu.Lock()
defer this.mu.Unlock()
if _, ok := this.warningBudgeMap[uid]; ok {
delete(this.warningBudgeMap, uid)
}
}
func (v *VoucherBiz) WarningBudget(ctx context.Context) { func (v *VoucherBiz) WarningBudget(ctx context.Context) {
uid := "warningBudget"
if b := v.Get(uid); b {
log.Warn("预警查询,上波还未执行完毕,此次暂不执行")
return
}
v.Add(uid)
defer v.Remove(uid)
start := time.Now() start := time.Now()
log.Warnf("预警查询,执行开始: %s", start.Format(time.DateTime)) log.Warnf("预警查询,执行开始: %s", start.Format(time.DateTime))
@ -80,24 +163,40 @@ func (v *VoucherBiz) Calculate(ctx context.Context, product *bo.ProductBo, wxRes
log.Warnf("预警查询,券预算明细,%s", str) log.Warnf("预警查询,券预算明细,%s", str)
if product.WarningBudget >= remainingBudget { if product.WarningBudget >= remainingBudget {
count, err := v.WarningBudgetIncr(ctx, req.StockId)
if err != nil {
return err
}
if count == 1 {
return v.WarningSend(ctx, str) return v.WarningSend(ctx, str)
} else {
log.Warnf("预警查询,当前达到预警第[%d]次,暂不做通知", count)
}
//w := v.WarningBudgetAdd(req)
//if w.Num == 1 {
// return v.WarningSend(ctx, str)
//} else if w.Num > 5 {
// v.WarningBudgetRemove(req.StockId)
//}
} }
return nil return nil
} }
func (v *VoucherBiz) WarningSend(ctx context.Context, str string) error { func (v *VoucherBiz) WarningSend(ctx context.Context, str string) error {
return v.DingMixRepo.SendMarkdownMessage(ctx, "券预算不足", str) return v.DingMixRepo.SendMarkdownMessage(ctx, "券预算不足", str)
} }
func formatAsCard(req *do.WarningBudget) string { func formatAsCard(req *do.WarningBudget) string {
var card strings.Builder var card strings.Builder
card.WriteString("### 🎫 " + req.StockName + "\n\n") card.WriteString("### " + req.StockName + "\n\n")
// 基本信息 // 基本信息
card.WriteString("#### 💰 基本信息\n") card.WriteString("#### 🎫 基本信息\n")
card.WriteString(fmt.Sprintf("- **批次号**: %s\n", req.StockId)) card.WriteString(fmt.Sprintf("- **批次号**: %s\n", req.StockId))
card.WriteString(fmt.Sprintf("- **活动号**: %s\n", req.StockNo)) card.WriteString(fmt.Sprintf("- **活动号**: %s\n", req.StockNo))
card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount)) card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount))

View File

@ -117,11 +117,14 @@ func (c *TalkClient) SendLinkMessage(title, text, messageURL, picURL string, atM
// SendMarkdownMessage 发送 Markdown 消息并可 @ 人员 // SendMarkdownMessage 发送 Markdown 消息并可 @ 人员
func (c *TalkClient) SendMarkdownMessage(title, text string, atMobiles []string, isAtAll bool) error { func (c *TalkClient) SendMarkdownMessage(title, text string, atMobiles []string, isAtAll bool) error {
if len(atMobiles) > 0 {
var atStr string var atStr string
for _, mobile := range atMobiles { for _, mobile := range atMobiles {
atStr += fmt.Sprintf("<font color='#003BFF'>@%s</font>", mobile) atStr += fmt.Sprintf("<font color='#00008B'>@%s</font>", mobile)
}
text += fmt.Sprintf("\n请尽快处理@相关人员%s", atStr)
} }
text += atStr
message := map[string]interface{}{ message := map[string]interface{}{
"msgtype": "markdown", "msgtype": "markdown",

View File

@ -142,14 +142,14 @@ func TestWarningBudgetText(t *testing.T) {
markdownTitle := "批次预算预警" markdownTitle := "批次预算预警"
//atMobiles := []string{"18666173766"} atMobiles := []string{"18666173766"}
webhookURL := "https://oapi.dingtalk.com/robot/send?access_token=5f10c2213cbf8168985cb2d061ebb1a5f70bd1dd47ec7cef58fa6fe545d52588" webhookURL := "https://oapi.dingtalk.com/robot/send?access_token=5f10c2213cbf8168985cb2d061ebb1a5f70bd1dd47ec7cef58fa6fe545d52588"
secret := "SEC77b63d70a9e22317144e712b4538ce1e0013db885c65f7f9bae283e8958b39eb" secret := "SEC77b63d70a9e22317144e712b4538ce1e0013db885c65f7f9bae283e8958b39eb"
client := NewDingTalkClient(webhookURL, secret) client := NewDingTalkClient(webhookURL, secret)
if err := client.SendMarkdownMessage(markdownTitle, str, nil, false); err != nil { if err := client.SendMarkdownMessage(markdownTitle, str, atMobiles, false); err != nil {
fmt.Println("Markdown 消息发送失败:", err) fmt.Println("Markdown 消息发送失败:", err)
} else { } else {
fmt.Println("Markdown 消息发送成功") fmt.Println("Markdown 消息发送成功")
@ -159,10 +159,10 @@ func TestWarningBudgetText(t *testing.T) {
func formatAsCard(req do.WarningBudget) string { func formatAsCard(req do.WarningBudget) string {
var card strings.Builder var card strings.Builder
card.WriteString("### 🎫 " + req.StockName + "\n\n") card.WriteString("### " + req.StockName + "\n\n")
// 基本信息 // 基本信息
card.WriteString("#### 💰 基本信息\n") card.WriteString("#### 🎫 基本信息\n")
card.WriteString(fmt.Sprintf("- **批次号**: %s\n", req.StockId)) card.WriteString(fmt.Sprintf("- **批次号**: %s\n", req.StockId))
card.WriteString(fmt.Sprintf("- **活动号**: %s\n", req.StockNo)) card.WriteString(fmt.Sprintf("- **活动号**: %s\n", req.StockNo))
card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount)) card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount))