Files
full/module/ec/order/internal/logic/summary/confirm.go
2026-09-22 21:15:34 +08:00

131 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package summary
import (
"context"
"time"
"bsm/full/module/ec/order/internal/impl"
"bsm/full/module/ec/order/internal/models"
pb "bsm/full/module/ec/order/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
)
// 确认订单,物流,优惠卷等其它信息
func Confirm(ctx context.Context, in *pb.ConfirmRequest) (reply *pb.ConfirmReply, err error) {
// parse authorization meta.
auth, err := service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
var (
logisFee int64 = 0
couponAmount int64 = 0
summary = new(models.OrderSummary)
coupon = new(models.OrderCoupon)
)
err = impl.DBService.Where("order_no = ?", in.OrderNo).First(&summary).Error
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
if in.AddressIdentity != "" {
/*
err = impl.DBService.Where("id=?", in.AddressId).First(&address).Error
if err == nil {
summary.Address = address.Detail
summary.County = address.Country
summary.Province = address.Province
summary.City = address.City
summary.Area = address.Area
summary.Contact = address.Contact
summary.Phone = address.Phone
logisFee = GetLogisticsFee(summary.TotalPrice, address.Province)
}
*/
}
if logisFee > 0 {
summary.TotalPrice = summary.TotalPrice + logisFee
}
if in.CouponIdentity != "" {
err = impl.DBService.Where("identity=?", in.CouponIdentity).First(&coupon).Error
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrRecordNotFound
}
// 校验优惠券归属passport_id 或 passport_identity 命中调用者才算本人所有。
if coupon.PassportIdentity != auth.Identity && coupon.PassportID != auth.ID {
return nil, errcode.ErrPermissionDenied
}
// 校验使用状态仅可用的优惠券status=2可使用。
if coupon.Status != 2 {
return nil, errcode.ErrUnavailable
}
// 校验有效期started/expired 为字符串,格式可解析时强制校验,无法解析时跳过。
now := time.Now()
if started, ok := parseCouponTime(coupon.Started); ok && now.Before(started) {
return nil, errcode.ErrUnavailable
}
if expired, ok := parseCouponTime(coupon.Expired); ok && now.After(expired) {
return nil, errcode.ErrUnavailable
}
// 核销优惠券:带状态条件更新并检查影响行数,避免重复使用同一张券。
result := impl.DBService.Model(&models.OrderCoupon{}).Where("identity = ? AND status = 2", in.CouponIdentity).Update("status", 3)
if result.Error != nil {
printer.Error(result.Error.Error())
return nil, errcode.ErrDB
}
if result.RowsAffected == 0 {
return nil, errcode.ErrUnavailable
}
couponAmount = coupon.Amount
}
if couponAmount > 0 {
// 优惠券为减免项,抵扣后总额不得为负:抵扣额不超过订单金额。
if couponAmount >= summary.TotalPrice {
summary.TotalPrice = 0
} else {
summary.TotalPrice = summary.TotalPrice - couponAmount
}
// 回写所用券与其抵扣额,供取消订单时退券使用。
summary.CouponIdentity = in.CouponIdentity
summary.CouponAmount = couponAmount
}
summary.Status = 1
// 显式指定需落库的列,保证总额抵扣为 0 时也能写入。
err = impl.DBService.Where("order_no = ?", in.OrderNo).
Select("total_price", "coupon_identity", "coupon_amount", "status").
Updates(summary).Error
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
return &pb.ConfirmReply{
TotalPrice: summary.TotalPrice,
}, nil
}
// parseCouponTime 解析优惠券 started/expired 时间字段,兼容"2006-01-02 15:04:05"与"2006-01-02"两种格式;
// 无法解析时返回 ok=false由调用方跳过该项校验。
func parseCouponTime(v string) (time.Time, bool) {
if v == "" {
return time.Time{}, false
}
for _, layout := range []string{"2006-01-02 15:04:05", "2006-01-02"} {
if t, err := time.ParseInLocation(layout, v, time.Local); err == nil {
return t, true
}
}
return time.Time{}, false
}