管理员绑电解电Java微服务后端架构实战

1. 架构概述

管理员绑电解电系统是hd平台的管理核心模块,需要支持管理员对订单和电池资产进行绑定操作,确保订单与电池资产的正确关联。本篇文章将深入讲解如何基于Java微服务架构实现一个高性能、高可用、安全可靠的管理员绑电解电系统。

1.1 系统架构图

1
2
3
4
5
6
7
8
9
10
11
12
13
管理端 → 管理网关 → 订单服务 → 设备服务

身份认证

校验订单信息

校验电池资产信息

更新订单信息

更改电池资产状态, 记录换电操作记录

返回换电操作已处理提示

1.2 核心组件

  • 管理网关(Admin Gateway):负责管理员请求的接入、身份认证、请求路由、流程编排
  • 订单服务(Order Service):负责订单管理、订单信息校验、订单信息更新
  • 设备服务(Device Service):负责电池资产管理、电池资产信息校验、电池资产状态更新、换电操作记录
  • 数据库(MySQL):持久化订单信息、电池资产信息、换电操作记录
  • 分布式锁(Redisson):保证绑电解电操作的并发安全
  • 分布式事务(Seata):保证订单更新和电池资产更新的原子性和一致性
  • 消息队列(Kafka/RocketMQ):异步处理换电操作记录和状态同步

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
/**
* 管理网关服务
* 负责管理员请求的接入、身份认证、请求路由、流程编排
*/
@RestController
@RequestMapping("/api/admin/gateway")
@Slf4j
public class AdminGatewayController {

@Autowired
private AdminAuthService adminAuthService;

@Autowired
private OrderServiceClient orderServiceClient;

/**
* 绑电解电请求
* 流程:身份认证 → 校验订单信息 → 校验电池资产信息 → 更新订单信息 → 更改电池资产状态, 记录换电操作记录 → 返回换电操作已处理提示
*/
@PostMapping("/battery/bind")
public Result<BatteryBindResult> bindBatteryToOrder(
@RequestHeader("Authorization") String token,
@RequestBody @Valid BatteryBindRequest request) {

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

// 2. 调用订单服务处理绑电解电
BatteryBindResult result = orderServiceClient.bindBatteryToOrder(
request.getOrderId(),
request.getBatteryId(),
adminInfo.getAdminId());

log.info("管理员绑电解电成功: adminId={}, orderId={}, batteryId={}, operationId={}",
adminInfo.getAdminId(), request.getOrderId(),
request.getBatteryId(), result.getOperationId());

return Result.success(result);

} catch (Exception e) {
log.error("管理员绑电解电失败: orderId={}, batteryId={}, error={}",
request.getOrderId(), request.getBatteryId(), 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 AdminAuthService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private AdminServiceClient adminServiceClient;

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

// 2. 从缓存中获取管理员信息
String adminCacheKey = "admin:info:" + adminId;
AdminInfo adminInfo = (AdminInfo) redisTemplate.opsForValue().get(adminCacheKey);

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

// 3. 缓存未命中,调用管理员服务查询
adminInfo = adminServiceClient.getAdminInfo(adminId);

// 4. 将管理员信息写入缓存
if (adminInfo != null) {
redisTemplate.opsForValue().set(adminCacheKey, adminInfo, 30, TimeUnit.MINUTES);
}

return adminInfo;

} 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获取adminId
return extractAdminIdFromJWT(jwtToken);
}

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

2.3 请求对象定义

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
/**
* 绑电解电请求对象
*/
@Data
public class BatteryBindRequest {

/**
* 订单ID
*/
@NotNull(message = "订单ID不能为空")
private Long orderId;

/**
* 电池资产ID
*/
@NotNull(message = "电池资产ID不能为空")
private Long batteryId;

/**
* 绑定备注
*/
private String remark;
}

/**
* 绑电解电结果对象
*/
@Data
public class BatteryBindResult {

/**
* 操作ID
*/
private Long operationId;

/**
* 订单ID
*/
private Long orderId;

/**
* 电池资产ID
*/
private Long batteryId;

/**
* 绑定状态
*/
private String bindStatus;

/**
* 绑定时间
*/
private LocalDateTime bindTime;

/**
* 操作结果消息
*/
private String message;
}

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
/**
* 订单服务
* 负责订单管理、订单信息校验、订单信息更新
*/
@Service
@Slf4j
public class OrderService {

@Autowired
private OrderMapper orderMapper;

@Autowired
private DeviceServiceClient deviceServiceClient;

@Autowired
private RedissonClient redissonClient;

@Autowired
private SeataTransactionManager seataTransactionManager;

/**
* 绑电解电处理
* 流程:校验订单信息 → 校验电池资产信息 → 更新订单信息 → 更改电池资产状态, 记录换电操作记录
*/
@GlobalTransactional(rollbackFor = Exception.class)
public BatteryBindResult bindBatteryToOrder(Long orderId, Long batteryId, Long adminId) {

// 获取分布式锁,防止并发操作
String lockKey = "order:bind:battery:" + orderId + ":" + batteryId;
RLock lock = redissonClient.getLock(lockKey);

try {
// 尝试获取锁,最多等待3秒,锁定后30秒自动释放
boolean lockAcquired = lock.tryLock(3, 30, TimeUnit.SECONDS);
if (!lockAcquired) {
throw new BusinessException("系统繁忙,请稍后重试");
}

// 1. 校验订单信息
Order order = validateOrder(orderId);

// 2. 校验电池资产信息
BatteryAsset batteryAsset = deviceServiceClient.validateBatteryAsset(batteryId);

// 3. 更新订单信息
updateOrderWithBattery(orderId, batteryId, adminId);

// 4. 更改电池资产状态, 记录换电操作记录
Long operationId = deviceServiceClient.updateBatteryAssetAndRecordOperation(
batteryId, orderId, adminId);

// 5. 构建返回结果
BatteryBindResult result = new BatteryBindResult();
result.setOperationId(operationId);
result.setOrderId(orderId);
result.setBatteryId(batteryId);
result.setBindStatus("BOUND");
result.setBindTime(LocalDateTime.now());
result.setMessage("绑电解电成功");

log.info("绑电解电成功: orderId={}, batteryId={}, adminId={}, operationId={}",
orderId, batteryId, adminId, operationId);

return result;

} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("获取分布式锁被中断: orderId={}, batteryId={}", orderId, batteryId, e);
throw new BusinessException("系统繁忙,请稍后重试");
} catch (Exception e) {
log.error("绑电解电失败: orderId={}, batteryId={}, adminId={}, error={}",
orderId, batteryId, adminId, e.getMessage(), e);
throw new BusinessException("绑电解电失败: " + e.getMessage());
} finally {
// 释放锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}

/**
* 校验订单信息
*/
private Order validateOrder(Long orderId) {
// 1. 查询订单信息
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new BusinessException("订单不存在");
}

// 2. 校验订单状态
if (!"ACTIVE".equals(order.getStatus()) && !"PAID".equals(order.getStatus())) {
throw new BusinessException("订单状态不允许绑定电池");
}

// 3. 校验订单是否已绑定电池
if (order.getBatteryId() != null) {
throw new BusinessException("订单已绑定电池,无法重复绑定");
}

return order;
}

/**
* 更新订单信息
*/
private void updateOrderWithBattery(Long orderId, Long batteryId, Long adminId) {
// 1. 更新订单电池ID
Order order = new Order();
order.setId(orderId);
order.setBatteryId(batteryId);
order.setBatteryBindTime(LocalDateTime.now());
order.setBatteryBindAdminId(adminId);
order.setUpdateTime(LocalDateTime.now());

// 2. 执行更新
int updateCount = orderMapper.updateById(order);
if (updateCount <= 0) {
throw new BusinessException("更新订单信息失败");
}

log.info("订单信息更新成功: orderId={}, batteryId={}, adminId={}",
orderId, batteryId, adminId);
}
}

3.2 订单服务Feign客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 订单服务Feign客户端
*/
@FeignClient(name = "order-service", path = "/api/order")
public interface OrderServiceClient {

/**
* 绑电解电
*/
@PostMapping("/battery/bind")
BatteryBindResult bindBatteryToOrder(
@RequestParam("orderId") Long orderId,
@RequestParam("batteryId") Long batteryId,
@RequestParam("adminId") Long adminId);
}

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
/**
* 设备服务
* 负责电池资产管理、电池资产信息校验、电池资产状态更新、换电操作记录
*/
@Service
@Slf4j
public class DeviceService {

// 使用@Autowired注解自动注入BatteryAssetMapper接口的实例
// 这是Spring框架的依赖注入功能,会自动将BatteryAssetMapper的实现类注入到batteryAssetMapper变量中
@Autowired
private BatteryAssetMapper batteryAssetMapper;//电池资产信息Mapper

@Autowired
private BatteryOperationMapper batteryOperationMapper;//电池操作记录Mapper

@Autowired
private KafkaTemplate<String, String> kafkaTemplate;//Kafka消息模板

/**
* 校验电池资产信息
*/
public BatteryAsset validateBatteryAsset(Long batteryId) {
// 1. 查询电池资产信息
BatteryAsset batteryAsset = batteryAssetMapper.selectById(batteryId);
if (batteryAsset == null) {
throw new BusinessException("电池资产不存在");
}

// 2. 校验电池资产状态 AVAILABLE IN_STOCK
if (!"AVAILABLE".equals(batteryAsset.getStatus()) &&
!"IN_STOCK".equals(batteryAsset.getStatus())) {
throw new BusinessException("电池资产状态不允许绑定订单");
}

// 3. 校验电池资产是否已绑定订单
if (batteryAsset.getOrderId() != null) {
throw new BusinessException("电池资产已绑定订单,无法重复绑定");
}

return batteryAsset;
}

/**
* 更新电池资产状态并记录换电操作记录
*/
@Transactional(rollbackFor = Exception.class)
public Long updateBatteryAssetAndRecordOperation(Long batteryId, Long orderId, Long adminId) {

// 1. 更新电池资产状态
BatteryAsset batteryAsset = new BatteryAsset();
batteryAsset.setId(batteryId);
batteryAsset.setOrderId(orderId);
batteryAsset.setStatus("BOUND");
batteryAsset.setBindTime(LocalDateTime.now());
batteryAsset.setUpdateTime(LocalDateTime.now());

// 更新电池资产信息,并获取更新记录数
int updateCount = batteryAssetMapper.updateById(batteryAsset);
// 判断更新是否成功,如果更新记录数小于等于0,说明更新失败
if (updateCount <= 0) {
// 抛出业务异常,提示更新电池资产状态失败
throw new BusinessException("更新电池资产状态失败");
}

// 2. 记录换电操作记录
BatteryOperation operation = new BatteryOperation();
operation.setOperationType("ADMIN_BIND");
operation.setBatteryId(batteryId);
operation.setOrderId(orderId);
operation.setAdminId(adminId);
operation.setStatus("SUCCESS");
operation.setOperationTime(LocalDateTime.now());
operation.setCreateTime(LocalDateTime.now());
operation.setUpdateTime(LocalDateTime.now());

batteryOperationMapper.insert(operation);

// 3. 异步发送消息到消息队列
sendBatteryBindMessage(operation);

log.info("电池资产状态更新和换电操作记录成功: batteryId={}, orderId={}, adminId={}, operationId={}",
batteryId, orderId, adminId, operation.getId());

return operation.getId();
}

/**
* 发送电池绑定消息到消息队列
*/
private void sendBatteryBindMessage(BatteryOperation operation) {
try {
/**
* 创建电池绑定消息对象
* 并设置消息的各项属性
* 包括操作ID、电池ID、订单ID、管理员ID和操作时间
*/
BatteryBindMessage message = new BatteryBindMessage();
message.setOperationId(operation.getId()); // 设置操作ID
message.setBatteryId(operation.getBatteryId()); // 设置电池ID
message.setOrderId(operation.getOrderId()); // 设置订单ID
message.setAdminId(operation.getAdminId()); // 设置管理员ID
message.setOperationTime(operation.getOperationTime()); // 设置操作时间

/**
* 将消息对象转换为JSON字符串
* 并通过Kafka发送到"battery-bind-topic"主题
*/
String messageJson = JSON.toJSONString(message);
kafkaTemplate.send("battery-bind-topic", messageJson);

/**
* 记录电池绑定消息发送成功的日志
* 输出操作ID以便追踪
*/
log.info("电池绑定消息发送成功: operationId={}", operation.getId());

} catch (Exception e) {
log.error("电池绑定消息发送失败: operationId={}, error={}",
operation.getId(), e.getMessage(), e);
// 消息发送失败不影响主流程,只记录日志
}
}
}

4.2 设备服务Feign客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 设备服务Feign客户端
*/
@FeignClient(name = "device-service", path = "/api/device")
public interface DeviceServiceClient {

/**
* 校验电池资产信息
*/
@GetMapping("/battery/validate/{batteryId}")
BatteryAsset validateBatteryAsset(@PathVariable("batteryId") Long batteryId);

/**
* 更新电池资产状态并记录换电操作记录
*/
@PostMapping("/battery/bind")
Long updateBatteryAssetAndRecordOperation(
@RequestParam("batteryId") Long batteryId,
@RequestParam("orderId") Long orderId,
@RequestParam("adminId") Long adminId);
}

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
/**
* 订单实体
*/
@Data
@TableName("t_order")
public class Order {

/**
* 订单ID
*/
@TableId(type = IdType.AUTO)
private Long id;

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

/**
* 订单编号
*/
private String orderNo;

/**
* 订单状态:UNPAID-未支付, PAID-已支付, ACTIVE-生效中, EXPIRED-已过期, CANCELLED-已取消
*/
private String status;

/**
* 电池ID
*/
private Long batteryId;

/**
* 电池绑定时间
*/
private LocalDateTime batteryBindTime;

/**
* 电池绑定管理员ID
*/
private Long batteryBindAdminId;

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

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

5.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
/**
* 电池资产实体
*/
@Data
@TableName("t_battery_asset")
public class BatteryAsset {

/**
* 电池资产ID
*/
@TableId(type = IdType.AUTO)
private Long id;

/**
* 电池编号
*/
private String batteryNo;

/**
* 电池状态:AVAILABLE-可用, IN_STOCK-库存中, BOUND-已绑定, IN_USE-使用中, MAINTENANCE-维护中
*/
private String status;

/**
* 订单ID
*/
private Long orderId;

/**
* 绑定时间
*/
private LocalDateTime bindTime;

/**
* 换电柜ID
*/
private Long cabinetId;

/**
* 槽位编号
*/
private Integer slotNo;

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

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

5.3 换电操作记录实体

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
/**
* 换电操作记录实体
*/
@Data
@TableName("t_battery_operation")
public class BatteryOperation {

/**
* 操作ID
*/
@TableId(type = IdType.AUTO)
private Long id;

/**
* 操作类型:TAKE-取电, SWAP-换电, RETURN-退电, ADMIN_BIND-管理员绑定
*/
private String operationType;

/**
* 电池ID
*/
private Long batteryId;

/**
* 订单ID
*/
private Long orderId;

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

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

/**
* 换电柜ID
*/
private Long cabinetId;

/**
* 槽位编号
*/
private Integer slotNo;

/**
* 操作状态:SUCCESS-成功, FAILED-失败, PROCESSING-处理中
*/
private String status;

/**
* 操作时间
*/
private LocalDateTime operationTime;

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

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

5.4 管理员信息实体

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
/**
* 管理员信息实体
*/
@Data
public class AdminInfo {

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

/**
* 管理员账号
*/
private String adminAccount;

/**
* 管理员姓名
*/
private String adminName;

/**
* 管理员角色
*/
private String role;

/**
* 权限列表
*/
private List<String> permissions;
}

6. 数据库设计

6.1 订单表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE `t_order` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '订单ID',
`user_id` BIGINT(20) NOT NULL COMMENT '用户ID',
`order_no` VARCHAR(64) NOT NULL COMMENT '订单编号',
`status` VARCHAR(32) NOT NULL COMMENT '订单状态:UNPAID-未支付, PAID-已支付, ACTIVE-生效中, EXPIRED-已过期, CANCELLED-已取消',
`battery_id` BIGINT(20) DEFAULT NULL COMMENT '电池ID',
`battery_bind_time` DATETIME DEFAULT NULL COMMENT '电池绑定时间',
`battery_bind_admin_id` BIGINT(20) DEFAULT NULL COMMENT '电池绑定管理员ID',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`),
KEY `idx_user_id` (`user_id`),
KEY `idx_battery_id` (`battery_id`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

6.2 电池资产表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CREATE TABLE `t_battery_asset` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '电池资产ID',
`battery_no` VARCHAR(64) NOT NULL COMMENT '电池编号',
`status` VARCHAR(32) NOT NULL COMMENT '电池状态:AVAILABLE-可用, IN_STOCK-库存中, BOUND-已绑定, IN_USE-使用中, MAINTENANCE-维护中',
`order_id` BIGINT(20) DEFAULT NULL COMMENT '订单ID',
`bind_time` DATETIME DEFAULT NULL COMMENT '绑定时间',
`cabinet_id` BIGINT(20) DEFAULT NULL COMMENT '换电柜ID',
`slot_no` INT(11) DEFAULT NULL COMMENT '槽位编号',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_battery_no` (`battery_no`),
KEY `idx_order_id` (`order_id`),
KEY `idx_status` (`status`),
KEY `idx_cabinet_id` (`cabinet_id`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='电池资产表';

6.3 换电操作记录表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CREATE TABLE `t_battery_operation` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '操作ID',
`operation_type` VARCHAR(32) NOT NULL COMMENT '操作类型:TAKE-取电, SWAP-换电, RETURN-退电, ADMIN_BIND-管理员绑定',
`battery_id` BIGINT(20) NOT NULL COMMENT '电池ID',
`order_id` BIGINT(20) DEFAULT NULL COMMENT '订单ID',
`user_id` BIGINT(20) DEFAULT NULL COMMENT '用户ID',
`admin_id` BIGINT(20) DEFAULT NULL COMMENT '管理员ID',
`cabinet_id` BIGINT(20) DEFAULT NULL COMMENT '换电柜ID',
`slot_no` INT(11) DEFAULT NULL COMMENT '槽位编号',
`status` VARCHAR(32) NOT NULL COMMENT '操作状态:SUCCESS-成功, FAILED-失败, PROCESSING-处理中',
`operation_time` DATETIME NOT NULL COMMENT '操作时间',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_battery_id` (`battery_id`),
KEY `idx_order_id` (`order_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_admin_id` (`admin_id`),
KEY `idx_operation_type` (`operation_type`),
KEY `idx_status` (`status`),
KEY `idx_operation_time` (`operation_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='换电操作记录表';

7. Mapper实现

7.1 订单Mapper

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

/**
* 根据订单ID查询订单
*/
@Select("SELECT * FROM t_order WHERE id = #{orderId}")
Order selectById(@Param("orderId") Long orderId);

/**
* 更新订单信息
*/
@Update("UPDATE t_order SET battery_id = #{batteryId}, " +
"battery_bind_time = #{batteryBindTime}, " +
"battery_bind_admin_id = #{batteryBindAdminId}, " +
"update_time = #{updateTime} " +
"WHERE id = #{id}")
int updateById(Order order);
}

7.2 电池资产Mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 电池资产Mapper
*/
@Mapper
public interface BatteryAssetMapper extends BaseMapper<BatteryAsset> {

/**
* 根据电池ID查询电池资产
*/
@Select("SELECT * FROM t_battery_asset WHERE id = #{batteryId}")
BatteryAsset selectById(@Param("batteryId") Long batteryId);

/**
* 更新电池资产信息
*/
@Update("UPDATE t_battery_asset SET order_id = #{orderId}, " +
"status = #{status}, " +
"bind_time = #{bindTime}, " +
"update_time = #{updateTime} " +
"WHERE id = #{id}")
int updateById(BatteryAsset batteryAsset);
}

7.3 换电操作记录Mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 换电操作记录Mapper
*/
@Mapper
public interface BatteryOperationMapper extends BaseMapper<BatteryOperation> {

/**
* 插入换电操作记录
*/
@Insert("INSERT INTO t_battery_operation " +
"(operation_type, battery_id, order_id, user_id, admin_id, " +
"cabinet_id, slot_no, status, operation_time, create_time, update_time) " +
"VALUES " +
"(#{operationType}, #{batteryId}, #{orderId}, #{userId}, #{adminId}, " +
"#{cabinetId}, #{slotNo}, #{status}, #{operationTime}, #{createTime}, #{updateTime})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(BatteryOperation operation);
}

8. 配置类

8.1 Seata分布式事务配置

1
2
3
4
5
6
7
8
9
10
11
/**
* Seata分布式事务配置
*/
@Configuration
public class SeataConfig {

@Bean
public GlobalTransactionScanner globalTransactionScanner() {
return new GlobalTransactionScanner("order-service", "my_test_tx_group");
}
}

8.2 Redisson分布式锁配置

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
/**
* Redisson分布式锁配置
*/
@Configuration
public class RedissonConfig {

@Value("${spring.redis.host}")
private String redisHost;

@Value("${spring.redis.port}")
private int redisPort;

@Value("${spring.redis.password}")
private String redisPassword;

@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://" + redisHost + ":" + redisPort)
.setPassword(redisPassword)
.setConnectionPoolSize(10)
.setConnectionMinimumIdleSize(5);

return Redisson.create(config);
}
}

8.3 Feign客户端配置

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
/**
* Feign客户端配置
*/
@Configuration
public class FeignConfig {

@Bean
public RequestInterceptor requestInterceptor() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
// 传递请求头
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
String token = request.getHeader("Authorization");
if (token != null) {
template.header("Authorization", token);
}
}
}
};
}

@Bean
public Retryer retryer() {
// 重试配置:最大重试3次,初始间隔100ms,最大间隔1000ms
return new Retryer.Default(100, 1000, 3);
}
}

