diff --git a/internal/application/order/service.go b/internal/application/order/service.go index ff1dce3..97d9d4f 100644 --- a/internal/application/order/service.go +++ b/internal/application/order/service.go @@ -9,6 +9,7 @@ import ( "cls/internal/domain/order" "cls/internal/domain/payment" "cls/internal/domain/price" + "cls/internal/domain/run_log" "cls/internal/domain/user" "cls/internal/infrastructure/payment/wechat_pay" "cls/pkg/logger" @@ -27,6 +28,7 @@ type OrderService struct { articleRepo article.ArticleRepository columnRepo column.ColumnRepository priceRepo price.PriceRepository + runLog run_log.RunLogRepository payService *wechat_pay.PayService userRepo user.UserRepository couponRepo coupon.CouponRepository @@ -37,7 +39,7 @@ type OrderService struct { func NewOrderService(repo order.AggregateRepository, articleRepo article.ArticleRepository, priceRepo price.PriceRepository, userRepo user.UserRepository, couponRepo coupon.CouponRepository, payService *wechat_pay.PayService, - orderRepo order.OrderRepository, + orderRepo order.OrderRepository, runLog run_log.RunLogRepository, columnRepo column.ColumnRepository, log logger.New) *OrderService { return &OrderService{ repo: repo, @@ -114,9 +116,6 @@ func (s *OrderService) CreateOrder(req *CreateOrderRequest, ePhone string) (*jsa descripton.Write([]byte(fmt.Sprintf("订购专栏【%s】%d个月", cInfo.Title, req.Duration))) } - fmt.Println("==============") - fmt.Printf("%+v\n", req) - fmt.Println("==============") couponInfo := &coupon.Coupon{ID: req.CouponId} if req.CouponId != 0 { @@ -131,14 +130,13 @@ func (s *OrderService) CreateOrder(req *CreateOrderRequest, ePhone string) (*jsa couponInfo.UserID = uInfo.Id couponInfo.UsedAt = time.Now() fmt.Printf("%+v\n", couponInfo) - if couponInfo.CanUse(req.Amount) { + if couponInfo.CanUse(priceInfo.GetFinalAmount()) { fmt.Println("can use") req.Amount = priceInfo.GetFinalAmountWitCoupon(couponInfo.Value) + couponInfo.Status = coupon.CouponStatusUsed } else { fmt.Println("cann't use it") } - - couponInfo.Status = coupon.CouponStatusUsed } else { fmt.Println("不使用优惠券") req.Amount = priceInfo.GetFinalAmount() @@ -155,38 +153,48 @@ func (s *OrderService) CreateOrder(req *CreateOrderRequest, ePhone string) (*jsa OrderNo: s.generateOrderNo(), UserID: uInfo.Id, TargetID: req.TargetID, - Coupon: req.CouponId, Type: req.Type, Amount: req.Amount, Duration: req.Duration, Status: order.OrderStatusPending, Description: req.Description, } + s.runLog.LogOrderInfo(o.OrderNo, fmt.Sprintf("%+v", o)) + // 创建聚合根 aggregate := order.NewOrderAggregate(o) err = aggregate.CreatePayment(payment.PaymentTypeWechat) if err != nil { + s.runLog.LogOrderInfo(o.OrderNo, err.Error(), "创建支付订单") s.log.Error(err) return nil, err } - aggregate.Coupon = couponInfo - - resp, err := s.payService.CreatePayment(&wechat_pay.PaymentInfo{ + if couponInfo.Status == coupon.CouponStatusUsed { + aggregate.Coupon = couponInfo + o.Coupon = req.CouponId + } + pInfo := &wechat_pay.PaymentInfo{ Description: descripton.String(), Attach: "", OutTradeNo: o.OrderNo, AmountTotal: req.Amount, OpenId: uInfo.Openid, - }) + } + s.runLog.LogOrderInfo(o.OrderNo, fmt.Sprintf("%+v", pInfo)) + + resp, err := s.payService.CreatePayment(pInfo) if err != nil { + s.runLog.LogOrderInfo(o.OrderNo, err.Error(), "创建WX支付订单") s.log.Error(err) return nil, err } + s.runLog.LogOrderInfo(o.OrderNo, fmt.Sprintf("%+v", resp)) // 保存聚合根 if err = s.repo.Save(aggregate); err != nil { + s.runLog.LogOrderInfo(o.OrderNo, err.Error(), "保存订单") s.log.Error(err) return nil, err } @@ -214,18 +222,21 @@ func (s *OrderService) CancelOrder(req *CreateOrderRequest, ePhone string) error agg, err := s.repo.GetByOrderNo(oInfo.OrderNo) if err != nil { + s.runLog.LogOrderInfo(oInfo.OrderNo, err.Error(), "GetByOrderNo") s.log.Error(err) return err } err = agg.HandlePaymentFailed("H5端取消支付") if err != nil { + s.runLog.LogOrderInfo(oInfo.OrderNo, err.Error(), "HandlePaymentFailed") s.log.Error(err) return err } err = s.repo.PayFailed(agg) if err != nil { + s.runLog.LogOrderInfo(oInfo.OrderNo, err.Error(), "PayFailed") s.log.Error(err) } @@ -233,22 +244,27 @@ func (s *OrderService) CancelOrder(req *CreateOrderRequest, ePhone string) error } func (s *OrderService) OrderNotify(transaction *payments.Transaction) error { - s.log.Info("订单回调======>") - s.log.Infof("%+v\n", *transaction) + s.runLog.LogOrderInfo(*transaction.OutTradeNo, fmt.Sprintf("%+v", *transaction)) aggregate, err := s.repo.GetByOrderNo(*transaction.OutTradeNo) if err != nil { + s.runLog.LogOrderInfo(*transaction.OutTradeNo, err.Error(), "GetByOrderNo") s.log.Error(err) return err } + s.runLog.LogOrderInfo(aggregate.Order.OrderNo, fmt.Sprintf("%+v", transaction)) if *transaction.Amount.Total != aggregate.Order.Amount { + s.runLog.LogOrderInfo(aggregate.Order.OrderNo, "订单金额不匹配") + err = aggregate.HandlePaymentFailed(fmt.Sprintf("订单金额不匹配 %+v", transaction)) if err != nil { + s.runLog.LogOrderInfo(aggregate.Order.OrderNo, err.Error(), "HandlePaymentFailed") s.log.Error(err) return err } err = s.repo.PayFailed(aggregate) if err != nil { + s.runLog.LogOrderInfo(aggregate.Order.OrderNo, err.Error(), "PayFailed") s.log.Error(err) return err } @@ -257,22 +273,26 @@ func (s *OrderService) OrderNotify(transaction *payments.Transaction) error { if *transaction.TradeState == "SUCCESS" { err = aggregate.HandlePaymentSuccess(*transaction.TransactionId, fmt.Sprintf("%+v", transaction)) if err != nil { + s.runLog.LogOrderInfo(aggregate.Order.OrderNo, err.Error(), "HandlePaymentSuccess") s.log.Error(err) return err } err = s.repo.PaySuccess(aggregate) if err != nil { + s.runLog.LogOrderInfo(aggregate.Order.OrderNo, err.Error(), "PaySuccess") s.log.Error(err) return err } } else { err = aggregate.HandlePaymentFailed(fmt.Sprintf("%+v", transaction)) if err != nil { + s.runLog.LogOrderInfo(aggregate.Order.OrderNo, err.Error(), "HandlePaymentFailed") s.log.Error(err) return err } err = s.repo.PayFailed(aggregate) if err != nil { + s.runLog.LogOrderInfo(aggregate.Order.OrderNo, err.Error(), "PayFailed") s.log.Error(err) return err } diff --git a/internal/application/order/service_test.go b/internal/application/order/service_test.go new file mode 100644 index 0000000..f7f2367 --- /dev/null +++ b/internal/application/order/service_test.go @@ -0,0 +1,56 @@ +package order + +import ( + "cls/internal/domain/coupon" + price2 "cls/internal/domain/price" + "testing" +) + +func TestCreateOrder(t *testing.T) { + price := &price2.Price{ + ID: 0, + TargetID: 2000798, + Type: 1, + Amount: 11, + OneMonthPrice: 0, + ThreeMonthsPrice: 0, + SixMonthsPrice: 0, + OneYearPrice: 0, + FirstMontDiscount: 0, + Discount: 0.1, + } + + c := coupon.Coupon{Status: coupon.CouponStatusNormal, MinAmount: 2, Value: 2} + t.Log(c.CanUse(price.GetFinalAmount())) + + //v := price.GetFinalAmount() + //t.Log("0.1", price.GetFinalAmountWitCoupon(1)) + t.Log("0.1", price.GetFinalAmount()) + price.Discount = 0.2 + //t.Log("0.2", price.GetFinalAmountWitCoupon(1)) + t.Log("0.2", price.GetFinalAmount()) + price.Discount = 0.3 + //t.Log("0.3", price.GetFinalAmountWitCoupon(1)) + t.Log("0.3", price.GetFinalAmount()) + price.Discount = 0.4 + t.Log("0.4", price.GetFinalAmountWitCoupon(1)) + //t.Log("0.4", price.GetFinalAmount()) + price.Discount = 0.5 + t.Log("0.5", price.GetFinalAmountWitCoupon(1)) + //t.Log("0.5", price.GetFinalAmount()) + price.Discount = 0.6 + //t.Log("0.6", price.GetFinalAmountWitCoupon(1)) + t.Log("0.6", price.GetFinalAmount()) + price.Discount = 0.7 + //t.Log("0.7", price.GetFinalAmountWitCoupon(1)) + t.Log("0.7", price.GetFinalAmount()) + price.Discount = 0.8 + //t.Log("0.8", price.GetFinalAmountWitCoupon(1)) + t.Log("0.8", price.GetFinalAmount()) + price.Discount = 0.9 + //t.Log("0.9", price.GetFinalAmountWitCoupon(1)) + t.Log("0.9", price.GetFinalAmount()) + price.Discount = 1 + //t.Log("1", price.GetFinalAmountWitCoupon(1)) + t.Log("1", price.GetFinalAmount()) +} diff --git a/internal/domain/order/aggregate.go b/internal/domain/order/aggregate.go index c6ba3ef..63a652a 100644 --- a/internal/domain/order/aggregate.go +++ b/internal/domain/order/aggregate.go @@ -65,6 +65,7 @@ func (a *OrderAggregate) HandlePaymentSuccess(transactionID string, notifyData s ContentId: a.Order.TargetID, ContentType: purchase.ContentType(a.Order.Type), Price: float64(a.Order.Amount) / 100, // 转换为元 + OrderNo: a.Order.OrderNo, Duration: a.Order.Duration, ContentSource: purchase.ContentSourceBuy, ExpiredAt: time.Now().AddDate(0, a.Order.Duration, 0), diff --git a/internal/domain/price/entity.go b/internal/domain/price/entity.go index 71186c9..ece1197 100644 --- a/internal/domain/price/entity.go +++ b/internal/domain/price/entity.go @@ -1,6 +1,7 @@ package price import ( + "math" "time" ) @@ -62,7 +63,7 @@ func (p *Price) GetFinalAmount() int64 { // float32 → float64,避免精度损失 final := float32(p.Amount) * p.Discount - return int64(final) + return int64(math.Round(float64(final))) } func (p *Price) GetFinalAmountWitCoupon(value int64) int64 { diff --git a/internal/domain/purchase/entity.go b/internal/domain/purchase/entity.go index 0cabeb1..7201c4d 100644 --- a/internal/domain/purchase/entity.go +++ b/internal/domain/purchase/entity.go @@ -20,12 +20,13 @@ const ( // Purchase 内容购买记录 type Purchase struct { - Id uint64 `xorm:"pk autoincr 'id'" json:"id"` - UserId uint64 `xorm:"notnull 'user_id'" json:"userId"` - ContentId uint64 `xorm:"notnull 'content_id'" json:"contentId"` - ContentType ContentType `xorm:"tinyint(1) notnull 'content_type'" ` - Price float64 `xorm:"decimal(10,2) notnull 'price'" json:"price"` - Duration int `xorm:"notnull 'duration'" json:"duration"` // 购买时长(月),文章为0表示永久 + Id uint64 `xorm:"pk autoincr 'id'" json:"id"` + UserId uint64 `xorm:"notnull 'user_id'" json:"userId"` + ContentId uint64 `xorm:"notnull 'content_id'" json:"contentId"` + ContentType ContentType `xorm:"tinyint(1) notnull 'content_type'" ` + Price float64 `xorm:"decimal(10,2) notnull 'price'" json:"price"` + Duration int `xorm:"notnull 'duration'" json:"duration"` // 购买时长(月),文章为0表示永久 + OrderNo string ExpiredAt time.Time `xorm:"notnull 'expired_at'" json:"expiredAt"` // 过期时间,文章为null表示永久 ContentSource ContentSource `xorm:"tinyint(1) notnull 'content_source'" ` Status int8 `xorm:"tinyint(1) default 1 'status'" json:"status"` diff --git a/internal/domain/run_log/entity.go b/internal/domain/run_log/entity.go new file mode 100644 index 0000000..9d50099 --- /dev/null +++ b/internal/domain/run_log/entity.go @@ -0,0 +1,20 @@ +package run_log + +import "time" + +type RunLogType uint8 + +const ( + LogTypeOrder = iota + 1 + LogTypePayment + LogTypePurchase +) + +type RunLog struct { + Id int64 + LogType RunLogType + OrderNo string + Remark string + Info string `xorm:"varchar(5000)"` + Created time.Time `xorm:"created"` +} diff --git a/internal/domain/run_log/repository.go b/internal/domain/run_log/repository.go new file mode 100644 index 0000000..d21d7c8 --- /dev/null +++ b/internal/domain/run_log/repository.go @@ -0,0 +1,7 @@ +package run_log + +type RunLogRepository interface { + Log(log *RunLog) + LogOrderInfo(orderNo string, info string, remark ...string) + LogPaymentInfo(orderNo string, info string, remark ...string) +} diff --git a/internal/infrastructure/persistence/auth/auth_repo.go b/internal/infrastructure/persistence/auth/auth_repo.go index b6e9f74..1aaeec7 100644 --- a/internal/infrastructure/persistence/auth/auth_repo.go +++ b/internal/infrastructure/persistence/auth/auth_repo.go @@ -55,7 +55,7 @@ func (a AuthRepositoryORM) LoginByPassword(phone, password string) (string, erro func (a AuthRepositoryORM) LoginByCaptcha(phone, openid string) (string, error) { u := &domainUser.User{} - has, err := a.engine.Where(builder.Eq{"phone": phone}.And(builder.Eq{"openid": openid})).Get(u) + has, err := a.engine.Where(builder.Eq{"phone": phone}).Get(u) if err != nil { a.log.Error(err) return "", err @@ -72,6 +72,10 @@ func (a AuthRepositoryORM) LoginByCaptcha(phone, openid string) (string, error) } } + if u.Openid != openid { + return "", errors.New(fmt.Sprintf("openid和手机号不匹配,[%s]-[%s]", phone, openid)) + } + if u.Status == 0 { return "", errors.New(fmt.Sprintf("用户【%d】禁止登录", u.Id)) } diff --git a/internal/infrastructure/persistence/run_log/run_log_repo.go b/internal/infrastructure/persistence/run_log/run_log_repo.go new file mode 100644 index 0000000..88b917e --- /dev/null +++ b/internal/infrastructure/persistence/run_log/run_log_repo.go @@ -0,0 +1,50 @@ +package run_log + +import ( + "cls/internal/domain/run_log" + "cls/pkg/logger" + "cls/pkg/xorm_engine" + "strings" +) + +type RunLogRepositoryORM struct { + engine *xorm_engine.Engine + log logger.Logger +} + +func NewRunLogRepositoryORM(engine *xorm_engine.Engine, log logger.New) run_log.RunLogRepository { + return &RunLogRepositoryORM{engine, log("cls:persistence:run_log")} +} + +var _ run_log.RunLogRepository = (*RunLogRepositoryORM)(nil) + +func (r *RunLogRepositoryORM) Log(log *run_log.RunLog) { + _, err := r.engine.Insert(log) + if err != nil { + r.log.Error(err) + } +} + +func (r *RunLogRepositoryORM) LogOrderInfo(orderNo string, info string, remark ...string) { + _, err := r.engine.Insert(&run_log.RunLog{ + LogType: run_log.LogTypeOrder, + OrderNo: orderNo, + Info: info, + Remark: strings.Join(remark, "|"), + }) + if err != nil { + r.log.Error(err) + } +} + +func (r *RunLogRepositoryORM) LogPaymentInfo(orderNo string, info string, remark ...string) { + _, err := r.engine.Insert(&run_log.RunLog{ + LogType: run_log.LogTypePayment, + OrderNo: orderNo, + Info: info, + Remark: strings.Join(remark, "|"), + }) + if err != nil { + r.log.Error(err) + } +} diff --git a/internal/modules/module.go b/internal/modules/module.go index 4bc1fbc..37c1ae4 100644 --- a/internal/modules/module.go +++ b/internal/modules/module.go @@ -9,6 +9,7 @@ import ( "cls/internal/domain/payment" "cls/internal/domain/price" "cls/internal/domain/purchase" + "cls/internal/domain/run_log" "cls/internal/domain/user" "cls/pkg/logger" "cls/pkg/xorm_engine" @@ -49,6 +50,7 @@ func registerModels(engine *xorm_engine.Engine, logger logger.New) { &order.Order{}, &payment.Payment{}, &coupon.Coupon{}, + &run_log.RunLog{}, ); err != nil { log.Error(err) } diff --git a/internal/modules/order_module.go b/internal/modules/order_module.go index bb43a79..9246f8c 100644 --- a/internal/modules/order_module.go +++ b/internal/modules/order_module.go @@ -3,6 +3,7 @@ package modules import ( service "cls/internal/application/order" repo "cls/internal/infrastructure/persistence/order" + "cls/internal/infrastructure/persistence/run_log" "cls/internal/interfaces" "cls/internal/interfaces/order" "go.uber.org/fx" @@ -14,6 +15,7 @@ var OrderModule = fx.Module("order", interfaces.AsHandler(order.NewOrderHandler), repo.NewOrderRepositoryORM, repo.NewAggregateRepositoryORM, + run_log.NewRunLogRepositoryORM, service.NewOrderService, ), )