From 8ec8f2a9807b8ebb50cc0a05638a1ee287d7bc8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Mon, 17 Mar 2025 19:48:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B4=BB=E5=8A=A8=E6=89=B9=E6=AC=A1=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E7=A1=AE=E8=AE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/cmb_cpn.proto | 2 + internal/biz/bo/product_bo.go | 20 +++++----- internal/biz/cmb.go | 57 +++++++++++++++------------- internal/biz/order.go | 9 ++--- internal/biz/vo/available_type.go | 46 ++++++++++++++++++++++ internal/biz/vo/cmb.go | 12 +++--- internal/data/model/product.gen.go | 20 +++++----- internal/pkg/helper/datetime.go | 14 +++++++ internal/pkg/helper/datetime_test.go | 31 +++++++++++++++ 9 files changed, 156 insertions(+), 55 deletions(-) create mode 100644 internal/biz/vo/available_type.go diff --git a/api/v1/cmb_cpn.proto b/api/v1/cmb_cpn.proto index 95e8319..9bf79df 100644 --- a/api/v1/cmb_cpn.proto +++ b/api/v1/cmb_cpn.proto @@ -124,8 +124,10 @@ message CmbQueryProductReply { string detail = 18 [json_name = "detail"]; // 批次开始日期 格式yyyy-mm-dd hh:mm:ss.sss + // 批次激活时间 string saleStartTime = 19 [json_name = "saleStartTime"]; // 批次结束时间,格式yyyy-mm-dd hh:mm:ss.sss + // 批次激活时间 string saleEndTime = 20 [json_name = "saleEndTime"]; // 错误码 diff --git a/internal/biz/bo/product_bo.go b/internal/biz/bo/product_bo.go index 70944dc..ea31ae8 100644 --- a/internal/biz/bo/product_bo.go +++ b/internal/biz/bo/product_bo.go @@ -7,13 +7,15 @@ import ( // ProductBo 领域实体Bo结构,字段和模型字段保持一致 type ProductBo struct { - ID int32 - Name string - ProductNo string - BatchName string - BatchNo string - MchId string - Channel vo.Channel - CreateTime *time.Time - UpdateTime *time.Time + ID int32 + Name string + ProductNo string + BatchName string + BatchNo string + MchId string + Channel vo.Channel + AvailableType vo.AvailableType + AvailableDays uint32 + CreateTime *time.Time + UpdateTime *time.Time } diff --git a/internal/biz/cmb.go b/internal/biz/cmb.go index 9c23b53..a09c6b5 100644 --- a/internal/biz/cmb.go +++ b/internal/biz/cmb.go @@ -112,22 +112,6 @@ func (v *VoucherBiz) CmbProductQuery(ctx context.Context, productNo string) (rep return err } - inputFormat := time.RFC3339 - - beginTime := "" - if wechatResp.AvailableBeginTime != nil { - // 解析开始时间 - availableBeginTime, _ := time.Parse(inputFormat, *wechatResp.AvailableBeginTime) - beginTime = availableBeginTime.Format(time.DateTime) - } - - endTime := "" - if wechatResp.AvailableEndTime != nil { - // 解析结束时间 - availableEndTime, _ := time.Parse(inputFormat, *wechatResp.AvailableEndTime) - endTime = availableEndTime.Format(time.DateTime) - } - reps = &v1.CmbQueryProductReply{ RespCode: vo.CmbResponseStatusSuccess.GetValue(), RespMsg: "成功", @@ -137,28 +121,49 @@ func (v *VoucherBiz) CmbProductQuery(ctx context.Context, productNo string) (rep MinAmount: "", AvailableType: "", AvailableDays: "", // 动态有效期天数 - StartTime: beginTime, - EndTime: endTime, + StartTime: "", + EndTime: "", AvailableStock: "", Detail: *wechatResp.Description, } + inputFormat := time.RFC3339 + + if wechatResp.AvailableBeginTime != nil { + // 解析开始时间 + availableBeginTime, _ := time.Parse(inputFormat, *wechatResp.AvailableBeginTime) + reps.StartTime = availableBeginTime.Format(time.DateTime) + } + + if wechatResp.AvailableEndTime != nil { + // 解析结束时间 + availableEndTime, _ := time.Parse(inputFormat, *wechatResp.AvailableEndTime) + reps.EndTime = availableEndTime.Format(time.DateTime) + } + + if wechatResp.StartTime != nil { + s, _ := time.Parse(inputFormat, *wechatResp.StartTime) + reps.SaleStartTime = s.Format(time.DateTime) + } + + if wechatResp.StopTime != nil { + e, _ := time.Parse(inputFormat, *wechatResp.StopTime) + reps.SaleEndTime = e.Format(time.DateTime) + } + reps.Amount = fmt.Sprintf("%d", *wechatResp.StockUseRule.FixedNormalCoupon.CouponAmount) reps.MinAmount = fmt.Sprintf("%d", *wechatResp.StockUseRule.FixedNormalCoupon.TransactionMinimum) availableStock := *wechatResp.StockUseRule.MaxCoupons - *wechatResp.DistributedCoupons reps.AvailableStock = fmt.Sprintf("%d", availableStock) - if wechatResp.StartTime != nil { - s, _ := time.Parse(inputFormat, *wechatResp.StartTime) - reps.SaleStartTime = s.Format(time.DateTime) - } - if wechatResp.StopTime != nil { - e, _ := time.Parse(inputFormat, *wechatResp.StopTime) - reps.SaleEndTime = e.Format(time.DateTime) + availableType, err := product.AvailableType.GetCmbAvailableType() + if err != nil { + return err } - reps.AvailableType = vo.CmbAvailableTypeFixed.GetValue() + reps.AvailableType = availableType.GetValue() + reps.AvailableDays = fmt.Sprintf("%d", product.AvailableDays) return nil }) diff --git a/internal/biz/order.go b/internal/biz/order.go index 4ecc846..3e544db 100644 --- a/internal/biz/order.go +++ b/internal/biz/order.go @@ -2,11 +2,10 @@ package biz import ( "context" - "errors" "fmt" "github.com/go-kratos/kratos/v2/log" "github.com/redis/go-redis/v9" - "gorm.io/gorm" + err2 "voucher/api/err" "voucher/internal/biz/bo" "voucher/internal/biz/vo" "voucher/internal/pkg/lock" @@ -34,8 +33,8 @@ func (v *VoucherBiz) order(ctx context.Context, req *bo.OrderCreateReqBo, produc // 真实发放 voucherNo, err = v.WechatCpnRepo.Order(ctx, order) if err != nil { - if err2 := v.fail(ctx, order, err.Error()); err2 != nil { - return nil, err2 + if err3 := v.fail(ctx, order, err.Error()); err3 != nil { + return nil, err3 } return nil, err } @@ -118,7 +117,7 @@ func (v *VoucherBiz) registerNotifyTag(ctx context.Context, stockCreatorMchID, s } wechatNotifyTag, err := v.WechatNotifyRegisterTagRepo.GetByStockIdAndMchId(ctx, stockCreatorMchID, stockID) - if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + if err != nil && !err2.IsDbNotFound(err) { return err } diff --git a/internal/biz/vo/available_type.go b/internal/biz/vo/available_type.go new file mode 100644 index 0000000..2cb16fe --- /dev/null +++ b/internal/biz/vo/available_type.go @@ -0,0 +1,46 @@ +package vo + +import "errors" + +type AvailableType uint8 + +const ( + AvailableTypeFixed AvailableType = iota + 1 + AvailableTypeDynamic +) + +var AvailableTypeMap = map[AvailableType]string{ + AvailableTypeFixed: "固定有效期", + AvailableTypeDynamic: "动态有效期", +} + +func (s AvailableType) GetText() string { + if t, ok := AvailableTypeMap[s]; ok { + return t + } + return "未知券领取类型" +} + +func (s AvailableType) GetValue() uint8 { + return uint8(s) +} + +func (s AvailableType) IsFixed() bool { + return s == AvailableTypeFixed +} + +func (s AvailableType) IsDynamic() bool { + return s == AvailableTypeDynamic +} + +var AvailableCmbTypeMap = map[AvailableType]CmbAvailableType{ + AvailableTypeFixed: CmbAvailableTypeFixed, + AvailableTypeDynamic: CmbAvailableTypeDynamic, +} + +func (s AvailableType) GetCmbAvailableType() (CmbAvailableType, error) { + if t, ok := AvailableCmbTypeMap[s]; ok { + return t, nil + } + return "", errors.New("未知券领取类型") +} diff --git a/internal/biz/vo/cmb.go b/internal/biz/vo/cmb.go index eede1d2..7707d5d 100644 --- a/internal/biz/vo/cmb.go +++ b/internal/biz/vo/cmb.go @@ -16,8 +16,8 @@ func (s CmbFuncName) GetValue() string { type CmbStatus string const ( - CmbStatusSuccess CmbStatus = "0" - CmbStatusUse CmbStatus = "1" + CmbStatusSuccess CmbStatus = "0" // 券可用 + CmbStatusUse CmbStatus = "1" // 券使用 CmbStatusExpired CmbStatus = "2" // 券过期-待确认是否通知 ) @@ -29,8 +29,8 @@ func (s CmbStatus) GetValue() string { type CmbResponseStatus string const ( - CmbResponseStatusSuccess CmbResponseStatus = "1000" - CmbResponseStatusFail CmbResponseStatus = "1001" + CmbResponseStatusSuccess CmbResponseStatus = "1000" // 响应成功 + CmbResponseStatusFail CmbResponseStatus = "1001" // 响应失败 ) func (s CmbResponseStatus) GetValue() string { @@ -41,8 +41,8 @@ func (s CmbResponseStatus) GetValue() string { type CmbAvailableType string const ( - CmbAvailableTypeFixed CmbAvailableType = "0" - CmbAvailableTypeDynamic CmbAvailableType = "1" + CmbAvailableTypeFixed CmbAvailableType = "0" // 固定有效期 + CmbAvailableTypeDynamic CmbAvailableType = "1" // 动态有效期 ) func (s CmbAvailableType) GetValue() string { diff --git a/internal/data/model/product.gen.go b/internal/data/model/product.gen.go index 598ddac..f1e3e69 100644 --- a/internal/data/model/product.gen.go +++ b/internal/data/model/product.gen.go @@ -12,15 +12,17 @@ const TableNameProduct = "product" // Product mapped from table type Product struct { - ID int32 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"` - Name string `gorm:"column:name;not null;comment:商品名称" json:"name"` // 商品名称 - ProductNo string `gorm:"column:product_no;not null;comment:商品编号" json:"product_no"` // 商品编号 - BatchName string `gorm:"column:batch_name;not null;comment:批次名称" json:"batch_name"` // 批次名称 - BatchNo string `gorm:"column:batch_no;not null;comment:立减金批次号" json:"batch_no"` // 立减金批次号 - MchId string `gorm:"column:mch_id;not null;comment:商户号,创建批次的商户号" json:"mch_id"` // 商户号,创建批次的商户号 - Channel uint8 `gorm:"column:channel;not null;comment:1:微信 2:支付宝" json:"channel"` // 1:微信 2:支付宝 - CreateTime *time.Time `gorm:"column:create_time;not null" json:"create_time"` - UpdateTime *time.Time `gorm:"column:update_time" json:"update_time"` + ID int32 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"` + Name string `gorm:"column:name;not null;comment:商品名称" json:"name"` // 商品名称 + ProductNo string `gorm:"column:product_no;not null;comment:商品编号" json:"product_no"` // 商品编号 + BatchName string `gorm:"column:batch_name;not null;comment:批次名称" json:"batch_name"` // 批次名称 + BatchNo string `gorm:"column:batch_no;not null;comment:立减金批次号" json:"batch_no"` // 立减金批次号 + MchId string `gorm:"column:mch_id;not null;comment:商户号,创建批次的商户号" json:"mch_id"` // 商户号,创建批次的商户号 + Channel uint8 `gorm:"column:channel;not null;comment:1:微信 2:支付宝" json:"channel"` // 1:微信 2:支付宝 + AvailableType uint8 `gorm:"column:available_type;not null;comment:1:固定有效期 2:动态有效期" json:"available_type"` + AvailableDays uint32 `gorm:"column:available_days;not null;comment:领取后多少天内" json:"available_days"` + CreateTime *time.Time `gorm:"column:create_time;not null" json:"create_time"` + UpdateTime *time.Time `gorm:"column:update_time" json:"update_time"` } // TableName Product's table name diff --git a/internal/pkg/helper/datetime.go b/internal/pkg/helper/datetime.go index e067c31..760e903 100755 --- a/internal/pkg/helper/datetime.go +++ b/internal/pkg/helper/datetime.go @@ -257,3 +257,17 @@ func IsInSameNaturalMonth(startTime, endTime time.Time) bool { expectedEndTime.Minute() == endTime.Minute() && expectedEndTime.Second() == endTime.Second() } + +func DaysBetween(start, end time.Time) (int, error) { + // 2. 统一到同一天的 0 点(避免时分秒干扰) + startDay := start.Truncate(24 * time.Hour) + endDay := end.Truncate(24 * time.Hour) + + // 3. 计算天数(含结束日) + days := int(endDay.Sub(startDay).Hours() / 24) + if end.After(endDay) { // 结束时间非 0 点,加 1 天 + days++ + } + + return days, nil +} diff --git a/internal/pkg/helper/datetime_test.go b/internal/pkg/helper/datetime_test.go index 5c5d61f..deab1a4 100644 --- a/internal/pkg/helper/datetime_test.go +++ b/internal/pkg/helper/datetime_test.go @@ -101,3 +101,34 @@ func TestRFC3339(t *testing.T) { fmt.Printf("格式化后的开始时间: %s\n", formattedBeginTime) fmt.Printf("格式化后的结束时间: %s\n", formattedEndTime) } + +func TestDaysBetween(t *testing.T) { + // 输入的时间字符串 + availableBeginTimeStr := "2025-03-07T00:00:00+08:00" + availableEndTimeStr := "2025-06-05T23:59:59+08:00" + + // 定义输入时间字符串的格式 + inputFormat := time.RFC3339 + + // 解析开始时间 + availableBeginTime, err := time.Parse(inputFormat, availableBeginTimeStr) + if err != nil { + fmt.Printf("解析开始时间出错: %v\n", err) + return + } + + // 解析结束时间 + availableEndTime, err := time.Parse(inputFormat, availableEndTimeStr) + if err != nil { + fmt.Printf("解析结束时间出错: %v\n", err) + return + } + + days, err := DaysBetween(availableBeginTime, availableEndTime) + if err != nil { + fmt.Printf("解析结束时间出错: %v\n", err) + return + } + + t.Log(days) +}