用户押金退还审核Java微服务后端架构实战

1. 架构概述

用户押金退还审核系统是电商平台和租赁平台的核心模块,需要支持用户申请押金退还、管理员审核、订单验证和押金退还等关键功能。本篇文章将深入讲解如何基于Java微服务架构实现一个高性能、高可用、资金安全的用户押金退还审核系统。

1.1 系统架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
用户端 → 用户网关 → 用户服务 → 数据库

身份认证

生成押金退还审核记录

返回退换审核中提示

管理端 → 管理网关 → 订单服务 → 用户服务

身份认证

有效订单查询请求

有效订单查询处理

审核通过,退还用户押金。入账用户账户。记录账户出入明细。

返回退换审核处理结果

1.2 核心组件

  • 用户网关(User Gateway):负责用户请求的接入、身份认证、请求路由
  • 管理网关(Management Gateway):负责管理用户请求的接入、身份认证、请求路由、流程编排
  • 用户服务(User Service):负责用户账户管理、押金退还审核记录、押金退还处理、账户明细记录
  • 订单服务(Order Service):负责订单管理、有效订单查询
  • 数据库(MySQL):持久化押金退还审核记录、账户信息、账户明细
  • 分布式锁(Redisson):保证账户操作的并发安全
  • 事务管理:保证退还流程的原子性和一致性

2. 用户网关服务实现

2.1 用户网关服务核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/**
* 用户网关服务
* 负责用户请求的接入、身份认证、请求路由
*/
@RestController
@RequestMapping("/api/user/gateway")
@Slf4j
public class UserGatewayController {

@Autowired
private AuthService authService;

@Autowired
private UserServiceClient userServiceClient;

/**
* 用户发起对押金退还请求
* 流程:身份认证 → 生成押金退还审核记录 → 返回退换审核中提示
*/
@PostMapping("/deposit/refund/request")
public Result<DepositRefundRequestResult> requestDepositRefund(
@RequestHeader("Authorization") String token,
@RequestBody DepositRefundRequest request) {

try {
// 1. 身份认证
UserInfo userInfo = authService.authenticate(token);
if (userInfo == null) {
return Result.error("身份认证失败");
}

// 2. 生成押金退还审核记录
DepositRefundReviewRequest reviewRequest = new DepositRefundReviewRequest();
reviewRequest.setUserId(userInfo.getUserId());
reviewRequest.setDepositId(request.getDepositId());
reviewRequest.setReason(request.getReason());
reviewRequest.setRemark(request.getRemark());

DepositRefundReviewResult reviewResult =
userServiceClient.createDepositRefundReview(reviewRequest);

if (!reviewResult.isSuccess()) {
return Result.error("申请押金退还失败: " + reviewResult.getMessage());
}

// 3. 返回退换审核中提示
DepositRefundRequestResult result = new DepositRefundRequestResult();
result.setReviewId(reviewResult.getReviewId());
result.setStatus(ReviewStatus.PENDING.getCode());
result.setMessage("押金退还申请已提交,正在审核中");
result.setCreateTime(new Date());

log.info("用户发起押金退还请求成功: userId={}, depositId={}, reviewId={}",
userInfo.getUserId(), request.getDepositId(), reviewResult.getReviewId());

return Result.success(result);

} catch (Exception e) {
log.error("用户发起押金退还请求失败: depositId={}, error={}",
request.getDepositId(), e.getMessage(), e);
return Result.error("申请押金退还失败: " + e.getMessage());
}
}

/**
* 查询押金退还审核状态
*/
@GetMapping("/deposit/refund/review/{reviewId}")
public Result<DepositRefundReviewInfo> getDepositRefundReviewStatus(
@RequestHeader("Authorization") String token,
@PathVariable Long reviewId) {

try {
// 1. 身份认证
UserInfo userInfo = authService.authenticate(token);
if (userInfo == null) {
return Result.error("身份认证失败");
}

// 2. 查询审核记录
DepositRefundReviewInfo reviewInfo =
userServiceClient.getDepositRefundReview(reviewId);

if (reviewInfo == null) {
return Result.error("审核记录不存在");
}

// 3. 检查权限
if (!userInfo.getUserId().equals(reviewInfo.getUserId())) {
return Result.error("无权限查看该审核记录");
}

return Result.success(reviewInfo);

} catch (Exception e) {
log.error("查询押金退还审核状态失败: reviewId={}, error={}",
reviewId, e.getMessage(), e);
return Result.error("查询失败: " + e.getMessage());
}
}
}

