package biz import ( "context" "fmt" "github.com/redis/go-redis/v9" "strings" "voucher/internal/biz/bo" "voucher/internal/biz/vo" "voucher/internal/pkg/lock" ) func (this *VoucherBiz) alarm(ctx context.Context, order *bo.OrderBo, errMsg string) error { // 1小时 内 指定的批次号 发放 发生错误 预警 c := vo.OrderConsumeFailAlarmKey.BuildCache([]string{order.ProductNo}) _, err := this.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(this.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error { // 二次获取,判定处理,以免获取锁后又执行了一次 cacheValue, err3 := this.rdb.Rdb.Get(ctx, c.Key).Result() if err3 != nil && err3 != redis.Nil { return fmt.Errorf(fmt.Sprintf("alarm 二次获取redis缓存%s异常:%v", c.Key, err)) } if len(cacheValue) > 0 { return nil // 有直接返回 } // 通知 title := fmt.Sprintf("券发放异常") if err = this.DingMixRepo.SendMarkdownMessage(ctx, title, alarmText(order, errMsg)); err != nil { return err } if err = this.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 alarmText(order *bo.OrderBo, errMsg string) string { var card strings.Builder // 警示信息(简化版) card.WriteString("⚠️ **券发放异常** ⚠️\n\n") // 订单信息(通过引用和代码块模拟小字体) card.WriteString("> **订单信息:**\n\n") card.WriteString(fmt.Sprintf("> AppID: `%s`\n\n", order.AppID)) card.WriteString(fmt.Sprintf("> OpenID: `%s`\n\n", order.Account)) card.WriteString(fmt.Sprintf("> 批次号: `%s`\n\n", order.BatchNo)) card.WriteString(fmt.Sprintf("> 活动号: `%s`\n\n", order.ProductNo)) card.WriteString(fmt.Sprintf("> 订单号: `%s`\n\n", order.OrderNo)) card.WriteString(fmt.Sprintf("> 招行订单号: `%s`\n", order.OutBizNo)) card.WriteString("\n") // 报警原因(通过引用和代码块模拟小字体) card.WriteString("> **❗ 报警原因:**\n\n") card.WriteString(fmt.Sprintf("> `%s`\n", errMsg)) return card.String() }