添加定时同步订单状态

This commit is contained in:
JNG 2026-06-10 16:09:03 +08:00
parent d03bdbba29
commit e78ef82305
11 changed files with 1616 additions and 1248 deletions

View File

@ -1314,3 +1314,15 @@
{"level":"\u001b[34mINFO\u001b[0m","time":"2025-03-29T11:31:52.521+0800","caller":"zookeeper/listener.go:179","message":"[ZkEventListener][listenServiceNodeEvent]Get a EventNodeDeleted event for path {/dubbo/com.fontree.microservices.fiee.bundle/providers/tri%3A%2F%2F192.168.88.77%3A20201%2Fcom.fontree.microservices.fiee.bundle%3Fanyhost%3Dtrue%26application%3Ddubbo.io%26bean.name%3DBundleProvider%26cluster%3Dfailover%26export%3Dtrue%26interface%3Dcom.fontree.microservices.fiee.bundle%26loadbalance%3Drandom%26message_size%3D4%26metadata-type%3Dlocal%26methods%3DBundleDetail%2CBundleList%2CCreateBundle%2CCreateOrderRecord%2CCreateValueAddBundle%2CDeleteBundle%2COrderRecordsDetail%2COrderRecordsList%2CUpdateBundle%2CUpdateOrderRecord%2CUpdateOrderRecordByOrderNo%2CValueAddBundleDetail%2CValueAddBundleList%26module%3Dsample%26name%3Ddubbo.io%26organization%3Ddubbo-go%26owner%3Ddubbo-go%26pid%3D93332%26registry%3Dzookeeper%26registry.role%3D3%26release%3Ddubbo-golang-3.0.0%26retries%3D0%26service.filter%3Dtps%2Ctracing%26side%3Dprovider%26timestamp%3D1743219110%26tps.limit.interval%3D1000%26tps.limit.rate%3D30%26tps.limit.rejected.handler%3DDefaultValueHandler%26tps.limit.strategy%3DfixedWindow%26tps.limiter%3Dmethod-service%26warmup%3D100}"}
{"level":"\u001b[33mWARN\u001b[0m","time":"2025-03-29T11:31:52.521+0800","caller":"zookeeper/listener.go:338","message":"listenDirEvent->listenSelf(zk path{/dubbo/com.fontree.microservices.fiee.bundle/providers/tri%3A%2F%2F192.168.88.77%3A20201%2Fcom.fontree.microservices.fiee.bundle%3Fanyhost%3Dtrue%26application%3Ddubbo.io%26bean.name%3DBundleProvider%26cluster%3Dfailover%26export%3Dtrue%26interface%3Dcom.fontree.microservices.fiee.bundle%26loadbalance%3Drandom%26message_size%3D4%26metadata-type%3Dlocal%26methods%3DBundleDetail%2CBundleList%2CCreateBundle%2CCreateOrderRecord%2CCreateValueAddBundle%2CDeleteBundle%2COrderRecordsDetail%2COrderRecordsList%2CUpdateBundle%2CUpdateOrderRecord%2CUpdateOrderRecordByOrderNo%2CValueAddBundleDetail%2CValueAddBundleList%26module%3Dsample%26name%3Ddubbo.io%26organization%3Ddubbo-go%26owner%3Ddubbo-go%26pid%3D93332%26registry%3Dzookeeper%26registry.role%3D3%26release%3Ddubbo-golang-3.0.0%26retries%3D0%26service.filter%3Dtps%2Ctracing%26side%3Dprovider%26timestamp%3D1743219110%26tps.limit.interval%3D1000%26tps.limit.rate%3D30%26tps.limit.rejected.handler%3DDefaultValueHandler%26tps.limit.strategy%3DfixedWindow%26tps.limiter%3Dmethod-service%26warmup%3D100}) goroutine exit now"}
{"level":"\u001b[33mWARN\u001b[0m","time":"2025-03-29T11:31:52.522+0800","caller":"zookeeper/listener.go:244","message":"delete oldNode{/dubbo/com.fontree.microservices.fiee.bundle/providers/tri%3A%2F%2F192.168.88.77%3A20201%2Fcom.fontree.microservices.fiee.bundle%3Fanyhost%3Dtrue%26application%3Ddubbo.io%26bean.name%3DBundleProvider%26cluster%3Dfailover%26export%3Dtrue%26interface%3Dcom.fontree.microservices.fiee.bundle%26loadbalance%3Drandom%26message_size%3D4%26metadata-type%3Dlocal%26methods%3DBundleDetail%2CBundleList%2CCreateBundle%2CCreateOrderRecord%2CCreateValueAddBundle%2CDeleteBundle%2COrderRecordsDetail%2COrderRecordsList%2CUpdateBundle%2CUpdateOrderRecord%2CUpdateOrderRecordByOrderNo%2CValueAddBundleDetail%2CValueAddBundleList%26module%3Dsample%26name%3Ddubbo.io%26organization%3Ddubbo-go%26owner%3Ddubbo-go%26pid%3D93332%26registry%3Dzookeeper%26registry.role%3D3%26release%3Ddubbo-golang-3.0.0%26retries%3D0%26service.filter%3Dtps%2Ctracing%26side%3Dprovider%26timestamp%3D1743219110%26tps.limit.interval%3D1000%26tps.limit.rate%3D30%26tps.limit.rejected.handler%3DDefaultValueHandler%26tps.limit.strategy%3DfixedWindow%26tps.limiter%3Dmethod-service%26warmup%3D100}"}
{"level":"\u001b[34mINFO\u001b[0m","time":"2026-06-09T16:49:58.344+0800","caller":"config/root_config.go:129","message":"[Config Center] Config center doesn't start"}
{"level":"\u001b[34mINFO\u001b[0m","time":"2026-06-09T16:49:58.345+0800","caller":"dubbo3/dubbo3_protocol.go:81","message":"[Triple Protocol] Export service: tri://:20201/grpc.reflection.v1alpha.ServerReflection?accesslog=&app.version=&application=dubbo.io&auth=&bean.name=XXX_serverReflectionServer&cluster=failover&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&interface=grpc.reflection.v1alpha.ServerReflection&loadbalance=random&message_size=4&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go&param.sign=&pid=19380&registry.role=3&release=dubbo-golang-3.0.0&retries=&serialization=&service.filter=tracing&side=provider&timestamp=1780994998&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&warmup="}
{"level":"\u001b[34mINFO\u001b[0m","time":"2026-06-09T16:49:58.346+0800","caller":"dubbo3/dubbo3_protocol.go:81","message":"[Triple Protocol] Export service: tri://:20201/com.fontree.microservices.fiee.bundle?accesslog=&app.version=&application=dubbo.io&auth=&bean.name=BundleProvider&cluster=failover&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&interface=com.fontree.microservices.fiee.bundle&loadbalance=random&message_size=4&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go&param.sign=&pid=19380&registry=zookeeper&registry.role=3&release=dubbo-golang-3.0.0&retries=0&serialization=&service.filter=tps%2Ctracing&side=provider&timestamp=1780994998&tps.limit.interval=1000&tps.limit.rate=30&tps.limit.rejected.handler=DefaultValueHandler&tps.limit.strategy=fixedWindow&tps.limiter=method-service&warmup=100"}
{"level":"\u001b[34mINFO\u001b[0m","time":"2026-06-09T16:49:58.346+0800","caller":"zookeeper/registry.go:67","message":"[Zookeeper Registry] New zookeeper registry with url map[host:127.0.0.1 port:2181 protocol:zookeeper registry:zookeeper registry.group: registry.label:true registry.namespace: registry.preferred:false registry.role:3 registry.timeout:10s registry.ttl:10s registry.weight:0 registry.zone: remote-client-name:dubbo.registries-zookeeper-127.0.0.1:2181 simplified:false]"}
{"level":"\u001b[34mINFO\u001b[0m","time":"2026-06-09T16:49:58.346+0800","caller":"zookeeper/client.go:53","message":"[Zookeeper Client] New zookeeper client with name = 127.0.0.1:2181, zkAddress = 127.0.0.1:2181, timeout = 5s"}
{"level":"\u001b[31mERROR\u001b[0m","time":"2026-06-09T16:49:58.347+0800","caller":"registry/base_registry.go:276","message":"facadeBasedRegistry.CreatePath(path{/dubbo/com.fontree.microservices.fiee.bundle/providers}) = error{Error while invoking zk.Create(path:/dubbo), the reason maybe is: : zk: could not connect to a server}"}
{"level":"\u001b[31mERROR\u001b[0m","time":"2026-06-09T16:49:58.347+0800","caller":"protocol/protocol.go:201","message":"provider service tri://:@:20201/?interface=com.fontree.microservices.fiee.bundle&group=&version= register registry zookeeper://:@127.0.0.1:2181/?interface=com.fontree.microservices.fiee.bundle&group=&version= error, error message is register(url:tri://172.18.0.1:20201/com.fontree.microservices.fiee.bundle?accesslog=&app.version=&application=dubbo.io&auth=&bean.name=BundleProvider&cluster=failover&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&interface=com.fontree.microservices.fiee.bundle&loadbalance=random&message_size=4&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go&param.sign=&pid=19380&registry=zookeeper&registry.role=3&release=dubbo-golang-3.0.0&retries=0&serialization=&service.filter=tps%2Ctracing&side=provider&timestamp=1780994998&tps.limit.interval=1000&tps.limit.rate=30&tps.limit.rejected.handler=DefaultValueHandler&tps.limit.strategy=fixedWindow&tps.limiter=method-service&warmup=100): @c{tri://172.18.0.1:20201/com.fontree.microservices.fiee.bundle?accesslog=&app.version=&application=dubbo.io&auth=&bean.name=BundleProvider&cluster=failover&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&interface=com.fontree.microservices.fiee.bundle&loadbalance=random&message_size=4&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go&param.sign=&pid=19380&registry=zookeeper&registry.role=3&release=dubbo-golang-3.0.0&retries=0&serialization=&service.filter=tps%2Ctracing&side=provider&timestamp=1780994998&tps.limit.interval=1000&tps.limit.rate=30&tps.limit.rejected.handler=DefaultValueHandler&tps.limit.strategy=fixedWindow&tps.limiter=method-service&warmup=100} registry fail: facadeBasedRegistry.CreatePath(path:/dubbo/com.fontree.microservices.fiee.bundle/providers): Error while invoking zk.Create(path:/dubbo), the reason maybe is: : zk: could not connect to a server"}
{"level":"\u001b[31mERROR\u001b[0m","time":"2026-06-09T16:49:58.347+0800","caller":"config/provider_config.go:173","message":"service with registeredTypeName = BundleProvider export failed! err: Registry protocol new exporter error, registry is {zookeeper://127.0.0.1:2181?registry=zookeeper&registry.group=&registry.label=true&registry.namespace=&registry.preferred=false&registry.role=3&registry.timeout=10s&registry.ttl=10s&registry.weight=0&registry.zone=&remote-client-name=dubbo.registries-zookeeper-127.0.0.1%3A2181&simplified=false}, url is {tri://:20201/com.fontree.microservices.fiee.bundle?accesslog=&app.version=&application=dubbo.io&auth=&bean.name=BundleProvider&cluster=failover&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&interface=com.fontree.microservices.fiee.bundle&loadbalance=random&message_size=4&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go&param.sign=&pid=19380&registry=zookeeper&registry.role=3&release=dubbo-golang-3.0.0&retries=0&serialization=&service.filter=tps%2Ctracing&side=provider&timestamp=1780994998&tps.limit.interval=1000&tps.limit.rate=30&tps.limit.rejected.handler=DefaultValueHandler&tps.limit.strategy=fixedWindow&tps.limiter=method-service&warmup=100}"}
{"level":"\u001b[34mINFO\u001b[0m","time":"2026-06-09T16:49:58.347+0800","caller":"dubbo/dubbo_protocol.go:83","message":"[DUBBO Protocol] Export service: dubbo://:63737/org.apache.dubbo.metadata.MetadataService?accesslog=&app.version=&application=dubbo.io&auth=&bean.name=MetadataService&cluster=&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&group=dubbo.io&interface=org.apache.dubbo.metadata.MetadataService&loadbalance=&message_size=0&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go&param.sign=&pid=19380&registry.role=3&release=dubbo-golang-3.0.0&retries=&serialization=&service.filter=echo%2Cmetrics%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider&timestamp=1780994998&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=1.0.0&warmup="}
{"level":"\u001b[34mINFO\u001b[0m","time":"2026-06-09T16:49:58.348+0800","caller":"configurable/exporter.go:77","message":"[Metadata Service] The MetadataService exports urls : [dubbo://:63737/org.apache.dubbo.metadata.MetadataService?accesslog=&app.version=&application=dubbo.io&auth=&bean.name=MetadataService&cluster=&config.tracing=&environment=&execute.limit=&execute.limit.rejected.handler=&export=true&group=dubbo.io&interface=org.apache.dubbo.metadata.MetadataService&loadbalance=&message_size=0&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go&param.sign=&pid=19380&registry.role=3&release=dubbo-golang-3.0.0&retries=&serialization=&service.filter=echo%2Cmetrics%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider&timestamp=1780994998&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=1.0.0&warmup=] "}
{"level":"\u001b[34mINFO\u001b[0m","time":"2026-06-09T16:49:59.607+0800","caller":"config/graceful_shutdown.go:81","message":"get signal interrupt, applicationConfig will shutdown."}
{"level":"\u001b[34mINFO\u001b[0m","time":"2026-06-09T16:49:59.607+0800","caller":"config/graceful_shutdown.go:121","message":"Graceful shutdown --- Destroy all registriesConfig. "}