8.4 Kafka消息队列配置

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
/**
* Kafka消息队列配置
*/
@Configuration
@EnableKafka
public class KafkaConfig {

@Value("${spring.kafka.bootstrap-servers}")
private String bootstrapServers;

@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.ACKS_CONFIG, "all");
configProps.put(ProducerConfig.RETRIES_CONFIG, 3);
configProps.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
configProps.put(ProducerConfig.LINGER_MS_CONFIG, 1);
configProps.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);

return new DefaultKafkaProducerFactory<>(configProps);
}

@Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
}

9. 性能优化策略

9.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
/**
* 订单缓存服务
*/
@Service
@Slf4j
public class OrderCacheService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

@Autowired
private OrderMapper orderMapper;

/**
* 获取订单信息(带缓存)
*/
public Order getOrderById(Long orderId) {
String cacheKey = "order:info:" + orderId;

// 1. 从缓存获取
Order order = (Order) redisTemplate.opsForValue().get(cacheKey);
if (order != null) {
return order;
}

// 2. 缓存未命中,从数据库查询
order = orderMapper.selectById(orderId);

// 3. 写入缓存,过期时间5分钟
if (order != null) {
redisTemplate.opsForValue().set(cacheKey, order, 5, TimeUnit.MINUTES);
}

return order;
}

