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

@ -7,19 +7,21 @@ import (
// ProductBo 领域实体Bo结构字段和模型字段保持一致 // ProductBo 领域实体Bo结构字段和模型字段保持一致
type ProductBo struct { type ProductBo struct {
ID int32 ID int32
Name string Name string
ProductNo string ProductNo string
BatchName string BatchName string
BatchNo string BatchNo string
MchId string MchId string
Channel vo.Channel Channel vo.Channel
AvailableType vo.AvailableType AvailableType vo.AvailableType
AvailableDays uint32 AvailableDays uint32
Amount int64 Amount int64
WarningBudget int64 AllBudget int64
StartTime *time.Time AvailableBudget int64
EndTime *time.Time WarningBudget int64
CreateTime *time.Time StartTime *time.Time
UpdateTime *time.Time EndTime *time.Time
CreateTime *time.Time
UpdateTime *time.Time
} }

View File

@ -3,13 +3,14 @@ package do
import "time" import "time"
type WxResp struct { type WxResp struct {
Amount int64 // 券面额 Amount int64 // 券面额
AllBudget int64 // 总预算 AllBudget int64 // 总预算
AllStock int64 // 总库存 AllStock int64 // 总库存
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),
} }
} }