From dc0ab16dca8404bccd483d4884f9243b18a64fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Tue, 11 Mar 2025 17:58:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A2=84=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config_test.yaml | 8 +++ internal/biz/cmb/cmb.go | 3 + internal/biz/cmb/order_consume.go | 70 ++++++++++++++++++++++- internal/biz/mixrepos/ding.go | 2 +- internal/biz/vo/cache.go | 19 +++++- internal/data/mixrepoimpl/provider_set.go | 1 + internal/pkg/ding/ding.go | 2 +- internal/pkg/ding/ding_test.go | 18 +++++- 8 files changed, 114 insertions(+), 9 deletions(-) diff --git a/configs/config_test.yaml b/configs/config_test.yaml index 3dbf33f..18e801b 100644 --- a/configs/config_test.yaml +++ b/configs/config_test.yaml @@ -78,6 +78,14 @@ cmb: orgNo: "LANSEXIONGDI" # 发码机构号,固定值,掌上生活优惠券系统提供 notifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试回调地址 +#告警配置 +alarm: + webhookURL: "https://oapi.dingtalk.com/robot/send?access_token=5f10c2213cbf8168985cb2d061ebb1a5f70bd1dd47ec7cef58fa6fe545d52588" + secret: "SEC77b63d70a9e22317144e712b4538ce1e0013db885c65f7f9bae283e8958b39eb" + isAll: false + atMobiles: + - "15221117226" + #配置日志 logs: business: business.log #业务日志路径:如果不写日志,则不配置或配置为空 diff --git a/internal/biz/cmb/cmb.go b/internal/biz/cmb/cmb.go index f366aaf..f66c123 100644 --- a/internal/biz/cmb/cmb.go +++ b/internal/biz/cmb/cmb.go @@ -19,6 +19,7 @@ type Cmb struct { WechatCpnRepo wechatrepo.WechatCpnRepo GenerateMixRepo mixrepos.GenerateMixRepo CmbMixRepo mixrepos.CmbMixRepo + DingMixRepo mixrepos.DingMixRepo } func NewCmb( @@ -32,6 +33,7 @@ func NewCmb( WechatCpnRepo wechatrepo.WechatCpnRepo, GenerateMixRepo mixrepos.GenerateMixRepo, CmbMixRepo mixrepos.CmbMixRepo, + DingMixRepo mixrepos.DingMixRepo, ) *Cmb { return &Cmb{ bc: bc, @@ -44,5 +46,6 @@ func NewCmb( WechatCpnRepo: WechatCpnRepo, GenerateMixRepo: GenerateMixRepo, CmbMixRepo: CmbMixRepo, + DingMixRepo: DingMixRepo, } } diff --git a/internal/biz/cmb/order_consume.go b/internal/biz/cmb/order_consume.go index f104d7b..7d28053 100644 --- a/internal/biz/cmb/order_consume.go +++ b/internal/biz/cmb/order_consume.go @@ -163,11 +163,75 @@ func (v *Cmb) success(ctx context.Context, order *bo.OrderBo, orderWechat *bo.Or return v.OrderRepo.Success(ctx, order.ID) } -func (v *Cmb) fail(ctx context.Context, order *bo.OrderBo, orderWechat *bo.OrderWechatBo, remarks string) error { +func (v *Cmb) fail(ctx context.Context, order *bo.OrderBo, orderWechat *bo.OrderWechatBo, errMsg string) error { - if err := v.OrderWechatRepo.Fail(ctx, orderWechat.ID, remarks); err != nil { + if err := v.OrderWechatRepo.Fail(ctx, orderWechat.ID, errMsg); err != nil { return err } - return v.OrderRepo.Fail(ctx, order.ID) + if err := v.OrderRepo.Fail(ctx, order.ID); err != nil { + return err + } + + return v.alarm(ctx, order, errMsg) +} + +func (v *Cmb) alarm(ctx context.Context, order *bo.OrderBo, errMsg string) error { + + // 1小时 内 指定的批次号 发放 发生错误 预警 + c := vo.OrderConsumeFailAlarmKey.BuildCache([]string{order.ProductNo}) + + _, err := v.rdb.Rdb.Get(ctx, c.Key).Result() + + if err == nil { + // 缓存存在,直接返回 + return nil + } + + if err != redis.Nil { + return fmt.Errorf(fmt.Sprintf("alarm 获取redis缓存%s异常:%v", c.Key, err)) + } + + cl := vo.OrderConsumeFailAlarmLockKey.BuildCache([]string{order.ProductNo}) + + return lock.NewMutex(v.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error { + // 二次获取,判定处理,以免获取锁后又执行了一次 + + cacheValue, err := v.rdb.Rdb.Get(ctx, c.Key).Result() + + if err != nil && err != redis.Nil { + return fmt.Errorf(fmt.Sprintf("alarm 二次获取redis缓存%s异常:%v", c.Key, err)) + } + + if cacheValue != "" { + return nil // 有直接返回 + } + + // 通知 + text := v.alarmText(ctx, order, errMsg) + if err = v.DingMixRepo.SendMarkdownMessage(ctx, text); err != nil { + return err + } + + if err := v.rdb.Rdb.Set(ctx, c.Key, order.ProductNo, c.TTL).Err(); err != nil { + return fmt.Errorf(fmt.Sprintf("设置redis缓存%s异常:%v", c.Key, err)) + } + + return nil + }) +} + +func (v *Cmb) alarmText(_ context.Context, order *bo.OrderBo, errMsg string) string { + + remarks := fmt.Sprintf("订单号:%s,商品编号:%s,错误原因:%s", order.OrderNo, order.ProductNo, errMsg) + + msg := "# " + + "