/**
* 更新订单缓存
*/
public void updateOrderCache(Long orderId, Order order) {
String cacheKey = "order:info:" + orderId;
redisTemplate.opsForValue().set(cacheKey, order, 5, TimeUnit.MINUTES);
}

/**
* 删除订单缓存
*/
public void deleteOrderCache(Long orderId) {
String cacheKey = "order:info:" + orderId;
redisTemplate.delete(cacheKey);
}
}

9.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
/**
* 异步处理服务
*/
@Service
@Slf4j
public class AsyncProcessService {

@Autowired
private KafkaTemplate<String, String> kafkaTemplate;

/**
* 异步发送电池绑定消息
*/
@Async("taskExecutor")
public CompletableFuture<Void> sendBatteryBindMessageAsync(BatteryOperation operation) {
try {
BatteryBindMessage message = new BatteryBindMessage();
message.setOperationId(operation.getId());
message.setBatteryId(operation.getBatteryId());
message.setOrderId(operation.getOrderId());
message.setAdminId(operation.getAdminId());
message.setOperationTime(operation.getOperationTime());

String messageJson = JSON.toJSONString(message);
kafkaTemplate.send("battery-bind-topic", messageJson);

log.info("异步发送电池绑定消息成功: operationId={}", operation.getId());

return CompletableFuture.completedFuture(null);

} catch (Exception e) {
log.error("异步发送电池绑定消息失败: operationId={}, error={}",
operation.getId(), e.getMessage(), e);
return CompletableFuture.failedFuture(e);
}
}
}