2.2 身份认证服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 身份认证服务
*/
@Service
@Slf4j
public class AuthService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private UserServiceClient userServiceClient;

/**
* 身份认证
* 验证Token有效性
*/
public UserInfo authenticate(String token) {
try {
// 1. 从Token中解析用户信息
String userId = parseToken(token);
if (StringUtils.isEmpty(userId)) {
return null;
}

// 2. 从缓存中获取用户信息
String userCacheKey = "user:info:" + userId;
UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(userCacheKey);

if (userInfo != null) {
return userInfo;
}

// 3. 缓存未命中,调用用户服务查询
userInfo = userServiceClient.getUserInfo(userId);

// 4. 将用户信息写入缓存
if (userInfo != null) {
redisTemplate.opsForValue().set(userCacheKey, userInfo, 30, TimeUnit.MINUTES);
}

return userInfo;

} catch (Exception e) {
log.error("身份认证失败: token={}, error={}", token, e.getMessage(), e);
return null;
}
}

/**
* 解析Token
*/
private String parseToken(String token) {
// JWT Token解析逻辑
if (StringUtils.isEmpty(token) || !token.startsWith("Bearer ")) {
return null;
}

String jwtToken = token.substring(7);
// 解析JWT获取userId
return extractUserIdFromJWT(jwtToken);
}

private String extractUserIdFromJWT(String jwtToken) {
// JWT解析实现
// 实际实现需要使用JWT库
return "user123";
}
}

3. 管理网关服务实现

3.1 管理网关服务核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/**
* 管理网关服务
* 负责管理用户请求的接入、身份认证、请求路由、流程编排
*/
@RestController
@RequestMapping("/api/management/gateway")
@Slf4j
public class ManagementGatewayController {

@Autowired
private AuthService authService;

@Autowired
private UserServiceClient userServiceClient;

@Autowired
private OrderServiceClient orderServiceClient;

/**
* 管理员审核请求
* 流程:身份认证 → 有效订单查询请求 → 有效订单查询处理 →
* 审核通过,退还用户押金。入账用户账户。记录账户出入明细。 → 返回退换审核处理结果
*/
@PostMapping("/deposit/refund/review")
public Result<DepositRefundReviewResult> reviewDepositRefund(
@RequestHeader("Authorization") String token,
@RequestBody DepositRefundReviewRequest request) {

try {
// 1. 身份认证
AdminInfo adminInfo = authService.authenticateAdmin(token);
if (adminInfo == null) {
return Result.error("身份认证失败");
}

// 2. 权限校验
if (!authService.hasPermission(adminInfo.getAdminId(), "DEPOSIT_REFUND_REVIEW")) {
return Result.error("无权限进行押金退还审核");
}

// 3. 查询审核记录
DepositRefundReviewInfo reviewInfo =
userServiceClient.getDepositRefundReview(request.getReviewId());
if (reviewInfo == null) {
return Result.error("审核记录不存在");
}

// 4. 检查审核状态
if (!ReviewStatus.PENDING.getCode().equals(reviewInfo.getStatus())) {
return Result.error("审核记录状态不允许审核,当前状态: " + reviewInfo.getStatus());
}

// 5. 有效订单查询请求
ValidOrderQueryRequest orderQueryRequest = new ValidOrderQueryRequest();
orderQueryRequest.setUserId(reviewInfo.getUserId());
orderQueryRequest.setProductId(reviewInfo.getProductId());

// 6. 有效订单查询处理
ValidOrderQueryResult orderQueryResult =
orderServiceClient.queryValidOrders(orderQueryRequest);

// 7. 审核通过,退还用户押金。入账用户账户。记录账户出入明细。
if (ReviewAction.APPROVE.getCode().equals(request.getAction())) {
// 审核通过
if (!orderQueryResult.hasValidOrders()) {
return Result.error("存在有效订单,不允许退还押金");
}

DepositRefundProcessRequest processRequest = new DepositRefundProcessRequest();
processRequest.setReviewId(request.getReviewId());
processRequest.setAdminId(adminInfo.getAdminId());
processRequest.setAdminName(adminInfo.getAdminName());
processRequest.setReviewComment(request.getReviewComment());

DepositRefundProcessResult processResult =
userServiceClient.processDepositRefund(processRequest);

if (!processResult.isSuccess()) {
return Result.error("押金退还处理失败: " + processResult.getMessage());
}

// 8. 返回退换审核处理结果
DepositRefundReviewResult result = new DepositRefundReviewResult();
result.setReviewId(request.getReviewId());
result.setStatus(ReviewStatus.APPROVED.getCode());
result.setMessage("审核通过,押金已退还");
result.setTransactionNo(processResult.getTransactionNo());
result.setReviewTime(new Date());

return Result.success(result);

} else if (ReviewAction.REJECT.getCode().equals(request.getAction())) {
// 审核拒绝
DepositRefundReviewRejectRequest rejectRequest =
new DepositRefundReviewRejectRequest();
rejectRequest.setReviewId(request.getReviewId());
rejectRequest.setAdminId(adminInfo.getAdminId());
rejectRequest.setAdminName(adminInfo.getAdminName());
rejectRequest.setRejectReason(request.getRejectReason());

DepositRefundReviewRejectResult rejectResult =
userServiceClient.rejectDepositRefundReview(rejectRequest);

if (!rejectResult.isSuccess()) {
return Result.error("审核拒绝失败: " + rejectResult.getMessage());
}

// 返回审核拒绝结果
DepositRefundReviewResult result = new DepositRefundReviewResult();
result.setReviewId(request.getReviewId());
result.setStatus(ReviewStatus.REJECTED.getCode());
result.setMessage("审核已拒绝");
result.setReviewTime(new Date());

return Result.success(result);
} else {
return Result.error("无效的审核操作");
}

} catch (Exception e) {
log.error("管理员审核押金退还失败: reviewId={}, error={}",
request.getReviewId(), e.getMessage(), e);
return Result.error("审核失败: " + e.getMessage());
}
}

/**
* 查询押金退还审核列表
*/
@PostMapping("/deposit/refund/review/list")
public Result<PageResult<DepositRefundReviewInfo>> getDepositRefundReviewList(
@RequestHeader("Authorization") String token,
@RequestBody DepositRefundReviewQueryRequest queryRequest) {

try {
// 1. 身份认证
AdminInfo adminInfo = authService.authenticateAdmin(token);
if (adminInfo == null) {
return Result.error("身份认证失败");
}

// 2. 查询审核列表
PageResult<DepositRefundReviewInfo> reviewList =
userServiceClient.getDepositRefundReviewList(queryRequest);

return Result.success(reviewList);

} catch (Exception e) {
log.error("查询押金退还审核列表失败: error={}", e.getMessage(), e);
return Result.error("查询失败: " + e.getMessage());
}
}
}

