记得上下班打卡 | 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
52399664
Commit
52399664
authored
Mar 25, 2026
by
姜秀龙
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
收钱吧 ai1
parent
1350071c
Changes
30
Hide whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
2916 additions
and
0 deletions
+2916
-0
sqb_goods_integration.sql
docu/sqb_goods_integration.sql
+69
-0
GoblinSqbCouponVo.java
...om/liquidnet/service/goblin/dto/vo/GoblinSqbCouponVo.java
+12
-0
GoblinSqbOrderCreateVo.java
...quidnet/service/goblin/dto/vo/GoblinSqbOrderCreateVo.java
+18
-0
GoblinSqbOrderDetailVo.java
...quidnet/service/goblin/dto/vo/GoblinSqbOrderDetailVo.java
+80
-0
GoblinSqbOrderVo.java
...com/liquidnet/service/goblin/dto/vo/GoblinSqbOrderVo.java
+28
-0
GoblinSqbPerfGoodsVo.java
...liquidnet/service/goblin/dto/vo/GoblinSqbPerfGoodsVo.java
+16
-0
IGoblinSqbGoodsService.java
...uidnet/service/goblin/service/IGoblinSqbGoodsService.java
+38
-0
IGoblinSqbOrderService.java
...uidnet/service/goblin/service/IGoblinSqbOrderService.java
+122
-0
SqbPerformanceGoodsController.java
...roller/zhengzai/goblin/SqbPerformanceGoodsController.java
+61
-0
ISqbPerformanceGoodsService.java
.../zhengzai/goblin/service/ISqbPerformanceGoodsService.java
+39
-0
SqbPerformanceGoodsServiceImpl.java
...i/goblin/service/impl/SqbPerformanceGoodsServiceImpl.java
+233
-0
GoblinSqbGoodsExt.java
...om/liquidnet/service/goblin/entity/GoblinSqbGoodsExt.java
+63
-0
GoblinSqbMallInfo.java
...om/liquidnet/service/goblin/entity/GoblinSqbMallInfo.java
+63
-0
GoblinSqbOrder.java
...a/com/liquidnet/service/goblin/entity/GoblinSqbOrder.java
+113
-0
GoblinSqbPerformanceGoods.java
...dnet/service/goblin/entity/GoblinSqbPerformanceGoods.java
+58
-0
GoblinSqbGoodsExtMapper.java
...uidnet/service/goblin/mapper/GoblinSqbGoodsExtMapper.java
+16
-0
GoblinSqbMallInfoMapper.java
...uidnet/service/goblin/mapper/GoblinSqbMallInfoMapper.java
+16
-0
GoblinSqbOrderMapper.java
...liquidnet/service/goblin/mapper/GoblinSqbOrderMapper.java
+16
-0
GoblinSqbPerformanceGoodsMapper.java
...ervice/goblin/mapper/GoblinSqbPerformanceGoodsMapper.java
+16
-0
FeignGoblinTaskClient.java
...dnet/service/feign/goblin/task/FeignGoblinTaskClient.java
+5
-0
GoblinTaskHandler.java
...dnet/service/executor/main/handler/GoblinTaskHandler.java
+15
-0
pom.xml
...dnet-service-goblin/liquidnet-service-goblin-impl/pom.xml
+6
-0
GoblinSqbCallbackController.java
...ervice/goblin/controller/GoblinSqbCallbackController.java
+58
-0
GoblinSqbGoodsController.java
...t/service/goblin/controller/GoblinSqbGoodsController.java
+56
-0
GoblinSqbOrderController.java
...t/service/goblin/controller/GoblinSqbOrderController.java
+152
-0
GoblinSqbJobController.java
...rvice/goblin/controller/Inner/GoblinSqbJobController.java
+40
-0
GoblinSqbGoodsServiceImpl.java
...ervice/goblin/service/impl/GoblinSqbGoodsServiceImpl.java
+546
-0
GoblinSqbOrderServiceImpl.java
...ervice/goblin/service/impl/GoblinSqbOrderServiceImpl.java
+861
-0
GoblinRedisUtils.java
...a/com/liquidnet/service/goblin/util/GoblinRedisUtils.java
+5
-0
GoblinSqbRedisUtils.java
...om/liquidnet/service/goblin/util/GoblinSqbRedisUtils.java
+95
-0
No files found.
docu/sqb_goods_integration.sql
0 → 100644
View file @
52399664
CREATE
TABLE
`goblin_sqb_mall_info`
(
`mid`
BIGINT
NOT
NULL
AUTO_INCREMENT
,
`mall_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'商城唯一ID'
,
`mall_sn`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'收钱吧商城编号'
,
`mall_name`
VARCHAR
(
128
)
NOT
NULL
COMMENT
'商城名称'
,
`signature`
VARCHAR
(
256
)
NOT
NULL
COMMENT
'商城密钥'
,
`store_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'关联我方店铺ID'
,
`status`
TINYINT
NOT
NULL
DEFAULT
1
COMMENT
'0-禁用 1-启用'
,
`created_at`
DATETIME
NOT
NULL
,
`updated_at`
DATETIME
NOT
NULL
,
PRIMARY
KEY
(
`mid`
),
UNIQUE
KEY
`uk_mall_sn`
(
`mall_sn`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COMMENT
=
'收钱吧商城信息'
;
CREATE
TABLE
`goblin_sqb_goods_ext`
(
`mid`
BIGINT
NOT
NULL
AUTO_INCREMENT
,
`spu_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'关联 goblin_goods.spu_id'
,
`sku_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'关联 goblin_goods_sku.sku_id'
,
`mall_sn`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'所属收钱吧商城编号'
,
`sqb_product_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'收钱吧商品ID'
,
`sqb_product_sn`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'收钱吧商品编号'
,
`sqb_sku_id`
VARCHAR
(
64
)
COMMENT
'收钱吧SKU ID'
,
`created_at`
DATETIME
NOT
NULL
,
`updated_at`
DATETIME
NOT
NULL
,
PRIMARY
KEY
(
`mid`
),
UNIQUE
KEY
`uk_spu_sku`
(
`spu_id`
,
`sku_id`
),
KEY
`idx_mall_sn`
(
`mall_sn`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COMMENT
=
'收钱吧商品扩展信息'
;
CREATE
TABLE
`goblin_sqb_performance_goods`
(
`mid`
BIGINT
NOT
NULL
AUTO_INCREMENT
,
`performances_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'演出ID'
,
`spu_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'商品ID'
,
`sku_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'SKU ID'
,
`sort`
INT
NOT
NULL
DEFAULT
0
COMMENT
'排序权重'
,
`status`
TINYINT
NOT
NULL
DEFAULT
1
COMMENT
'0-禁用 1-启用'
,
`created_at`
DATETIME
NOT
NULL
,
`updated_at`
DATETIME
NOT
NULL
,
PRIMARY
KEY
(
`mid`
),
UNIQUE
KEY
`uk_perf_sku`
(
`performances_id`
,
`sku_id`
),
KEY
`idx_performances_id`
(
`performances_id`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COMMENT
=
'演出-收钱吧商品关联'
;
CREATE
TABLE
`goblin_sqb_order`
(
`mid`
BIGINT
NOT
NULL
AUTO_INCREMENT
,
`order_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'本地订单ID'
,
`user_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'用户ID'
,
`performances_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'关联演出ID'
,
`spu_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'商品ID'
,
`sku_id`
VARCHAR
(
64
)
NOT
NULL
COMMENT
'SKU ID'
,
`quantity`
INT
NOT
NULL
DEFAULT
1
,
`amount`
BIGINT
NOT
NULL
COMMENT
'支付金额(分)'
,
`sqb_order_sn`
VARCHAR
(
64
)
COMMENT
'收钱吧订单号'
,
`sqb_order_signature`
VARCHAR
(
256
)
COMMENT
'收钱吧订单签名'
,
`sqb_acquiring_sn`
VARCHAR
(
64
)
COMMENT
'收钱吧收单号'
,
`sqb_checkout_items_id`
VARCHAR
(
64
)
COMMENT
'结算明细ID'
,
`coupon_sn`
VARCHAR
(
64
)
COMMENT
'券码编号'
,
`coupon_qr_code`
VARCHAR
(
512
)
COMMENT
'核销二维码'
,
`coupon_expire_time`
DATETIME
COMMENT
'券码过期时间'
,
`status`
TINYINT
NOT
NULL
DEFAULT
0
COMMENT
'0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败'
,
`refund_reason`
VARCHAR
(
256
)
COMMENT
'退款原因'
,
`created_at`
DATETIME
NOT
NULL
,
`updated_at`
DATETIME
NOT
NULL
,
PRIMARY
KEY
(
`mid`
),
UNIQUE
KEY
`uk_order_id`
(
`order_id`
),
KEY
`idx_user_id`
(
`user_id`
),
KEY
`idx_performances_id`
(
`performances_id`
),
KEY
`idx_sqb_acquiring_sn`
(
`sqb_acquiring_sn`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COMMENT
=
'收钱吧订单'
;
liquidnet-bus-api/liquidnet-service-goblin-api/src/main/java/com/liquidnet/service/goblin/dto/vo/GoblinSqbCouponVo.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
dto
.
vo
;
import
lombok.Data
;
import
java.io.Serializable
;
@Data
public
class
GoblinSqbCouponVo
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
private
String
couponSn
;
private
String
couponQrCode
;
private
String
couponExpireTime
;
}
liquidnet-bus-api/liquidnet-service-goblin-api/src/main/java/com/liquidnet/service/goblin/dto/vo/GoblinSqbOrderCreateVo.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
dto
.
vo
;
import
lombok.Data
;
import
java.io.Serializable
;
@Data
public
class
GoblinSqbOrderCreateVo
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
private
String
orderId
;
private
String
acquiringSn
;
// paymentVoucher fields
private
String
timeStamp
;
private
String
packageStr
;
private
String
paySign
;
private
String
appId
;
private
String
signType
;
private
String
nonceStr
;
}
liquidnet-bus-api/liquidnet-service-goblin-api/src/main/java/com/liquidnet/service/goblin/dto/vo/GoblinSqbOrderDetailVo.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
dto
.
vo
;
import
io.swagger.annotations.ApiModelProperty
;
import
lombok.Data
;
import
java.io.Serializable
;
import
java.math.BigDecimal
;
/**
* 收钱吧订单列表/详情展示 VO
* 融合:GoblinStoreOrderVo(基础信息)+ GoblinOrderSkuVo(商品信息)+ GoblinSqbOrderVo(收钱吧扩展)
*/
@Data
public
class
GoblinSqbOrderDetailVo
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
// ========== 来自 GoblinStoreOrderVo(现有订单体系) ==========
@ApiModelProperty
(
value
=
"本地订单ID"
)
private
String
orderId
;
@ApiModelProperty
(
value
=
"订单号"
)
private
String
orderCode
;
@ApiModelProperty
(
value
=
"goblin订单状态[0-待付款|2-已付款|5-取消]"
)
private
Integer
status
;
@ApiModelProperty
(
value
=
"实付金额"
)
private
BigDecimal
priceActual
;
@ApiModelProperty
(
value
=
"创建时间"
)
private
String
createdAt
;
@ApiModelProperty
(
value
=
"支付时间"
)
private
String
payTime
;
// ========== 来自 GoblinOrderSkuVo(sku商品信息) ==========
@ApiModelProperty
(
value
=
"商品SPU ID"
)
private
String
spuId
;
@ApiModelProperty
(
value
=
"商品名称"
)
private
String
spuName
;
@ApiModelProperty
(
value
=
"商品SKU ID"
)
private
String
skuId
;
@ApiModelProperty
(
value
=
"款式名称"
)
private
String
skuName
;
@ApiModelProperty
(
value
=
"款式图片"
)
private
String
skuImage
;
@ApiModelProperty
(
value
=
"购买数量"
)
private
Integer
quantity
;
// ========== 来自 GoblinSqbOrderVo(收钱吧扩展字段) ==========
@ApiModelProperty
(
value
=
"收钱吧订单状态:0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败"
)
private
Integer
sqbStatus
;
@ApiModelProperty
(
value
=
"关联演出ID"
)
private
String
performancesId
;
@ApiModelProperty
(
value
=
"收钱吧收单号"
)
private
String
sqbAcquiringSn
;
@ApiModelProperty
(
value
=
"券码编号"
)
private
String
couponSn
;
@ApiModelProperty
(
value
=
"核销二维码"
)
private
String
couponQrCode
;
@ApiModelProperty
(
value
=
"券码过期时间"
)
private
String
couponExpireTime
;
@ApiModelProperty
(
value
=
"核销状态:0-未核销 1-已核销"
)
private
Integer
couponUsedStatus
;
}
liquidnet-bus-api/liquidnet-service-goblin-api/src/main/java/com/liquidnet/service/goblin/dto/vo/GoblinSqbOrderVo.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
dto
.
vo
;
import
lombok.Data
;
import
java.io.Serializable
;
@Data
public
class
GoblinSqbOrderVo
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
private
String
orderId
;
private
String
userId
;
private
String
performancesId
;
private
String
spuId
;
private
String
skuId
;
private
Integer
quantity
;
private
Long
amount
;
private
String
sqbOrderSn
;
private
String
sqbOrderSignature
;
private
String
sqbAcquiringSn
;
private
String
sqbCheckoutItemsId
;
private
String
couponSn
;
private
String
couponQrCode
;
private
String
couponExpireTime
;
private
Integer
status
;
// 0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败
private
Integer
couponUsedStatus
;
// 0-未核销 1-已核销
private
String
refundReason
;
private
String
createdAt
;
private
String
updatedAt
;
}
liquidnet-bus-api/liquidnet-service-goblin-api/src/main/java/com/liquidnet/service/goblin/dto/vo/GoblinSqbPerfGoodsVo.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
dto
.
vo
;
import
lombok.Data
;
import
java.io.Serializable
;
@Data
public
class
GoblinSqbPerfGoodsVo
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
private
String
spuId
;
private
String
spuName
;
private
String
skuId
;
private
String
skuName
;
private
Long
price
;
private
String
coverPic
;
private
Integer
sort
;
}
liquidnet-bus-api/liquidnet-service-goblin-api/src/main/java/com/liquidnet/service/goblin/service/IGoblinSqbGoodsService.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
service
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo
;
import
java.util.List
;
import
java.util.Map
;
/**
* 收钱吧商品同步服务接口
*/
public
interface
IGoblinSqbGoodsService
{
/**
* 获取所有商城及商品列表
* 自动拉取所有商城,循环获取每个商城的商品列表,聚合返回
*
* @return 商城商品列表
*/
ResponseDto
<
List
<
GoblinSqbPerfGoodsVo
>>
getAllMallProducts
();
/**
* 批量同步商品到 goblin 系统
* 已存在则更新,不存在则新增(spuType=33)
*
* @param items 待同步商品列表,每项包含 mallSn、sqbProductId、sqbProductSn
* @return 同步结果
*/
ResponseDto
<
String
>
syncGoods
(
List
<
Map
<
String
,
String
>>
items
);
/**
* 查询演出关联商品列表(先查 Redis 缓存,未命中则查 MySQL 并写入缓存)
*
* @param performancesId 演出ID
* @return 演出关联商品列表
*/
ResponseDto
<
List
<
GoblinSqbPerfGoodsVo
>>
getPerfGoods
(
String
performancesId
);
}
liquidnet-bus-api/liquidnet-service-goblin-api/src/main/java/com/liquidnet/service/goblin/service/IGoblinSqbOrderService.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
service
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbCouponVo
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderCreateVo
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderDetailVo
;
import
java.util.List
;
import
java.util.Map
;
/**
* 收钱吧订单服务接口
*/
public
interface
IGoblinSqbOrderService
{
/**
* 创建收钱吧订单
*
* @param userId 用户ID(从 token 获取)
* @param spuId 商品 SPU ID
* @param skuId 商品 SKU ID
* @param quantity 购买数量
* @param performancesId 关联演出 ID
* @return 订单创建结果(orderId、acquiringSn、paymentVoucher)
*/
ResponseDto
<
GoblinSqbOrderCreateVo
>
createOrder
(
String
userId
,
String
spuId
,
String
skuId
,
Integer
quantity
,
String
performancesId
);
/**
* 查询支付状态
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 支付状态(0-待支付 1-已支付 9-失败)
*/
ResponseDto
<
Integer
>
queryPayStatus
(
String
userId
,
String
orderId
);
/**
* 获取核销二维码(券码)
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 券码信息(couponSn、couponQrCode、couponExpireTime)
*/
ResponseDto
<
GoblinSqbCouponVo
>
queryCoupon
(
String
userId
,
String
orderId
);
/**
* 申请退款
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 退款结果
*/
ResponseDto
<
Boolean
>
refund
(
String
userId
,
String
orderId
);
/**
* 主动同步核销状态
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 同步结果
*/
ResponseDto
<
Boolean
>
syncCouponStatus
(
String
userId
,
String
orderId
);
/**
* 支付成功回调(收钱吧主动推送)
*
* @param params 回调参数
* @return "success"
*/
ResponseDto
<
String
>
handlePayCallback
(
Map
<
String
,
Object
>
params
);
/**
* 退款成功回调(收钱吧主动推送)
*
* @param params 回调参数
* @return "success"
*/
ResponseDto
<
String
>
handleRefundCallback
(
Map
<
String
,
Object
>
params
);
/**
* 券状态变更回调(收钱吧主动推送)
*
* @param params 回调参数
* @return "success"
*/
ResponseDto
<
String
>
handleCouponCallback
(
Map
<
String
,
Object
>
params
);
/**
* 演出结束自动退款(定时任务调用)
*
* @param performancesId 演出ID
* @return 处理结果摘要(成功/失败笔数)
*/
ResponseDto
<
String
>
autoRefundByPerformance
(
String
performancesId
);
/**
* 查询用户收钱吧订单列表
*
* @param userId 用户ID
* @return 收钱吧订单列表(仅 skuType=33 的订单,按下单时间倒序)
*/
ResponseDto
<
List
<
GoblinSqbOrderDetailVo
>>
getOrderList
(
String
userId
);
/**
* 查询收钱吧订单详情
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 订单详情(基础信息 + 收钱吧扩展信息)
*/
ResponseDto
<
GoblinSqbOrderDetailVo
>
getOrderDetail
(
String
userId
,
String
orderId
);
/**
* 再次付款(待支付状态下重新拉起微信支付)
*
* @param userId 用户ID
* @param orderId 本地订单ID
* @return 新的 paymentVoucher(复用原 orderId)
*/
ResponseDto
<
GoblinSqbOrderCreateVo
>
repay
(
String
userId
,
String
orderId
);
}
liquidnet-bus-client/liquidnet-client-admin/liquidnet-client-admin-web/src/main/java/com/liquidnet/client/admin/web/controller/zhengzai/goblin/SqbPerformanceGoodsController.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
client
.
admin
.
web
.
controller
.
zhengzai
.
goblin
;
import
com.liquidnet.client.admin.common.core.controller.BaseController
;
import
com.liquidnet.client.admin.zhengzai.goblin.service.ISqbPerformanceGoodsService
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo
;
import
io.swagger.annotations.*
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.*
;
import
java.util.List
;
/**
* 演出-收钱吧商品关联管理接口
*/
@Slf4j
@RestController
@Api
(
tags
=
"收钱吧-演出商品关联管理"
)
@RequestMapping
(
"sqb/performance/goods"
)
public
class
SqbPerformanceGoodsController
extends
BaseController
{
@Autowired
private
ISqbPerformanceGoodsService
sqbPerformanceGoodsService
;
@PostMapping
(
"bind"
)
@ApiOperation
(
"关联演出与商品"
)
@ApiResponse
(
code
=
200
,
message
=
"接口返回对象参数"
)
@ApiImplicitParams
({
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"performancesId"
,
value
=
"演出ID"
),
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"skuIds"
,
value
=
"SKU ID列表(多个逗号分隔)"
),
@ApiImplicitParam
(
type
=
"form"
,
required
=
false
,
dataType
=
"Integer"
,
name
=
"sort"
,
value
=
"排序权重"
,
example
=
"0"
),
})
public
ResponseDto
<
Boolean
>
bind
(
@RequestParam
(
"performancesId"
)
String
performancesId
,
@RequestParam
(
"skuIds"
)
List
<
String
>
skuIds
,
@RequestParam
(
value
=
"sort"
,
required
=
false
,
defaultValue
=
"0"
)
Integer
sort
)
{
return
sqbPerformanceGoodsService
.
bind
(
performancesId
,
skuIds
,
sort
);
}
@DeleteMapping
(
"unbind"
)
@ApiOperation
(
"解除演出与商品关联"
)
@ApiResponse
(
code
=
200
,
message
=
"接口返回对象参数"
)
@ApiImplicitParams
({
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"performancesId"
,
value
=
"演出ID"
),
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"skuId"
,
value
=
"SKU ID"
),
})
public
ResponseDto
<
Boolean
>
unbind
(
@RequestParam
(
"performancesId"
)
String
performancesId
,
@RequestParam
(
"skuId"
)
String
skuId
)
{
return
sqbPerformanceGoodsService
.
unbind
(
performancesId
,
skuId
);
}
@GetMapping
(
"list"
)
@ApiOperation
(
"查询演出关联商品列表(管理后台)"
)
@ApiResponse
(
code
=
200
,
message
=
"接口返回对象参数"
)
@ApiImplicitParams
({
@ApiImplicitParam
(
type
=
"query"
,
required
=
true
,
dataType
=
"String"
,
name
=
"performancesId"
,
value
=
"演出ID"
),
})
public
ResponseDto
<
List
<
GoblinSqbPerfGoodsVo
>>
list
(
@RequestParam
(
"performancesId"
)
String
performancesId
)
{
return
sqbPerformanceGoodsService
.
list
(
performancesId
);
}
}
liquidnet-bus-client/liquidnet-client-admin/liquidnet-client-admin-zhengzai/src/main/java/com/liquidnet/client/admin/zhengzai/goblin/service/ISqbPerformanceGoodsService.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
client
.
admin
.
zhengzai
.
goblin
.
service
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo
;
import
java.util.List
;
/**
* 演出-收钱吧商品关联 服务接口
*/
public
interface
ISqbPerformanceGoodsService
{
/**
* 关联演出与商品(批量)
*
* @param performancesId 演出ID
* @param skuIds SKU ID 列表
* @param sort 排序权重
* @return 操作结果
*/
ResponseDto
<
Boolean
>
bind
(
String
performancesId
,
List
<
String
>
skuIds
,
Integer
sort
);
/**
* 解除演出与商品关联
*
* @param performancesId 演出ID
* @param skuId SKU ID
* @return 操作结果
*/
ResponseDto
<
Boolean
>
unbind
(
String
performancesId
,
String
skuId
);
/**
* 查询演出关联的收钱吧商品列表
*
* @param performancesId 演出ID
* @return 商品列表
*/
ResponseDto
<
List
<
GoblinSqbPerfGoodsVo
>>
list
(
String
performancesId
);
}
liquidnet-bus-client/liquidnet-client-admin/liquidnet-client-admin-zhengzai/src/main/java/com/liquidnet/client/admin/zhengzai/goblin/service/impl/SqbPerformanceGoodsServiceImpl.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
client
.
admin
.
zhengzai
.
goblin
.
service
.
impl
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper
;
import
com.liquidnet.client.admin.zhengzai.goblin.service.ISqbPerformanceGoodsService
;
import
com.liquidnet.common.cache.redis.util.RedisDataSourceUtil
;
import
com.liquidnet.commons.lang.util.IDGenerator
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo
;
import
com.liquidnet.service.goblin.entity.GoblinGoods
;
import
com.liquidnet.service.goblin.entity.GoblinGoodsSku
;
import
com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods
;
import
com.liquidnet.service.goblin.mapper.GoblinGoodsMapper
;
import
com.liquidnet.service.goblin.mapper.GoblinGoodsSkuMapper
;
import
com.liquidnet.service.goblin.mapper.GoblinSqbPerformanceGoodsMapper
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
java.time.LocalDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.function.Function
;
import
java.util.stream.Collectors
;
/**
* 演出-收钱吧商品关联 服务实现
* 直连 MySQL,写入/删除时同步删除 Redis 缓存
*/
@Slf4j
@Service
public
class
SqbPerformanceGoodsServiceImpl
implements
ISqbPerformanceGoodsService
{
/** Redis key 前缀,与 goblin C端接口保持一致 */
private
static
final
String
PERF_GOODS_CACHE_KEY_PREFIX
=
"goblin:sqb:perf:goods:"
;
@Autowired
private
GoblinSqbPerformanceGoodsMapper
performanceGoodsMapper
;
@Autowired
private
GoblinGoodsMapper
goblinGoodsMapper
;
@Autowired
private
GoblinGoodsSkuMapper
goblinGoodsSkuMapper
;
@Autowired
private
RedisDataSourceUtil
redisDataSourceUtil
;
@Override
public
ResponseDto
<
Boolean
>
bind
(
String
performancesId
,
List
<
String
>
skuIds
,
Integer
sort
)
{
if
(
performancesId
==
null
||
performancesId
.
isEmpty
())
{
return
ResponseDto
.
failure
(
"演出ID不能为空"
);
}
if
(
skuIds
==
null
||
skuIds
.
isEmpty
())
{
return
ResponseDto
.
failure
(
"SKU列表不能为空"
);
}
String
now
=
LocalDateTime
.
now
().
format
(
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
));
int
sortVal
=
(
sort
!=
null
)
?
sort
:
0
;
try
{
for
(
String
skuId
:
skuIds
)
{
if
(
skuId
==
null
||
skuId
.
isEmpty
())
{
continue
;
}
// 查询 SKU 获取 spuId
LambdaQueryWrapper
<
GoblinGoodsSku
>
skuQuery
=
new
LambdaQueryWrapper
<>();
skuQuery
.
eq
(
GoblinGoodsSku:
:
getSkuId
,
skuId
).
last
(
"LIMIT 1"
);
GoblinGoodsSku
sku
=
goblinGoodsSkuMapper
.
selectOne
(
skuQuery
);
if
(
sku
==
null
)
{
log
.
warn
(
"[演出商品关联] SKU不存在,skuId={}"
,
skuId
);
continue
;
}
// 检查是否已存在关联
LambdaQueryWrapper
<
GoblinSqbPerformanceGoods
>
existQuery
=
new
LambdaQueryWrapper
<>();
existQuery
.
eq
(
GoblinSqbPerformanceGoods:
:
getPerformancesId
,
performancesId
)
.
eq
(
GoblinSqbPerformanceGoods:
:
getSkuId
,
skuId
)
.
last
(
"LIMIT 1"
);
GoblinSqbPerformanceGoods
existing
=
performanceGoodsMapper
.
selectOne
(
existQuery
);
if
(
existing
!=
null
)
{
// 已存在则更新 sort 和 status
LambdaUpdateWrapper
<
GoblinSqbPerformanceGoods
>
updateWrapper
=
new
LambdaUpdateWrapper
<>();
updateWrapper
.
eq
(
GoblinSqbPerformanceGoods:
:
getPerformancesId
,
performancesId
)
.
eq
(
GoblinSqbPerformanceGoods:
:
getSkuId
,
skuId
)
.
set
(
GoblinSqbPerformanceGoods:
:
getSort
,
sortVal
)
.
set
(
GoblinSqbPerformanceGoods:
:
getStatus
,
1
)
.
set
(
GoblinSqbPerformanceGoods:
:
getUpdatedAt
,
now
);
performanceGoodsMapper
.
update
(
null
,
updateWrapper
);
}
else
{
// 新增关联记录
GoblinSqbPerformanceGoods
entity
=
new
GoblinSqbPerformanceGoods
();
entity
.
setPerformancesId
(
performancesId
);
entity
.
setSpuId
(
sku
.
getSpuId
());
entity
.
setSkuId
(
skuId
);
entity
.
setSort
(
sortVal
);
entity
.
setStatus
(
1
);
entity
.
setCreatedAt
(
now
);
entity
.
setUpdatedAt
(
now
);
performanceGoodsMapper
.
insert
(
entity
);
}
}
// 删除 Redis 缓存,由 C端接口下次请求时重建
delPerfGoodsCache
(
performancesId
);
return
ResponseDto
.
success
(
Boolean
.
TRUE
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[演出商品关联] bind 异常,performancesId={}"
,
performancesId
,
e
);
return
ResponseDto
.
failure
(
"关联操作失败:"
+
e
.
getMessage
());
}
}
@Override
public
ResponseDto
<
Boolean
>
unbind
(
String
performancesId
,
String
skuId
)
{
if
(
performancesId
==
null
||
performancesId
.
isEmpty
())
{
return
ResponseDto
.
failure
(
"演出ID不能为空"
);
}
if
(
skuId
==
null
||
skuId
.
isEmpty
())
{
return
ResponseDto
.
failure
(
"SKU ID不能为空"
);
}
try
{
LambdaUpdateWrapper
<
GoblinSqbPerformanceGoods
>
updateWrapper
=
new
LambdaUpdateWrapper
<>();
updateWrapper
.
eq
(
GoblinSqbPerformanceGoods:
:
getPerformancesId
,
performancesId
)
.
eq
(
GoblinSqbPerformanceGoods:
:
getSkuId
,
skuId
)
.
set
(
GoblinSqbPerformanceGoods:
:
getStatus
,
0
)
.
set
(
GoblinSqbPerformanceGoods:
:
getUpdatedAt
,
LocalDateTime
.
now
().
format
(
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
)));
int
rows
=
performanceGoodsMapper
.
update
(
null
,
updateWrapper
);
if
(
rows
==
0
)
{
return
ResponseDto
.
failure
(
"关联记录不存在"
);
}
// 删除 Redis 缓存
delPerfGoodsCache
(
performancesId
);
return
ResponseDto
.
success
(
Boolean
.
TRUE
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[演出商品关联] unbind 异常,performancesId={}, skuId={}"
,
performancesId
,
skuId
,
e
);
return
ResponseDto
.
failure
(
"解除关联失败:"
+
e
.
getMessage
());
}
}
@Override
public
ResponseDto
<
List
<
GoblinSqbPerfGoodsVo
>>
list
(
String
performancesId
)
{
if
(
performancesId
==
null
||
performancesId
.
isEmpty
())
{
return
ResponseDto
.
failure
(
"演出ID不能为空"
);
}
try
{
// 查询启用状态的关联记录
LambdaQueryWrapper
<
GoblinSqbPerformanceGoods
>
query
=
new
LambdaQueryWrapper
<>();
query
.
eq
(
GoblinSqbPerformanceGoods:
:
getPerformancesId
,
performancesId
)
.
eq
(
GoblinSqbPerformanceGoods:
:
getStatus
,
1
)
.
orderByAsc
(
GoblinSqbPerformanceGoods:
:
getSort
);
List
<
GoblinSqbPerformanceGoods
>
relations
=
performanceGoodsMapper
.
selectList
(
query
);
if
(
relations
==
null
||
relations
.
isEmpty
())
{
return
ResponseDto
.
success
(
new
ArrayList
<>());
}
// 收集 skuId 和 spuId
List
<
String
>
skuIds
=
relations
.
stream
()
.
map
(
GoblinSqbPerformanceGoods:
:
getSkuId
)
.
collect
(
Collectors
.
toList
());
List
<
String
>
spuIds
=
relations
.
stream
()
.
map
(
GoblinSqbPerformanceGoods:
:
getSpuId
)
.
distinct
()
.
collect
(
Collectors
.
toList
());
// 批量查询 SKU 信息
LambdaQueryWrapper
<
GoblinGoodsSku
>
skuQuery
=
new
LambdaQueryWrapper
<>();
skuQuery
.
in
(
GoblinGoodsSku:
:
getSkuId
,
skuIds
);
List
<
GoblinGoodsSku
>
skuList
=
goblinGoodsSkuMapper
.
selectList
(
skuQuery
);
Map
<
String
,
GoblinGoodsSku
>
skuMap
=
skuList
.
stream
()
.
collect
(
Collectors
.
toMap
(
GoblinGoodsSku:
:
getSkuId
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
// 批量查询 SPU 信息
LambdaQueryWrapper
<
GoblinGoods
>
spuQuery
=
new
LambdaQueryWrapper
<>();
spuQuery
.
in
(
GoblinGoods:
:
getSpuId
,
spuIds
);
List
<
GoblinGoods
>
spuList
=
goblinGoodsMapper
.
selectList
(
spuQuery
);
Map
<
String
,
GoblinGoods
>
spuMap
=
spuList
.
stream
()
.
collect
(
Collectors
.
toMap
(
GoblinGoods:
:
getSpuId
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
// 组装 VO
List
<
GoblinSqbPerfGoodsVo
>
result
=
new
ArrayList
<>();
for
(
GoblinSqbPerformanceGoods
rel
:
relations
)
{
GoblinSqbPerfGoodsVo
vo
=
new
GoblinSqbPerfGoodsVo
();
vo
.
setSkuId
(
rel
.
getSkuId
());
vo
.
setSpuId
(
rel
.
getSpuId
());
vo
.
setSort
(
rel
.
getSort
());
GoblinGoodsSku
sku
=
skuMap
.
get
(
rel
.
getSkuId
());
if
(
sku
!=
null
)
{
vo
.
setSkuName
(
sku
.
getName
());
vo
.
setPrice
(
sku
.
getPrice
()
!=
null
?
sku
.
getPrice
().
longValue
()
:
null
);
}
GoblinGoods
spu
=
spuMap
.
get
(
rel
.
getSpuId
());
if
(
spu
!=
null
)
{
vo
.
setSpuName
(
spu
.
getName
());
vo
.
setCoverPic
(
spu
.
getCoverPic
());
}
result
.
add
(
vo
);
}
return
ResponseDto
.
success
(
result
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[演出商品关联] list 异常,performancesId={}"
,
performancesId
,
e
);
return
ResponseDto
.
failure
(
"查询失败:"
+
e
.
getMessage
());
}
}
/**
* 删除演出关联商品的 Redis 缓存
*/
private
void
delPerfGoodsCache
(
String
performancesId
)
{
try
{
String
cacheKey
=
PERF_GOODS_CACHE_KEY_PREFIX
+
performancesId
;
redisDataSourceUtil
.
getRedisGoblinUtil
().
del
(
cacheKey
);
log
.
info
(
"[演出商品关联] 已删除 Redis 缓存,key={}"
,
cacheKey
);
}
catch
(
Exception
e
)
{
log
.
warn
(
"[演出商品关联] 删除 Redis 缓存失败,performancesId={}"
,
performancesId
,
e
);
}
}
}
liquidnet-bus-do/liquidnet-service-goblin-do/src/main/java/com/liquidnet/service/goblin/entity/GoblinSqbGoodsExt.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
entity
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
import
java.io.Serializable
;
/**
* <p>
* 收钱吧商品扩展信息
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode
(
callSuper
=
false
)
@TableName
(
"goblin_sqb_goods_ext"
)
public
class
GoblinSqbGoodsExt
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
@TableId
(
value
=
"mid"
,
type
=
IdType
.
AUTO
)
private
Long
mid
;
/**
* 关联 goblin_goods.spu_id
*/
private
String
spuId
;
/**
* 关联 goblin_goods_sku.sku_id
*/
private
String
skuId
;
/**
* 所属收钱吧商城编号
*/
private
String
mallSn
;
/**
* 收钱吧商品ID
*/
private
String
sqbProductId
;
/**
* 收钱吧商品编号
*/
private
String
sqbProductSn
;
/**
* 收钱吧SKU ID
*/
private
String
sqbSkuId
;
private
String
createdAt
;
private
String
updatedAt
;
}
liquidnet-bus-do/liquidnet-service-goblin-do/src/main/java/com/liquidnet/service/goblin/entity/GoblinSqbMallInfo.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
entity
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
import
java.io.Serializable
;
/**
* <p>
* 收钱吧商城信息
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode
(
callSuper
=
false
)
@TableName
(
"goblin_sqb_mall_info"
)
public
class
GoblinSqbMallInfo
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
@TableId
(
value
=
"mid"
,
type
=
IdType
.
AUTO
)
private
Long
mid
;
/**
* 商城唯一ID
*/
private
String
mallId
;
/**
* 收钱吧商城编号
*/
private
String
mallSn
;
/**
* 商城名称
*/
private
String
mallName
;
/**
* 商城密钥
*/
private
String
signature
;
/**
* 关联我方店铺ID
*/
private
String
storeId
;
/**
* 状态 0-禁用 1-启用
*/
private
Integer
status
;
private
String
createdAt
;
private
String
updatedAt
;
}
liquidnet-bus-do/liquidnet-service-goblin-do/src/main/java/com/liquidnet/service/goblin/entity/GoblinSqbOrder.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
entity
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
import
java.io.Serializable
;
/**
* <p>
* 收钱吧订单
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode
(
callSuper
=
false
)
@TableName
(
"goblin_sqb_order"
)
public
class
GoblinSqbOrder
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
@TableId
(
value
=
"mid"
,
type
=
IdType
.
AUTO
)
private
Long
mid
;
/**
* 本地订单ID
*/
private
String
orderId
;
/**
* 用户ID
*/
private
String
userId
;
/**
* 关联演出ID
*/
private
String
performancesId
;
/**
* 商品ID
*/
private
String
spuId
;
/**
* SKU ID
*/
private
String
skuId
;
/**
* 购买数量
*/
private
Integer
quantity
;
/**
* 支付金额(分)
*/
private
Long
amount
;
/**
* 收钱吧订单号
*/
private
String
sqbOrderSn
;
/**
* 收钱吧订单签名
*/
private
String
sqbOrderSignature
;
/**
* 收钱吧收单号
*/
private
String
sqbAcquiringSn
;
/**
* 结算明细ID
*/
private
String
sqbCheckoutItemsId
;
/**
* 券码编号
*/
private
String
couponSn
;
/**
* 核销二维码
*/
private
String
couponQrCode
;
/**
* 券码过期时间
*/
private
String
couponExpireTime
;
/**
* 状态 0-待支付 1-已支付 2-已核销 3-已退款 4-退款中 9-失败
*/
private
Integer
status
;
/**
* 退款原因
*/
private
String
refundReason
;
private
String
createdAt
;
private
String
updatedAt
;
}
liquidnet-bus-do/liquidnet-service-goblin-do/src/main/java/com/liquidnet/service/goblin/entity/GoblinSqbPerformanceGoods.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
entity
;
import
com.baomidou.mybatisplus.annotation.IdType
;
import
com.baomidou.mybatisplus.annotation.TableId
;
import
com.baomidou.mybatisplus.annotation.TableName
;
import
lombok.Data
;
import
lombok.EqualsAndHashCode
;
import
java.io.Serializable
;
/**
* <p>
* 演出-收钱吧商品关联
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
@Data
@EqualsAndHashCode
(
callSuper
=
false
)
@TableName
(
"goblin_sqb_performance_goods"
)
public
class
GoblinSqbPerformanceGoods
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
@TableId
(
value
=
"mid"
,
type
=
IdType
.
AUTO
)
private
Long
mid
;
/**
* 演出ID
*/
private
String
performancesId
;
/**
* 商品ID
*/
private
String
spuId
;
/**
* SKU ID
*/
private
String
skuId
;
/**
* 排序权重
*/
private
Integer
sort
;
/**
* 状态 0-禁用 1-启用
*/
private
Integer
status
;
private
String
createdAt
;
private
String
updatedAt
;
}
liquidnet-bus-do/liquidnet-service-goblin-do/src/main/java/com/liquidnet/service/goblin/mapper/GoblinSqbGoodsExtMapper.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
mapper
;
import
com.liquidnet.service.goblin.entity.GoblinSqbGoodsExt
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
/**
* <p>
* 收钱吧商品扩展信息 Mapper 接口
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
public
interface
GoblinSqbGoodsExtMapper
extends
BaseMapper
<
GoblinSqbGoodsExt
>
{
}
liquidnet-bus-do/liquidnet-service-goblin-do/src/main/java/com/liquidnet/service/goblin/mapper/GoblinSqbMallInfoMapper.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
mapper
;
import
com.liquidnet.service.goblin.entity.GoblinSqbMallInfo
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
/**
* <p>
* 收钱吧商城信息 Mapper 接口
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
public
interface
GoblinSqbMallInfoMapper
extends
BaseMapper
<
GoblinSqbMallInfo
>
{
}
liquidnet-bus-do/liquidnet-service-goblin-do/src/main/java/com/liquidnet/service/goblin/mapper/GoblinSqbOrderMapper.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
mapper
;
import
com.liquidnet.service.goblin.entity.GoblinSqbOrder
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
/**
* <p>
* 收钱吧订单 Mapper 接口
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
public
interface
GoblinSqbOrderMapper
extends
BaseMapper
<
GoblinSqbOrder
>
{
}
liquidnet-bus-do/liquidnet-service-goblin-do/src/main/java/com/liquidnet/service/goblin/mapper/GoblinSqbPerformanceGoodsMapper.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
mapper
;
import
com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods
;
import
com.baomidou.mybatisplus.core.mapper.BaseMapper
;
/**
* <p>
* 演出-收钱吧商品关联 Mapper 接口
* </p>
*
* @author liquidnet
* @since 2025-01-01
*/
public
interface
GoblinSqbPerformanceGoodsMapper
extends
BaseMapper
<
GoblinSqbPerformanceGoods
>
{
}
liquidnet-bus-feign/liquidnet-api-feign-goblin/src/main/java/com/liquidnet/service/feign/goblin/task/FeignGoblinTaskClient.java
View file @
52399664
...
...
@@ -5,6 +5,8 @@ import feign.hystrix.FallbackFactory;
import
org.springframework.cloud.openfeign.FeignClient
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestParam
;
@Component
@FeignClient
(
name
=
"liquidnet-service-goblin"
,
...
...
@@ -28,4 +30,7 @@ public interface FeignGoblinTaskClient {
@GetMapping
(
"/rsc/maizhi/job/refundRes"
)
ResponseDto
<
Boolean
>
refundRes
();
@PostMapping
(
"/goblin/job/sqb/autoRefund"
)
ResponseDto
<
String
>
sqbAutoRefund
(
@RequestParam
(
"performancesId"
)
String
performancesId
);
}
liquidnet-bus-service/liquidnet-service-executor-all/liquidnet-service-executor-main/src/main/java/com/liquidnet/service/executor/main/handler/GoblinTaskHandler.java
View file @
52399664
...
...
@@ -68,4 +68,19 @@ public class GoblinTaskHandler {
XxlJobHelper
.
handleFail
();
}
}
/**
* 演出结束自动退款
* xxl-job 配置:JobHandler = sev-goblin:sqbAutoRefund,任务参数传入 performancesId
*/
@XxlJob
(
value
=
"sev-goblin:sqbAutoRefund"
)
public
void
sqbAutoRefund
()
{
String
performancesId
=
XxlJobHelper
.
getJobParam
();
try
{
XxlJobHelper
.
handleSuccess
(
"结果:"
+
feignGoblinTaskClient
.
sqbAutoRefund
(
performancesId
).
getData
());
}
catch
(
Exception
e
)
{
XxlJobHelper
.
log
(
e
);
XxlJobHelper
.
handleFail
();
}
}
}
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/pom.xml
View file @
52399664
...
...
@@ -60,6 +60,12 @@
<version>
1.0-SNAPSHOT
</version>
<scope>
compile
</scope>
</dependency>
<dependency>
<groupId>
com.liquidnet
</groupId>
<artifactId>
liquidnet-service-goblin-do
</artifactId>
<version>
1.0-SNAPSHOT
</version>
<scope>
compile
</scope>
</dependency>
<dependency>
<groupId>
com.liquidnet
</groupId>
<artifactId>
liquidnet-common-third-zxlnft
</artifactId>
...
...
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/controller/GoblinSqbCallbackController.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
controller
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.service.IGoblinSqbOrderService
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RestController
;
import
java.util.Map
;
/**
* 收钱吧回调 Controller(收钱吧主动推送,无需鉴权)
*/
@Slf4j
@Api
(
tags
=
"收钱吧回调"
)
@RestController
public
class
GoblinSqbCallbackController
{
@Autowired
private
IGoblinSqbOrderService
goblinSqbOrderService
;
/**
* 支付成功回调
* URL: https://testgoblin.zhengzai.tv/goblin/sqb/order/callback
*/
@PostMapping
(
"/goblin/sqb/order/callback"
)
@ApiOperation
(
"支付成功回调"
)
public
ResponseDto
<
String
>
payCallback
(
@RequestBody
Map
<
String
,
Object
>
params
)
{
log
.
info
(
"[收钱吧支付回调] 收到推送"
);
return
goblinSqbOrderService
.
handlePayCallback
(
params
);
}
/**
* 退款成功回调
* URL: https://testgoblin.zhengzai.tv/goblin/sqb/refund/callback
*/
@PostMapping
(
"/goblin/sqb/refund/callback"
)
@ApiOperation
(
"退款成功回调"
)
public
ResponseDto
<
String
>
refundCallback
(
@RequestBody
Map
<
String
,
Object
>
params
)
{
log
.
info
(
"[收钱吧退款回调] 收到推送"
);
return
goblinSqbOrderService
.
handleRefundCallback
(
params
);
}
/**
* 券状态变更回调
* URL: https://testgoblin.zhengzai.tv/goblin/sqb/coupon/callback
*/
@PostMapping
(
"/goblin/sqb/coupon/callback"
)
@ApiOperation
(
"券状态变更回调"
)
public
ResponseDto
<
String
>
couponCallback
(
@RequestBody
Map
<
String
,
Object
>
params
)
{
log
.
info
(
"[收钱吧券状态回调] 收到推送"
);
return
goblinSqbOrderService
.
handleCouponCallback
(
params
);
}
}
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/controller/GoblinSqbGoodsController.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
controller
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo
;
import
com.liquidnet.service.goblin.service.IGoblinSqbGoodsService
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.*
;
import
java.util.List
;
import
java.util.Map
;
/**
* 收钱吧商品同步 Controller
*/
@Slf4j
@Api
(
tags
=
"收钱吧商品同步"
)
@RestController
@RequestMapping
(
"/goblin/sqb/goods"
)
public
class
GoblinSqbGoodsController
{
@Autowired
IGoblinSqbGoodsService
goblinSqbGoodsService
;
/**
* 查询所有商城及商品列表
* 后端自动拉取所有商城,循环获取每个商城的商品列表,聚合返回前端
*/
@GetMapping
(
"/list"
)
@ApiOperation
(
"查询收钱吧所有商城及商品列表"
)
public
ResponseDto
<
List
<
GoblinSqbPerfGoodsVo
>>
list
()
{
return
goblinSqbGoodsService
.
getAllMallProducts
();
}
/**
* 批量同步商品到 goblin 系统
* 已存在则更新,不存在则新增(spuType=33)
*/
@PostMapping
(
"/sync"
)
@ApiOperation
(
"批量同步收钱吧商品"
)
public
ResponseDto
<
String
>
sync
(
@RequestBody
List
<
Map
<
String
,
String
>>
items
)
{
return
goblinSqbGoodsService
.
syncGoods
(
items
);
}
/**
* 查询演出关联商品列表(C端)
* 先查 Redis 缓存(TTL 5min),未命中则查 MySQL 并写入缓存
*/
@GetMapping
(
"/performance/goods/{performancesId}"
)
@ApiOperation
(
"查询演出关联商品列表"
)
public
ResponseDto
<
List
<
GoblinSqbPerfGoodsVo
>>
getPerfGoods
(
@PathVariable
String
performancesId
)
{
return
goblinSqbGoodsService
.
getPerfGoods
(
performancesId
);
}
}
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/controller/GoblinSqbOrderController.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
controller
;
import
com.liquidnet.commons.lang.util.CurrentUtil
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbCouponVo
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderCreateVo
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderDetailVo
;
import
com.liquidnet.service.goblin.service.IGoblinSqbOrderService
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiImplicitParam
;
import
io.swagger.annotations.ApiImplicitParams
;
import
io.swagger.annotations.ApiOperation
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.*
;
import
javax.validation.constraints.Min
;
import
javax.validation.constraints.NotBlank
;
import
javax.validation.constraints.NotNull
;
/**
* 收钱吧订单 Controller
*/
@Slf4j
@Api
(
tags
=
"收钱吧订单"
)
@RestController
@RequestMapping
(
"/goblin/sqb/order"
)
public
class
GoblinSqbOrderController
{
@Autowired
private
IGoblinSqbOrderService
goblinSqbOrderService
;
/**
* 创建收钱吧订单
*/
@PostMapping
(
"/create"
)
@ApiOperation
(
"创建收钱吧订单"
)
@ApiImplicitParams
({
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"spuId"
,
value
=
"商品 SPU ID"
),
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"skuId"
,
value
=
"商品 SKU ID"
),
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"Integer"
,
name
=
"quantity"
,
value
=
"购买数量"
,
example
=
"1"
),
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"performancesId"
,
value
=
"关联演出 ID"
),
})
public
ResponseDto
<
GoblinSqbOrderCreateVo
>
createOrder
(
@NotBlank
(
message
=
"spuId 不能为空"
)
@RequestParam
(
"spuId"
)
String
spuId
,
@NotBlank
(
message
=
"skuId 不能为空"
)
@RequestParam
(
"skuId"
)
String
skuId
,
@NotNull
(
message
=
"quantity 不能为空"
)
@Min
(
value
=
1
,
message
=
"购买数量至少为 1"
)
@RequestParam
(
"quantity"
)
Integer
quantity
,
@NotBlank
(
message
=
"performancesId 不能为空"
)
@RequestParam
(
"performancesId"
)
String
performancesId
)
{
String
userId
=
CurrentUtil
.
getCurrentUid
();
log
.
info
(
"[收钱吧下单] 接收请求 userId={}, spuId={}, skuId={}, quantity={}, performancesId={}"
,
userId
,
spuId
,
skuId
,
quantity
,
performancesId
);
return
goblinSqbOrderService
.
createOrder
(
userId
,
spuId
,
skuId
,
quantity
,
performancesId
);
}
/**
* 查询支付状态
*/
@GetMapping
(
"/pay/status"
)
@ApiOperation
(
"查询支付状态"
)
@ApiImplicitParam
(
type
=
"query"
,
required
=
true
,
dataType
=
"String"
,
name
=
"orderId"
,
value
=
"本地订单ID"
)
public
ResponseDto
<
Integer
>
queryPayStatus
(
@NotBlank
(
message
=
"orderId 不能为空"
)
@RequestParam
(
"orderId"
)
String
orderId
)
{
String
userId
=
CurrentUtil
.
getCurrentUid
();
log
.
info
(
"[收钱吧支付状态] 接收请求 userId={}, orderId={}"
,
userId
,
orderId
);
return
goblinSqbOrderService
.
queryPayStatus
(
userId
,
orderId
);
}
/**
* 获取核销二维码(券码)
*/
@GetMapping
(
"/coupon/{orderId}"
)
@ApiOperation
(
"获取核销二维码"
)
@ApiImplicitParam
(
type
=
"path"
,
required
=
true
,
dataType
=
"String"
,
name
=
"orderId"
,
value
=
"本地订单ID"
)
public
ResponseDto
<
GoblinSqbCouponVo
>
queryCoupon
(
@NotBlank
(
message
=
"orderId 不能为空"
)
@PathVariable
(
"orderId"
)
String
orderId
)
{
String
userId
=
CurrentUtil
.
getCurrentUid
();
log
.
info
(
"[收钱吧券码] 接收请求 userId={}, orderId={}"
,
userId
,
orderId
);
return
goblinSqbOrderService
.
queryCoupon
(
userId
,
orderId
);
}
/**
* 申请退款
*/
@PostMapping
(
"/refund"
)
@ApiOperation
(
"申请退款"
)
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"orderId"
,
value
=
"本地订单ID"
)
public
ResponseDto
<
Boolean
>
refund
(
@NotBlank
(
message
=
"orderId 不能为空"
)
@RequestParam
(
"orderId"
)
String
orderId
)
{
String
userId
=
CurrentUtil
.
getCurrentUid
();
log
.
info
(
"[收钱吧退款] 接收请求 userId={}, orderId={}"
,
userId
,
orderId
);
return
goblinSqbOrderService
.
refund
(
userId
,
orderId
);
}
/**
* 主动同步核销状态
*/
@PostMapping
(
"/coupon/sync"
)
@ApiOperation
(
"主动同步核销状态"
)
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"orderId"
,
value
=
"本地订单ID"
)
public
ResponseDto
<
Boolean
>
syncCouponStatus
(
@NotBlank
(
message
=
"orderId 不能为空"
)
@RequestParam
(
"orderId"
)
String
orderId
)
{
String
userId
=
CurrentUtil
.
getCurrentUid
();
log
.
info
(
"[收钱吧核销同步] 接收请求 userId={}, orderId={}"
,
userId
,
orderId
);
return
goblinSqbOrderService
.
syncCouponStatus
(
userId
,
orderId
);
}
/**
* 用户收钱吧订单列表
*/
@GetMapping
(
"/list"
)
@ApiOperation
(
"用户收钱吧订单列表"
)
public
ResponseDto
<
java
.
util
.
List
<
GoblinSqbOrderDetailVo
>>
getOrderList
()
{
String
userId
=
CurrentUtil
.
getCurrentUid
();
log
.
info
(
"[收钱吧订单列表] 接收请求 userId={}"
,
userId
);
return
goblinSqbOrderService
.
getOrderList
(
userId
);
}
/**
* 订单详情
*/
@GetMapping
(
"/detail/{orderId}"
)
@ApiOperation
(
"收钱吧订单详情"
)
@ApiImplicitParam
(
type
=
"path"
,
required
=
true
,
dataType
=
"String"
,
name
=
"orderId"
,
value
=
"本地订单ID"
)
public
ResponseDto
<
GoblinSqbOrderDetailVo
>
getOrderDetail
(
@NotBlank
(
message
=
"orderId 不能为空"
)
@PathVariable
(
"orderId"
)
String
orderId
)
{
String
userId
=
CurrentUtil
.
getCurrentUid
();
log
.
info
(
"[收钱吧订单详情] 接收请求 userId={}, orderId={}"
,
userId
,
orderId
);
return
goblinSqbOrderService
.
getOrderDetail
(
userId
,
orderId
);
}
/**
* 再次付款(待支付状态下重新拉起微信支付)
*/
@PostMapping
(
"/repay"
)
@ApiOperation
(
"再次付款"
)
@ApiImplicitParam
(
type
=
"form"
,
required
=
true
,
dataType
=
"String"
,
name
=
"orderId"
,
value
=
"本地订单ID"
)
public
ResponseDto
<
GoblinSqbOrderCreateVo
>
repay
(
@NotBlank
(
message
=
"orderId 不能为空"
)
@RequestParam
(
"orderId"
)
String
orderId
)
{
String
userId
=
CurrentUtil
.
getCurrentUid
();
log
.
info
(
"[收钱吧再次付款] 接收请求 userId={}, orderId={}"
,
userId
,
orderId
);
return
goblinSqbOrderService
.
repay
(
userId
,
orderId
);
}
}
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/controller/Inner/GoblinSqbJobController.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
controller
.
Inner
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.service.IGoblinSqbOrderService
;
import
io.swagger.annotations.Api
;
import
io.swagger.annotations.ApiOperation
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RequestParam
;
import
org.springframework.web.bind.annotation.RestController
;
/**
* 收钱吧定时任务入口(供 executor 服务通过 Feign 调用)
*
* xxl-job 配置说明(Task 11.4 运维配置):
* - 在 xxl-job 平台新增定时任务
* - JobHandler:sev-goblin:sqbAutoRefund
* - CRON:按演出结束时间触发(如演出结束后 5 分钟:0 5 * * * ?,具体由运维根据演出时间动态配置)
* - 执行器:liquidnet-service-executor
* - 任务参数:performancesId=xxx(演出ID)
* - 路由策略:第一个
*/
@Slf4j
@Api
(
tags
=
"收钱吧定时任务"
)
@RestController
@RequestMapping
(
"/goblin/job/sqb"
)
public
class
GoblinSqbJobController
{
@Autowired
private
IGoblinSqbOrderService
goblinSqbOrderService
;
@PostMapping
(
"/autoRefund"
)
@ApiOperation
(
"演出结束自动退款"
)
public
ResponseDto
<
String
>
autoRefund
(
@RequestParam
(
"performancesId"
)
String
performancesId
)
{
log
.
info
(
"[收钱吧自动退款] 收到任务,performancesId={}"
,
performancesId
);
return
goblinSqbOrderService
.
autoRefundByPerformance
(
performancesId
);
}
}
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/service/impl/GoblinSqbGoodsServiceImpl.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
service
.
impl
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.liquidnet.commons.lang.util.CollectionUtil
;
import
com.liquidnet.commons.lang.util.IDGenerator
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.base.SqlMapping
;
import
com.liquidnet.service.base.constant.MQConst
;
import
com.liquidnet.service.goblin.dto.vo.GoblinGoodsInfoVo
;
import
com.liquidnet.service.goblin.dto.vo.GoblinGoodsSkuInfoVo
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo
;
import
com.liquidnet.service.goblin.entity.GoblinGoods
;
import
com.liquidnet.service.goblin.entity.GoblinGoodsSku
;
import
com.liquidnet.service.goblin.entity.GoblinSqbGoodsExt
;
import
com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods
;
import
com.liquidnet.service.goblin.mapper.GoblinGoodsMapper
;
import
com.liquidnet.service.goblin.mapper.GoblinGoodsSkuMapper
;
import
com.liquidnet.service.goblin.mapper.GoblinSqbGoodsExtMapper
;
import
com.liquidnet.service.goblin.mapper.GoblinSqbPerformanceGoodsMapper
;
import
com.liquidnet.service.goblin.param.shouqianba.request.CommonRequest
;
import
com.liquidnet.service.goblin.param.shouqianba.request.MallListQueryRequest
;
import
com.liquidnet.service.goblin.param.shouqianba.request.MallProductsQueryRequest
;
import
com.liquidnet.service.goblin.param.shouqianba.response.data.MallListQueryData
;
import
com.liquidnet.service.goblin.param.shouqianba.response.data.MallProductsQueryData
;
import
com.liquidnet.service.goblin.service.IGoblinShouQianBaService
;
import
com.liquidnet.service.goblin.service.IGoblinSqbGoodsService
;
import
com.liquidnet.service.goblin.util.GoblinMongoUtils
;
import
com.liquidnet.service.goblin.util.GoblinRedisUtils
;
import
com.liquidnet.service.goblin.util.GoblinSqbRedisUtils
;
import
com.liquidnet.service.goblin.util.QueueUtils
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
org.springframework.util.CollectionUtils
;
import
org.springframework.util.StringUtils
;
import
java.math.BigDecimal
;
import
java.time.LocalDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.util.*
;
import
java.util.function.Function
;
import
java.util.stream.Collectors
;
/**
* 收钱吧商品同步服务实现
*/
@Slf4j
@Service
public
class
GoblinSqbGoodsServiceImpl
implements
IGoblinSqbGoodsService
{
// TODO: 后期移入配置文件
private
static
final
String
SQB_APPID
=
"test_appid"
;
private
static
final
String
SQB_MERCHANT_ID
=
"todo_merchant_id"
;
private
static
final
String
SQB_MERCHANT_USER_ID
=
"todo_merchant_user_id"
;
private
static
final
String
SQB_ROLE
=
"super_admin"
;
private
static
final
String
SQB_STORE_ID
=
"0"
;
// 平台级商品 store_id=0
@Autowired
private
GoblinSqbGoodsExtMapper
goblinSqbGoodsExtMapper
;
@Autowired
private
IGoblinShouQianBaService
goblinShouQianBaService
;
@Autowired
private
GoblinSqbPerformanceGoodsMapper
goblinSqbPerformanceGoodsMapper
;
@Autowired
private
GoblinGoodsMapper
goblinGoodsMapper
;
@Autowired
private
GoblinGoodsSkuMapper
goblinGoodsSkuMapper
;
@Autowired
private
GoblinSqbRedisUtils
goblinSqbRedisUtils
;
@Autowired
private
GoblinRedisUtils
goblinRedisUtils
;
@Autowired
private
GoblinMongoUtils
goblinMongoUtils
;
@Autowired
private
QueueUtils
queueUtils
;
// ================================ 获取所有商城商品 ================================
/**
* 获取所有商城及商品列表
* 自动拉取所有商城,循环获取每个商城的商品列表,聚合返回
*/
@Override
public
ResponseDto
<
List
<
GoblinSqbPerfGoodsVo
>>
getAllMallProducts
()
{
List
<
GoblinSqbPerfGoodsVo
>
result
=
new
ArrayList
<>();
try
{
// 1. 调用收钱吧获取商城列表
MallListQueryRequest
mallListRequest
=
new
MallListQueryRequest
();
mallListRequest
.
setAppid
(
SQB_APPID
);
// 过滤条件:只查已上线的商城
MallListQueryRequest
.
Filter
filter
=
new
MallListQueryRequest
.
Filter
();
CommonRequest
.
Seller
seller
=
buildSeller
();
filter
.
setSeller
(
seller
);
filter
.
setState
((
byte
)
1
);
// 1=已上线
mallListRequest
.
setFilter
(
filter
);
// 分页:每次取 100 个
MallListQueryRequest
.
Cursor
cursor
=
new
MallListQueryRequest
.
Cursor
();
cursor
.
setCount
(
100
);
mallListRequest
.
setCursor
(
cursor
);
List
<
MallListQueryData
>
mallList
=
goblinShouQianBaService
.
queryMallList
(
mallListRequest
);
if
(
CollectionUtils
.
isEmpty
(
mallList
))
{
log
.
warn
(
"[收钱吧] 未查询到任何商城信息"
);
return
ResponseDto
.
success
(
result
);
}
// 2. 循环每个商城获取商品列表
for
(
MallListQueryData
mall
:
mallList
)
{
String
mallSn
=
mall
.
getMallSn
();
if
(
StringUtils
.
isEmpty
(
mallSn
))
continue
;
try
{
MallProductsQueryRequest
productsRequest
=
new
MallProductsQueryRequest
();
productsRequest
.
setAppid
(
SQB_APPID
);
productsRequest
.
setSeller
(
seller
);
CommonRequest
.
Mall
mallId
=
new
CommonRequest
.
Mall
();
mallId
.
setMallSn
(
mallSn
);
mallId
.
setSignature
(
mall
.
getSignature
());
productsRequest
.
setMallID
(
mallId
);
List
<
MallProductsQueryData
>
productsDataList
=
goblinShouQianBaService
.
queryMallProducts
(
productsRequest
);
if
(
CollectionUtils
.
isEmpty
(
productsDataList
))
{
log
.
warn
(
"[收钱吧商品同步] 商城 {} 获取商品列表为空"
,
mallSn
);
continue
;
}
// 3. 将商品数据转换为 VO
for
(
MallProductsQueryData
productsData
:
productsDataList
)
{
if
(
productsData
==
null
||
CollectionUtils
.
isEmpty
(
productsData
.
getSkuModels
()))
continue
;
String
coverPic
=
null
;
if
(!
CollectionUtils
.
isEmpty
(
productsData
.
getConverImages
()))
{
coverPic
=
productsData
.
getConverImages
().
get
(
0
);
}
for
(
MallProductsQueryData
.
Sku
sku
:
productsData
.
getSkuModels
())
{
GoblinSqbPerfGoodsVo
vo
=
new
GoblinSqbPerfGoodsVo
();
vo
.
setSpuId
(
productsData
.
getSpuId
());
vo
.
setSpuName
(
productsData
.
getTitle
());
vo
.
setSkuId
(
sku
.
getSkuId
());
String
skuName
=
sku
.
getSkuName
()
!=
null
?
sku
.
getSkuName
()
:
sku
.
getSkuTitle
();
vo
.
setSkuName
(
skuName
);
vo
.
setPrice
(
sku
.
getPrice
());
vo
.
setCoverPic
(
coverPic
);
result
.
add
(
vo
);
}
}
}
catch
(
Exception
e
)
{
log
.
error
(
"[收钱吧商品同步] 获取商城 {} 商品列表异常"
,
mallSn
,
e
);
}
}
}
catch
(
Exception
e
)
{
log
.
error
(
"[收钱吧商品同步] getAllMallProducts 异常"
,
e
);
return
ResponseDto
.
failure
(
"获取商城商品列表失败:"
+
e
.
getMessage
());
}
log
.
info
(
"[收钱吧商品同步] getAllMallProducts 完成,共 {} 个 SKU"
,
result
.
size
());
return
ResponseDto
.
success
(
result
);
}
// ================================ 批量同步商品到 goblin 系统 ================================
/**
* 批量同步商品到 goblin 系统
* <p>
* 流程:对每个 sqbProductId,调用收钱吧查询商品详情,
* 写入 goblin_goods + goblin_goods_sku + goblin_sqb_goods_ext
* </p>
*
* @param items 待同步商品列表:每项包含 mallSn、sqbProductId、sqbProductSn(来自收钱吧)
* @return 同步结果摘要
*/
@Override
@Transactional
(
rollbackFor
=
Exception
.
class
)
public
ResponseDto
<
String
>
syncGoods
(
List
<
Map
<
String
,
String
>>
items
)
{
if
(
CollectionUtils
.
isEmpty
(
items
))
{
return
ResponseDto
.
failure
(
"同步商品列表不能为空"
);
}
int
newCount
=
0
;
int
existCount
=
0
;
int
failCount
=
0
;
String
now
=
LocalDateTime
.
now
().
format
(
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
));
for
(
Map
<
String
,
String
>
item
:
items
)
{
String
sqbProductId
=
item
.
get
(
"sqbProductId"
);
String
sqbProductSn
=
item
.
get
(
"sqbProductSn"
);
String
mallSn
=
item
.
get
(
"mallSn"
);
String
mallSignature
=
item
.
get
(
"mallSignature"
);
// 商城签名,用于查商品
if
(
StringUtils
.
isEmpty
(
sqbProductId
))
{
log
.
warn
(
"[收钱吧商品同步] sqbProductId 为空,跳过: {}"
,
item
);
failCount
++;
continue
;
}
try
{
// Step 1: 幂等检查 —— 已存在则跳过
LambdaQueryWrapper
<
GoblinSqbGoodsExt
>
queryWrapper
=
new
LambdaQueryWrapper
<>();
queryWrapper
.
eq
(
GoblinSqbGoodsExt:
:
getSqbProductId
,
sqbProductId
);
GoblinSqbGoodsExt
existing
=
goblinSqbGoodsExtMapper
.
selectOne
(
queryWrapper
);
if
(
existing
!=
null
)
{
log
.
info
(
"[收钱吧商品同步] 商品已存在,sqbProductId={}, spuId={}, skuId={}"
,
sqbProductId
,
existing
.
getSpuId
(),
existing
.
getSkuId
());
existCount
++;
continue
;
}
// Step 2: 查询收钱吧商品详情(通过 queryMallProducts)
MallProductsQueryData
productsData
=
null
;
if
(!
StringUtils
.
isEmpty
(
mallSn
))
{
MallProductsQueryRequest
productsRequest
=
new
MallProductsQueryRequest
();
productsRequest
.
setAppid
(
SQB_APPID
);
productsRequest
.
setSeller
(
buildSeller
());
CommonRequest
.
Mall
mallId
=
new
CommonRequest
.
Mall
();
mallId
.
setMallSn
(
mallSn
);
mallId
.
setSignature
(
mallSignature
);
productsRequest
.
setMallID
(
mallId
);
List
<
MallProductsQueryData
>
productsDataList
=
goblinShouQianBaService
.
queryMallProducts
(
productsRequest
);
if
(!
CollectionUtils
.
isEmpty
(
productsDataList
))
{
for
(
MallProductsQueryData
data
:
productsDataList
)
{
if
(
sqbProductId
.
equals
(
data
.
getSpuId
()))
{
productsData
=
data
;
break
;
}
}
}
}
// Step 3: 生成本地 spuId
String
spuId
=
IDGenerator
.
nextSnowId
();
// Step 4: 构建 GoblinGoodsInfoVo
GoblinGoodsInfoVo
goodsInfoVo
=
buildGoblinGoodsInfoVo
(
spuId
,
productsData
,
now
);
log
.
info
(
"[收钱吧商品同步] 准备写入商品,spuId={}"
,
spuId
);
// Step 5: 遍历 SKU,构建 GoblinGoodsSkuInfoVo 及写 EXT 表
List
<
MallProductsQueryData
.
Sku
>
sqbSkus
=
productsData
!=
null
&&
!
CollectionUtils
.
isEmpty
(
productsData
.
getSkuModels
())
?
productsData
.
getSkuModels
()
:
Collections
.
emptyList
();
List
<
GoblinGoodsSkuInfoVo
>
skuInfoVos
=
new
ArrayList
<>();
List
<
String
>
skuIdList
=
new
ArrayList
<>();
if
(
CollectionUtils
.
isEmpty
(
sqbSkus
))
{
// 没有 SKU 数据时,创建一个占位 SKU
String
skuId
=
IDGenerator
.
nextSnowId
();
GoblinGoodsSkuInfoVo
skuVo
=
buildGoblinGoodsSkuInfoVo
(
skuId
,
spuId
,
null
,
productsData
,
now
);
skuInfoVos
.
add
(
skuVo
);
skuIdList
.
add
(
skuId
);
GoblinSqbGoodsExt
ext
=
buildExt
(
spuId
,
skuId
,
mallSn
,
sqbProductId
,
sqbProductSn
,
null
,
now
);
goblinSqbGoodsExtMapper
.
insert
(
ext
);
log
.
info
(
"[收钱吧商品同步] 写入占位 SKU,skuId={}"
,
skuId
);
}
else
{
for
(
MallProductsQueryData
.
Sku
sqbSku
:
sqbSkus
)
{
String
skuId
=
IDGenerator
.
nextSnowId
();
GoblinGoodsSkuInfoVo
skuVo
=
buildGoblinGoodsSkuInfoVo
(
skuId
,
spuId
,
sqbSku
,
productsData
,
now
);
skuInfoVos
.
add
(
skuVo
);
skuIdList
.
add
(
skuId
);
GoblinSqbGoodsExt
ext
=
buildExt
(
spuId
,
skuId
,
mallSn
,
sqbProductId
,
sqbProductSn
,
sqbSku
.
getSkuId
(),
now
);
goblinSqbGoodsExtMapper
.
insert
(
ext
);
log
.
info
(
"[收钱吧商品同步] 写入 SKU,skuId={}, sqbSkuId={}"
,
skuId
,
sqbSku
.
getSkuId
());
}
}
goodsInfoVo
.
setSkuIdList
(
skuIdList
);
// Step 6: 组装价格区间
if
(!
CollectionUtils
.
isEmpty
(
skuInfoVos
))
{
long
minPrice
=
skuInfoVos
.
stream
().
mapToLong
(
s
->
s
.
getPrice
()
!=
null
?
s
.
getPrice
().
multiply
(
BigDecimal
.
valueOf
(
100
)).
longValue
()
:
0L
).
min
().
orElse
(
0L
);
long
maxPrice
=
skuInfoVos
.
stream
().
mapToLong
(
s
->
s
.
getPrice
()
!=
null
?
s
.
getPrice
().
multiply
(
BigDecimal
.
valueOf
(
100
)).
longValue
()
:
0L
).
max
().
orElse
(
0L
);
goodsInfoVo
.
setPriceGe
(
BigDecimal
.
valueOf
(
minPrice
).
divide
(
BigDecimal
.
valueOf
(
100
)));
goodsInfoVo
.
setPriceLe
(
BigDecimal
.
valueOf
(
maxPrice
).
divide
(
BigDecimal
.
valueOf
(
100
)));
goodsInfoVo
.
setSellPrice
(
BigDecimal
.
valueOf
(
minPrice
).
divide
(
BigDecimal
.
valueOf
(
100
)));
}
// Step 7: 写 MongoDB
goblinMongoUtils
.
setGoodsInfoVo
(
goodsInfoVo
);
goblinMongoUtils
.
setGoodsSkuInfoVos
(
skuInfoVos
);
// Step 8: 发队列通知及 Redis 库存写入
LinkedList
<
String
>
toMqSqls
=
CollectionUtil
.
linkedListString
();
LinkedList
<
Object
[]>
initGoodsObjs
=
CollectionUtil
.
linkedListObjectArr
();
LinkedList
<
Object
[]>
initGoodsSkuObjs
=
CollectionUtil
.
linkedListObjectArr
();
LinkedList
<
Object
[]>
initGoodsImageObjs
=
CollectionUtil
.
linkedListObjectArr
();
toMqSqls
.
add
(
SqlMapping
.
get
(
"goblin_goods.insert"
));
initGoodsObjs
.
add
(
new
Object
[]{
spuId
,
goodsInfoVo
.
getSpuNo
(),
goodsInfoVo
.
getSpuBarCode
(),
goodsInfoVo
.
getSpuErpCode
(),
goodsInfoVo
.
getErpType
(),
goodsInfoVo
.
getName
(),
goodsInfoVo
.
getSubtitle
(),
goodsInfoVo
.
getSellPrice
(),
goodsInfoVo
.
getPriceGe
(),
goodsInfoVo
.
getPriceLe
(),
goodsInfoVo
.
getIntro
(),
goodsInfoVo
.
getDetails
(),
goodsInfoVo
.
getCoverPic
(),
goodsInfoVo
.
getVideo
(),
goodsInfoVo
.
getSpecMode
(),
goodsInfoVo
.
getStoreId
(),
goodsInfoVo
.
getCateFid
(),
goodsInfoVo
.
getCateSid
(),
goodsInfoVo
.
getCateTid
(),
goodsInfoVo
.
getStoreCateFid
(),
goodsInfoVo
.
getStoreCateSid
(),
goodsInfoVo
.
getStoreCateTid
(),
goodsInfoVo
.
getBrandId
(),
goodsInfoVo
.
getShelvesHandle
(),
goodsInfoVo
.
getShelvesTime
(),
goodsInfoVo
.
getSpuValidity
(),
goodsInfoVo
.
getVirtualFlg
(),
goodsInfoVo
.
getStatus
(),
goodsInfoVo
.
getShelvesStatus
(),
goodsInfoVo
.
getSpuAppear
(),
goodsInfoVo
.
getShelvesAt
(),
goodsInfoVo
.
getCreatedBy
(),
goodsInfoVo
.
getCreatedAt
(),
goodsInfoVo
.
getLogisticsTemplate
()
});
toMqSqls
.
add
(
SqlMapping
.
get
(
"goblin_goods_sku.insert"
));
for
(
GoblinGoodsSkuInfoVo
skuInfoVo
:
skuInfoVos
)
{
// 写 Redis 库存
goblinRedisUtils
.
setSkuStock
(
null
,
skuInfoVo
.
getSkuId
(),
skuInfoVo
.
getStock
());
initGoodsSkuObjs
.
add
(
new
Object
[]{
skuInfoVo
.
getSkuId
(),
skuInfoVo
.
getSpuId
(),
skuInfoVo
.
getSkuNo
(),
skuInfoVo
.
getSkuBarCode
(),
skuInfoVo
.
getSkuErpCode
(),
skuInfoVo
.
getErpType
(),
skuInfoVo
.
getErpHosting
(),
skuInfoVo
.
getErpWarehouseNo
(),
skuInfoVo
.
getSkuType
(),
skuInfoVo
.
getName
(),
skuInfoVo
.
getSubtitle
(),
skuInfoVo
.
getSellPrice
(),
skuInfoVo
.
getSkuPic
(),
skuInfoVo
.
getSkuIsbn
(),
skuInfoVo
.
getStock
(),
skuInfoVo
.
getSkuStock
(),
skuInfoVo
.
getWarningStock
(),
skuInfoVo
.
getPrice
(),
skuInfoVo
.
getPriceMember
(),
skuInfoVo
.
getWeight
(),
skuInfoVo
.
getBuyFactor
(),
skuInfoVo
.
getBuyRoster
(),
skuInfoVo
.
getBuyLimit
(),
skuInfoVo
.
getStoreId
(),
skuInfoVo
.
getSkuValidity
(),
skuInfoVo
.
getVirtualFlg
(),
skuInfoVo
.
getStatus
(),
skuInfoVo
.
getShelvesStatus
(),
skuInfoVo
.
getSkuAppear
(),
skuInfoVo
.
getShelvesAt
(),
goodsInfoVo
.
getCreatedBy
(),
goodsInfoVo
.
getCreatedAt
(),
skuInfoVo
.
getLogisticsTemplate
()
});
}
toMqSqls
.
add
(
SqlMapping
.
get
(
"goblin_goods_image.insert_byreplace"
));
if
(!
CollectionUtils
.
isEmpty
(
goodsInfoVo
.
getImageList
()))
{
goodsInfoVo
.
getImageList
().
forEach
(
imageUrl
->
initGoodsImageObjs
.
add
(
new
Object
[]{
spuId
,
imageUrl
}));
}
queueUtils
.
sendMsgByRedis
(
MQConst
.
GoblinQueue
.
SQL_GOODS
.
getKey
(),
SqlMapping
.
gets
(
toMqSqls
,
initGoodsObjs
,
initGoodsSkuObjs
,
initGoodsImageObjs
));
log
.
info
(
"[收钱吧商品同步] Redis及MongoDB写入、MQ消息发送成功,sqbProductId={}"
,
sqbProductId
);
newCount
++;
}
catch
(
Exception
e
)
{
log
.
error
(
"[收钱吧商品同步] 处理商品异常,sqbProductId={}"
,
sqbProductId
,
e
);
failCount
++;
}
}
String
summary
=
String
.
format
(
"同步完成:新增 %d 条,已存在 %d 条,失败 %d 条"
,
newCount
,
existCount
,
failCount
);
log
.
info
(
"[收钱吧商品同步] {}"
,
summary
);
return
ResponseDto
.
success
(
summary
);
}
// ================================ 查询演出关联商品 ================================
/**
* 查询演出关联商品列表
* 先查 Redis 缓存(TTL 5min),未命中则查 MySQL 并写入缓存
*/
@Override
public
ResponseDto
<
List
<
GoblinSqbPerfGoodsVo
>>
getPerfGoods
(
String
performancesId
)
{
// 1. 先查 Redis 缓存
List
<
GoblinSqbPerfGoodsVo
>
cached
=
goblinSqbRedisUtils
.
getPerfGoods
(
performancesId
);
if
(
cached
!=
null
)
{
return
ResponseDto
.
success
(
cached
);
}
// 2. 缓存未命中,查 MySQL
try
{
LambdaQueryWrapper
<
GoblinSqbPerformanceGoods
>
query
=
new
LambdaQueryWrapper
<>();
query
.
eq
(
GoblinSqbPerformanceGoods:
:
getPerformancesId
,
performancesId
)
.
eq
(
GoblinSqbPerformanceGoods:
:
getStatus
,
1
)
.
orderByAsc
(
GoblinSqbPerformanceGoods:
:
getSort
);
List
<
GoblinSqbPerformanceGoods
>
relations
=
goblinSqbPerformanceGoodsMapper
.
selectList
(
query
);
if
(
CollectionUtils
.
isEmpty
(
relations
))
{
List
<
GoblinSqbPerfGoodsVo
>
empty
=
new
ArrayList
<>();
goblinSqbRedisUtils
.
setPerfGoods
(
performancesId
,
empty
);
return
ResponseDto
.
success
(
empty
);
}
// 收集 skuId 和 spuId
List
<
String
>
skuIds
=
relations
.
stream
()
.
map
(
GoblinSqbPerformanceGoods:
:
getSkuId
)
.
collect
(
Collectors
.
toList
());
List
<
String
>
spuIds
=
relations
.
stream
()
.
map
(
GoblinSqbPerformanceGoods:
:
getSpuId
)
.
distinct
()
.
collect
(
Collectors
.
toList
());
// 批量查询 SKU 信息
LambdaQueryWrapper
<
GoblinGoodsSku
>
skuQuery
=
new
LambdaQueryWrapper
<>();
skuQuery
.
in
(
GoblinGoodsSku:
:
getSkuId
,
skuIds
);
List
<
GoblinGoodsSku
>
skuList
=
goblinGoodsSkuMapper
.
selectList
(
skuQuery
);
Map
<
String
,
GoblinGoodsSku
>
skuMap
=
skuList
.
stream
()
.
collect
(
Collectors
.
toMap
(
GoblinGoodsSku:
:
getSkuId
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
// 批量查询 SPU 信息
LambdaQueryWrapper
<
GoblinGoods
>
spuQuery
=
new
LambdaQueryWrapper
<>();
spuQuery
.
in
(
GoblinGoods:
:
getSpuId
,
spuIds
);
List
<
GoblinGoods
>
spuList
=
goblinGoodsMapper
.
selectList
(
spuQuery
);
Map
<
String
,
GoblinGoods
>
spuMap
=
spuList
.
stream
()
.
collect
(
Collectors
.
toMap
(
GoblinGoods:
:
getSpuId
,
Function
.
identity
(),
(
a
,
b
)
->
a
));
// 组装 VO
List
<
GoblinSqbPerfGoodsVo
>
result
=
new
ArrayList
<>();
for
(
GoblinSqbPerformanceGoods
rel
:
relations
)
{
GoblinSqbPerfGoodsVo
vo
=
new
GoblinSqbPerfGoodsVo
();
vo
.
setSkuId
(
rel
.
getSkuId
());
vo
.
setSpuId
(
rel
.
getSpuId
());
vo
.
setSort
(
rel
.
getSort
());
GoblinGoodsSku
sku
=
skuMap
.
get
(
rel
.
getSkuId
());
if
(
sku
!=
null
)
{
vo
.
setSkuName
(
sku
.
getName
());
vo
.
setPrice
(
sku
.
getPrice
()
!=
null
?
sku
.
getPrice
().
longValue
()
:
null
);
}
GoblinGoods
spu
=
spuMap
.
get
(
rel
.
getSpuId
());
if
(
spu
!=
null
)
{
vo
.
setSpuName
(
spu
.
getName
());
vo
.
setCoverPic
(
spu
.
getCoverPic
());
}
result
.
add
(
vo
);
}
// 3. 写入 Redis 缓存(TTL 5min)
goblinSqbRedisUtils
.
setPerfGoods
(
performancesId
,
result
);
return
ResponseDto
.
success
(
result
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[收钱吧] getPerfGoods 异常,performancesId={}"
,
performancesId
,
e
);
return
ResponseDto
.
failure
(
"查询演出关联商品失败:"
+
e
.
getMessage
());
}
}
// ================================ 私有辅助方法 ================================
private
CommonRequest
.
Seller
buildSeller
()
{
CommonRequest
.
Seller
seller
=
new
CommonRequest
.
Seller
();
seller
.
setMerchantId
(
SQB_MERCHANT_ID
);
seller
.
setMerchantUserId
(
SQB_MERCHANT_USER_ID
);
seller
.
setRole
(
SQB_ROLE
);
return
seller
;
}
/**
* 构建 GoblinGoodsInfoVo(收钱吧商品固定字段)
*/
private
GoblinGoodsInfoVo
buildGoblinGoodsInfoVo
(
String
spuId
,
MallProductsQueryData
productsData
,
String
now
)
{
GoblinGoodsInfoVo
goods
=
new
GoblinGoodsInfoVo
();
goods
.
setSpuId
(
spuId
);
goods
.
setSpuNo
(
spuId
);
goods
.
setSpuType
(
33
);
// 33 = 收钱吧商品
goods
.
setVirtualFlg
(
"1"
);
// 虚拟商品
goods
.
setStatus
(
"3"
);
// 3 = 审核通过(收钱吧商品无需审核)
goods
.
setShelvesHandle
(
"2"
);
// 2 = 直接上架售卖
goods
.
setShelvesStatus
(
"3"
);
// 3 = 上架
goods
.
setSpuAppear
(
"0"
);
// 默认展示
goods
.
setDelFlg
(
"0"
);
// 未删除
goods
.
setStoreId
(
SQB_STORE_ID
);
goods
.
setCreatedBy
(
"system"
);
goods
.
setCreatedAt
(
LocalDateTime
.
now
());
goods
.
setUpdatedBy
(
"system"
);
goods
.
setUpdatedAt
(
LocalDateTime
.
now
());
goods
.
setImageList
(
new
ArrayList
<>());
if
(
productsData
!=
null
)
{
goods
.
setName
(
productsData
.
getTitle
());
goods
.
setIntro
(
productsData
.
getProductIntroduction
());
if
(!
CollectionUtils
.
isEmpty
(
productsData
.
getConverImages
()))
{
goods
.
setCoverPic
(
productsData
.
getConverImages
().
get
(
0
));
goods
.
setImageList
(
new
ArrayList
<>(
productsData
.
getConverImages
()));
}
}
else
{
goods
.
setName
(
"收钱吧商品-"
+
spuId
);
}
goods
.
setSkuIdList
(
new
ArrayList
<>());
return
goods
;
}
/**
* 构建 GoblinGoodsSkuInfoVo 实体
*/
private
GoblinGoodsSkuInfoVo
buildGoblinGoodsSkuInfoVo
(
String
skuId
,
String
spuId
,
MallProductsQueryData
.
Sku
sqbSku
,
MallProductsQueryData
productsData
,
String
now
)
{
GoblinGoodsSkuInfoVo
sku
=
new
GoblinGoodsSkuInfoVo
();
sku
.
setSkuId
(
skuId
);
sku
.
setSpuId
(
spuId
);
sku
.
setSkuNo
(
skuId
);
sku
.
setSkuType
(
33
);
// 33 = 收钱吧商品
sku
.
setVirtualFlg
(
"1"
);
// 虚拟商品
sku
.
setStatus
(
"3"
);
// 审核通过
sku
.
setShelvesHandle
(
"2"
);
// 直接上架
sku
.
setShelvesStatus
(
"3"
);
// 上架
sku
.
setSkuAppear
(
"0"
);
sku
.
setBuyFactor
(
"0"
);
// 全部用户可买
sku
.
setBuyLimit
(
0
);
// 不限购
sku
.
setDelFlg
(
"0"
);
sku
.
setStoreId
(
SQB_STORE_ID
);
sku
.
setCreatedBy
(
"system"
);
sku
.
setCreatedAt
(
LocalDateTime
.
now
());
sku
.
setUpdatedBy
(
"system"
);
sku
.
setUpdatedAt
(
LocalDateTime
.
now
());
if
(
sqbSku
!=
null
)
{
// SKU 名称
String
skuName
=
sqbSku
.
getSkuName
()
!=
null
?
sqbSku
.
getSkuName
()
:
sqbSku
.
getSkuTitle
();
sku
.
setName
(
skuName
!=
null
?
skuName
:
(
productsData
!=
null
?
productsData
.
getTitle
()
:
"规格"
));
// 收钱吧价格单位为分,转元
if
(
sqbSku
.
getPrice
()
!=
null
)
{
BigDecimal
priceYuan
=
BigDecimal
.
valueOf
(
sqbSku
.
getPrice
()).
divide
(
BigDecimal
.
valueOf
(
100
));
sku
.
setPrice
(
priceYuan
);
sku
.
setSellPrice
(
priceYuan
);
}
// 库存:优先取 quantity,若为 null 则默认 9999
int
stock
=
sqbSku
.
getQuantity
()
!=
null
?
sqbSku
.
getQuantity
().
intValue
()
:
9999
;
sku
.
setStock
(
stock
);
sku
.
setSkuStock
(
stock
);
if
(
productsData
!=
null
&&
!
CollectionUtils
.
isEmpty
(
productsData
.
getConverImages
()))
{
sku
.
setSkuPic
(
productsData
.
getConverImages
().
get
(
0
));
}
}
else
{
sku
.
setName
(
productsData
!=
null
?
productsData
.
getTitle
()
:
"规格"
);
sku
.
setPrice
(
BigDecimal
.
ZERO
);
sku
.
setSellPrice
(
BigDecimal
.
ZERO
);
sku
.
setStock
(
0
);
sku
.
setSkuStock
(
0
);
}
return
sku
;
}
/**
* 构建 GoblinSqbGoodsExt 实体
*/
private
GoblinSqbGoodsExt
buildExt
(
String
spuId
,
String
skuId
,
String
mallSn
,
String
sqbProductId
,
String
sqbProductSn
,
String
sqbSkuId
,
String
now
)
{
GoblinSqbGoodsExt
ext
=
new
GoblinSqbGoodsExt
();
ext
.
setSpuId
(
spuId
);
ext
.
setSkuId
(
skuId
);
ext
.
setMallSn
(
mallSn
);
ext
.
setSqbProductId
(
sqbProductId
);
ext
.
setSqbProductSn
(
sqbProductSn
);
ext
.
setSqbSkuId
(
sqbSkuId
);
ext
.
setCreatedAt
(
now
);
ext
.
setUpdatedAt
(
now
);
return
ext
;
}
}
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/service/impl/GoblinSqbOrderServiceImpl.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
service
.
impl
;
import
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper
;
import
com.liquidnet.commons.lang.util.IDGenerator
;
import
com.liquidnet.service.base.ResponseDto
;
import
com.liquidnet.service.goblin.dto.vo.*
;
import
com.liquidnet.service.goblin.entity.GoblinSqbPerformanceGoods
;
import
com.liquidnet.service.goblin.mapper.GoblinSqbPerformanceGoodsMapper
;
import
com.liquidnet.service.goblin.param.shouqianba.request.*
;
import
com.liquidnet.service.goblin.param.shouqianba.response.data.*
;
import
com.liquidnet.service.goblin.service.IGoblinShouQianBaService
;
import
com.liquidnet.service.goblin.service.IGoblinSqbOrderService
;
import
com.liquidnet.service.goblin.util.GoblinRedisUtils
;
import
com.liquidnet.service.goblin.util.GoblinSqbRedisUtils
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Service
;
import
org.springframework.util.CollectionUtils
;
import
java.time.LocalDateTime
;
import
java.time.format.DateTimeFormatter
;
import
java.util.*
;
import
java.util.stream.Collectors
;
/**
* 收钱吧订单服务实现
*/
@Slf4j
@Service
public
class
GoblinSqbOrderServiceImpl
implements
IGoblinSqbOrderService
{
@Autowired
private
GoblinSqbRedisUtils
goblinSqbRedisUtils
;
@Autowired
private
GoblinRedisUtils
goblinRedisUtils
;
@Autowired
private
com
.
liquidnet
.
service
.
goblin
.
util
.
GoblinMongoUtils
goblinMongoUtils
;
@Autowired
private
com
.
liquidnet
.
service
.
goblin
.
util
.
QueueUtils
queueUtils
;
@Autowired
private
GoblinSqbPerformanceGoodsMapper
goblinSqbPerformanceGoodsMapper
;
@Autowired
private
IGoblinShouQianBaService
goblinShouQianBaService
;
private
static
final
DateTimeFormatter
DTF
=
DateTimeFormatter
.
ofPattern
(
"yyyy-MM-dd HH:mm:ss"
);
// ========== 常量(后期可移至配置文件) ==========
// TODO: 将以下常量移至 application.yml 配置
private
static
final
String
SQB_APPID
=
"test_appid"
;
private
static
final
String
SQB_MERCHANT_ID
=
"todo_merchant_id"
;
private
static
final
String
SQB_MERCHANT_USER_ID
=
"todo_merchant_user_id"
;
private
static
final
String
SQB_ROLE
=
"super_admin"
;
// ================================ 创建订单 ================================
@Override
public
ResponseDto
<
GoblinSqbOrderCreateVo
>
createOrder
(
String
userId
,
String
spuId
,
String
skuId
,
Integer
quantity
,
String
performancesId
)
{
log
.
info
(
"[收钱吧下单] 开始 userId={}, spuId={}, skuId={}, quantity={}, performancesId={}"
,
userId
,
spuId
,
skuId
,
quantity
,
performancesId
);
// Step 1: 分布式锁防重
boolean
locked
=
goblinSqbRedisUtils
.
tryOrderLock
(
userId
,
skuId
);
if
(!
locked
)
{
log
.
warn
(
"[收钱吧下单] 获取防重锁失败,userId={}, skuId={}"
,
userId
,
skuId
);
return
ResponseDto
.
failure
(
"请勿重复下单"
);
}
try
{
// Step 2: 校验演出-商品关联
LambdaQueryWrapper
<
GoblinSqbPerformanceGoods
>
query
=
new
LambdaQueryWrapper
<>();
query
.
eq
(
GoblinSqbPerformanceGoods:
:
getPerformancesId
,
performancesId
)
.
eq
(
GoblinSqbPerformanceGoods:
:
getSkuId
,
skuId
)
.
eq
(
GoblinSqbPerformanceGoods:
:
getStatus
,
1
);
GoblinSqbPerformanceGoods
relation
=
goblinSqbPerformanceGoodsMapper
.
selectOne
(
query
);
if
(
relation
==
null
)
{
log
.
warn
(
"[收钱吧下单] 演出-商品关联不存在或已禁用,performancesId={}, skuId={}"
,
performancesId
,
skuId
);
return
ResponseDto
.
failure
(
"商品与演出关联不存在"
);
}
// Step 3: 原子扣减库存
long
remaining
=
goblinRedisUtils
.
decrSkuStock
(
null
,
skuId
,
quantity
);
if
(
remaining
<
0
)
{
goblinRedisUtils
.
incrSkuStock
(
null
,
skuId
,
quantity
);
log
.
warn
(
"[收钱吧下单] 库存不足,skuId={}, quantity={}"
,
skuId
,
quantity
);
return
ResponseDto
.
failure
(
"库存不足"
);
}
log
.
info
(
"[收钱吧下单] 扣减库存成功,skuId={}, remaining={}"
,
skuId
,
remaining
);
// Step 4: 构建收钱吧公共请求参数
CommonRequest
.
Seller
seller
=
buildSeller
();
// Step 4.1: createSettlement → 得 checkoutItemsId
// TODO: 从商品数据中获取价格、图片、标题等信息填充 CheckoutItem
SettlementCreateRequest
settlementReq
=
new
SettlementCreateRequest
();
settlementReq
.
setAppid
(
SQB_APPID
);
settlementReq
.
setSeller
(
seller
);
CommonRequest
.
Buyer
buyer
=
new
CommonRequest
.
Buyer
();
buyer
.
setBuyerId
(
userId
);
settlementReq
.
setBuyer
(
buyer
);
// TODO: settlementReq.setCheckoutItems(...); settlementReq.setAmount(...);
SettlementCreateData
settlementData
=
goblinShouQianBaService
.
createSettlement
(
settlementReq
);
if
(
settlementData
==
null
)
{
goblinRedisUtils
.
incrSkuStock
(
null
,
skuId
,
quantity
);
return
ResponseDto
.
failure
(
"创建结算明细失败"
);
}
String
checkoutItemsId
=
settlementData
.
getCheckoutItemsId
();
log
.
info
(
"[收钱吧下单] createSettlement 成功,checkoutItemsId={}"
,
checkoutItemsId
);
// Step 4.2: createOrder → 得 sqbOrderSn + sqbOrderSignature + sqbAcquiringSn
OrderCreateRequest
orderReq
=
new
OrderCreateRequest
();
orderReq
.
setAppid
(
SQB_APPID
);
orderReq
.
setSeller
(
seller
);
orderReq
.
setCheckoutItemsId
(
checkoutItemsId
);
orderReq
.
setBuyer
(
buyer
);
orderReq
.
setRequestId
(
IDGenerator
.
nextSnowId
());
orderReq
.
setSubject
(
"收钱吧商品"
);
OrderCreateData
orderData
=
goblinShouQianBaService
.
createOrder
(
orderReq
);
if
(
orderData
==
null
)
{
goblinRedisUtils
.
incrSkuStock
(
null
,
skuId
,
quantity
);
return
ResponseDto
.
failure
(
"创建收钱吧订单失败"
);
}
String
sqbOrderSn
=
orderData
.
getOrderSn
();
String
sqbOrderSignature
=
orderData
.
getOrderSignature
();
String
sqbAcquiringSn
=
orderData
.
getAcquiring
()
!=
null
?
orderData
.
getAcquiring
().
getAcquiringSn
()
:
null
;
log
.
info
(
"[收钱吧下单] createOrder 成功,sqbOrderSn={}, sqbAcquiringSn={}"
,
sqbOrderSn
,
sqbAcquiringSn
);
// Step 4.3: queryCashier → 得 selectedSignature + seq
CashierQueryRequest
cashierReq
=
buildCashierQueryRequest
(
seller
,
sqbAcquiringSn
,
sqbOrderSignature
);
CashierQueryData
cashierData
=
goblinShouQianBaService
.
queryCashier
(
cashierReq
);
if
(
cashierData
==
null
)
{
goblinRedisUtils
.
incrSkuStock
(
null
,
skuId
,
quantity
);
return
ResponseDto
.
failure
(
"查询收银台失败"
);
}
String
selectedSignature
=
cashierData
.
getSelectedSignature
();
String
seq
=
cashierData
.
getSeq
();
log
.
info
(
"[收钱吧下单] queryCashier 成功,selectedSignature={}, seq={}"
,
selectedSignature
,
seq
);
// Step 4.4: createWechatPrepayOrder → 得 paymentVoucher
CreateWechatPrepayOrderRequest
prepayReq
=
buildWechatPrepayRequest
(
seller
,
sqbAcquiringSn
,
sqbOrderSignature
,
selectedSignature
,
seq
,
cashierData
);
CreateWechatPrepayOrderData
prepayData
=
goblinShouQianBaService
.
createWechatPrepayOrder
(
prepayReq
);
if
(
prepayData
==
null
||
prepayData
.
getPaymentVoucher
()
==
null
)
{
goblinRedisUtils
.
incrSkuStock
(
null
,
skuId
,
quantity
);
return
ResponseDto
.
failure
(
"创建微信预支付失败"
);
}
CreateWechatPrepayOrderData
.
PaymentVoucher
pv
=
prepayData
.
getPaymentVoucher
();
log
.
info
(
"[收钱吧下单] createWechatPrepayOrder 成功,timeStamp={}"
,
pv
.
getTimeStamp
());
String
orderId
=
IDGenerator
.
nextSnowId
();
String
masterOrderCode
=
IDGenerator
.
nextTimeId
();
String
orderCode
=
IDGenerator
.
nextTimeId
();
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
// 获取商品和 SKU 信息,供后续订单基础信息拼接使用
GoblinGoodsInfoVo
goodsInfo
=
goblinMongoUtils
.
getGoodsInfoVo
(
spuId
);
GoblinGoodsSkuInfoVo
skuInfo
=
goblinMongoUtils
.
getGoodsSkuInfoVo
(
skuId
);
// 1) 构建基础商城订单 VO (GoblinStoreOrderVo)
GoblinStoreOrderVo
storeOrderVo
=
new
GoblinStoreOrderVo
();
storeOrderVo
.
setMasterOrderCode
(
masterOrderCode
);
storeOrderVo
.
setOrderId
(
orderId
);
storeOrderVo
.
setStoreId
(
goodsInfo
!=
null
?
goodsInfo
.
getStoreId
()
:
"SQB_STORE"
);
storeOrderVo
.
setStoreName
(
goodsInfo
!=
null
?
goodsInfo
.
getStoreName
()
:
"收钱吧商品聚合门店"
);
storeOrderVo
.
setOrderCode
(
orderCode
);
storeOrderVo
.
setUserId
(
userId
);
// 姓名手机号为了兼容留空或占位即可
storeOrderVo
.
setUserName
(
""
);
storeOrderVo
.
setUserMobile
(
""
);
java
.
math
.
BigDecimal
skuPrice
=
skuInfo
!=
null
?
skuInfo
.
getPrice
()
:
java
.
math
.
BigDecimal
.
ZERO
;
java
.
math
.
BigDecimal
priceTotal
=
skuPrice
.
multiply
(
new
java
.
math
.
BigDecimal
(
quantity
));
storeOrderVo
.
setPriceTotal
(
priceTotal
);
storeOrderVo
.
setPriceActual
(
priceTotal
);
// 收钱吧暂无运费及优惠逻辑,实际价格等于总价
storeOrderVo
.
setPriceRefund
(
java
.
math
.
BigDecimal
.
ZERO
);
storeOrderVo
.
setPriceExpress
(
java
.
math
.
BigDecimal
.
ZERO
);
storeOrderVo
.
setPriceCoupon
(
java
.
math
.
BigDecimal
.
ZERO
);
storeOrderVo
.
setStorePriceCoupon
(
java
.
math
.
BigDecimal
.
ZERO
);
storeOrderVo
.
setPriceVoucher
(
java
.
math
.
BigDecimal
.
ZERO
);
// 待支付
storeOrderVo
.
setStatus
(
0
);
storeOrderVo
.
setPayType
(
"SQB_PAY"
);
// 标识为收钱吧支付类型
storeOrderVo
.
setDeviceFrom
(
"shouqianba"
);
storeOrderVo
.
setOrderType
(
0
);
storeOrderVo
.
setWriteOffCode
(
"EMPTY"
);
storeOrderVo
.
setPayCountdownMinute
(
15
);
storeOrderVo
.
setCreatedAt
(
now
);
// 2) 构建基础商城 SKU VO (GoblinOrderSkuVo)
String
orderSkuId
=
IDGenerator
.
nextSnowId
();
GoblinOrderSkuVo
orderSkuVo
=
new
GoblinOrderSkuVo
();
orderSkuVo
.
setOrderSkuId
(
orderSkuId
);
orderSkuVo
.
setOrderId
(
orderId
);
orderSkuVo
.
setSpuId
(
spuId
);
orderSkuVo
.
setSpuName
(
goodsInfo
!=
null
?
goodsInfo
.
getName
()
:
"收钱吧未知商品"
);
orderSkuVo
.
setSkuId
(
skuId
);
orderSkuVo
.
setNum
(
quantity
);
orderSkuVo
.
setSkuPrice
(
skuPrice
);
orderSkuVo
.
setSkuPriceActual
(
priceTotal
);
// 按件数总价
orderSkuVo
.
setSkuName
(
skuInfo
!=
null
?
skuInfo
.
getName
()
:
"收钱吧未匹配SKU"
);
orderSkuVo
.
setSkuImage
(
skuInfo
!=
null
?
skuInfo
.
getSkuPic
()
:
""
);
orderSkuVo
.
setSkuSpecs
(
"[]"
);
orderSkuVo
.
setPriceVoucher
(
java
.
math
.
BigDecimal
.
ZERO
);
orderSkuVo
.
setCreatedAt
(
now
);
// 特殊标识类型:33
orderSkuVo
.
setSkuType
(
33
);
// 存入 Redis 并绑定关系
List
<
String
>
orderSkuVoIds
=
new
ArrayList
<>();
orderSkuVoIds
.
add
(
orderSkuId
);
storeOrderVo
.
setOrderSkuVoIds
(
orderSkuVoIds
);
goblinRedisUtils
.
setGoblinOrderSku
(
orderSkuId
,
orderSkuVo
);
goblinRedisUtils
.
setGoblinOrder
(
orderId
,
storeOrderVo
);
goblinRedisUtils
.
setMasterCode
(
masterOrderCode
,
orderId
);
// 3) 构建收钱吧专属特有字段 (GoblinSqbOrderVo)
GoblinSqbOrderVo
orderVo
=
new
GoblinSqbOrderVo
();
orderVo
.
setOrderId
(
orderId
);
orderVo
.
setUserId
(
userId
);
orderVo
.
setPerformancesId
(
performancesId
);
orderVo
.
setSpuId
(
spuId
);
orderVo
.
setSkuId
(
skuId
);
orderVo
.
setQuantity
(
quantity
);
orderVo
.
setSqbOrderSn
(
sqbOrderSn
);
orderVo
.
setSqbOrderSignature
(
sqbOrderSignature
);
orderVo
.
setSqbAcquiringSn
(
sqbAcquiringSn
);
orderVo
.
setSqbCheckoutItemsId
(
checkoutItemsId
);
// 内部状态同步使用
orderVo
.
setStatus
(
0
);
orderVo
.
setCreatedAt
(
now
);
orderVo
.
setUpdatedAt
(
now
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
goblinRedisUtils
.
addOrderList
(
userId
,
orderId
);
log
.
info
(
"[收钱吧下单] 订单写入 Redis 成功,orderId={}"
,
orderId
);
// Step 6: 写 MongoDB + 发送复用的 SQL MQ
// 写入 Mongo
goblinMongoUtils
.
insertGoblinOrderSkuVo
(
orderSkuVo
);
goblinMongoUtils
.
insertGoblinStoreOrderVo
(
storeOrderVo
);
// 收钱吧扩展暂无需单独写 MongoDB 除非专门需要查询
// 拼接 SQL 数据并丢队列
java
.
util
.
LinkedList
<
String
>
sqls
=
new
java
.
util
.
LinkedList
<>();
sqls
.
add
(
com
.
liquidnet
.
service
.
base
.
SqlMapping
.
get
(
"goblin.order.create.sku_insert"
));
sqls
.
add
(
com
.
liquidnet
.
service
.
base
.
SqlMapping
.
get
(
"goblin.order.create.order_insert"
));
java
.
util
.
LinkedList
<
Object
[]>
sqlDataSku
=
new
java
.
util
.
LinkedList
<>();
sqlDataSku
.
add
(
new
Object
[]{
orderSkuVo
.
getOrderSkuId
(),
orderSkuVo
.
getOrderId
(),
orderSkuVo
.
getSpuId
(),
orderSkuVo
.
getSpuName
(),
(
goodsInfo
!=
null
?
goodsInfo
.
getCoverPic
()
:
""
),
orderSkuVo
.
getSkuId
(),
orderSkuVo
.
getNum
(),
orderSkuVo
.
getSkuPrice
(),
orderSkuVo
.
getSkuPriceActual
(),
orderSkuVo
.
getSkuName
(),
""
,
orderSkuVo
.
getSkuImage
(),
orderSkuVo
.
getSkuSpecs
(),
orderSkuVo
.
getPriceVoucher
(),
""
,
""
,
""
,
""
,
0
,
// ERP 等非必要参数直接置空
orderSkuVo
.
getCreatedAt
()
});
java
.
util
.
LinkedList
<
Object
[]>
sqlDataOrder
=
new
java
.
util
.
LinkedList
<>();
sqlDataOrder
.
add
(
new
Object
[]{
storeOrderVo
.
getMasterOrderCode
(),
storeOrderVo
.
getOrderId
(),
storeOrderVo
.
getStoreId
(),
storeOrderVo
.
getStoreName
(),
storeOrderVo
.
getOrderCode
(),
storeOrderVo
.
getUserId
(),
storeOrderVo
.
getUserName
(),
storeOrderVo
.
getUserMobile
(),
storeOrderVo
.
getPriceTotal
(),
storeOrderVo
.
getPayCode
(),
storeOrderVo
.
getPriceActual
(),
storeOrderVo
.
getPriceRefund
(),
storeOrderVo
.
getPriceExpress
(),
storeOrderVo
.
getPriceCoupon
(),
storeOrderVo
.
getStorePriceCoupon
(),
storeOrderVo
.
getPriceVoucher
(),
storeOrderVo
.
getStatus
(),
storeOrderVo
.
getUcouponId
(),
storeOrderVo
.
getStoreCouponId
(),
storeOrderVo
.
getPayType
(),
storeOrderVo
.
getDeviceFrom
(),
storeOrderVo
.
getSource
(),
storeOrderVo
.
getVersion
(),
storeOrderVo
.
getIsMember
(),
storeOrderVo
.
getOrderType
(),
storeOrderVo
.
getWriteOffCode
(),
storeOrderVo
.
getPayCountdownMinute
(),
storeOrderVo
.
getIpAddress
(),
storeOrderVo
.
getMarketId
(),
storeOrderVo
.
getMarketType
(),
storeOrderVo
.
getCreatedAt
(),
""
,
""
});
// 借用 Base 模块中 ConsumerGoblinOrderCPRdsReceiver 这个队列消费类进行写库
if
(
queueUtils
!=
null
)
{
queueUtils
.
sendMsgByRedis
(
com
.
liquidnet
.
service
.
base
.
constant
.
MQConst
.
GoblinQueue
.
GOBLIN_ORDER_CREATE_PAY
.
getKey
(),
com
.
liquidnet
.
service
.
base
.
SqlMapping
.
gets
(
sqls
,
sqlDataSku
,
sqlDataOrder
));
// 待支付超时回调等处理也可放入原有股票队列逻辑,暂省略
}
log
.
info
(
"[收钱吧下单] 完成 MongoDB 及 MQ 落库,orderId={}, masterCode={}"
,
orderId
,
masterOrderCode
);
// Step 7: 释放分布式锁,构建返回
goblinSqbRedisUtils
.
releaseOrderLock
(
userId
,
skuId
);
GoblinSqbOrderCreateVo
createVo
=
new
GoblinSqbOrderCreateVo
();
createVo
.
setOrderId
(
orderId
);
createVo
.
setAcquiringSn
(
sqbAcquiringSn
);
createVo
.
setTimeStamp
(
pv
.
getTimeStamp
());
createVo
.
setPackageStr
(
pv
.
getPackageStr
());
createVo
.
setPaySign
(
pv
.
getPaySign
());
createVo
.
setAppId
(
pv
.
getAppId
());
createVo
.
setSignType
(
pv
.
getSignType
());
createVo
.
setNonceStr
(
pv
.
getNonceStr
());
log
.
info
(
"[收钱吧下单] 下单成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
createVo
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[收钱吧下单] 下单异常,userId={}, skuId={}"
,
userId
,
skuId
,
e
);
goblinRedisUtils
.
incrSkuStock
(
null
,
skuId
,
quantity
);
goblinSqbRedisUtils
.
releaseOrderLock
(
userId
,
skuId
);
return
ResponseDto
.
failure
(
"下单失败:"
+
e
.
getMessage
());
}
}
// ================================ 查询支付状态 ================================
@Override
public
ResponseDto
<
Integer
>
queryPayStatus
(
String
userId
,
String
orderId
)
{
log
.
info
(
"[收钱吧支付状态] 开始查询 userId={}, orderId={}"
,
userId
,
orderId
);
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
if
(
orderVo
==
null
)
{
return
ResponseDto
.
failure
(
"订单不存在"
);
}
if
(!
userId
.
equals
(
orderVo
.
getUserId
()))
{
return
ResponseDto
.
failure
(
"无权限访问该订单"
);
}
// 已支付直接返回
if
(
Integer
.
valueOf
(
1
).
equals
(
orderVo
.
getStatus
()))
{
return
ResponseDto
.
success
(
1
);
}
// 调用收钱吧查询收单状态
CommonRequest
.
Seller
seller
=
buildSeller
();
CashierQueryRequest
cashierReq
=
buildCashierQueryRequest
(
seller
,
orderVo
.
getSqbAcquiringSn
(),
orderVo
.
getSqbOrderSignature
());
CashierQueryData
cashierData
=
goblinShouQianBaService
.
queryCashier
(
cashierReq
);
if
(
cashierData
==
null
)
{
log
.
warn
(
"[收钱吧支付状态] queryCashier 调用失败,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
orderVo
.
getStatus
());
}
// acquiringState 通过 cashierData.getAmount() > 0 && 收单签名有效来推断 —— 实际以收钱吧文档为准
// 简化处理:收银台能正常返回且 selectedSignature 非空,视为已支付
if
(
cashierData
.
getSelectedSignature
()
!=
null
)
{
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
orderVo
.
setStatus
(
1
);
orderVo
.
setUpdatedAt
(
now
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
1
);
log
.
info
(
"[收钱吧支付状态] 确认支付成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
1
);
}
return
ResponseDto
.
success
(
orderVo
.
getStatus
());
}
// ================================ 获取券码 ================================
@Override
public
ResponseDto
<
GoblinSqbCouponVo
>
queryCoupon
(
String
userId
,
String
orderId
)
{
log
.
info
(
"[收钱吧券码] 开始获取 userId={}, orderId={}"
,
userId
,
orderId
);
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
if
(
orderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(!
userId
.
equals
(
orderVo
.
getUserId
()))
return
ResponseDto
.
failure
(
"无权限访问该订单"
);
if
(!
Integer
.
valueOf
(
1
).
equals
(
orderVo
.
getStatus
()))
return
ResponseDto
.
failure
(
"订单未支付,无法获取券码"
);
// 幂等:已存在则直接返回
if
(
orderVo
.
getCouponQrCode
()
!=
null
&&
!
orderVo
.
getCouponQrCode
().
isEmpty
())
{
GoblinSqbCouponVo
couponVo
=
new
GoblinSqbCouponVo
();
couponVo
.
setCouponSn
(
orderVo
.
getCouponSn
());
couponVo
.
setCouponQrCode
(
orderVo
.
getCouponQrCode
());
couponVo
.
setCouponExpireTime
(
orderVo
.
getCouponExpireTime
());
return
ResponseDto
.
success
(
couponVo
);
}
// 调用收钱吧查询券码
CouponQueryRequest
req
=
new
CouponQueryRequest
();
req
.
setAppid
(
SQB_APPID
);
req
.
setSeller
(
buildSeller
());
CouponQueryRequest
.
OrderInfo
orderInfo
=
new
CouponQueryRequest
.
OrderInfo
();
orderInfo
.
setSn
(
orderVo
.
getSqbOrderSn
());
orderInfo
.
setSignature
(
orderVo
.
getSqbOrderSignature
());
req
.
setOrderID
(
orderInfo
);
CouponQueryData
couponData
=
goblinShouQianBaService
.
queryCoupon
(
req
);
if
(
couponData
==
null
)
return
ResponseDto
.
failure
(
"获取券码失败"
);
// 更新订单 coupon 字段
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
orderVo
.
setCouponSn
(
couponData
.
getVoucherNo
());
orderVo
.
setCouponQrCode
(
couponData
.
getUrl
());
orderVo
.
setCouponExpireTime
(
couponData
.
getOperationTime
());
orderVo
.
setUpdatedAt
(
now
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
// 券码信息保存在 Redis,MySQL和MongoDB商城基础模型无此字段,无需同步
GoblinSqbCouponVo
couponVo
=
new
GoblinSqbCouponVo
();
couponVo
.
setCouponSn
(
couponData
.
getVoucherNo
());
couponVo
.
setCouponQrCode
(
couponData
.
getUrl
());
couponVo
.
setCouponExpireTime
(
couponData
.
getOperationTime
());
log
.
info
(
"[收钱吧券码] 获取成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
couponVo
);
}
// ================================ 申请退款 ================================
@Override
public
ResponseDto
<
Boolean
>
refund
(
String
userId
,
String
orderId
)
{
log
.
info
(
"[收钱吧退款] 开始 userId={}, orderId={}"
,
userId
,
orderId
);
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
if
(
orderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(!
userId
.
equals
(
orderVo
.
getUserId
()))
return
ResponseDto
.
failure
(
"无权限访问该订单"
);
if
(!
Integer
.
valueOf
(
1
).
equals
(
orderVo
.
getStatus
()))
return
ResponseDto
.
failure
(
"订单状态不可退款"
);
if
(
Integer
.
valueOf
(
1
).
equals
(
orderVo
.
getCouponUsedStatus
()))
return
ResponseDto
.
failure
(
"券码已核销,不可退款"
);
// 更新状态为退款中
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
orderVo
.
setStatus
(
4
);
orderVo
.
setUpdatedAt
(
now
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
4
);
try
{
// 调用收钱吧退款
CouponRefundRequest
refundReq
=
new
CouponRefundRequest
();
refundReq
.
setAppid
(
SQB_APPID
);
refundReq
.
setSeller
(
buildSeller
());
refundReq
.
setRequestSource
(
"EXTERN"
);
refundReq
.
setRequestId
(
IDGenerator
.
nextSnowId
());
CouponRefundRequest
.
OrderInfo
orderInfo
=
new
CouponRefundRequest
.
OrderInfo
();
orderInfo
.
setSn
(
orderVo
.
getSqbOrderSn
());
orderInfo
.
setSignature
(
orderVo
.
getSqbOrderSignature
());
refundReq
.
setOrderID
(
orderInfo
);
CouponRefundRequest
.
RefundInfo
refundInfo
=
new
CouponRefundRequest
.
RefundInfo
();
// amount 字段待接入实际金额(GoblinSqbOrderVo 需补充 amount)
refundInfo
.
setApplyAmount
(
0L
);
// TODO: 替换为实际订单金额
refundInfo
.
setType
((
byte
)
2
);
// 2-按金额退款
refundInfo
.
setRefundReason
(
"用户申请退款"
);
refundReq
.
setRefundInfo
(
refundInfo
);
CouponRefundData
refundData
=
goblinShouQianBaService
.
refundCoupon
(
refundReq
);
if
(
refundData
==
null
)
{
// 退款失败,回滚状态
orderVo
.
setStatus
(
1
);
orderVo
.
setUpdatedAt
(
LocalDateTime
.
now
().
format
(
DTF
));
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
1
);
return
ResponseDto
.
failure
(
"退款失败,请稍后重试"
);
}
// 退款成功
orderVo
.
setStatus
(
3
);
orderVo
.
setUpdatedAt
(
LocalDateTime
.
now
().
format
(
DTF
));
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
goblinRedisUtils
.
incrSkuStock
(
null
,
orderVo
.
getSkuId
(),
orderVo
.
getQuantity
());
syncOrderStatus
(
orderId
,
3
);
log
.
info
(
"[收钱吧退款] 退款成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
Boolean
.
TRUE
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[收钱吧退款] 退款异常,orderId={},回滚 status=1"
,
orderId
,
e
);
orderVo
.
setStatus
(
1
);
orderVo
.
setUpdatedAt
(
LocalDateTime
.
now
().
format
(
DTF
));
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
1
);
return
ResponseDto
.
failure
(
"退款失败:"
+
e
.
getMessage
());
}
}
// ================================ 同步核销状态 ================================
@Override
public
ResponseDto
<
Boolean
>
syncCouponStatus
(
String
userId
,
String
orderId
)
{
log
.
info
(
"[收钱吧核销同步] 开始 userId={}, orderId={}"
,
userId
,
orderId
);
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
if
(
orderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(!
userId
.
equals
(
orderVo
.
getUserId
()))
return
ResponseDto
.
failure
(
"无权限访问该订单"
);
CouponStatusSyncRequest
req
=
new
CouponStatusSyncRequest
();
req
.
setAppid
(
SQB_APPID
);
req
.
setRedeemSource
(
"EXTERN"
);
// TODO: 填充 voucherNos(券号列表)、redeemMerchantId、redeemExternalOrderSn、clientSn、status
// 需要从 orderVo 中获取 couponSn 和其他必要字段
if
(
orderVo
.
getCouponSn
()
!=
null
)
{
req
.
setVoucherNos
(
java
.
util
.
Collections
.
singletonList
(
orderVo
.
getCouponSn
()));
}
req
.
setRedeemMerchantId
(
SQB_MERCHANT_ID
);
req
.
setRedeemExternalOrderSn
(
IDGenerator
.
nextSnowId
());
req
.
setClientSn
(
"terminal_001"
);
// TODO: 配置终端号
req
.
setStatus
((
byte
)
1
);
// 1-已核销
boolean
couponUsed
=
goblinShouQianBaService
.
syncCouponStatus
(
req
);
if
(
couponUsed
)
{
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
orderVo
.
setStatus
(
2
);
orderVo
.
setCouponUsedStatus
(
1
);
orderVo
.
setUpdatedAt
(
now
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
2
);
log
.
info
(
"[收钱吧核销同步] 核销成功,orderId={}"
,
orderId
);
}
return
ResponseDto
.
success
(
couponUsed
);
}
// ================================ 回调处理 ================================
@Override
public
ResponseDto
<
String
>
handlePayCallback
(
Map
<
String
,
Object
>
params
)
{
log
.
info
(
"[收钱吧支付回调] 收到回调参数: {}"
,
params
);
String
orderId
=
getOrderIdFromParams
(
params
);
if
(
orderId
==
null
)
return
ResponseDto
.
failure
(
"缺少订单标识"
);
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
if
(
orderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(
Integer
.
valueOf
(
1
).
equals
(
orderVo
.
getStatus
()))
return
ResponseDto
.
success
(
"success"
);
// 幂等
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
orderVo
.
setStatus
(
1
);
orderVo
.
setUpdatedAt
(
now
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
1
);
log
.
info
(
"[收钱吧支付回调] 更新支付状态成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
"success"
);
}
@Override
public
ResponseDto
<
String
>
handleRefundCallback
(
Map
<
String
,
Object
>
params
)
{
log
.
info
(
"[收钱吧退款回调] 收到回调参数: {}"
,
params
);
String
orderId
=
getOrderIdFromParams
(
params
);
if
(
orderId
==
null
)
return
ResponseDto
.
failure
(
"缺少订单标识"
);
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
if
(
orderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(
Integer
.
valueOf
(
3
).
equals
(
orderVo
.
getStatus
()))
return
ResponseDto
.
success
(
"success"
);
// 幂等
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
orderVo
.
setStatus
(
3
);
orderVo
.
setUpdatedAt
(
now
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
goblinRedisUtils
.
incrSkuStock
(
null
,
orderVo
.
getSkuId
(),
orderVo
.
getQuantity
());
syncOrderStatus
(
orderId
,
3
);
log
.
info
(
"[收钱吧退款回调] 更新退款状态成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
"success"
);
}
@Override
public
ResponseDto
<
String
>
handleCouponCallback
(
Map
<
String
,
Object
>
params
)
{
log
.
info
(
"[收钱吧券状态回调] 收到回调参数: {}"
,
params
);
String
orderId
=
getOrderIdFromParams
(
params
);
if
(
orderId
==
null
)
return
ResponseDto
.
failure
(
"缺少订单标识"
);
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
if
(
orderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(
Integer
.
valueOf
(
2
).
equals
(
orderVo
.
getStatus
()))
return
ResponseDto
.
success
(
"success"
);
// 幂等
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
orderVo
.
setStatus
(
2
);
orderVo
.
setCouponUsedStatus
(
1
);
orderVo
.
setUpdatedAt
(
now
);
goblinSqbRedisUtils
.
setSqbOrder
(
orderId
,
orderVo
);
syncOrderStatus
(
orderId
,
2
);
log
.
info
(
"[收钱吧券状态回调] 核销状态更新成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
"success"
);
}
// ================================ 自动退款(定时任务) ================================
@Override
public
ResponseDto
<
String
>
autoRefundByPerformance
(
String
performancesId
)
{
log
.
info
(
"[收钱吧自动退款] 开始处理演出 performancesId={}"
,
performancesId
);
// 查询该演出下 status=1(已支付)的订单(MySQL)
// TODO: 通过 GoblinSqbOrderMapper 查询(需注入后解注释)
// LambdaQueryWrapper<GoblinSqbOrder> query = new LambdaQueryWrapper<>();
// query.eq(GoblinSqbOrder::getPerformancesId, performancesId).eq(GoblinSqbOrder::getStatus, 1);
// List<GoblinSqbOrder> orders = goblinSqbOrderMapper.selectList(query);
int
successCount
=
0
;
int
failCount
=
0
;
// TODO: 取消注释并接入 GoblinSqbOrderMapper 后启用
// for (GoblinSqbOrder order : orders) {
// try {
// ResponseDto<Boolean> result = refund(order.getUserId(), order.getOrderId());
// if (result != null && result.isSuccess()) {
// successCount++;
// } else {
// log.warn("[收钱吧自动退款] 订单退款失败 orderId={}", order.getOrderId());
// failCount++;
// }
// } catch (Exception e) {
// log.error("[收钱吧自动退款] 订单退款异常 orderId={}", order.getOrderId(), e);
// failCount++;
// }
// }
String
summary
=
String
.
format
(
"演出 %s 自动退款完成:成功 %d 笔,失败 %d 笔"
,
performancesId
,
successCount
,
failCount
);
log
.
info
(
"[收钱吧自动退款] {}"
,
summary
);
return
ResponseDto
.
success
(
summary
);
}
// ================================ 订单列表 ================================
@Override
public
ResponseDto
<
List
<
GoblinSqbOrderDetailVo
>>
getOrderList
(
String
userId
)
{
log
.
info
(
"[收钱吧订单列表] userId={}"
,
userId
);
List
<
String
>
orderIds
=
goblinRedisUtils
.
getOrderList
(
userId
);
if
(
CollectionUtils
.
isEmpty
(
orderIds
))
{
return
ResponseDto
.
success
(
Collections
.
emptyList
());
}
List
<
GoblinSqbOrderDetailVo
>
result
=
new
ArrayList
<>();
for
(
String
orderId
:
orderIds
)
{
GoblinStoreOrderVo
storeOrderVo
=
goblinRedisUtils
.
getGoblinOrder
(
orderId
);
if
(
storeOrderVo
==
null
)
continue
;
// 取第一个 orderSkuId 判断 skuType
List
<
String
>
orderSkuVoIds
=
storeOrderVo
.
getOrderSkuVoIds
();
if
(
CollectionUtils
.
isEmpty
(
orderSkuVoIds
))
continue
;
GoblinOrderSkuVo
skuVo
=
goblinRedisUtils
.
getGoblinOrderSkuVo
(
orderSkuVoIds
.
get
(
0
));
if
(
skuVo
==
null
)
continue
;
// 过滤:只处理 skuType=33 的收钱吧订单
if
(!
Integer
.
valueOf
(
33
).
equals
(
skuVo
.
getSkuType
()))
continue
;
GoblinSqbOrderVo
sqbOrderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
result
.
add
(
buildDetailVo
(
storeOrderVo
,
skuVo
,
sqbOrderVo
));
}
// 按创建时间倒序排列
result
.
sort
((
a
,
b
)
->
{
if
(
a
.
getCreatedAt
()
==
null
)
return
1
;
if
(
b
.
getCreatedAt
()
==
null
)
return
-
1
;
return
b
.
getCreatedAt
().
compareTo
(
a
.
getCreatedAt
());
});
log
.
info
(
"[收钱吧订单列表] userId={}, 共 {} 条"
,
userId
,
result
.
size
());
return
ResponseDto
.
success
(
result
);
}
// ================================ 订单详情 ================================
@Override
public
ResponseDto
<
GoblinSqbOrderDetailVo
>
getOrderDetail
(
String
userId
,
String
orderId
)
{
log
.
info
(
"[收钱吧订单详情] userId={}, orderId={}"
,
userId
,
orderId
);
GoblinStoreOrderVo
storeOrderVo
=
goblinRedisUtils
.
getGoblinOrder
(
orderId
);
if
(
storeOrderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(!
userId
.
equals
(
storeOrderVo
.
getUserId
()))
return
ResponseDto
.
failure
(
"无权限访问该订单"
);
List
<
String
>
orderSkuVoIds
=
storeOrderVo
.
getOrderSkuVoIds
();
if
(
CollectionUtils
.
isEmpty
(
orderSkuVoIds
))
return
ResponseDto
.
failure
(
"订单数据异常"
);
GoblinOrderSkuVo
skuVo
=
goblinRedisUtils
.
getGoblinOrderSkuVo
(
orderSkuVoIds
.
get
(
0
));
if
(
skuVo
==
null
)
return
ResponseDto
.
failure
(
"订单商品数据异常"
);
// 校验是收钱吧订单
if
(!
Integer
.
valueOf
(
33
).
equals
(
skuVo
.
getSkuType
()))
{
return
ResponseDto
.
failure
(
"该订单非收钱吧订单"
);
}
GoblinSqbOrderVo
sqbOrderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
GoblinSqbOrderDetailVo
detailVo
=
buildDetailVo
(
storeOrderVo
,
skuVo
,
sqbOrderVo
);
log
.
info
(
"[收钱吧订单详情] 查询成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
detailVo
);
}
// ================================ 再次付款 ================================
@Override
public
ResponseDto
<
GoblinSqbOrderCreateVo
>
repay
(
String
userId
,
String
orderId
)
{
log
.
info
(
"[收钱吧再次付款] userId={}, orderId={}"
,
userId
,
orderId
);
GoblinSqbOrderVo
orderVo
=
goblinSqbRedisUtils
.
getSqbOrder
(
orderId
);
if
(
orderVo
==
null
)
return
ResponseDto
.
failure
(
"订单不存在"
);
if
(!
userId
.
equals
(
orderVo
.
getUserId
()))
return
ResponseDto
.
failure
(
"无权限访问该订单"
);
if
(!
Integer
.
valueOf
(
0
).
equals
(
orderVo
.
getStatus
()))
{
return
ResponseDto
.
failure
(
"订单状态不支持再次付款(仅待支付状态可重新拉起)"
);
}
if
(
orderVo
.
getSqbAcquiringSn
()
==
null
)
{
return
ResponseDto
.
failure
(
"订单数据异常,缺少收单号"
);
}
try
{
CommonRequest
.
Seller
seller
=
buildSeller
();
// 重新查询收银台,获取新的 selectedSignature + seq
CashierQueryRequest
cashierReq
=
buildCashierQueryRequest
(
seller
,
orderVo
.
getSqbAcquiringSn
(),
orderVo
.
getSqbOrderSignature
());
CashierQueryData
cashierData
=
goblinShouQianBaService
.
queryCashier
(
cashierReq
);
if
(
cashierData
==
null
)
return
ResponseDto
.
failure
(
"查询收银台失败,请稍后重试"
);
// 重新创建微信预支付
CreateWechatPrepayOrderRequest
prepayReq
=
buildWechatPrepayRequest
(
seller
,
orderVo
.
getSqbAcquiringSn
(),
orderVo
.
getSqbOrderSignature
(),
cashierData
.
getSelectedSignature
(),
cashierData
.
getSeq
(),
cashierData
);
CreateWechatPrepayOrderData
prepayData
=
goblinShouQianBaService
.
createWechatPrepayOrder
(
prepayReq
);
if
(
prepayData
==
null
||
prepayData
.
getPaymentVoucher
()
==
null
)
{
return
ResponseDto
.
failure
(
"创建微信预支付失败,请稍后重试"
);
}
CreateWechatPrepayOrderData
.
PaymentVoucher
pv
=
prepayData
.
getPaymentVoucher
();
GoblinSqbOrderCreateVo
createVo
=
new
GoblinSqbOrderCreateVo
();
createVo
.
setOrderId
(
orderId
);
createVo
.
setAcquiringSn
(
orderVo
.
getSqbAcquiringSn
());
createVo
.
setTimeStamp
(
pv
.
getTimeStamp
());
createVo
.
setPackageStr
(
pv
.
getPackageStr
());
createVo
.
setPaySign
(
pv
.
getPaySign
());
createVo
.
setAppId
(
pv
.
getAppId
());
createVo
.
setSignType
(
pv
.
getSignType
());
createVo
.
setNonceStr
(
pv
.
getNonceStr
());
log
.
info
(
"[收钱吧再次付款] 成功,orderId={}"
,
orderId
);
return
ResponseDto
.
success
(
createVo
);
}
catch
(
Exception
e
)
{
log
.
error
(
"[收钱吧再次付款] 异常,orderId={}"
,
orderId
,
e
);
return
ResponseDto
.
failure
(
"再次付款失败:"
+
e
.
getMessage
());
}
}
// ================================ 私有辅助方法 ================================
private
CommonRequest
.
Seller
buildSeller
()
{
CommonRequest
.
Seller
seller
=
new
CommonRequest
.
Seller
();
seller
.
setMerchantId
(
SQB_MERCHANT_ID
);
seller
.
setMerchantUserId
(
SQB_MERCHANT_USER_ID
);
seller
.
setRole
(
SQB_ROLE
);
return
seller
;
}
private
CashierQueryRequest
buildCashierQueryRequest
(
CommonRequest
.
Seller
seller
,
String
acquiringSn
,
String
acquiringSignature
)
{
CashierQueryRequest
req
=
new
CashierQueryRequest
();
req
.
setAppid
(
SQB_APPID
);
req
.
setSeller
(
seller
);
req
.
setPaymentMode
(
4
);
// 4-微信小程序支付
CashierQueryRequest
.
PaymentEnv
paymentEnv
=
new
CashierQueryRequest
.
PaymentEnv
();
paymentEnv
.
setClient
(
"wechat"
);
req
.
setPaymentEnv
(
paymentEnv
);
CommonRequest
.
Acquiring
acquiring
=
new
CommonRequest
.
Acquiring
();
acquiring
.
setAcquiringSn
(
acquiringSn
);
acquiring
.
setSignature
(
acquiringSignature
);
req
.
setAcquiringInfo
(
acquiring
);
return
req
;
}
private
CreateWechatPrepayOrderRequest
buildWechatPrepayRequest
(
CommonRequest
.
Seller
seller
,
String
acquiringSn
,
String
acquiringSignature
,
String
selectedSignature
,
String
seq
,
CashierQueryData
cashierData
)
{
CreateWechatPrepayOrderRequest
req
=
new
CreateWechatPrepayOrderRequest
();
req
.
setAppid
(
SQB_APPID
);
req
.
setSeller
(
seller
);
req
.
setAcquiringSn
(
acquiringSn
);
req
.
setSignature
(
acquiringSignature
);
req
.
setSelectedSignature
(
selectedSignature
);
req
.
setSeq
(
seq
);
// 使用收银台返回的第一个可用支付工具
if
(
cashierData
!=
null
&&
!
CollectionUtils
.
isEmpty
(
cashierData
.
getPayTools
()))
{
List
<
CreateWechatPrepayOrderRequest
.
UsingPayTool
>
usingPayTools
=
new
ArrayList
<>();
cashierData
.
getPayTools
().
stream
()
.
filter
(
t
->
Boolean
.
TRUE
.
equals
(
t
.
getSelectable
()))
.
findFirst
()
.
ifPresent
(
payTool
->
{
CreateWechatPrepayOrderRequest
.
UsingPayTool
usingTool
=
new
CreateWechatPrepayOrderRequest
.
UsingPayTool
();
usingTool
.
setId
(
payTool
.
getId
());
usingTool
.
setPayTool
(
payTool
.
getType
());
usingTool
.
setPayMode
(
4
);
usingTool
.
setAmount
(
payTool
.
getShowAmount
()
!=
null
?
String
.
valueOf
(
payTool
.
getShowAmount
())
:
"0"
);
usingTool
.
setIdentity
(
"wzwl"
);
usingTool
.
setRequestSn
(
IDGenerator
.
nextSnowId
());
usingPayTools
.
add
(
usingTool
);
});
req
.
setUsingPayTools
(
usingPayTools
);
}
return
req
;
}
private
GoblinSqbOrderDetailVo
buildDetailVo
(
GoblinStoreOrderVo
storeOrderVo
,
GoblinOrderSkuVo
skuVo
,
GoblinSqbOrderVo
sqbOrderVo
)
{
GoblinSqbOrderDetailVo
detailVo
=
new
GoblinSqbOrderDetailVo
();
// 来自 GoblinStoreOrderVo
detailVo
.
setOrderId
(
storeOrderVo
.
getOrderId
());
detailVo
.
setOrderCode
(
storeOrderVo
.
getOrderCode
());
detailVo
.
setStatus
(
storeOrderVo
.
getStatus
());
detailVo
.
setPriceActual
(
storeOrderVo
.
getPriceActual
());
detailVo
.
setCreatedAt
(
storeOrderVo
.
getCreatedAt
());
detailVo
.
setPayTime
(
storeOrderVo
.
getPayTime
());
// 来自 GoblinOrderSkuVo
detailVo
.
setSpuId
(
skuVo
.
getSpuId
());
detailVo
.
setSpuName
(
skuVo
.
getSpuName
());
detailVo
.
setSkuId
(
skuVo
.
getSkuId
());
detailVo
.
setSkuName
(
skuVo
.
getSkuName
());
detailVo
.
setSkuImage
(
skuVo
.
getSkuImage
());
detailVo
.
setQuantity
(
skuVo
.
getNum
());
// 来自 GoblinSqbOrderVo
if
(
sqbOrderVo
!=
null
)
{
detailVo
.
setSqbStatus
(
sqbOrderVo
.
getStatus
());
detailVo
.
setPerformancesId
(
sqbOrderVo
.
getPerformancesId
());
detailVo
.
setSqbAcquiringSn
(
sqbOrderVo
.
getSqbAcquiringSn
());
detailVo
.
setCouponSn
(
sqbOrderVo
.
getCouponSn
());
detailVo
.
setCouponQrCode
(
sqbOrderVo
.
getCouponQrCode
());
detailVo
.
setCouponExpireTime
(
sqbOrderVo
.
getCouponExpireTime
());
detailVo
.
setCouponUsedStatus
(
sqbOrderVo
.
getCouponUsedStatus
());
}
return
detailVo
;
}
private
String
getOrderIdFromParams
(
Map
<
String
,
Object
>
params
)
{
String
orderId
=
(
String
)
params
.
get
(
"orderId"
);
if
(
orderId
==
null
)
orderId
=
(
String
)
params
.
get
(
"acquiringSn"
);
return
orderId
;
}
private
void
syncOrderStatus
(
String
orderId
,
int
status
)
{
GoblinStoreOrderVo
storeOrderVo
=
goblinRedisUtils
.
getGoblinOrder
(
orderId
);
if
(
storeOrderVo
==
null
)
return
;
String
now
=
LocalDateTime
.
now
().
format
(
DTF
);
storeOrderVo
.
setStatus
(
status
);
if
(
status
==
1
)
{
// 支付成功
storeOrderVo
.
setPayTime
(
now
);
}
// 1. 同步 Redis
goblinRedisUtils
.
setGoblinOrder
(
orderId
,
storeOrderVo
);
// 2. 同步 MongoDB
goblinMongoUtils
.
updateGoblinStoreOrderVo
(
orderId
,
storeOrderVo
);
// 3. 同步 MySQL (复用下单 MQ 发送 Update 语句)
String
sql
;
Object
[]
data
;
if
(
status
==
1
)
{
sql
=
"UPDATE goblin_store_order SET status = ?, pay_time = ?, updated_at = ? WHERE order_id = ?"
;
data
=
new
Object
[]{
status
,
now
,
now
,
orderId
};
}
else
{
sql
=
"UPDATE goblin_store_order SET status = ?, updated_at = ? WHERE order_id = ?"
;
data
=
new
Object
[]{
status
,
now
,
orderId
};
}
java
.
util
.
LinkedList
<
String
>
sqls
=
new
java
.
util
.
LinkedList
<>();
sqls
.
add
(
sql
);
java
.
util
.
LinkedList
<
Object
[]>
sqlData
=
new
java
.
util
.
LinkedList
<>();
sqlData
.
add
(
data
);
if
(
queueUtils
!=
null
)
{
queueUtils
.
sendMsgByRedis
(
com
.
liquidnet
.
service
.
base
.
constant
.
MQConst
.
GoblinQueue
.
GOBLIN_ORDER_CREATE_PAY
.
getKey
(),
com
.
liquidnet
.
service
.
base
.
SqlMapping
.
gets
(
sqls
,
sqlData
));
}
}
}
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/util/GoblinRedisUtils.java
View file @
52399664
...
...
@@ -1697,6 +1697,11 @@ public class GoblinRedisUtils {
redisUtil
.
set
(
redisKey
,
list
);
}
//主订单下包含的子订单
public
void
setMasterCode
(
String
masterCode
,
String
orderIds
)
{
String
redisKey
=
GoblinRedisConst
.
REDIS_GOBLIN_ORDER_MASTER
.
concat
(
masterCode
);
redisUtil
.
set
(
redisKey
,
orderIds
);
}
public
String
[]
getMasterCode
(
String
masterCode
)
{
String
redisKey
=
GoblinRedisConst
.
REDIS_GOBLIN_ORDER_MASTER
.
concat
(
masterCode
);
...
...
liquidnet-bus-service/liquidnet-service-goblin/liquidnet-service-goblin-impl/src/main/java/com/liquidnet/service/goblin/util/GoblinSqbRedisUtils.java
0 → 100644
View file @
52399664
package
com
.
liquidnet
.
service
.
goblin
.
util
;
import
com.fasterxml.jackson.core.type.TypeReference
;
import
com.liquidnet.common.cache.redis.util.RedisUtil
;
import
com.liquidnet.commons.lang.util.JsonUtils
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbOrderVo
;
import
com.liquidnet.service.goblin.dto.vo.GoblinSqbPerfGoodsVo
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.stereotype.Component
;
import
org.springframework.util.StringUtils
;
import
java.util.List
;
/**
* 收钱吧相关 Redis 操作封装
*/
@Slf4j
@Component
public
class
GoblinSqbRedisUtils
{
// Redis Key 前缀
private
static
final
String
KEY_SQB_ORDER
=
"goblin:sqb:order:"
;
private
static
final
String
KEY_SQB_PERF_GOODS
=
"goblin:sqb:perf:goods:"
;
private
static
final
String
KEY_SQB_ORDER_LOCK
=
"goblin:sqb:order:lock:"
;
// TTL 常量(秒)
private
static
final
long
TTL_ORDER
=
2
*
60
*
60
;
// 2h
private
static
final
long
TTL_PERF_GOODS
=
5
*
60
;
// 5min
private
static
final
long
TTL_ORDER_LOCK
=
10
;
// 10s
@Autowired
private
RedisUtil
redisUtil
;
/* ---------------------------------------- 订单操作(TTL 2h) ---------------------------------------- */
public
void
setSqbOrder
(
String
orderId
,
GoblinSqbOrderVo
vo
)
{
redisUtil
.
set
(
KEY_SQB_ORDER
.
concat
(
orderId
),
JsonUtils
.
toJson
(
vo
),
TTL_ORDER
);
}
public
GoblinSqbOrderVo
getSqbOrder
(
String
orderId
)
{
String
valStr
=
(
String
)
redisUtil
.
get
(
KEY_SQB_ORDER
.
concat
(
orderId
));
if
(
StringUtils
.
isEmpty
(
valStr
))
{
return
null
;
}
return
JsonUtils
.
fromJson
(
valStr
,
GoblinSqbOrderVo
.
class
);
}
public
void
delSqbOrder
(
String
orderId
)
{
redisUtil
.
del
(
KEY_SQB_ORDER
.
concat
(
orderId
));
}
/* ---------------------------------------- 演出关联商品缓存(TTL 5min) ---------------------------------------- */
public
void
setPerfGoods
(
String
performancesId
,
List
<
GoblinSqbPerfGoodsVo
>
list
)
{
redisUtil
.
set
(
KEY_SQB_PERF_GOODS
.
concat
(
performancesId
),
JsonUtils
.
toJson
(
list
),
TTL_PERF_GOODS
);
}
public
List
<
GoblinSqbPerfGoodsVo
>
getPerfGoods
(
String
performancesId
)
{
String
valStr
=
(
String
)
redisUtil
.
get
(
KEY_SQB_PERF_GOODS
.
concat
(
performancesId
));
if
(
StringUtils
.
isEmpty
(
valStr
))
{
return
null
;
}
return
JsonUtils
.
fromJson
(
valStr
,
new
TypeReference
<
List
<
GoblinSqbPerfGoodsVo
>>()
{});
}
public
void
delPerfGoods
(
String
performancesId
)
{
redisUtil
.
del
(
KEY_SQB_PERF_GOODS
.
concat
(
performancesId
));
}
/* ---------------------------------------- 下单防重锁(TTL 10s) ---------------------------------------- */
/**
* 尝试获取下单防重锁
*
* @param userId 用户ID
* @param skuId SKU ID
* @return true 获取成功,false 已被锁定
*/
public
boolean
tryOrderLock
(
String
userId
,
String
skuId
)
{
String
key
=
KEY_SQB_ORDER_LOCK
.
concat
(
userId
).
concat
(
":"
).
concat
(
skuId
);
return
redisUtil
.
lock
(
key
,
1
,
TTL_ORDER_LOCK
);
}
/**
* 释放下单防重锁
*
* @param userId 用户ID
* @param skuId SKU ID
*/
public
void
releaseOrderLock
(
String
userId
,
String
skuId
)
{
String
key
=
KEY_SQB_ORDER_LOCK
.
concat
(
userId
).
concat
(
":"
).
concat
(
skuId
);
redisUtil
.
uLock
(
key
);
}
}
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