View File

@ -73,6 +73,18 @@ func (b *BundleProvider) GetUserOrderList(_ context.Context, req *bundle.GetUser
return logic.GetUserOrderListWithDetails(req)
}
// UpdateOrderExpiration 更新订单过期时间(定时任务可调用)
// 根据 BundleBalance.ExpiredAt 更新 BundleOrderRecords.ExpirationTime
func (b *BundleProvider) UpdateOrderExpiration(_ context.Context, req *bundle.UpdateOrderExpirationRequest) (res *bundle.UpdateOrderExpirationResponse, err error) {
return logic.UpdateOrderExpiration(req)
}
// CompleteExpiredOrders 完成已过期订单(定时任务可调用)
// 将已支付且已过期的订单状态更新为已完成
func (b *BundleProvider) CompleteExpiredOrders(_ context.Context, req *bundle.CompleteExpiredOrdersRequest) (res *bundle.CompleteExpiredOrdersResponse, err error) {
return logic.CompleteExpiredOrders(req)
}
// 增值套餐相关
func (b *BundleProvider) CreateValueAddBundle(_ context.Context, req *bundle.CreateValueAddBundleRequest) (res *bundle.CreateValueAddBundleResponse, err error) {
if err = req.Validate(); err != nil {

View File

@ -141,27 +141,3 @@ func MarkValueAddExpiredByDue(now time.Time) (int64, error) {
})
return r.RowsAffected, r.Error
}
// IsValueAddRecordUsable 判断增值订单余量是否可用
// 规则IsExpired=true 直接不可用;先用后付 + 到期未付 直接不可用。
func IsValueAddRecordUsable(rec *model.BundleOrderValueAdd) bool {
if rec == nil {
return false
}
if rec.IsExpired {
return false
}
if rec.OrderMode == model.OrderModePayLater {
if rec.PayLaterStatus == model.PayLaterStatusOverdue {
return false
}
if rec.PayLaterStatus == model.PayLaterStatusPending && rec.DueTime != "" {
if t, err := time.ParseInLocation("2006-01-02 15:04:05", rec.DueTime, time.Local); err == nil {
if time.Now().After(t) {
return false
}
}
}
}
return true
}