4. 用户服务实现

4.1 用户服务核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
/**
* 用户服务
* 负责用户账户管理、押金退还审核记录、押金退还处理、账户明细记录
*/
@Service
@Slf4j
public class UserDepositRefundService {

@Autowired
private UserAccountMapper userAccountMapper;

@Autowired
private UserDepositMapper userDepositMapper;

@Autowired
private DepositRefundReviewMapper depositRefundReviewMapper;

@Autowired
private AccountDetailMapper accountDetailMapper;

@Autowired
private RedissonClient redissonClient;

/**
* 生成押金退还审核记录
*/
@Transactional(rollbackFor = Exception.class)
public DepositRefundReviewResult createDepositRefundReview(
DepositRefundReviewRequest request) {

try {
// 1. 查询用户押金
UserDeposit deposit = userDepositMapper.selectById(request.getDepositId());
if (deposit == null) {
return DepositRefundReviewResult.failed("押金记录不存在");
}

// 2. 检查押金状态
if (!DepositStatus.PAID.getCode().equals(deposit.getStatus())) {
return DepositRefundReviewResult.failed("押金状态不允许申请退还,当前状态: " + deposit.getStatus());
}

// 3. 检查是否已有审核记录
DepositRefundReview existingReview =
depositRefundReviewMapper.selectByDepositId(request.getDepositId());
if (existingReview != null &&
ReviewStatus.PENDING.getCode().equals(existingReview.getStatus())) {
return DepositRefundReviewResult.failed("该押金已有待审核的退还申请");
}

// 4. 创建审核记录
DepositRefundReview review = new DepositRefundReview();
review.setUserId(request.getUserId());
review.setDepositId(request.getDepositId());
review.setProductId(deposit.getProductId());
review.setProductName(deposit.getProductName());
review.setAmount(deposit.getAmount());
review.setReason(request.getReason());
review.setRemark(request.getRemark());
review.setStatus(ReviewStatus.PENDING.getCode());
review.setCreateTime(new Date());
review.setUpdateTime(new Date());

depositRefundReviewMapper.insert(review);

log.info("生成押金退还审核记录成功: reviewId={}, userId={}, depositId={}",
review.getId(), request.getUserId(), request.getDepositId());

// 5. 构建返回结果
DepositRefundReviewResult result = new DepositRefundReviewResult();
result.setSuccess(true);
result.setReviewId(review.getId());
result.setMessage("押金退还申请已提交,等待审核");

return result;

} catch (Exception e) {
log.error("生成押金退还审核记录失败: userId={}, depositId={}, error={}",
request.getUserId(), request.getDepositId(), e.getMessage(), e);
return DepositRefundReviewResult.failed("申请押金退还失败: " + e.getMessage());
}
}

/**
* 审核通过,退还用户押金。入账用户账户。记录账户出入明细。
*/
@Transactional(rollbackFor = Exception.class)
public DepositRefundProcessResult processDepositRefund(
DepositRefundProcessRequest request) {

String lockKey = "deposit:refund:lock:" + request.getReviewId();
RLock lock = redissonClient.getLock(lockKey);

try {
if (lock.tryLock(10, TimeUnit.SECONDS)) {
// 1. 查询审核记录
DepositRefundReview review =
depositRefundReviewMapper.selectById(request.getReviewId());
if (review == null) {
return DepositRefundProcessResult.failed("审核记录不存在");
}

// 2. 检查审核状态
if (!ReviewStatus.PENDING.getCode().equals(review.getStatus())) {
return DepositRefundProcessResult.failed("审核记录状态不允许处理,当前状态: " + review.getStatus());
}

// 3. 查询用户押金
UserDeposit deposit = userDepositMapper.selectById(review.getDepositId());
if (deposit == null) {
return DepositRefundProcessResult.failed("押金记录不存在");
}

// 4. 查询用户账户
UserAccount account = userAccountMapper.selectByUserId(review.getUserId());
if (account == null) {
return DepositRefundProcessResult.failed("用户账户不存在");
}

// 5. 退还用户押金。入账用户账户。
BigDecimal balanceBefore = account.getBalance();
account.setBalance(account.getBalance().add(review.getAmount()));
account.setAvailableAmount(account.getAvailableAmount().add(review.getAmount()));
account.setFrozenAmount(account.getFrozenAmount().subtract(review.getAmount()));
account.setTotalIncome(account.getTotalIncome().add(review.getAmount()));
account.setUpdateTime(new Date());
userAccountMapper.updateById(account);

// 6. 更新押金状态
deposit.setStatus(DepositStatus.REFUNDED.getCode());
deposit.setRefundTime(new Date());
deposit.setUpdateTime(new Date());
userDepositMapper.updateById(deposit);

// 7. 更新审核记录
review.setStatus(ReviewStatus.APPROVED.getCode());
review.setAdminId(request.getAdminId());
review.setAdminName(request.getAdminName());
review.setReviewComment(request.getReviewComment());
review.setReviewTime(new Date());
review.setUpdateTime(new Date());
depositRefundReviewMapper.updateById(review);

// 8. 记录账户出入明细
String transactionNo = generateTransactionNo();
recordAccountDetail(account, review.getAmount(),
AccountDetailType.DEPOSIT_REFUND, balanceBefore, account.getBalance(),
review.getProductId(), review.getProductName(), transactionNo,
deposit.getId(), review.getId());

log.info("押金退还处理成功: reviewId={}, depositId={}, userId={}, amount={}, transactionNo={}",
request.getReviewId(), deposit.getId(), review.getUserId(),
review.getAmount(), transactionNo);

// 9. 构建返回结果
DepositRefundProcessResult result = new DepositRefundProcessResult();
result.setSuccess(true);
result.setReviewId(request.getReviewId());
result.setTransactionNo(transactionNo);
result.setAmount(review.getAmount());
result.setBalanceAfter(account.getBalance());
result.setMessage("押金退还成功");

return result;

} else {
return DepositRefundProcessResult.failed("押金退还处理超时,请稍后再试");
}
} catch (Exception e) {
log.error("押金退还处理失败: reviewId={}, error={}",
request.getReviewId(), e.getMessage(), e);
return DepositRefundProcessResult.failed("押金退还处理失败: " + e.getMessage());
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}

/**
* 审核拒绝
*/
@Transactional(rollbackFor = Exception.class)
public DepositRefundReviewRejectResult rejectDepositRefundReview(
DepositRefundReviewRejectRequest request) {

try {
// 1. 查询审核记录
DepositRefundReview review =
depositRefundReviewMapper.selectById(request.getReviewId());
if (review == null) {
return DepositRefundReviewRejectResult.failed("审核记录不存在");
}

// 2. 检查审核状态
if (!ReviewStatus.PENDING.getCode().equals(review.getStatus())) {
return DepositRefundReviewRejectResult.failed("审核记录状态不允许拒绝,当前状态: " + review.getStatus());
}

// 3. 更新审核记录
review.setStatus(ReviewStatus.REJECTED.getCode());
review.setAdminId(request.getAdminId());
review.setAdminName(request.getAdminName());
review.setRejectReason(request.getRejectReason());
review.setReviewTime(new Date());
review.setUpdateTime(new Date());
depositRefundReviewMapper.updateById(review);

log.info("押金退还审核拒绝成功: reviewId={}, rejectReason={}",
request.getReviewId(), request.getRejectReason());

// 4. 构建返回结果
DepositRefundReviewRejectResult result = new DepositRefundReviewRejectResult();
result.setSuccess(true);
result.setReviewId(request.getReviewId());
result.setMessage("审核已拒绝");

return result;

} catch (Exception e) {
log.error("押金退还审核拒绝失败: reviewId={}, error={}",
request.getReviewId(), e.getMessage(), e);
return DepositRefundReviewRejectResult.failed("审核拒绝失败: " + e.getMessage());
}
}

/**
* 查询押金退还审核记录
*/
public DepositRefundReviewInfo getDepositRefundReview(Long reviewId) {
DepositRefundReview review = depositRefundReviewMapper.selectById(reviewId);
if (review == null) {
return null;
}

return convertToReviewInfo(review);
}

/**
* 查询押金退还审核列表
*/
public PageResult<DepositRefundReviewInfo> getDepositRefundReviewList(
DepositRefundReviewQueryRequest queryRequest) {

// 构建查询条件
QueryWrapper<DepositRefundReview> queryWrapper = new QueryWrapper<>();
if (queryRequest.getUserId() != null) {
queryWrapper.eq("user_id", queryRequest.getUserId());
}
if (StringUtils.isNotEmpty(queryRequest.getStatus())) {
queryWrapper.eq("status", queryRequest.getStatus());
}
if (queryRequest.getStartTime() != null) {
queryWrapper.ge("create_time", queryRequest.getStartTime());
}
if (queryRequest.getEndTime() != null) {
queryWrapper.le("create_time", queryRequest.getEndTime());
}
queryWrapper.orderByDesc("create_time");

// 分页查询
Page<DepositRefundReview> page = new Page<>(
queryRequest.getPageNum(), queryRequest.getPageSize());
Page<DepositRefundReview> pageResult =
depositRefundReviewMapper.selectPage(page, queryWrapper);

// 转换为返回对象
List<DepositRefundReviewInfo> reviewInfoList = pageResult.getRecords().stream()
.map(this::convertToReviewInfo)
.collect(Collectors.toList());

PageResult<DepositRefundReviewInfo> result = new PageResult<>();
result.setList(reviewInfoList);
result.setTotal(pageResult.getTotal());
result.setPageNum(queryRequest.getPageNum());
result.setPageSize(queryRequest.getPageSize());

return result;
}

/**
* 记录账户出入明细
*/
private void recordAccountDetail(UserAccount account, BigDecimal amount,
AccountDetailType type,
BigDecimal balanceBefore,
BigDecimal balanceAfter,
Long productId,
String productName,
String transactionNo,
Long depositId,
Long reviewId) {
AccountDetail detail = new AccountDetail();
detail.setAccountId(account.getId());
detail.setUserId(account.getUserId());
detail.setAccountNo(account.getAccountNo());
detail.setTransactionNo(transactionNo);
detail.setProductId(productId);
detail.setProductName(productName);
detail.setDepositId(depositId);
detail.setReviewId(reviewId);
detail.setType(type.getCode());
detail.setTypeName(type.getName());
detail.setAmount(amount);
detail.setBalanceBefore(balanceBefore);
detail.setBalanceAfter(balanceAfter);
detail.setDirection(type.getDirection());
detail.setStatus(AccountDetailStatus.SUCCESS);
detail.setRemark(type.getRemark() + " - " + productName);
detail.setCreateTime(new Date());
detail.setUpdateTime(new Date());

accountDetailMapper.insert(detail);
}

/**
* 转换审核记录为审核信息
*/
private DepositRefundReviewInfo convertToReviewInfo(DepositRefundReview review) {
DepositRefundReviewInfo info = new DepositRefundReviewInfo();
info.setReviewId(review.getId());
info.setUserId(review.getUserId());
info.setDepositId(review.getDepositId());
info.setProductId(review.getProductId());
info.setProductName(review.getProductName());
info.setAmount(review.getAmount());
info.setReason(review.getReason());
info.setRemark(review.getRemark());
info.setStatus(review.getStatus());
info.setAdminId(review.getAdminId());
info.setAdminName(review.getAdminName());
info.setReviewComment(review.getReviewComment());
info.setRejectReason(review.getRejectReason());
info.setCreateTime(review.getCreateTime());
info.setReviewTime(review.getReviewTime());
info.setUpdateTime(review.getUpdateTime());
return info;
}

/**
* 生成交易号
*/
private String generateTransactionNo() {
return "TXN" + System.currentTimeMillis() + RandomUtils.nextInt(10000, 99999);
}
}

5. 订单服务实现

5.1 订单服务核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* 订单服务
* 负责订单管理、有效订单查询
*/
@Service
@Slf4j
public class OrderService {

@Autowired
private OrderMapper orderMapper;

/**
* 有效订单查询处理
* 查询用户是否有有效订单(未完成、未取消的订单)
*/
public ValidOrderQueryResult queryValidOrders(ValidOrderQueryRequest request) {
try {
// 1. 查询有效订单
List<Order> validOrders = orderMapper.selectValidOrdersByUserIdAndProductId(
request.getUserId(), request.getProductId());

// 2. 构建返回结果
ValidOrderQueryResult result = new ValidOrderQueryResult();
result.setHasValidOrders(!validOrders.isEmpty());
result.setValidOrderCount(validOrders.size());
result.setValidOrders(validOrders.stream()
.map(this::convertToOrderInfo)
.collect(Collectors.toList()));

log.info("有效订单查询完成: userId={}, productId={}, validOrderCount={}",
request.getUserId(), request.getProductId(), validOrders.size());

return result;

} catch (Exception e) {
log.error("有效订单查询失败: userId={}, productId={}, error={}",
request.getUserId(), request.getProductId(), e.getMessage(), e);
return ValidOrderQueryResult.failed("有效订单查询失败: " + e.getMessage());
}
}

/**
* 转换订单为订单信息
*/
private OrderInfo convertToOrderInfo(Order order) {
OrderInfo info = new OrderInfo();
info.setOrderId(order.getId());
info.setOrderNo(order.getOrderNo());
info.setUserId(order.getUserId());
info.setProductId(order.getProductId());
info.setAmount(order.getAmount());
info.setStatus(order.getStatus());
info.setCreateTime(order.getCreateTime());
info.setUpdateTime(order.getUpdateTime());
return info;
}
}

6. 数据模型定义

6.1 押金退还审核记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/**
* 押金退还审核记录
*/
@Data
@TableName("deposit_refund_review")
public class DepositRefundReview {
/**
* 主键ID
*/
@TableId(type = IdType.AUTO)
private Long id;

/**
* 用户ID
*/
private Long userId;

/**
* 押金ID
*/
private Long depositId;

/**
* 商品ID
*/
private Long productId;

/**
* 商品名称
*/
private String productName;

/**
* 押金金额
*/
private BigDecimal amount;

/**
* 退还原因
*/
private String reason;

/**
* 备注
*/
private String remark;

/**
* 审核状态:PENDING-待审核, APPROVED-已通过, REJECTED-已拒绝
*/
private String status;

/**
* 审核管理员ID
*/
private Long adminId;

/**
* 审核管理员名称
*/
private String adminName;

/**
* 审核意见
*/
private String reviewComment;

/**
* 拒绝原因
*/
private String rejectReason;

/**
* 创建时间
*/
private Date createTime;

/**
* 审核时间
*/
private Date reviewTime;

/**
* 更新时间
*/
private Date updateTime;
}

6.2 审核状态枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 审核状态枚举
*/
public enum ReviewStatus {
PENDING("PENDING", "待审核"),
APPROVED("APPROVED", "已通过"),
REJECTED("REJECTED", "已拒绝");

private final String code;
private final String name;

ReviewStatus(String code, String name) {
this.code = code;
this.name = name;
}

public String getCode() {
return code;
}

public String getName() {
return name;
}
}

6.3 审核操作枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 审核操作枚举
*/
public enum ReviewAction {
APPROVE("APPROVE", "通过"),
REJECT("REJECT", "拒绝");

private final String code;
private final String name;

ReviewAction(String code, String name) {
this.code = code;
this.name = name;
}

public String getCode() {
return code;
}

public String getName() {
return name;
}
}

7. 数据库Mapper实现

7.1 DepositRefundReviewMapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 押金退还审核Mapper
*/
@Mapper
public interface DepositRefundReviewMapper extends BaseMapper<DepositRefundReview> {

/**
* 根据押金ID查询审核记录
*/
@Select("SELECT * FROM deposit_refund_review " +
"WHERE deposit_id = #{depositId} AND deleted = 0 " +
"ORDER BY create_time DESC LIMIT 1")
DepositRefundReview selectByDepositId(@Param("depositId") Long depositId);
}

7.2 OrderMapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 订单Mapper
*/
@Mapper
public interface OrderMapper extends BaseMapper<Order> {

/**
* 查询有效订单(未完成、未取消的订单)
*/
@Select("SELECT * FROM `order` " +
"WHERE user_id = #{userId} " +
"AND product_id = #{productId} " +
"AND status NOT IN ('COMPLETED', 'CANCELLED', 'REFUNDED') " +
"AND deleted = 0 " +
"ORDER BY create_time DESC")
List<Order> selectValidOrdersByUserIdAndProductId(
@Param("userId") Long userId,
@Param("productId") Long productId);
}

8. 数据库表设计

8.1 押金退还审核表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- 押金退还审核表
CREATE TABLE `deposit_refund_review` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`deposit_id` bigint(20) NOT NULL COMMENT '押金ID',
`product_id` bigint(20) NOT NULL COMMENT '商品ID',
`product_name` varchar(200) NOT NULL COMMENT '商品名称',
`amount` decimal(15,2) NOT NULL COMMENT '押金金额',
`reason` varchar(500) DEFAULT NULL COMMENT '退还原因',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`status` varchar(20) NOT NULL DEFAULT 'PENDING' COMMENT '审核状态:PENDING-待审核, APPROVED-已通过, REJECTED-已拒绝',
`admin_id` bigint(20) DEFAULT NULL COMMENT '审核管理员ID',
`admin_name` varchar(100) DEFAULT NULL COMMENT '审核管理员名称',
`review_comment` varchar(500) DEFAULT NULL COMMENT '审核意见',
`reject_reason` varchar(500) DEFAULT NULL COMMENT '拒绝原因',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`review_time` datetime DEFAULT NULL COMMENT '审核时间',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否删除',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_deposit_id` (`deposit_id`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='押金退还审核表';

9. 总结

本文详细介绍了用户押金退还审核的Java微服务架构实现,包括:

