This commit is contained in:
ziming 2025-07-01 17:24:16 +08:00
parent b7c52ce699
commit 8c5396729e
7 changed files with 45 additions and 68 deletions

View File

@ -17,6 +17,8 @@ type ProductBo struct {
AvailableType vo.AvailableType AvailableType vo.AvailableType
AvailableDays uint32 AvailableDays uint32
Amount int64 Amount int64
AllBudget int64
AvailableBudget int64
WarningBudget int64 WarningBudget int64
StartTime *time.Time StartTime *time.Time
EndTime *time.Time EndTime *time.Time

View File

@ -9,7 +9,8 @@ type WxResp struct {
UsedStock int64 // 已使用库存 UsedStock int64 // 已使用库存
UsedBudget int64 // 已使用预算 UsedBudget int64 // 已使用预算
AvailableStock int64 // 可用库存 AvailableStock int64 // 可用库存
RemainingBudget int64 // 剩余预算 AvailableBudget int64 // 可用预算
StockUsageRate float64 // 券使用率
StartTime *time.Time StartTime *time.Time
EndTime *time.Time EndTime *time.Time
} }

View File

@ -88,6 +88,8 @@ func (v *VoucherBiz) WxResp(wxResp *cashcoupons.Stock) (reps *do.WxResp) {
remainingBudget := availableStock * couponAmount remainingBudget := availableStock * couponAmount
stockUsageRate := float64(*wxResp.DistributedCoupons) / float64(*wxResp.StockUseRule.MaxCoupons) * 100
req := &do.WxResp{ req := &do.WxResp{
Amount: couponAmount, Amount: couponAmount,
AllBudget: *wxResp.StockUseRule.MaxAmount / 100, AllBudget: *wxResp.StockUseRule.MaxAmount / 100,
@ -95,7 +97,8 @@ func (v *VoucherBiz) WxResp(wxResp *cashcoupons.Stock) (reps *do.WxResp) {
UsedStock: *wxResp.DistributedCoupons, UsedStock: *wxResp.DistributedCoupons,
UsedBudget: *wxResp.DistributedCoupons * couponAmount, UsedBudget: *wxResp.DistributedCoupons * couponAmount,
AvailableStock: availableStock, AvailableStock: availableStock,
RemainingBudget: remainingBudget, AvailableBudget: remainingBudget,
StockUsageRate: stockUsageRate,
} }
inputFormat := time.RFC3339 inputFormat := time.RFC3339

View File

@ -138,53 +138,24 @@ func (v *VoucherBiz) warningBudget(ctx context.Context) error {
return nil return nil
} }
func (v *VoucherBiz) GetWarningBudget(product *bo.ProductBo, wxResp *cashcoupons.Stock) (*do.WarningBudget, error) {
availableStock := *wxResp.StockUseRule.MaxCoupons - *wxResp.DistributedCoupons
couponAmount := *wxResp.StockUseRule.FixedNormalCoupon.CouponAmount / 100
remainingBudget := availableStock * couponAmount
stockUsageRate := float64(*wxResp.DistributedCoupons) / float64(*wxResp.StockUseRule.MaxCoupons) * 100
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,
StockUsageRate: stockUsageRate,
}
return req, nil
}
func (v *VoucherBiz) Calculate(ctx context.Context, product *bo.ProductBo, wxResp *cashcoupons.Stock) error { func (v *VoucherBiz) Calculate(ctx context.Context, product *bo.ProductBo, wxResp *cashcoupons.Stock) error {
req, err := v.GetWarningBudget(product, wxResp) w := v.WxResp(wxResp)
err := v.ProductRepo.UpdateWarningBudget(ctx, product.ID, w)
if err != nil { if err != nil {
return err return err
} }
err = v.ProductRepo.UpdateWarningBudget(ctx, product.ID, v.WxResp(wxResp)) if product.WarningBudget >= w.AvailableBudget {
if err != nil {
return err
}
if product.WarningBudget >= req.RemainingBudget { count, err2 := v.WarningBudgetIncr(ctx, product.BatchNo)
count, err2 := v.WarningBudgetIncr(ctx, req.StockId)
if err2 != nil { if err2 != nil {
return err2 return err2
} }
if count == 1 { if count == 1 {
return v.WarningSend(ctx, formatAsCard(req)) return v.WarningSend(ctx, formatAsCard(product, w))
} else { } else {
log.Warnf("预警查询,当前达到预警第[%d]次,暂不做通知", count) log.Warnf("预警查询,当前达到预警第[%d]次,暂不做通知", count)
} }
@ -198,15 +169,15 @@ 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(product *bo.ProductBo, req *do.WxResp) string {
var card strings.Builder var card strings.Builder
card.WriteString("### " + req.StockName + "\n\n") card.WriteString("### " + product.BatchName + "\n\n")
// 基本信息 // 基本信息
card.WriteString("#### 🎫 基本信息\n") card.WriteString("#### 🎫 基本信息\n")
card.WriteString(fmt.Sprintf("- **批次号**: %s\n", req.StockId)) card.WriteString(fmt.Sprintf("- **批次号**: %s\n", product.BatchNo))
card.WriteString(fmt.Sprintf("- **活动号**: %s\n", req.StockNo)) card.WriteString(fmt.Sprintf("- **活动号**: %s\n", product.ProductNo))
card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount)) card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount))
card.WriteString(fmt.Sprintf("- **总预算**: %d元\n", req.AllBudget)) card.WriteString(fmt.Sprintf("- **总预算**: %d元\n", req.AllBudget))
card.WriteString(fmt.Sprintf("- **总库存**: %d张\n", req.AllStock)) card.WriteString(fmt.Sprintf("- **总库存**: %d张\n", req.AllStock))
@ -217,7 +188,7 @@ func formatAsCard(req *do.WarningBudget) string {
card.WriteString(fmt.Sprintf("- **已发券数**: %d张\n", req.UsedStock)) card.WriteString(fmt.Sprintf("- **已发券数**: %d张\n", req.UsedStock))
card.WriteString(fmt.Sprintf("- **已发券金额**: %d元\n", req.UsedBudget)) card.WriteString(fmt.Sprintf("- **已发券金额**: %d元\n", req.UsedBudget))
card.WriteString(fmt.Sprintf("- **剩余库存**: %d张\n", req.AvailableStock)) card.WriteString(fmt.Sprintf("- **剩余库存**: %d张\n", req.AvailableStock))
card.WriteString(fmt.Sprintf("- **剩余预算**: %d元\n", req.RemainingBudget)) card.WriteString(fmt.Sprintf("- **剩余预算**: %d元\n", req.AvailableBudget))
card.WriteString("\n") card.WriteString("\n")
card.WriteString("#### 📈 使用率\n") card.WriteString("#### 📈 使用率\n")

View File

@ -23,7 +23,7 @@ type Product struct {
AvailableDays uint32 `gorm:"column:available_days;not null;comment:领取后多少天内" json:"available_days"` AvailableDays uint32 `gorm:"column:available_days;not null;comment:领取后多少天内" json:"available_days"`
Amount int64 `gorm:"column:amount;not null;default:0" json:"amount"` Amount int64 `gorm:"column:amount;not null;default:0" json:"amount"`
AllBudget int64 `gorm:"column:all_budget;not null;default:0" json:"all_budget"` AllBudget int64 `gorm:"column:all_budget;not null;default:0" json:"all_budget"`
RemainingBudget int64 `gorm:"column:remaining_budget;not null;default:0" json:"remaining_budget"` AvailableBudget int64 `gorm:"column:available_budget;not null;default:0" json:"available_budget"`
WarningBudget int64 `gorm:"column:warning_budget;not null;default:0" json:"warning_budget"` // 预警预算=0不做预警 WarningBudget int64 `gorm:"column:warning_budget;not null;default:0" json:"warning_budget"` // 预警预算=0不做预警
WarningPerson string `gorm:"column:warning_person" json:"warning_person"` WarningPerson string `gorm:"column:warning_person" json:"warning_person"`
StartTime *time.Time `gorm:"column:start_time;not null" json:"start_time"` StartTime *time.Time `gorm:"column:start_time;not null" json:"start_time"`

View File

@ -59,7 +59,7 @@ func (r *ProductRepoImpl) UpdateWarningBudget(ctx context.Context, id int32, req
u := model.Product{ u := model.Product{
AllBudget: req.AllBudget, AllBudget: req.AllBudget,
RemainingBudget: req.RemainingBudget, AvailableBudget: req.AvailableBudget,
UpdateTime: &now, UpdateTime: &now,
} }

View File

@ -68,7 +68,7 @@ func TestWarningSend(t *testing.T) {
return return
} }
err = smsService.SendSMS(context.Background(), []string{"18666173766"}, "", params) err = smsService.SendSMS(context.Background(), []string{"18666173766"}, "SMS_489705720", params)
if err != nil { if err != nil {
t.Errorf("发送短信失败: %v", err) t.Errorf("发送短信失败: %v", err)
return return
@ -84,8 +84,8 @@ func buildTemplateParams(req *do.WarningBudget) map[string]string {
"all_stock": strconv.Itoa(int(req.AllStock)), "all_stock": strconv.Itoa(int(req.AllStock)),
"used_stock": strconv.Itoa(int(req.UsedStock)), "used_stock": strconv.Itoa(int(req.UsedStock)),
"used_budget": strconv.Itoa(int(req.UsedBudget)), "used_budget": strconv.Itoa(int(req.UsedBudget)),
"remaining_budget": strconv.Itoa(int(req.RemainingBudget)), "available_budget": strconv.Itoa(int(req.RemainingBudget)),
"remaining_stock": strconv.Itoa(int(req.AvailableStock)), "available_stock": strconv.Itoa(int(req.AvailableStock)),
"budget_usage_rate": fmt.Sprintf("%.1f", req.StockUsageRate), "budget_usage_rate": fmt.Sprintf("%.1f", req.StockUsageRate),
} }
} }