package biz import ( "context" "fmt" "github.com/go-kratos/kratos/v2/log" "github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons" "strings" "time" "voucher/internal/biz/bo" "voucher/internal/biz/do" ) func (v *VoucherBiz) WarningBudget(ctx context.Context) { start := time.Now() log.Warnf("预警查询,执行开始: %s", start.Format(time.DateTime)) if err := v.warningBudget(ctx); err != nil { log.Errorf("预警查询,执行失败: %v", err) } end := time.Now() elapsed := end.Sub(start) log.Warnf("预警查询,开始执行时间%s,执行结束时间%s,代码块执行耗时: %s", start.Format(time.DateTime), end.Format(time.DateTime), elapsed) return } func (v *VoucherBiz) warningBudget(ctx context.Context) error { err := v.ProductRepo.FindWarningBudget(ctx, func(ctx context.Context, rows []*bo.ProductBo) error { for _, row := range rows { wxResp, err := v.WechatCpnRepo.QueryProduct(ctx, row.MchId, row.BatchNo) if err != nil { log.Context(ctx).Errorf("预警查询,查询微信券失败: %v", err) } else { if err = v.Calculate(ctx, row, wxResp); err != nil { log.Context(ctx).Errorf("预警查询,处理失败: %v", err) } } time.Sleep(time.Second * 2) } return nil }) if err != nil { return err } return nil } func (v *VoucherBiz) Calculate(ctx context.Context, product *bo.ProductBo, wxResp *cashcoupons.Stock) error { availableStock := *wxResp.StockUseRule.MaxCoupons - *wxResp.DistributedCoupons couponAmount := *wxResp.StockUseRule.FixedNormalCoupon.CouponAmount / 100 remainingBudget := availableStock * couponAmount req := &do.WarningBudget{ StockName: product.Name, StockId: product.BatchNo, StockNo: product.ProductNo, Amount: couponAmount, AllBudget: *wxResp.StockUseRule.MaxAmount / 100, AllStock: *wxResp.StockUseRule.MaxCoupons, UsedStock: *wxResp.DistributedCoupons, UsedBudget: *wxResp.DistributedCoupons * couponAmount, AvailableStock: availableStock, RemainingBudget: remainingBudget, } str := formatAsCard(req) log.Warnf("预警查询,券预算明细,%s", str) if product.WarningBudget >= remainingBudget { return v.WarningSend(ctx, str) } 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("#### 💰 基本信息\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)) card.WriteString(fmt.Sprintf("- **总预算**: %d元\n", req.AllBudget)) card.WriteString(fmt.Sprintf("- **总库存**: %d张\n", req.AllStock)) card.WriteString("\n") // 使用情况 card.WriteString("#### 📊 使用情况\n") card.WriteString(fmt.Sprintf("- **已发券数**: %d张\n", req.UsedStock)) card.WriteString(fmt.Sprintf("- **已发券金额**: %d元\n", req.UsedBudget)) card.WriteString(fmt.Sprintf("- **剩余库存**: %d张\n", req.AvailableStock)) card.WriteString(fmt.Sprintf("- **剩余预算**: %d元\n", req.RemainingBudget)) card.WriteString("\n") // 使用率 stockUsageRate := float64(req.UsedStock) / float64(req.AllStock) * 100 card.WriteString("#### 📈 使用率\n") card.WriteString(fmt.Sprintf("- **使用率**: %.1f%%\n", stockUsageRate)) return card.String() }