  1. 用户网关服务:负责用户请求接入、身份认证、请求路由
  2. 管理网关服务:负责管理用户请求接入、身份认证、请求路由、流程编排
  3. 用户服务:负责押金退还审核记录、押金退还处理、账户明细记录
  4. 订单服务:负责有效订单查询
  5. 用户发起退还请求流程
    • 用户发起对押金退还请求
    • 身份认证
    • 生成押金退还审核记录
    • 返回退换审核中提示
  6. 管理员审核流程
    • 管理员审核请求
    • 身份认证
    • 有效订单查询请求
    • 有效订单查询处理
    • 审核通过,退还用户押金。入账用户账户。记录账户出入明细。
    • 返回退换审核处理结果
  7. 审核管理:支持审核通过、审核拒绝
  8. 订单验证:审核前验证是否有有效订单

该架构具有以下优势:

  • 资金安全:分布式锁 + 事务管理,保证资金安全
  • 审核流程:完整的审核流程,支持审核通过和拒绝
  • 订单验证:审核前验证有效订单,防止误退押金
  • 高可用:数据库持久化,支持数据恢复
  • 可扩展:微服务架构,支持水平扩展
  • 完整性:完整的审核记录和账户明细记录

通过本文的实战代码,可以快速搭建一个高性能、高可用、资金安全的用户押金退还审核系统。