立减金发放平台报警通知

" + + "
\n" + + "" + + "不好了,订单发放发生异常了,错误内容" + + "[%s]@相关人员。" + + "" + + return fmt.Sprintf(msg, remarks) } diff --git a/internal/biz/mixrepos/ding.go b/internal/biz/mixrepos/ding.go index 5908baa..86ca87c 100644 --- a/internal/biz/mixrepos/ding.go +++ b/internal/biz/mixrepos/ding.go @@ -3,5 +3,5 @@ package mixrepos import "context" type DingMixRepo interface { - SendMarkdownMessage(_ context.Context, text string) error + SendMarkdownMessage(ctx context.Context, text string) error } diff --git a/internal/biz/vo/cache.go b/internal/biz/vo/cache.go index 0a2b62d..fcdc1ab 100644 --- a/internal/biz/vo/cache.go +++ b/internal/biz/vo/cache.go @@ -16,6 +16,9 @@ const ( NotifyConsume CacheKey = "notify_consume" NotifyRetryConsume CacheKey = "notify_retry_consume" + OrderConsumeFailAlarmKey CacheKey = "order_consume_fail_alarm" + OrderConsumeFailAlarmLockKey CacheKey = "order_consume_fail_alarm_lock" + WechatNotifyRegisterTagCacheKey CacheKey = "wechat_notify_register_tag" WechatNotifyRegisterTagCacheLockKey CacheKey = "wechat_notify_register_tag_lock" @@ -27,9 +30,11 @@ var CacheKeyMap = map[CacheKey]time.Duration{ CmbQueryLockKey: 30 * time.Second, CmbProductQueryLockKey: 30 * time.Second, OrderConsume: 60 * time.Second, + OrderConsumeFailAlarmKey: 3600 * time.Second, // 1小时 + OrderConsumeFailAlarmLockKey: 60 * time.Second, NotifyConsume: 60 * time.Second, NotifyRetryConsume: 60 * time.Second, - WechatNotifyRegisterTagCacheKey: 86400 * time.Second, + WechatNotifyRegisterTagCacheKey: 86400 * time.Second, // 1天 WechatNotifyRegisterTagCacheLockKey: 30 * time.Second, WechatNotifyConsumeLockKey: 30 * time.Second, } @@ -45,7 +50,11 @@ func (s CacheKey) GetValue() string { func (s CacheKey) BuildCache(strArr []string) *Cache { - k := helper.BuildStr(s.GetValue(), strArr) + k := "" + + if len(strArr) > 0 { + k = helper.BuildStr(s.GetValue(), strArr) + } c := &Cache{ Key: k, @@ -63,7 +72,11 @@ func (s CacheKey) BuildCache(strArr []string) *Cache { func (s CacheKey) BuildCacheUint64(ids []uint64) *Cache { - k := helper.BuildStr(s.GetValue(), ids) + k := "" + + if len(ids) > 0 { + k = helper.BuildStr(s.GetValue(), ids) + } c := &Cache{ Key: k, diff --git a/internal/data/mixrepoimpl/provider_set.go b/internal/data/mixrepoimpl/provider_set.go index 6c9900f..9a3ba66 100644 --- a/internal/data/mixrepoimpl/provider_set.go +++ b/internal/data/mixrepoimpl/provider_set.go @@ -9,4 +9,5 @@ var ProviderMixRepoImplSet = wire.NewSet( NewGenerateMixRepoImpl, NewMQSendMixRepoImpl, NewCmbMixRepoImpl, + NewDingMixRepoImpl, ) diff --git a/internal/pkg/ding/ding.go b/internal/pkg/ding/ding.go index 966c673..2aedcab 100644 --- a/internal/pkg/ding/ding.go +++ b/internal/pkg/ding/ding.go @@ -119,7 +119,7 @@ func (c *TalkClient) SendLinkMessage(title, text, messageURL, picURL string, atM func (c *TalkClient) SendMarkdownMessage(title, text string, atMobiles []string, isAtAll bool) error { var atStr string for _, mobile := range atMobiles { - atStr += fmt.Sprintf("@%s", mobile) + atStr += fmt.Sprintf("@%s", mobile) } text += atStr diff --git a/internal/pkg/ding/ding_test.go b/internal/pkg/ding/ding_test.go index 5be4f4b..a6c1f57 100644 --- a/internal/pkg/ding/ding_test.go +++ b/internal/pkg/ding/ding_test.go @@ -47,7 +47,8 @@ func TestSendLinkMessage(t *testing.T) { func TestSendMarkdownMessage(t *testing.T) { markdownTitle := "测试 Markdown 消息" - markdownText := "#

立减金发放平台报警通知

\n不好了,订单发放发生异常了,错误内容[批次号不存在]@相关人员。" + //markdownText := "#

立减金发放平台报警通知

\n不好了,订单发放发生异常了,错误内容[批次号不存在]@相关人员。" + markdownText := alarmText("批次号不存在") atMobiles := []string{"18666173766", "15102807142"} @@ -62,6 +63,21 @@ func TestSendMarkdownMessage(t *testing.T) { } } +func alarmText(errMsg string) string { + + remarks := fmt.Sprintf("订单号:%s,商品编号:%s,错误原因:%s", "123456", "001", errMsg) + + msg := "# " + + "

立减金发放平台报警通知

" + + "
\n" + + "" + + "不好了,订单发放发生异常了,错误内容" + + "[%s]@相关人员。" + + "" + + return fmt.Sprintf(msg, remarks) +} + func TestSend(t *testing.T) { webhookURL := "your_webhook_url" secret := "your_secret"