View File

@ -116,10 +116,6 @@ func UpdateOrderRecordByOrderNO(orderRecord *model.BundleOrderRecords) (res *bun
valueAdd.CheckoutSessionId = orderRecord.CheckoutSessionId
valueAdd.CheckoutSessionUrl = orderRecord.CheckoutSessionUrl
}
// 先用后付:主订单支付成功 → 子订单先用后付状态置为已付
if orderRecord.Status == 2 {
valueAdd.PayLaterStatus = model.PayLaterStatusPaid
}
if orderRecord.Status == 2 {
tempValues := make([]*model.BundleOrderValueAdd, 0)
@ -456,11 +452,11 @@ func CreateOrderAddRecord(req *bundle.OrderAddRecord) (res *bundle.CommonRespons
tx.Rollback()
}
}()
// 生成订单号和UUID
orderNo := utils.GetOrderNo()
mainOrderUUID := app.ModuleClients.SfNode.Generate().Base64()
// 增值订单默认先用后付(规则 2增值服务订单只有先用后付类型
addOrderMode := req.OrderMode
if addOrderMode == 0 {
@ -474,67 +470,63 @@ func CreateOrderAddRecord(req *bundle.OrderAddRecord) (res *bundle.CommonRespons
if addOrderMode == model.OrderModePayLater && addContractTplType == 0 {
addContractTplType = model.ContractTplValueAddPayLater
}
// 计算总金额
var totalAmount float32
for _, i := range req.AddPriceOptionsList {
totalAmount += i.Amount
}
// 创建增值服务主订单记录
mainOrder := &model.BundleOrderRecords{
UUID: mainOrderUUID,
OrderNo: orderNo,
BundleUUID: req.BundleUuid, // 保留原套餐UUID用于追溯
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
TotalAmount: totalAmount,
Amount: 0, // 增值服务订单不涉及套餐金额
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Status: model.BundleStatusSignedUnpaid, // 已签未支付
OrderMode: addOrderMode,
DueTime: req.DueTime,
PayLaterStatus: addPayLaterStatus,
ContractTplType: addContractTplType,
OrderType: model.OrderTypeValueAdd, // 标记为增值服务订单
ExpirationTime: req.ExpirationDate,
UUID: mainOrderUUID,
OrderNo: orderNo,
BundleUUID: req.BundleUuid, // 保留原套餐UUID用于追溯
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
TotalAmount: totalAmount,
Amount: 0, // 增值服务订单不涉及套餐金额
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Status: model.BundleStatusSignedUnpaid, // 已签未支付
OrderMode: addOrderMode,
DueTime: req.DueTime,
PayLaterStatus: addPayLaterStatus,
ContractTplType: addContractTplType,
OrderType: model.OrderTypeValueAdd, // 标记为增值服务订单
ExpirationTime: req.ExpirationDate,
}
// 创建主订单
if err = tx.Create(mainOrder).Error; err != nil {
tx.Rollback()
return nil, commonErr.ReturnError(err, msg.ErrorCreateOrderInfo, "创建增值服务主订单失败")
}
// 创建子订单记录
var childOrders []*model.BundleOrderValueAdd
for _, i := range req.AddPriceOptionsList {
childOrder := &model.BundleOrderValueAdd{
UUID: app.ModuleClients.SfNode.Generate().Base64(),
OrderUUID: mainOrderUUID, // 关联到新创建的增值服务主订单
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
ServiceType: i.ServiceType,
CurrencyType: i.CurrencyType,
Amount: float64(i.Amount),
OrderNo: orderNo,
Num: i.Num,
Unit: i.Unit,
ValueAddUUID: i.ValueUid,
Source: 2,
PaymentStatus: 1,
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Snapshot: req.Snapshot,
OrderMode: addOrderMode,
DueTime: req.DueTime,
PayLaterStatus: addPayLaterStatus,
ContractTplType: addContractTplType,
UUID: app.ModuleClients.SfNode.Generate().Base64(),
OrderUUID: mainOrderUUID, // 关联到新创建的增值服务主订单
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
ServiceType: i.ServiceType,
CurrencyType: i.CurrencyType,
Amount: float64(i.Amount),
OrderNo: orderNo,
Num: i.Num,
Unit: i.Unit,
ValueAddUUID: i.ValueUid,
Source: 2,
PaymentStatus: 1,
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Snapshot: req.Snapshot,
}
childOrders = append(childOrders, childOrder)
@ -548,7 +540,7 @@ func CreateOrderAddRecord(req *bundle.OrderAddRecord) (res *bundle.CommonRespons
}
}
}
// 批量创建子订单(提高性能)
if err = tx.Model(&model.BundleOrderValueAdd{}).Create(childOrders).Error; err != nil {
tx.Rollback()
@ -614,6 +606,10 @@ func OrderRecordsListV2(req *bundle.OrderRecordsRequestV2) (res *bundle.OrderRec
if req.OrderType != 0 {
modelObj = modelObj.Where("bundle_order_records.order_type = ?", req.OrderType)
}
// 批量订单号筛选
if len(req.OrderNos) > 0 {
modelObj = modelObj.Where("bundle_order_records.order_no IN ?", req.OrderNos)
}
err = modelObj.Count(&count).Error
if req.PageSize != 0 && req.Page != 0 {
modelObj = modelObj.Limit(int(req.PageSize)).Offset(int(req.Page-1) * int(req.PageSize))
@ -679,10 +675,6 @@ func OrderRecordsListV2(req *bundle.OrderRecordsRequestV2) (res *bundle.OrderRec
CheckoutSessionId: sub.CheckoutSessionId,
CustomerID: sub.CustomerID,
VideoNum: videoNum,
OrderMode: sub.OrderMode,
DueTime: sub.DueTime,
PayLaterStatus: sub.PayLaterStatus,
ContractTplType: sub.ContractTplType,
}
}
}
@ -1132,10 +1124,6 @@ func UpdateOrderRecordByOrderUuid(orderRecord *model.BundleOrderRecords) (res *b
valueAdd.CheckoutSessionId = orderRecord.CheckoutSessionId
valueAdd.CheckoutSessionUrl = orderRecord.CheckoutSessionUrl
}
// 先用后付:主订单支付成功 → 子订单先用后付状态置为已付
if orderRecord.Status == 2 {
valueAdd.PayLaterStatus = model.PayLaterStatusPaid
}
if orderRecord.Status == 2 {
tempValues := make([]*model.BundleOrderValueAdd, 0)
@ -1464,14 +1452,6 @@ func GetUserOrderListWithDetails(req *bundle.GetUserOrderListRequest) (*bundle.G
if req.ValueAddSource != 0 {
db = db.Where("source = ?", req.ValueAddSource)
}
// 增值订单模式
if req.ValueAddOrderMode != 0 {
db = db.Where("order_mode = ?", req.ValueAddOrderMode)
}
// 增值先用后付状态
if req.ValueAddPayLaterStatus != 0 {
db = db.Where("pay_later_status = ?", req.ValueAddPayLaterStatus)
}
// 增值是否过期
if req.ValueAddIsExpired {
db = db.Where("is_expired = ?", true)
@ -1545,24 +1525,21 @@ func GetUserOrderListWithDetails(req *bundle.GetUserOrderListRequest) (*bundle.G
if len(order.BundleOrderValueAdd) > 0 {
for _, valueAdd := range order.BundleOrderValueAdd {
valueAddDetail := &bundle.OrderValueAddDetail{
UUID: valueAdd.UUID,
OrderNo: valueAdd.OrderNo,
ServiceType: valueAdd.ServiceType,
CurrencyType: valueAdd.CurrencyType,
Amount: float32(valueAdd.Amount),
Num: valueAdd.Num,
Unit: valueAdd.Unit,
ValueAddUUID: valueAdd.ValueAddUUID,
Source: int32(valueAdd.Source),
PaymentStatus: int32(valueAdd.PaymentStatus),
HandlingFee: valueAdd.HandlingFee,
EquityType: valueAdd.EquityType,
QuotaType: valueAdd.QuotaType,
QuotaValue: valueAdd.QuotaValue,
IsExpired: valueAdd.IsExpired,
OrderMode: valueAdd.OrderMode,
PayLaterStatus: valueAdd.PayLaterStatus,
ContractTplType: valueAdd.ContractTplType,
UUID: valueAdd.UUID,
OrderNo: valueAdd.OrderNo,
ServiceType: valueAdd.ServiceType,
CurrencyType: valueAdd.CurrencyType,
Amount: float32(valueAdd.Amount),
Num: valueAdd.Num,
Unit: valueAdd.Unit,
ValueAddUUID: valueAdd.ValueAddUUID,
Source: int32(valueAdd.Source),
PaymentStatus: int32(valueAdd.PaymentStatus),
HandlingFee: valueAdd.HandlingFee,
EquityType: valueAdd.EquityType,
QuotaType: valueAdd.QuotaType,
QuotaValue: valueAdd.QuotaValue,
IsExpired: valueAdd.IsExpired,
}
if !valueAdd.CreatedAt.IsZero() {
valueAddDetail.CreatedAt = valueAdd.CreatedAt.Format("2006-01-02 15:04:05")
@ -1573,9 +1550,6 @@ func GetUserOrderListWithDetails(req *bundle.GetUserOrderListRequest) (*bundle.G
if valueAdd.PaymentTime != "" {
valueAddDetail.PaymentTime = valueAdd.PaymentTime
}
if valueAdd.DueTime != "" {
valueAddDetail.DueTime = valueAdd.DueTime
}
detail.ValueAddList = append(detail.ValueAddList, valueAddDetail)
}
}
@ -1588,3 +1562,58 @@ func GetUserOrderListWithDetails(req *bundle.GetUserOrderListRequest) (*bundle.G
List: resultList,
}, nil
}
// UpdateOrderExpiration 更新订单过期时间
// 根据 BundleBalance.ExpiredAt 更新 BundleOrderRecords.ExpirationTime
// 只更新 ExpirationTime 为空或为NULL的记录
func UpdateOrderExpiration() (int64, error) {
// 使用JOIN查询从BundleBalance获取ExpiredAt并更新BundleOrderRecords
// UPDATE bundle_order_records bor
// INNER JOIN bundle_balance bb ON bor.uuid = bb.order_uuid
// SET bor.expiration_time = bb.expired_at
// WHERE (bor.expiration_time IS NULL OR bor.expiration_time = '')
result := app.ModuleClients.BundleDB.Exec(`
UPDATE bundle_order_records bor
INNER JOIN bundle_balance bb ON bor.uuid = bb.order_uuid
SET bor.expiration_time = DATE_FORMAT(bb.expired_at, '%Y-%m-%d %H:%i:%s')
WHERE (bor.expiration_time IS NULL OR bor.expiration_time = '')
AND bb.expired_at IS NOT NULL
AND bb.deleted_at IS NULL
AND bor.deleted_at IS NULL
`)
if result.Error != nil {
return 0, fmt.Errorf("更新订单过期时间失败: %v", result.Error)
}
return result.RowsAffected, nil
}
// CompleteExpiredOrders 完成已过期的订单
// 将 status=2(已支付) 且 expiration_time < 当前时间 的订单更新为 status=4(已完成)
func CompleteExpiredOrders() (int64, error) {
// UPDATE bundle_order_records
// SET status = 4
// WHERE status = 2
// AND expiration_time IS NOT NULL
// AND expiration_time != ''
// AND expiration_time < NOW()
// AND deleted_at IS NULL
result := app.ModuleClients.BundleDB.Exec(`
UPDATE bundle_order_records
SET status = 4
WHERE status = 2
AND expiration_time IS NOT NULL
AND expiration_time != ''
AND STR_TO_DATE(expiration_time, '%Y-%m-%d %H:%i:%s') < NOW()
AND deleted_at IS NULL
`)
if result.Error != nil {
return 0, fmt.Errorf("完成已过期订单失败: %v", result.Error)
}
return result.RowsAffected, nil
}

View File

@ -54,33 +54,29 @@ func CreateOrderRecord(req *bundle.OrderCreateRecord) (res *bundle.CommonRespons
addPayLaterStatus = model.PayLaterStatusPending
}
addRecords = append(addRecords, model.BundleOrderValueAdd{
UUID: app.ModuleClients.SfNode.Generate().Base64(),
OrderNo: orderNo,
OrderUUID: orderUUID,
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
ServiceType: i.ServiceType,
CurrencyType: i.CurrencyType,
Amount: float64(i.Amount),
Num: i.Num,
Unit: i.Unit,
ValueAddUUID: i.ValueUid,
Source: int(i.Source),
PaymentStatus: int(i.PaymentStatus),
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Snapshot: req.Snapshot,
HandlingFee: i.HandlingFee,
EquityType: i.EquityType,
QuotaType: i.QuotaType,
QuotaValue: i.QuotaValue,
IsExpired: i.IsExpired,
OrderMode: addOrderMode,
DueTime: i.DueTime,
PayLaterStatus: addPayLaterStatus,
ContractTplType: i.ContractTplType,
UUID: app.ModuleClients.SfNode.Generate().Base64(),
OrderNo: orderNo,
OrderUUID: orderUUID,
CustomerID: req.CustomerID,
CustomerNum: req.CustomerNum,
CustomerName: req.CustomerName,
ServiceType: i.ServiceType,
CurrencyType: i.CurrencyType,
Amount: float64(i.Amount),
Num: i.Num,
Unit: i.Unit,
ValueAddUUID: i.ValueUid,
Source: int(i.Source),
PaymentStatus: int(i.PaymentStatus),
SignContract: req.SignContract,
Signature: req.Signature,
SignedTime: req.SignedTime,
Snapshot: req.Snapshot,
HandlingFee: i.HandlingFee,
EquityType: i.EquityType,
QuotaType: i.QuotaType,
QuotaValue: i.QuotaValue,
IsExpired: i.IsExpired,
})
}
orderMode := req.OrderMode
@ -295,3 +291,32 @@ func OrderListByOrderUuid(req *bundle.OrderInfoByOrderUuidRequest) (res *bundle.
func GetUserOrderListWithDetails(req *bundle.GetUserOrderListRequest) (*bundle.GetUserOrderListResponse, error) {
return dao.GetUserOrderListWithDetails(req)
}
// UpdateOrderExpiration 更新订单过期时间定时任务
// 根据 BundleBalance.ExpiredAt 更新 BundleOrderRecords.ExpirationTime
// 只更新 ExpirationTime 为空的记录
func UpdateOrderExpiration(req *bundle.UpdateOrderExpirationRequest) (*bundle.UpdateOrderExpirationResponse, error) {
res := &bundle.UpdateOrderExpirationResponse{}
updated, err := dao.UpdateOrderExpiration()
if err != nil {
res.Msg = fmt.Sprintf("更新失败: %v", err)
return res, err
}
res.Updated = updated
res.Msg = fmt.Sprintf("成功更新 %d 条订单记录", updated)
return res, nil
}
// CompleteExpiredOrders 完成已过期订单定时任务
// 将已支付且已过期的订单状态更新为已完成(status=4)
func CompleteExpiredOrders(req *bundle.CompleteExpiredOrdersRequest) (*bundle.CompleteExpiredOrdersResponse, error) {
res := &bundle.CompleteExpiredOrdersResponse{}
completed, err := dao.CompleteExpiredOrders()
if err != nil {
res.Msg = fmt.Sprintf("更新失败: %v", err)
return res, err
}
res.Completed = completed
res.Msg = fmt.Sprintf("成功完成 %d 条订单", completed)
return res, nil
}

View File

@ -83,10 +83,10 @@ type BundleOrderValueAdd struct {
QuotaType int32 `json:"quotaType" gorm:"column:quota_type;type:int;default:1;comment:额度类型 1:不限额度 2:每月限额度"`
QuotaValue int32 `json:"quotaValue" gorm:"column:quota_value;type:int;comment:额度值"`
IsExpired bool `json:"isExpired" gorm:"column:is_expired;default:false;comment:是否过期作废 false:不作废 true:作废"`
OrderMode int32 `json:"orderMode" gorm:"column:order_mode;type:int;default:2;comment:订单模式 1:普通 2:先用后付(增值默认2);index:idx_valueadd_order_mode"`
DueTime string `json:"dueTime" gorm:"column:due_time;type:varchar(64);comment:先用后付到期应付时间"`
PayLaterStatus int32 `json:"payLaterStatus" gorm:"column:pay_later_status;type:int;default:0;comment:先用后付状态 0:无 1:待付款 2:已付款 3:逾期未付;index:idx_valueadd_paylater_status"`
ContractTplType int32 `json:"contractTplType" gorm:"column:contract_tpl_type;type:int;default:0;comment:合同模板类型 3:增值先用后付"`
//OrderMode int32 `json:"orderMode" gorm:"column:order_mode;type:int;default:2;comment:订单模式 1:普通 2:先用后付(增值默认2);index:idx_valueadd_order_mode"`
//DueTime string `json:"dueTime" gorm:"column:due_time;type:varchar(64);comment:先用后付到期应付时间"`
//PayLaterStatus int32 `json:"payLaterStatus" gorm:"column:pay_later_status;type:int;default:0;comment:先用后付状态 0:无 1:待付款 2:已付款 3:逾期未付;index:idx_valueadd_paylater_status"`
//ContractTplType int32 `json:"contractTplType" gorm:"column:contract_tpl_type;type:int;default:0;comment:合同模板类型 3:增值先用后付"`
}
type PlatformIDs []uint32
@ -130,6 +130,7 @@ const (
BundleStatusSignedUnpaid int64 = 1 // 已签未支付
BundleStatusPaid int64 = 2 // 已签已支付
BundleStatusOverdue int64 = 3 // 先用后付到期未付
BundleStatusCompleted int64 = 4 // 已完成(已支付且已过期)
)
// 先用后付状态

View File

@ -38,6 +38,8 @@ service Bundle {
rpc CheckOrderEligibility(CheckOrderEligibilityRequest) returns (CheckOrderEligibilityResponse) {} // ()
rpc MarkOverdueOrders(MarkOverdueOrdersRequest) returns (MarkOverdueOrdersResponse) {} // ()
rpc GetUserOrderList(GetUserOrderListRequest) returns (GetUserOrderListResponse) {} // 30+
rpc UpdateOrderExpiration(UpdateOrderExpirationRequest) returns (UpdateOrderExpirationResponse) {} // ()
rpc CompleteExpiredOrders(CompleteExpiredOrdersRequest) returns (CompleteExpiredOrdersResponse) {} // ()
//
rpc CreateValueAddBundle(CreateValueAddBundleRequest) returns (CreateValueAddBundleResponse) {}
@ -315,6 +317,7 @@ message OrderRecordsRequestV2{
int32 orderMode = 18 [json_name = "orderMode"]; // 1: 2:
int32 payLaterStatus = 19 [json_name = "payLaterStatus"]; // 0: 1: 2: 3:
int32 orderType = 20 [json_name = "orderType"]; // 1: 2:
repeated string orderNos = 21; //
}
message OrderRecordsResponseV2{
repeated OrderBundleRecordInfo bundleInfo = 1;
@ -564,6 +567,27 @@ message MarkOverdueOrdersResponse {
int32 valueAddExpiredCount = 2 [json_name = "valueAddExpiredCount"];
string msg = 3 [json_name = "msg"];
}
//
message UpdateOrderExpirationRequest {
}
//
message UpdateOrderExpirationResponse {
int64 updated = 1; //
string msg = 2; //
}
//
message CompleteExpiredOrdersRequest {
}
//
message CompleteExpiredOrdersResponse {
int64 completed = 1; //
string msg = 2; //
}
message AddInfo{
string orderNo = 1 [json_name = "orderNo"];
int32 num = 2 [json_name = "num"];
@ -2599,8 +2623,8 @@ message GetUserOrderListRequest {
int32 valueAddServiceType = 35; // 1: 2: 3: 4: 5:
int32 valueAddPaymentStatus = 36; // 1: 2:
int32 valueAddSource = 37; // 1: 2: 3:
int32 valueAddOrderMode = 38; // 1: 2:
int32 valueAddPayLaterStatus = 39; //
// int32 valueAddOrderMode = 38; // 1: 2:
// int32 valueAddPayLaterStatus = 39; //
bool valueAddIsExpired = 40; //
}

File diff suppressed because it is too large Load Diff

View File

@ -229,6 +229,18 @@ func (this *MarkOverdueOrdersRequest) Validate() error {
func (this *MarkOverdueOrdersResponse) Validate() error {
return nil
}
func (this *UpdateOrderExpirationRequest) Validate() error {
return nil
}
func (this *UpdateOrderExpirationResponse) Validate() error {
return nil
}
func (this *CompleteExpiredOrdersRequest) Validate() error {
return nil
}
func (this *CompleteExpiredOrdersResponse) Validate() error {
return nil
}
func (this *AddInfo) Validate() error {
return nil
}

View File

@ -55,6 +55,8 @@ type BundleClient interface {
CheckOrderEligibility(ctx context.Context, in *CheckOrderEligibilityRequest, opts ...grpc_go.CallOption) (*CheckOrderEligibilityResponse, common.ErrorWithAttachment)
MarkOverdueOrders(ctx context.Context, in *MarkOverdueOrdersRequest, opts ...grpc_go.CallOption) (*MarkOverdueOrdersResponse, common.ErrorWithAttachment)
GetUserOrderList(ctx context.Context, in *GetUserOrderListRequest, opts ...grpc_go.CallOption) (*GetUserOrderListResponse, common.ErrorWithAttachment)
UpdateOrderExpiration(ctx context.Context, in *UpdateOrderExpirationRequest, opts ...grpc_go.CallOption) (*UpdateOrderExpirationResponse, common.ErrorWithAttachment)
CompleteExpiredOrders(ctx context.Context, in *CompleteExpiredOrdersRequest, opts ...grpc_go.CallOption) (*CompleteExpiredOrdersResponse, common.ErrorWithAttachment)
// 增值套餐
CreateValueAddBundle(ctx context.Context, in *CreateValueAddBundleRequest, opts ...grpc_go.CallOption) (*CreateValueAddBundleResponse, common.ErrorWithAttachment)
ValueAddBundleList(ctx context.Context, in *ValueAddBundleListRequest, opts ...grpc_go.CallOption) (*ValueAddBundleListResponse, common.ErrorWithAttachment)
@ -190,6 +192,8 @@ type BundleClientImpl struct {
CheckOrderEligibility func(ctx context.Context, in *CheckOrderEligibilityRequest) (*CheckOrderEligibilityResponse, error)
MarkOverdueOrders func(ctx context.Context, in *MarkOverdueOrdersRequest) (*MarkOverdueOrdersResponse, error)
GetUserOrderList func(ctx context.Context, in *GetUserOrderListRequest) (*GetUserOrderListResponse, error)
UpdateOrderExpiration func(ctx context.Context, in *UpdateOrderExpirationRequest) (*UpdateOrderExpirationResponse, error)
CompleteExpiredOrders func(ctx context.Context, in *CompleteExpiredOrdersRequest) (*CompleteExpiredOrdersResponse, error)
CreateValueAddBundle func(ctx context.Context, in *CreateValueAddBundleRequest) (*CreateValueAddBundleResponse, error)
ValueAddBundleList func(ctx context.Context, in *ValueAddBundleListRequest) (*ValueAddBundleListResponse, error)
ValueAddBundleDetail func(ctx context.Context, in *ValueAddBundleDetailRequest) (*ValueAddBundleDetailResponse, error)
@ -454,6 +458,18 @@ func (c *bundleClient) GetUserOrderList(ctx context.Context, in *GetUserOrderLis
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/GetUserOrderList", in, out)
}
func (c *bundleClient) UpdateOrderExpiration(ctx context.Context, in *UpdateOrderExpirationRequest, opts ...grpc_go.CallOption) (*UpdateOrderExpirationResponse, common.ErrorWithAttachment) {
out := new(UpdateOrderExpirationResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/UpdateOrderExpiration", in, out)
}
func (c *bundleClient) CompleteExpiredOrders(ctx context.Context, in *CompleteExpiredOrdersRequest, opts ...grpc_go.CallOption) (*CompleteExpiredOrdersResponse, common.ErrorWithAttachment) {
out := new(CompleteExpiredOrdersResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
return out, c.cc.Invoke(ctx, "/"+interfaceKey+"/CompleteExpiredOrders", in, out)
}
func (c *bundleClient) CreateValueAddBundle(ctx context.Context, in *CreateValueAddBundleRequest, opts ...grpc_go.CallOption) (*CreateValueAddBundleResponse, common.ErrorWithAttachment) {
out := new(CreateValueAddBundleResponse)
interfaceKey := ctx.Value(constant.InterfaceKey).(string)
@ -1013,6 +1029,8 @@ type BundleServer interface {
CheckOrderEligibility(context.Context, *CheckOrderEligibilityRequest) (*CheckOrderEligibilityResponse, error)
MarkOverdueOrders(context.Context, *MarkOverdueOrdersRequest) (*MarkOverdueOrdersResponse, error)
GetUserOrderList(context.Context, *GetUserOrderListRequest) (*GetUserOrderListResponse, error)
UpdateOrderExpiration(context.Context, *UpdateOrderExpirationRequest) (*UpdateOrderExpirationResponse, error)
CompleteExpiredOrders(context.Context, *CompleteExpiredOrdersRequest) (*CompleteExpiredOrdersResponse, error)
// 增值套餐
CreateValueAddBundle(context.Context, *CreateValueAddBundleRequest) (*CreateValueAddBundleResponse, error)
ValueAddBundleList(context.Context, *ValueAddBundleListRequest) (*ValueAddBundleListResponse, error)
@ -1203,6 +1221,12 @@ func (UnimplementedBundleServer) MarkOverdueOrders(context.Context, *MarkOverdue
func (UnimplementedBundleServer) GetUserOrderList(context.Context, *GetUserOrderListRequest) (*GetUserOrderListResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserOrderList not implemented")
}
func (UnimplementedBundleServer) UpdateOrderExpiration(context.Context, *UpdateOrderExpirationRequest) (*UpdateOrderExpirationResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateOrderExpiration not implemented")
}
func (UnimplementedBundleServer) CompleteExpiredOrders(context.Context, *CompleteExpiredOrdersRequest) (*CompleteExpiredOrdersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CompleteExpiredOrders not implemented")
}
func (UnimplementedBundleServer) CreateValueAddBundle(context.Context, *CreateValueAddBundleRequest) (*CreateValueAddBundleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateValueAddBundle not implemented")
}
@ -2278,6 +2302,64 @@ func _Bundle_GetUserOrderList_Handler(srv interface{}, ctx context.Context, dec
return interceptor(ctx, in, info, handler)
}
func _Bundle_UpdateOrderExpiration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateOrderExpirationRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("UpdateOrderExpiration", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Bundle_CompleteExpiredOrders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(CompleteExpiredOrdersRequest)
if err := dec(in); err != nil {
return nil, err
}
base := srv.(dubbo3.Dubbo3GrpcService)
args := []interface{}{}
args = append(args, in)
md, _ := metadata.FromIncomingContext(ctx)
invAttachment := make(map[string]interface{}, len(md))
for k, v := range md {
invAttachment[k] = v
}
invo := invocation.NewRPCInvocation("CompleteExpiredOrders", args, invAttachment)
if interceptor == nil {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
info := &grpc_go.UnaryServerInfo{
Server: srv,
FullMethod: ctx.Value("XXX_TRIPLE_GO_INTERFACE_NAME").(string),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
result := base.XXX_GetProxyImpl().Invoke(ctx, invo)
return result, result.Error()
}
return interceptor(ctx, in, info, handler)
}
func _Bundle_CreateValueAddBundle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc_go.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateValueAddBundleRequest)
if err := dec(in); err != nil {
@ -4945,6 +5027,14 @@ var Bundle_ServiceDesc = grpc_go.ServiceDesc{
MethodName: "GetUserOrderList",
Handler: _Bundle_GetUserOrderList_Handler,
},
{
MethodName: "UpdateOrderExpiration",
Handler: _Bundle_UpdateOrderExpiration_Handler,
},
{
MethodName: "CompleteExpiredOrders",
Handler: _Bundle_CompleteExpiredOrders_Handler,
},
{
MethodName: "CreateValueAddBundle",
Handler: _Bundle_CreateValueAddBundle_Handler,

View File

@ -107,11 +107,6 @@ func loadMysqlConn(conn string) *gorm.DB {
fmt.Println("[migrate BundleOrderRecords]", col, err)
}
}
if !db.Migrator().HasColumn(&model.BundleOrderValueAdd{}, col) {
if err := db.Migrator().AddColumn(&model.BundleOrderValueAdd{}, col); err != nil {
fmt.Println("[migrate BundleOrderValueAdd]", col, err)
}
}
}
if !db.Migrator().HasColumn(&model.Contract{}, "contract_template_type") {
if err := db.Migrator().AddColumn(&model.Contract{}, "contract_template_type"); err != nil {