diff --git a/internal/biz/alarm.go b/internal/biz/alarm.go
index cf4ea72..99e2f27 100644
--- a/internal/biz/alarm.go
+++ b/internal/biz/alarm.go
@@ -62,7 +62,7 @@ func (v *VoucherBiz) alarmText(_ context.Context, order *bo.OrderBo, errMsg stri
" \n" +
"" +
"不好了,订单发放发生异常了" +
- "[%s]请尽快处理@相关人员。" +
+ "[%s]" +
""
return fmt.Sprintf(msg, remarks)
diff --git a/internal/biz/cron_notice.go b/internal/biz/cron_notice.go
index 6197c74..a611bb4 100644
--- a/internal/biz/cron_notice.go
+++ b/internal/biz/cron_notice.go
@@ -89,7 +89,7 @@ func (v *VoucherBiz) timeSliceQuery(ctx context.Context, startTime, endTime time
duration := 2 * time.Hour
eg := new(errgroup.Group)
- eg.SetLimit(8)
+ eg.SetLimit(10)
for start := startTime; start.Before(endTime); start = start.Add(duration) {
diff --git a/internal/biz/do/warning_budget.go b/internal/biz/do/warning_budget.go
index 6313e48..d142ddd 100644
--- a/internal/biz/do/warning_budget.go
+++ b/internal/biz/do/warning_budget.go
@@ -1,5 +1,7 @@
package do
+import "time"
+
type WarningBudget struct {
StockName string // 券名称
StockId string // 券ID
@@ -12,3 +14,9 @@ type WarningBudget struct {
AvailableStock int64 // 可用库存
RemainingBudget int64 // 剩余预算
}
+
+type WarningBudgetLog struct {
+ WarningBudget *WarningBudget
+ Num int
+ LastTime time.Time
+}
diff --git a/internal/biz/vo/cache.go b/internal/biz/vo/cache.go
index 038bb7e..2be695f 100644
--- a/internal/biz/vo/cache.go
+++ b/internal/biz/vo/cache.go
@@ -29,6 +29,10 @@ const (
ProductQueryLockKey CacheKey = "product_query_lock"
)
+var (
+ WarningBudgetSendIncr CacheKey = "warning_budget_send_incr"
+)
+
var CacheKeyMap = map[CacheKey]time.Duration{
CmbOrderLockKey: 30 * time.Second,
CmbQueryLockKey: 30 * time.Second,
@@ -43,6 +47,8 @@ var CacheKeyMap = map[CacheKey]time.Duration{
ProductQueryKey: 30 * 86400 * time.Second, // 30天
ProductQueryLockKey: 30 * time.Second,
+
+ WarningBudgetSendIncr: 1 * time.Hour,
}
type Cache struct {
diff --git a/internal/biz/voucher.go b/internal/biz/voucher.go
index 7c92587..734e2e1 100644
--- a/internal/biz/voucher.go
+++ b/internal/biz/voucher.go
@@ -3,6 +3,7 @@ package biz
import (
"sync"
"voucher/internal/biz/cmb"
+ "voucher/internal/biz/do"
"voucher/internal/biz/mixrepos"
"voucher/internal/biz/repo"
"voucher/internal/biz/wechatrepo"
@@ -24,8 +25,9 @@ type VoucherBiz struct {
DingMixRepo mixrepos.DingMixRepo
CmbMixRepo mixrepos.CmbMixRepo
- mu sync.RWMutex
- queryMap map[string]bool
+ mu sync.RWMutex
+ queryMap map[string]bool
+ warningBudgeMap map[string]*do.WarningBudgetLog
}
func NewVoucherBiz(
@@ -56,7 +58,8 @@ func NewVoucherBiz(
DingMixRepo: DingMixRepo,
CmbMixRepo: CmbMixRepo,
- queryMap: make(map[string]bool),
+ queryMap: make(map[string]bool),
+ warningBudgeMap: make(map[string]*do.WarningBudgetLog),
}
}
diff --git a/internal/biz/warning_budget.go b/internal/biz/warning_budget.go
index 4c36de4..37f514b 100644
--- a/internal/biz/warning_budget.go
+++ b/internal/biz/warning_budget.go
@@ -9,10 +9,93 @@ import (
"time"
"voucher/internal/biz/bo"
"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) {
+ uid := "warningBudget"
+
+ if b := v.Get(uid); b {
+ log.Warn("预警查询,上波还未执行完毕,此次暂不执行")
+ return
+ }
+
+ v.Add(uid)
+ defer v.Remove(uid)
+
start := time.Now()
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)
if product.WarningBudget >= remainingBudget {
- return v.WarningSend(ctx, str)
+
+ count, err := v.WarningBudgetIncr(ctx, req.StockId)
+ if err != nil {
+ return err
+ }
+
+ if count == 1 {
+ 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
}
func (v *VoucherBiz) WarningSend(ctx context.Context, str string) error {
-
return v.DingMixRepo.SendMarkdownMessage(ctx, "券预算不足", str)
}
func formatAsCard(req *do.WarningBudget) string {
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.StockNo))
card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount))
diff --git a/internal/pkg/ding/ding.go b/internal/pkg/ding/ding.go
index cfa9305..a2eacd6 100644
--- a/internal/pkg/ding/ding.go
+++ b/internal/pkg/ding/ding.go
@@ -117,11 +117,14 @@ func (c *TalkClient) SendLinkMessage(title, text, messageURL, picURL string, atM
// SendMarkdownMessage 发送 Markdown 消息并可 @ 人员
func (c *TalkClient) SendMarkdownMessage(title, text string, atMobiles []string, isAtAll bool) error {
- var atStr string
- for _, mobile := range atMobiles {
- atStr += fmt.Sprintf("@%s", mobile)
+
+ if len(atMobiles) > 0 {
+ var atStr string
+ for _, mobile := range atMobiles {
+ atStr += fmt.Sprintf("@%s", mobile)
+ }
+ text += fmt.Sprintf("\n请尽快处理@相关人员%s", atStr)
}
- text += atStr
message := map[string]interface{}{
"msgtype": "markdown",
diff --git a/internal/pkg/ding/ding_test.go b/internal/pkg/ding/ding_test.go
index c086bb7..494055b 100644
--- a/internal/pkg/ding/ding_test.go
+++ b/internal/pkg/ding/ding_test.go
@@ -142,14 +142,14 @@ func TestWarningBudgetText(t *testing.T) {
markdownTitle := "批次预算预警"
- //atMobiles := []string{"18666173766"}
+ atMobiles := []string{"18666173766"}
webhookURL := "https://oapi.dingtalk.com/robot/send?access_token=5f10c2213cbf8168985cb2d061ebb1a5f70bd1dd47ec7cef58fa6fe545d52588"
secret := "SEC77b63d70a9e22317144e712b4538ce1e0013db885c65f7f9bae283e8958b39eb"
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)
} else {
fmt.Println("Markdown 消息发送成功")
@@ -159,10 +159,10 @@ func TestWarningBudgetText(t *testing.T) {
func formatAsCard(req do.WarningBudget) string {
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.StockNo))
card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount))