记得上下班打卡 | git大法好,push需谨慎
Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
liquidnet-bus-v1
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
董敬伟
liquidnet-bus-v1
Commits
9ca7ab4f
Commit
9ca7ab4f
authored
Jul 10, 2026
by
wangyifan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
增加退款锁
parent
0d43204a
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
325 additions
and
90 deletions
+325
-90
GoblinRedisConst.java
...m/liquidnet/service/goblin/constant/GoblinRedisConst.java
+6
-0
RedisKeyExpireConst.java
.../liquidnet/service/base/constant/RedisKeyExpireConst.java
+10
-0
GoblinSqbServiceImpl.java
...net/service/goblin/service/impl/GoblinSqbServiceImpl.java
+173
-32
GoblinSqbRedisUtils.java
...om/liquidnet/service/goblin/util/GoblinSqbRedisUtils.java
+21
-0
GoblinSqbOrderServiceImpl.java
...service/order/service/impl/GoblinSqbOrderServiceImpl.java
+93
-58
GoblinSqbRedisUtils.java
...om/liquidnet/service/order/utils/GoblinSqbRedisUtils.java
+22
-0
No files found.
liquidnet-bus-api/liquidnet-service-goblin-api/src/main/java/com/liquidnet/service/goblin/constant/GoblinRedisConst.java
View file @
9ca7ab4f
...
@@ -461,6 +461,12 @@ public class GoblinRedisConst {
...
@@ -461,6 +461,12 @@ public class GoblinRedisConst {
*/
*/
public
static
final
String
REFUND_ORDER_LOCK
=
PREFIX
.
concat
(
"refund:lock:"
);
public
static
final
String
REFUND_ORDER_LOCK
=
PREFIX
.
concat
(
"refund:lock:"
);
/**
* 收钱吧退款库存已归还标记(防回调重复还库存)
* {goblin:sqb:refund:stock:${orderId}}
*/
public
static
final
String
SQB_REFUND_STOCK_FLAG
=
PREFIX
.
concat
(
"sqb:refund:stock:"
);
/**
/**
* 收钱吧 用户订单列表
* 收钱吧 用户订单列表
*/
*/
...
...
liquidnet-bus-common/liquidnet-common-service-base/src/main/java/com/liquidnet/service/base/constant/RedisKeyExpireConst.java
View file @
9ca7ab4f
...
@@ -67,6 +67,16 @@ public class RedisKeyExpireConst {
...
@@ -67,6 +67,16 @@ public class RedisKeyExpireConst {
*/
*/
public
static
final
long
SQB_ORDER_LOCK_EXPIRE
=
10
;
public
static
final
long
SQB_ORDER_LOCK_EXPIRE
=
10
;
/**
* 收钱吧退款锁过期时间 (60秒,覆盖调收钱吧退款 API)
*/
public
static
final
long
SQB_REFUND_LOCK_EXPIRE
=
60
;
/**
* 收钱吧退款库存归还标记过期时间 (30天)
*/
public
static
final
long
SQB_REFUND_STOCK_FLAG_EXPIRE
=
30
*
24
*
60
*
60
;
// 已发布状态徽章过期时间:30天
// 已发布状态徽章过期时间:30天
public
static
final
long
CAOMEI_BADGE_PUBLISHED_EXPIRE
=
30
*
24
*
60
*
60
;
public
static
final
long
CAOMEI_BADGE_PUBLISHED_EXPIRE
=
30
*
24
*
60
*
60
;
}
}
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/service/impl/GoblinSqbServiceImpl.java
View file @
9ca7ab4f
...
@@ -121,7 +121,8 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
...
@@ -121,7 +121,8 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
/**
/**
* 申请退款。
* 申请退款。
* 流程:校验订单状态 → 主单锁定为退款中(61) → 调收钱吧退款API → 成功后子单(61) + 扩展单落库。
* 流程:退款锁 → 校验 → 主单 Redis/Mongo 置退款中(61) → 调收钱吧退款API
* → 成功后重读子单写 61 + 主单/子单/扩展单一次 MQ 落库。
* 退款最终完成以收钱吧回调 targetState=20 为准,本方法不主动扣/还库存。
* 退款最终完成以收钱吧回调 targetState=20 为准,本方法不主动扣/还库存。
*/
*/
@Override
@Override
...
@@ -143,6 +144,9 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
...
@@ -143,6 +144,9 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
if
(
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_6
.
getValue
()).
equals
(
storeSt
))
{
if
(
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_6
.
getValue
()).
equals
(
storeSt
))
{
return
ResponseDto
.
failure
(
"订单已退款,不可重复退款"
);
return
ResponseDto
.
failure
(
"订单已退款,不可重复退款"
);
}
}
if
(
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
()).
equals
(
storeSt
))
{
return
ResponseDto
.
failure
(
"退款处理中,请勿重复操作"
);
}
if
(!
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_2
.
getValue
()).
equals
(
storeSt
))
{
if
(!
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_2
.
getValue
()).
equals
(
storeSt
))
{
return
ResponseDto
.
failure
(
"订单状态不可退款"
);
return
ResponseDto
.
failure
(
"订单状态不可退款"
);
}
}
...
@@ -169,28 +173,47 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
...
@@ -169,28 +173,47 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
return
ResponseDto
.
failure
(
"券码已核销,不可退款"
);
return
ResponseDto
.
failure
(
"券码已核销,不可退款"
);
}
}
// 更新状态为退款中
if
(!
goblinSqbRedisUtils
.
tryRefundLock
(
orderId
))
{
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
return
ResponseDto
.
failure
(
"退款处理中,请勿重复操作"
);
orderVo
.
setUpdatedAt
(
now
);
}
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
4
);
List
<
GoblinOrderSkuVo
>
orderSkuVos
=
new
ArrayList
<>();
boolean
sqbAccepted
=
false
;
for
(
String
skuVoId
:
storeOrderVo
.
getOrderSkuVoIds
())
{
List
<
String
>
lockedSkuVoIds
=
null
;
GoblinOrderSkuVo
goblinOrderSkuVo
=
goblinRedisUtils
.
getGoblinOrderSkuVo
(
skuVoId
);
List
<
CouponRefundRequest
.
RefundItem
>
refundItems
=
null
;
if
(
null
!=
goblinOrderSkuVo
)
{
try
{
orderSkuVos
.
add
(
goblinOrderSkuVo
);
// 锁内再读,防止并发
storeOrderVo
=
goblinRedisUtils
.
getGoblinOrder
(
orderId
);
if
(
storeOrderVo
==
null
)
{
return
ResponseDto
.
failure
(
"订单不存在"
);
}
storeSt
=
storeOrderVo
.
getStatus
();
if
(
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_6
.
getValue
()).
equals
(
storeSt
))
{
return
ResponseDto
.
failure
(
"订单已退款,不可重复退款"
);
}
if
(
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
()).
equals
(
storeSt
))
{
return
ResponseDto
.
failure
(
"退款处理中,请勿重复操作"
);
}
}
if
(!
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_2
.
getValue
()).
equals
(
storeSt
))
{
return
ResponseDto
.
failure
(
"订单状态不可退款"
);
}
}
try
{
lockedSkuVoIds
=
storeOrderVo
.
getOrderSkuVoIds
();
List
<
CouponRefundRequest
.
RefundItem
>
refundItems
=
buildRefundItem
(
orderSkuVos
);
refundItems
=
buildRefundItem
(
loadOrderSkuVos
(
lockedSkuVoIds
)
);
if
(
CollectionUtils
.
isEmpty
(
refundItems
))
{
if
(
CollectionUtils
.
isEmpty
(
refundItems
))
{
log
.
error
(
"[收钱吧-退款] 退款商品明细为空, orderId={}"
,
orderId
);
log
.
error
(
"[收钱吧-退款] 退款商品明细为空, orderId={}"
,
orderId
);
syncOrderStatus
(
orderId
,
1
);
return
ResponseDto
.
failure
(
"退款商品数据异常"
);
return
ResponseDto
.
failure
(
"退款商品数据异常"
);
}
}
// 主单先置退款中(Redis/Mongo);调外部 API 前释放锁,避免阻塞退款回调
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
orderVo
.
setUpdatedAt
(
now
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
markStoreOrderRefunding
(
orderId
);
}
finally
{
goblinSqbRedisUtils
.
releaseRefundLock
(
orderId
);
}
try
{
CouponRefundData
refundData
=
sqbBiz
.
refundCoupon
(
orderVo
.
getSqbOrderSn
(),
CouponRefundData
refundData
=
sqbBiz
.
refundCoupon
(
orderVo
.
getSqbOrderSn
(),
orderVo
.
getSqbOrderSignature
(),
orderVo
.
getSqbOrderSignature
(),
orderVo
.
getAmount
(),
orderVo
.
getAmount
(),
...
@@ -201,63 +224,181 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
...
@@ -201,63 +224,181 @@ public class GoblinSqbServiceImpl implements IGoblinSqbService {
);
);
log
.
info
(
"[收钱吧-退款] 收钱吧退款响应: {}, orderId: {}."
,
JsonUtils
.
toJson
(
refundData
),
orderId
);
log
.
info
(
"[收钱吧-退款] 收钱吧退款响应: {}, orderId: {}."
,
JsonUtils
.
toJson
(
refundData
),
orderId
);
if
(
refundData
==
null
)
{
if
(
refundData
==
null
)
{
// 退款失败,回滚状态
orderVo
.
setUpdatedAt
(
LocalDateTime
.
now
().
format
(
DTF
));
orderVo
.
setUpdatedAt
(
LocalDateTime
.
now
().
format
(
DTF
));
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
1
);
if
(
goblinSqbRedisUtils
.
tryRefundLock
(
orderId
))
{
try
{
rollbackRefundingIfNeeded
(
orderId
);
}
finally
{
goblinSqbRedisUtils
.
releaseRefundLock
(
orderId
);
}
}
else
{
rollbackRefundingIfNeeded
(
orderId
);
}
return
ResponseDto
.
failure
(
"退款失败,请稍后重试"
);
return
ResponseDto
.
failure
(
"退款失败,请稍后重试"
);
}
}
sqbAccepted
=
true
;
// 退款申请成功(最终完成以回调为准)
// 退款申请成功(最终完成以回调为准)
orderVo
.
setRefundSn
(
refundData
.
getTicketsSn
());
orderVo
.
setRefundSn
(
refundData
.
getTicketsSn
());
orderVo
.
setRefundSignature
(
refundData
.
getTicketSignature
());
orderVo
.
setRefundSignature
(
refundData
.
getTicketSignature
());
orderVo
.
setRefundReason
(
reason
);
orderVo
.
setRefundReason
(
reason
);
orderVo
.
setUpdatedAt
(
LocalDateTime
.
now
().
format
(
DTF
));
orderVo
.
setUpdatedAt
(
LocalDateTime
.
now
().
format
(
DTF
));
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
persistRefundSuccess
(
orderId
,
orderVo
,
orderSkuVos
,
now
);
if
(
goblinSqbRedisUtils
.
tryRefundLock
(
orderId
))
{
try
{
persistRefundApplying
(
orderId
,
orderVo
,
lockedSkuVoIds
);
}
finally
{
goblinSqbRedisUtils
.
releaseRefundLock
(
orderId
);
}
}
else
{
// 回调可能正持锁写终态;无锁仍重读落库,persist 内会跳过已是 6 的子单
persistRefundApplying
(
orderId
,
orderVo
,
lockedSkuVoIds
);
}
log
.
info
(
"[收钱吧-退款] 调用收钱吧退款成功,orderId={}"
,
orderId
);
log
.
info
(
"[收钱吧-退款] 调用收钱吧退款成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
Boolean
.
TRUE
);
return
ResponseDto
.
success
(
Boolean
.
TRUE
);
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
log
.
error
(
"[收钱吧退款] 退款异常,orderId={},回滚主单为已支付"
,
orderId
,
e
);
log
.
error
(
"[收钱吧退款] 退款异常,orderId={}"
,
orderId
,
e
);
if
(!
sqbAccepted
)
{
orderVo
.
setUpdatedAt
(
LocalDateTime
.
now
().
format
(
DTF
));
orderVo
.
setUpdatedAt
(
LocalDateTime
.
now
().
format
(
DTF
));
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
1
);
if
(
goblinSqbRedisUtils
.
tryRefundLock
(
orderId
))
{
try
{
rollbackRefundingIfNeeded
(
orderId
);
}
finally
{
goblinSqbRedisUtils
.
releaseRefundLock
(
orderId
);
}
}
else
{
rollbackRefundingIfNeeded
(
orderId
);
}
}
else
{
log
.
error
(
"[收钱吧退款] 收钱吧已受理但本地持久化异常,等待回调收敛,orderId={}"
,
orderId
);
}
return
ResponseDto
.
failure
(
"退款失败:"
+
e
.
getMessage
());
return
ResponseDto
.
failure
(
"退款失败:"
+
e
.
getMessage
());
}
}
}
}
private
List
<
GoblinOrderSkuVo
>
loadOrderSkuVos
(
List
<
String
>
orderSkuVoIds
)
{
List
<
GoblinOrderSkuVo
>
orderSkuVos
=
new
ArrayList
<>();
if
(
CollectionUtils
.
isEmpty
(
orderSkuVoIds
))
{
return
orderSkuVos
;
}
for
(
String
skuVoId
:
orderSkuVoIds
)
{
GoblinOrderSkuVo
goblinOrderSkuVo
=
goblinRedisUtils
.
getGoblinOrderSkuVo
(
skuVoId
);
if
(
goblinOrderSkuVo
!=
null
)
{
orderSkuVos
.
add
(
goblinOrderSkuVo
);
}
}
return
orderSkuVos
;
}
/** 主单 Redis/Mongo 置为退款中(61),不单独发 MQ(与子单/扩展单一并落库) */
private
void
markStoreOrderRefunding
(
String
orderId
)
{
GoblinStoreOrderVo
storeOrderVo
=
goblinRedisUtils
.
getGoblinOrder
(
orderId
);
if
(
storeOrderVo
==
null
)
{
return
;
}
storeOrderVo
.
setStatus
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
());
goblinRedisUtils
.
setGoblinOrder
(
orderId
,
storeOrderVo
);
goblinMongoUtils
.
updateGoblinStoreOrderVo
(
orderId
,
storeOrderVo
);
}
/** 仅当主单仍为退款中时回滚为已支付 */
private
void
rollbackRefundingIfNeeded
(
String
orderId
)
{
GoblinStoreOrderVo
storeOrderVo
=
goblinRedisUtils
.
getGoblinOrder
(
orderId
);
if
(
storeOrderVo
==
null
)
{
return
;
}
if
(
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
()).
equals
(
storeOrderVo
.
getStatus
()))
{
syncOrderStatus
(
orderId
,
1
);
}
}
/**
/**
* 退款申请成功后,批量持久化子单状态(Redis/Mongo) + 扩展单退款字段 + 子单 status (MySQL)。
* 退款申请成功后:重读子单写 61(跳过已终态 6),主单 61 + 扩展退款字段 + 子单一次 MQ 落库。
* 合并为一次 MQ 消息发送,避免多消息顺序不一致。
*/
*/
private
void
persistRefund
Success
(
String
orderId
,
GoblinSqbOrderVo
orderVo
,
List
<
GoblinOrderSkuVo
>
orderSkuVos
,
String
nowStr
)
{
private
void
persistRefund
Applying
(
String
orderId
,
GoblinSqbOrderVo
orderVo
,
List
<
String
>
orderSkuVoIds
)
{
LocalDateTime
nowLdt
=
LocalDateTime
.
now
();
LocalDateTime
nowLdt
=
LocalDateTime
.
now
();
String
now
=
orderVo
.
getUpdatedAt
()
!=
null
?
orderVo
.
getUpdatedAt
()
:
nowLdt
.
format
(
DTF
);
GoblinStoreOrderVo
storeOrderVo
=
goblinRedisUtils
.
getGoblinOrder
(
orderId
);
// 回调已完成:只补扩展退款字段,不回写主单/子单状态
if
(
storeOrderVo
!=
null
&&
Integer
.
valueOf
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_6
.
getValue
()).
equals
(
storeOrderVo
.
getStatus
()))
{
log
.
warn
(
"[收钱吧-退款] 主单已退款完成,仅落库扩展退款字段 orderId={}"
,
orderId
);
LinkedList
<
String
>
sqls
=
new
LinkedList
<>();
LinkedList
<
String
>
sqls
=
new
LinkedList
<>();
sqls
.
add
(
SqlMapping
.
get
(
"goblin_sqb_order.update_refund"
));
sqls
.
add
(
SqlMapping
.
get
(
"goblin_sqb_order.update_refund"
));
sqls
.
add
(
SqlMapping
.
get
(
"goblin_order.store.refundSkuPrice"
));
LinkedList
<
Object
[]>
sqbArgs
=
new
LinkedList
<>();
sqbArgs
.
add
(
new
Object
[]{
orderVo
.
getRefundSn
(),
orderVo
.
getRefundSignature
(),
orderVo
.
getRefundReason
(),
now
,
orderId
,
now
,
now
});
if
(
queueUtils
!=
null
)
{
queueUtils
.
sendMsgByRedis
(
MQConst
.
GoblinQueue
.
GOBLIN_ORDER_CREATE_PAY
.
getKey
(),
SqlMapping
.
gets
(
sqls
,
sqbArgs
));
}
return
;
}
LinkedList
<
String
>
sqls
=
new
LinkedList
<>();
sqls
.
add
(
SqlMapping
.
get
(
"goblin_sqb.syncStatus"
));
sqls
.
add
(
SqlMapping
.
get
(
"goblin_sqb_order.update_refund"
));
LinkedList
<
Object
[]>
mainArgs
=
new
LinkedList
<>();
mainArgs
.
add
(
new
Object
[]{
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
(),
now
,
orderId
,
nowLdt
,
nowLdt
});
LinkedList
<
Object
[]>
sqbArgs
=
new
LinkedList
<>();
LinkedList
<
Object
[]>
sqbArgs
=
new
LinkedList
<>();
sqbArgs
.
add
(
new
Object
[]{
sqbArgs
.
add
(
new
Object
[]{
orderVo
.
getRefundSn
(),
orderVo
.
getRefundSignature
(),
orderVo
.
getRefundReason
(),
orderVo
.
getRefundSn
(),
orderVo
.
getRefundSignature
(),
orderVo
.
getRefundReason
(),
orderVo
.
getUpdatedAt
(),
orderId
,
orderVo
.
getUpdatedAt
(),
orderVo
.
getUpdatedAt
()
now
,
orderId
,
now
,
now
});
});
LinkedList
<
Object
[]>
skuArgs
=
new
LinkedList
<>();
LinkedList
<
Object
[]>
skuArgs
=
new
LinkedList
<>();
if
(!
CollectionUtils
.
isEmpty
(
orderSkuVos
))
{
if
(!
CollectionUtils
.
isEmpty
(
orderSkuVoIds
))
{
for
(
GoblinOrderSkuVo
skuVo
:
orderSkuVos
)
{
for
(
String
skuVoId
:
orderSkuVoIds
)
{
// 必须重读:避免用调 SQB 前的旧快照覆盖回调已写好的终态
GoblinOrderSkuVo
skuVo
=
goblinRedisUtils
.
getGoblinOrderSkuVo
(
skuVoId
);
if
(
skuVo
==
null
)
{
continue
;
}
int
skuStatus
=
skuVo
.
getStatus
();
if
(
skuStatus
==
GoblinStatusConst
.
Status
.
ORDER_STATUS_6
.
getValue
())
{
log
.
warn
(
"[收钱吧-退款] 子单已退款完成,跳过写61 orderSkuId={}"
,
skuVo
.
getOrderSkuId
());
continue
;
}
if
(
skuStatus
==
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
())
{
skuArgs
.
add
(
new
Object
[]{
skuVo
.
getPriceRefund
()
!=
null
?
skuVo
.
getPriceRefund
()
:
BigDecimal
.
ZERO
,
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
(),
nowLdt
,
skuVo
.
getOrderSkuId
(),
nowLdt
,
nowLdt
});
continue
;
}
skuVo
.
setStatus
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
());
skuVo
.
setStatus
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
());
skuVo
.
setPriceRefund
(
BigDecimal
.
ZERO
);
skuVo
.
setPriceRefund
(
BigDecimal
.
ZERO
);
goblinRedisUtils
.
setGoblinOrderSku
(
skuVo
.
getOrderSkuId
(),
skuVo
);
goblinRedisUtils
.
setGoblinOrderSku
(
skuVo
.
getOrderSkuId
(),
skuVo
);
goblinMongoUtils
.
updateGoblinOrderSkuVo
(
skuVo
.
getOrderSkuId
(),
skuVo
);
goblinMongoUtils
.
updateGoblinOrderSkuVo
(
skuVo
.
getOrderSkuId
(),
skuVo
);
skuArgs
.
add
(
new
Object
[]{
BigDecimal
.
ZERO
,
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
(),
nowLdt
,
skuVo
.
getOrderSkuId
(),
nowLdt
,
nowLdt
});
skuArgs
.
add
(
new
Object
[]{
BigDecimal
.
ZERO
,
GoblinStatusConst
.
Status
.
ORDER_STATUS_61
.
getValue
(),
nowLdt
,
skuVo
.
getOrderSkuId
(),
nowLdt
,
nowLdt
});
}
}
}
}
if
(
queueUtils
!=
null
)
{
if
(
queueUtils
!=
null
)
{
if
(!
skuArgs
.
isEmpty
())
{
sqls
.
add
(
SqlMapping
.
get
(
"goblin_order.store.refundSkuPrice"
));
queueUtils
.
sendMsgByRedis
(
MQConst
.
GoblinQueue
.
GOBLIN_ORDER_CREATE_PAY
.
getKey
(),
queueUtils
.
sendMsgByRedis
(
MQConst
.
GoblinQueue
.
GOBLIN_ORDER_CREATE_PAY
.
getKey
(),
SqlMapping
.
gets
(
sqls
,
sqbArgs
,
skuArgs
));
SqlMapping
.
gets
(
sqls
,
mainArgs
,
sqbArgs
,
skuArgs
));
}
else
{
queueUtils
.
sendMsgByRedis
(
MQConst
.
GoblinQueue
.
GOBLIN_ORDER_CREATE_PAY
.
getKey
(),
SqlMapping
.
gets
(
sqls
,
mainArgs
,
sqbArgs
));
}
}
}
}
}
...
...
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/util/GoblinSqbRedisUtils.java
View file @
9ca7ab4f
...
@@ -90,6 +90,27 @@ public class GoblinSqbRedisUtils {
...
@@ -90,6 +90,27 @@ public class GoblinSqbRedisUtils {
redisUtil
.
uLock
(
key
);
redisUtil
.
uLock
(
key
);
}
}
/* ---------------------------------------- 退款锁 / 库存归还幂等 ---------------------------------------- */
/**
* 尝试获取收钱吧退款锁(发起退款与退款回调共用,按 orderId)
*/
public
boolean
tryRefundLock
(
String
orderId
)
{
String
key
=
GoblinRedisConst
.
REFUND_ORDER_LOCK
.
concat
(
orderId
);
return
redisUtil
.
lock
(
key
,
1
,
RedisKeyExpireConst
.
SQB_REFUND_LOCK_EXPIRE
);
}
public
void
releaseRefundLock
(
String
orderId
)
{
redisUtil
.
uLock
(
GoblinRedisConst
.
REFUND_ORDER_LOCK
.
concat
(
orderId
));
}
/**
* 标记退款库存已归还。首次标记成功返回 true(应执行 incrSkuStock),已标记过返回 false。
*/
public
boolean
tryMarkRefundStockRestored
(
String
orderId
)
{
String
key
=
GoblinRedisConst
.
SQB_REFUND_STOCK_FLAG
.
concat
(
orderId
);
return
redisUtil
.
lock
(
key
,
1
,
RedisKeyExpireConst
.
SQB_REFUND_STOCK_FLAG_EXPIRE
);
}
// 获取 收钱吧订单id列表
// 获取 收钱吧订单id列表
public
List
<
String
>
getSqbOrderList
(
String
uid
)
{
public
List
<
String
>
getSqbOrderList
(
String
uid
)
{
...
...
liquidnet-bus-service/liquidnet-service-order/liquidnet-service-order-impl/src/main/java/com/liquidnet/service/order/service/impl/GoblinSqbOrderServiceImpl.java
View file @
9ca7ab4f
...
@@ -257,8 +257,10 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
...
@@ -257,8 +257,10 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
orderSkuVo
.
setSkuImage
(
skuImg
);
orderSkuVo
.
setSkuImage
(
skuImg
);
orderSkuVo
.
setSkuSpecs
(
"[]"
);
orderSkuVo
.
setSkuSpecs
(
"[]"
);
orderSkuVo
.
setPriceVoucher
(
BigDecimal
.
ZERO
);
orderSkuVo
.
setPriceVoucher
(
BigDecimal
.
ZERO
);
orderSkuVo
.
setPriceRefund
(
BigDecimal
.
ZERO
);
orderSkuVo
.
setCreatedAt
(
now
);
orderSkuVo
.
setCreatedAt
(
now
);
// 特殊标识类型:33
// 待支付;特殊标识类型:33
orderSkuVo
.
setStatus
(
GoblinStatusConst
.
Status
.
ORDER_STATUS_0
.
getValue
());
orderSkuVo
.
setSkuType
(
33
);
orderSkuVo
.
setSkuType
(
33
);
// 存入 Redis 并绑定关系
// 存入 Redis 并绑定关系
...
@@ -519,7 +521,7 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
...
@@ -519,7 +521,7 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
/**
/**
* 退款完成回调(targetState=20)。
* 退款完成回调(targetState=20)。
* 主单 status→6(退款通过) + priceRefund→实付金额,子单 status→6 + priceRefund→实付金额,
* 主单 status→6(退款通过) + priceRefund→实付金额,子单 status→6 + priceRefund→实付金额,
* 恢复库存,主单/子单/扩展单批量落库 MySQL。
* 恢复库存
(幂等)
,主单/子单/扩展单批量落库 MySQL。
*/
*/
@Override
@Override
public
ResponseDto
<
String
>
handleRefundCallback
(
RefundCallbackContent
refundCallbackContent
)
{
public
ResponseDto
<
String
>
handleRefundCallback
(
RefundCallbackContent
refundCallbackContent
)
{
...
@@ -536,6 +538,12 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
...
@@ -536,6 +538,12 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
final
String
orderId
=
goblinSqbRedisUtils
.
getOrderIdBySqbOrderSn
(
sqbOrderSn
);
final
String
orderId
=
goblinSqbRedisUtils
.
getOrderIdBySqbOrderSn
(
sqbOrderSn
);
if
(
StringUtil
.
isBlank
(
orderId
))
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(
StringUtil
.
isBlank
(
orderId
))
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(!
tryAcquireRefundLock
(
orderId
))
{
log
.
warn
(
"[收钱吧退款回调] 获取退款锁失败,稍后重试 orderId={}"
,
orderId
);
return
ResponseDto
.
failure
(
"退款处理中"
);
}
try
{
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
if
(
orderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(
orderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
GoblinStoreOrderVo
storeOrderVo
=
goblinRedisUtils
.
getGoblinOrder
(
orderId
);
GoblinStoreOrderVo
storeOrderVo
=
goblinRedisUtils
.
getGoblinOrder
(
orderId
);
...
@@ -574,8 +582,14 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
...
@@ -574,8 +582,14 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
goblinRedisUtils
.
setGoblinOrder
(
orderId
,
storeOrderVo
);
goblinRedisUtils
.
setGoblinOrder
(
orderId
,
storeOrderVo
);
goblinMongoUtils
.
updateGoblinStoreOrderVo
(
orderId
,
storeOrderVo
);
goblinMongoUtils
.
updateGoblinStoreOrderVo
(
orderId
,
storeOrderVo
);
// 恢复库存
// 恢复库存(SETNX 幂等,防重复回调双还)
if
(
goblinSqbRedisUtils
.
tryMarkRefundStockRestored
(
orderId
))
{
goblinRedisUtils
.
incrSkuStock
(
null
,
orderVo
.
getSkuId
(),
orderVo
.
getQuantity
());
goblinRedisUtils
.
incrSkuStock
(
null
,
orderVo
.
getSkuId
(),
orderVo
.
getQuantity
());
log
.
info
(
"[收钱吧退款回调] 库存已归还 orderId={}, skuId={}, qty={}"
,
orderId
,
orderVo
.
getSkuId
(),
orderVo
.
getQuantity
());
}
else
{
log
.
info
(
"[收钱吧退款回调] 库存已归还过,跳过 orderId={}"
,
orderId
);
}
// 批量落库 MySQL
// 批量落库 MySQL
LinkedList
<
String
>
sqls
=
new
LinkedList
<>();
LinkedList
<
String
>
sqls
=
new
LinkedList
<>();
...
@@ -606,6 +620,9 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
...
@@ -606,6 +620,9 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
log
.
info
(
"[收钱吧退款回调] targetState=20 更新退款状态成功,orderId={}"
,
orderId
);
log
.
info
(
"[收钱吧退款回调] targetState=20 更新退款状态成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
"success"
);
return
ResponseDto
.
success
(
"success"
);
}
finally
{
goblinSqbRedisUtils
.
releaseRefundLock
(
orderId
);
}
}
}
@Override
@Override
...
@@ -892,6 +909,24 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
...
@@ -892,6 +909,24 @@ public class GoblinSqbOrderServiceImpl implements IGoblinSqbOrderService {
return
0
;
return
0
;
}
}
/**
* 退款回调抢锁:与发起退款共用 orderId 锁;短暂重试,避免发起侧刚释放前回调直接失败。
*/
private
boolean
tryAcquireRefundLock
(
String
orderId
)
{
for
(
int
i
=
0
;
i
<
5
;
i
++)
{
if
(
goblinSqbRedisUtils
.
tryRefundLock
(
orderId
))
{
return
true
;
}
try
{
Thread
.
sleep
(
200L
);
}
catch
(
InterruptedException
e
)
{
Thread
.
currentThread
().
interrupt
();
return
false
;
}
}
return
false
;
}
/**
/**
* 将收钱吧内部状态码映射为商城主单 status,同步 Redis / MongoDB / MySQL。
* 将收钱吧内部状态码映射为商城主单 status,同步 Redis / MongoDB / MySQL。
*/
*/
...
...
liquidnet-bus-service/liquidnet-service-order/liquidnet-service-order-impl/src/main/java/com/liquidnet/service/order/utils/GoblinSqbRedisUtils.java
View file @
9ca7ab4f
...
@@ -88,6 +88,28 @@ public class GoblinSqbRedisUtils {
...
@@ -88,6 +88,28 @@ public class GoblinSqbRedisUtils {
redisUtil
.
uLock
(
key
);
redisUtil
.
uLock
(
key
);
}
}
/* ---------------------------------------- 退款锁 / 库存归还幂等 ---------------------------------------- */
/**
* 尝试获取收钱吧退款锁(发起退款与退款回调共用,按 orderId)
*/
public
boolean
tryRefundLock
(
String
orderId
)
{
String
key
=
GoblinRedisConst
.
REFUND_ORDER_LOCK
.
concat
(
orderId
);
return
redisUtil
.
lock
(
key
,
1
,
RedisKeyExpireConst
.
SQB_REFUND_LOCK_EXPIRE
);
}
public
void
releaseRefundLock
(
String
orderId
)
{
redisUtil
.
uLock
(
GoblinRedisConst
.
REFUND_ORDER_LOCK
.
concat
(
orderId
));
}
/**
* 标记退款库存已归还。首次标记成功返回 true(应执行 incrSkuStock),已标记过返回 false。
*/
public
boolean
tryMarkRefundStockRestored
(
String
orderId
)
{
String
key
=
GoblinRedisConst
.
SQB_REFUND_STOCK_FLAG
.
concat
(
orderId
);
return
redisUtil
.
lock
(
key
,
1
,
RedisKeyExpireConst
.
SQB_REFUND_STOCK_FLAG_EXPIRE
);
}
/**
/**
* 添加用户 收钱吧订单列表
* 添加用户 收钱吧订单列表
*
*
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment