This commit is contained in:
JNG 2026-06-09 13:51:25 +08:00
parent 87fc823c1c
commit a9738b1bc8
3 changed files with 80 additions and 15 deletions

View File

@ -27,6 +27,27 @@ func ExistsOverduePayLaterBundle(customerID string) (bool, error) {
return cnt > 0, nil
}
// ExistsUnpaidNormalBundle 是否存在「套餐 + 普通支付 + 未支付」订单
// 命中即应拦截普通支付类型的套餐下单。
func ExistsUnpaidNormalBundle(customerID string) (bool, error) {
if customerID == "" {
return false, nil
}
var cnt int64
err := app.ModuleClients.BundleDB.
Model(&model.BundleOrderRecords{}).
Where("customer_id = ? AND order_mode = ? AND status IN (?, ?)",
customerID,
model.OrderModeNormal,
model.BundleStatusSignedUnpaid,
model.BundleStatusOverdue,
).Count(&cnt).Error
if err != nil {
return false, err
}
return cnt > 0, nil
}
// ExistsPendingPayLaterBundle 是否存在「套餐 + 先用后付 + 待付款(未逾期)」订单。
// 命中即提示用户去支付,避免同时存在多个先用后付未支付套餐订单。
func ExistsPendingPayLaterBundle(customerID string) (bool, error) {
@ -70,6 +91,29 @@ func ExistsActiveBundle(customerID string) (bool, error) {
return cnt > 0, nil
}
// ExistsValidBundleForValueAdd 是否存在有效的套餐订单可用于增值服务下单
// 规则:普通已支付 或 先用后付未逾期未支付
func ExistsValidBundleForValueAdd(customerID string) (bool, error) {
if customerID == "" {
return false, nil
}
var cnt int64
err := app.ModuleClients.BundleDB.
Model(&model.BundleOrderRecords{}).
Where(`customer_id = ? AND (
(order_mode = ? AND status = ?) OR
(order_mode = ? AND pay_later_status IN (?, ?) AND status != ?)
)`,
customerID,
model.OrderModeNormal, model.BundleStatusPaid,
model.OrderModePayLater, model.PayLaterStatusPending, model.PayLaterStatusPaid, model.BundleStatusOverdue,
).Count(&cnt).Error
if err != nil {
return false, err
}
return cnt > 0, nil
}
// MarkBundleOverdue 套餐先用后付到期扫描:将到期且未付的订单置为 Overdue
func MarkBundleOverdue(now time.Time) (int64, error) {
t := now.Format("2006-01-02 15:04:05")

View File

@ -7,11 +7,11 @@ import (
"time"
)
// CheckOrderEligibility 下单前置校验(先用后付)
// CheckOrderEligibility 下单前置校验
// 规则:
// 4. 套餐订单 + 先用后付 + 到期未付:禁止所有套餐下单
// 5. 套餐到期未付不影响增值下单(无限续杯)
// 3. 套餐有效期内只允许存在一笔
// 1. 套餐订单:存在先用后付逾期未支付 或 普通订单未支付 → 拦截下单
// 2. 增值服务:必须有套餐订单且该套餐是(普通已支付 或 先用后付未逾期)→ 才能下单
// 3. 套餐订单有效期内只允许存在一笔
func CheckOrderEligibility(req *bundle.CheckOrderEligibilityRequest) (*bundle.CheckOrderEligibilityResponse, error) {
res := &bundle.CheckOrderEligibilityResponse{Allow: true}
if req == nil || req.CustomerID == "" {
@ -21,9 +21,9 @@ func CheckOrderEligibility(req *bundle.CheckOrderEligibilityRequest) (*bundle.Ch
return res, nil
}
// 套餐下单校验(增值订单不受这些限制:无限续杯)
// 套餐下单校验
if req.OrderKind == model.OrderKindBundle {
// 1. 先用后付套餐逾期未付 → 不可再下单任何套餐(普通/先用后付)。
// 1. 先用后付逾期未付 → 拦截
overdue, err := dao.ExistsOverduePayLaterBundle(req.CustomerID)
if err != nil {
return res, err
@ -35,19 +35,19 @@ func CheckOrderEligibility(req *bundle.CheckOrderEligibilityRequest) (*bundle.Ch
return res, nil
}
// 2. 已存在先用后付且未支付(待付款)的套餐订单 → 提示去支付。
pending, err := dao.ExistsPendingPayLaterBundle(req.CustomerID)
// 2. 普通订单未支付 → 拦截
unpaid, err := dao.ExistsUnpaidNormalBundle(req.CustomerID)
if err != nil {
return res, err
}
if pending {
if unpaid {
res.Allow = false
res.Reason = model.OrderGateReasonPayLaterBundlePending
res.Msg = "当前已存在未支付订单,请去我的页面支付"
res.Reason = "NORMAL_BUNDLE_UNPAID"
res.Msg = "当前已存在未支付订单,请先完成支付"
return res, nil
}
// 3. 有效期内已存在套餐订单 → 保证套餐有效期内只存在一笔
// 3. 有效期内已存在套餐订单 → 保证套餐有效期内只存在一笔
exists, err := dao.ExistsActiveBundle(req.CustomerID)
if err != nil {
return res, err
@ -58,6 +58,18 @@ func CheckOrderEligibility(req *bundle.CheckOrderEligibilityRequest) (*bundle.Ch
res.Msg = "有效期内已存在套餐订单"
return res, nil
}
} else if req.OrderKind == model.OrderKindValueAdd {
// 增值服务下单校验:必须有有效的套餐订单
hasValid, err := dao.ExistsValidBundleForValueAdd(req.CustomerID)
if err != nil {
return res, err
}
if !hasValid {
res.Allow = false
res.Reason = "NO_VALID_BUNDLE"
res.Msg = "请先购买套餐订单后再购买增值服务"
return res, nil
}
}
return res, nil

View File

@ -16,10 +16,14 @@ import (
func CreateOrderRecord(req *bundle.OrderCreateRecord) (res *bundle.CommonResponse, err error) {
res = new(bundle.CommonResponse)
// 先用后付下单前置校验
// 下单前置校验
orderKind := model.OrderKindBundle
if req.OrderType == model.OrderTypeValueAdd {
orderKind = model.OrderKindValueAdd
}
if eligibility, e := CheckOrderEligibility(&bundle.CheckOrderEligibilityRequest{
CustomerID: req.CustomerID,
OrderKind: model.OrderKindBundle,
OrderKind: orderKind,
OrderMode: req.OrderMode,
}); e != nil {
return nil, e
@ -86,6 +90,11 @@ func CreateOrderRecord(req *bundle.OrderCreateRecord) (res *bundle.CommonRespons
if orderMode == model.OrderModePayLater && payLaterStatus == 0 && req.Status != model.BundleStatusPaid {
payLaterStatus = model.PayLaterStatusPending
}
// OrderType 从请求中获取,不写死
orderType := req.OrderType
if orderType == 0 {
orderType = model.OrderTypeBundle // 默认套餐订单
}
orderRecord := &model.BundleOrderRecords{
UUID: orderUUID,
OrderNo: orderNo,
@ -117,7 +126,7 @@ func CreateOrderRecord(req *bundle.OrderCreateRecord) (res *bundle.CommonRespons
DueTime: req.DueTime,
PayLaterStatus: payLaterStatus,
ContractTplType: req.ContractTplType,
OrderType: model.OrderTypeBundle, // 套餐订单
OrderType: orderType,
}
res, err = dao.CreateOrderRecord(orderRecord)
return