/**
* 异步任务配置
*/
@Configuration
@EnableAsync
public class AsyncConfig {

@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("async-task-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}

9.3 数据库连接池优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# application.yml
# Spring数据源配置
spring:
datasource:
# HikariCP连接池配置
hikari:
# 连接池中维护的最小空闲连接数
minimum-idle: 5
# 连接池的最大容量
maximum-pool-size: 20
# 连接池获取连接的最大等待时间(毫秒)
connection-timeout: 30000
# 连接在连接池中的最大空闲时间(毫秒),超时后被回收
idle-timeout: 600000
# 连接在连接池中的最大存活时间(毫秒),超时后被强制关闭
max-lifetime: 1800000
# 用于验证连接是否有效的查询语句
connection-test-query: SELECT 1

10. 监控告警

10.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
/**
* 业务指标监控
*/
@Component
@Slf4j
public class BusinessMetricsMonitor {

@Autowired
private MeterRegistry meterRegistry;

/**
* 记录绑电解电操作
*/
public void recordBatteryBindOperation(String status) {
Counter.builder("battery.bind.operation")
.tag("status", status)
.register(meterRegistry)
.increment();
}

/**
* 记录绑电解电操作耗时
*/
public void recordBatteryBindOperationDuration(long duration) {
Timer.builder("battery.bind.operation.duration")
.register(meterRegistry)
.record(duration, TimeUnit.MILLISECONDS);
}

/**
* 记录订单校验失败
*/
public void recordOrderValidationFailure(String reason) {
Counter.builder("order.validation.failure")
.tag("reason", reason)
.register(meterRegistry)
.increment();
}

/**
* 记录电池资产校验失败
*/
public void recordBatteryAssetValidationFailure(String reason) {
Counter.builder("battery.asset.validation.failure")
.tag("reason", reason)
.register(meterRegistry)
.increment();
}
}

10.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
# prometheus-alert-rules.yml
groups:
- name: battery_bind_alerts
rules:
- alert: BatteryBindOperationFailureRateHigh
expr: rate(battery_bind_operation_total{status="failed"}[5m]) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "绑电解电操作失败率过高"
description: "绑电解电操作失败率超过10%,当前值: {{ $value }}"

- alert: BatteryBindOperationDurationHigh
expr: histogram_quantile(0.95, rate(battery_bind_operation_duration_seconds_bucket[5m])) > 3
for: 5m
labels:
severity: warning
annotations:
summary: "绑电解电操作耗时过高"
description: "绑电解电操作95分位耗时超过3秒,当前值: {{ $value }}秒"

- alert: OrderValidationFailureRateHigh
expr: rate(order_validation_failure_total[5m]) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "订单校验失败率过高"
description: "订单校验失败率超过5%,当前值: {{ $value }}"

10.3 日志监控

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
/**
* 日志监控切面
*/
@Aspect
@Component
@Slf4j
public class LoggingAspect {

@Autowired
private BusinessMetricsMonitor metricsMonitor;

/**
* 绑电解电操作日志监控
*/
@Around("execution(* com.example.service.OrderService.bindBatteryToOrder(..))")
public Object monitorBatteryBindOperation(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
String status = "success";

try {
Object result = joinPoint.proceed();
return result;

} catch (Exception e) {
status = "failed";
log.error("绑电解电操作异常: error={}", e.getMessage(), e);
throw e;

} finally {
long duration = System.currentTimeMillis() - startTime;
metricsMonitor.recordBatteryBindOperation(status);
metricsMonitor.recordBatteryBindOperationDuration(duration);

log.info("绑电解电操作完成: status={}, duration={}ms", status, duration);
}
}
}

11. 总结

本文深入讲解了管理员绑电解电的Java微服务后端架构实战,涵盖了以下核心内容:

  1. 系统架构设计:采用微服务架构,通过管理网关、订单服务、设备服务协同完成绑电解电操作
  2. 身份认证:通过JWT Token实现管理员身份认证,确保操作安全性
  3. 订单校验:校验订单存在性、状态有效性、是否已绑定电池
  4. 电池资产校验:校验电池资产存在性、状态有效性、是否已绑定订单
  5. 分布式事务:使用Seata保证订单更新和电池资产更新的原子性和一致性
  6. 分布式锁:使用Redisson防止并发操作导致的数据不一致
  7. 异步处理:通过消息队列异步处理换电操作记录和状态同步
  8. 性能优化:通过缓存、异步处理、连接池优化提升系统性能
  9. 监控告警:通过业务指标监控、告警规则、日志监控保障系统稳定运行

通过本文的学习,读者可以掌握如何基于Java微服务架构实现一个高性能、高可用、安全可靠的管理员绑电解电系统,为实际项目开发提供参考和指导。