mirror of
https://gitee.com/san-bing/JChargePointProtocol
synced 2026-05-05 10:29:56 +08:00
添加查询充电枪状态接口
This commit is contained in:
@@ -6,8 +6,10 @@
|
||||
*/
|
||||
package sanbing.jcpp.app.adapter.controller;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import sanbing.jcpp.app.adapter.request.GunCreateRequest;
|
||||
@@ -17,16 +19,22 @@ import sanbing.jcpp.app.adapter.response.ApiResponse;
|
||||
import sanbing.jcpp.app.adapter.response.GunWithStatusResponse;
|
||||
import sanbing.jcpp.app.adapter.response.PageResponse;
|
||||
import sanbing.jcpp.app.dal.entity.Gun;
|
||||
import sanbing.jcpp.app.data.kv.AttrKeyEnum;
|
||||
import sanbing.jcpp.app.data.kv.AttributeKvEntry;
|
||||
import sanbing.jcpp.app.service.AttributeService;
|
||||
import sanbing.jcpp.app.service.GunService;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/guns")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class GunController extends BaseController {
|
||||
|
||||
private final GunService gunService;
|
||||
private final AttributeService attributeService;
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<Gun>> createGun(@Valid @RequestBody GunCreateRequest request) {
|
||||
@@ -58,4 +66,50 @@ public class GunController extends BaseController {
|
||||
PageResponse<GunWithStatusResponse> guns = gunService.queryGunsWithStatus(request);
|
||||
return ResponseEntity.ok(ApiResponse.success("查询成功", guns));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据枪编号获取充电枪运行状态
|
||||
*/
|
||||
@GetMapping("/status/{gunCode}")
|
||||
public ResponseEntity<ApiResponse<String>> getGunStatusByGunCode(@PathVariable String gunCode) {
|
||||
try {
|
||||
// 首先根据枪编号查找充电枪
|
||||
Gun gun = gunService.findByGunCode(gunCode);
|
||||
if (gun == null) {
|
||||
return ResponseEntity.ok(ApiResponse.error("充电枪不存在", null));
|
||||
}
|
||||
|
||||
// 通过AttributeService获取充电枪运行状态
|
||||
ListenableFuture<Optional<AttributeKvEntry>> attributeFuture =
|
||||
attributeService.find(gun.getId(), AttrKeyEnum.GUN_RUN_STATUS.getCode());
|
||||
|
||||
Optional<AttributeKvEntry> attributeResult = attributeFuture.get();
|
||||
String status = null;
|
||||
if (attributeResult.isPresent()) {
|
||||
AttributeKvEntry entry = attributeResult.get();
|
||||
status = entry.getStrValue().orElse(null);
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success("查询成功", status));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.ok(ApiResponse.error("查询失败: " + e.getMessage(), null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据充电枪编码获取充电枪详细信息
|
||||
*/
|
||||
@GetMapping("/code/{gunCode}")
|
||||
public ResponseEntity<ApiResponse<GunWithStatusResponse>> getGunByCode(@PathVariable String gunCode) {
|
||||
try {
|
||||
GunWithStatusResponse gun = gunService.findGunWithStatusByCode(gunCode);
|
||||
if (gun == null) {
|
||||
return ResponseEntity.ok(ApiResponse.error("充电枪不存在", null));
|
||||
}
|
||||
return ResponseEntity.ok(ApiResponse.success("查询成功", gun));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.ok(ApiResponse.error("查询失败: " + e.getMessage(), null));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
*/
|
||||
package sanbing.jcpp.app.adapter.controller;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import sanbing.jcpp.app.adapter.request.PileCreateRequest;
|
||||
@@ -18,18 +20,24 @@ import sanbing.jcpp.app.adapter.response.PageResponse;
|
||||
import sanbing.jcpp.app.adapter.response.PileOptionResponse;
|
||||
import sanbing.jcpp.app.adapter.response.PileWithStatusResponse;
|
||||
import sanbing.jcpp.app.dal.entity.Pile;
|
||||
import sanbing.jcpp.app.data.kv.AttrKeyEnum;
|
||||
import sanbing.jcpp.app.data.kv.AttributeKvEntry;
|
||||
import sanbing.jcpp.app.exception.JCPPException;
|
||||
import sanbing.jcpp.app.service.AttributeService;
|
||||
import sanbing.jcpp.app.service.PileService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/piles")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class PileController extends BaseController {
|
||||
|
||||
private final PileService pileService;
|
||||
private final AttributeService attributeService;
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ApiResponse<Pile>> createPile(@Valid @RequestBody PileCreateRequest request) {
|
||||
@@ -70,4 +78,34 @@ public class PileController extends BaseController {
|
||||
List<PileOptionResponse> options = pileService.getPileOptions();
|
||||
return ResponseEntity.ok(ApiResponse.success("查询成功", options));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据桩编号获取充电桩状态
|
||||
*/
|
||||
@GetMapping("/status/{pileCode}")
|
||||
public ResponseEntity<ApiResponse<String>> getPileStatusByPileCode(@PathVariable String pileCode) {
|
||||
try {
|
||||
// 首先根据桩编号查找充电桩
|
||||
Pile pile = pileService.findByPileCode(pileCode);
|
||||
if (pile == null) {
|
||||
return ResponseEntity.ok(ApiResponse.error("充电桩不存在", null));
|
||||
}
|
||||
|
||||
// 通过AttributeService获取充电桩状态
|
||||
ListenableFuture<Optional<AttributeKvEntry>> attributeFuture =
|
||||
attributeService.find(pile.getId(), AttrKeyEnum.STATUS.getCode());
|
||||
|
||||
Optional<AttributeKvEntry> attributeResult = attributeFuture.get();
|
||||
String status = null;
|
||||
if (attributeResult.isPresent()) {
|
||||
AttributeKvEntry entry = attributeResult.get();
|
||||
status = entry.getStrValue().orElse(null);
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success("查询成功", status));
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.ok(ApiResponse.error("查询失败: " + e.getMessage(), null));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.app.adapter.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import sanbing.jcpp.app.adapter.dto.*;
|
||||
import sanbing.jcpp.app.adapter.request.RpcRequest;
|
||||
import sanbing.jcpp.app.adapter.response.ApiResponse;
|
||||
import sanbing.jcpp.app.service.PileProtocolService;
|
||||
import sanbing.jcpp.infrastructure.util.jackson.JacksonUtil;
|
||||
import sanbing.jcpp.proto.gen.DownlinkProto.*;
|
||||
|
||||
/**
|
||||
* RPC控制器 - 通用化的充电桩下行指令接口
|
||||
*
|
||||
* @author 九筒
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/rpc")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class RpcController extends BaseController {
|
||||
|
||||
private final PileProtocolService pileProtocolService;
|
||||
|
||||
/**
|
||||
* 单向RPC接口 - 不等待充电桩返回消息
|
||||
*/
|
||||
@PostMapping("/oneway")
|
||||
public ResponseEntity<ApiResponse<String>> onewayRpc(@RequestBody RpcRequest request) {
|
||||
try {
|
||||
log.info("收到单向RPC请求: method={}, parameter={}", request.getMethod(), request.getParameter());
|
||||
|
||||
// 根据method调用对应的服务方法
|
||||
executeRpcMethod(request.getMethod(), request.getParameter());
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success("指令发送成功", null));
|
||||
} catch (Exception e) {
|
||||
log.error("单向RPC调用失败: method={}, error={}", request.getMethod(), e.getMessage(), e);
|
||||
return ResponseEntity.ok(ApiResponse.error("指令发送失败: " + e.getMessage(), null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 双向RPC接口 - 等待充电桩返回消息(带超时)
|
||||
* TODO: 实现双向RPC逻辑,包括超时处理
|
||||
*/
|
||||
@PostMapping("/bidirectional")
|
||||
public ResponseEntity<ApiResponse<String>> bidirectionalRpc(@RequestBody RpcRequest request) {
|
||||
// TODO: 实现双向RPC,需要等待充电桩响应,包含超时时间参数
|
||||
return ResponseEntity.ok(ApiResponse.error("双向RPC功能待实现", null));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行RPC方法
|
||||
*/
|
||||
private void executeRpcMethod(String method, JsonNode parameter) throws Exception {
|
||||
switch (method) {
|
||||
case "startCharge":
|
||||
handleStartCharge(parameter);
|
||||
break;
|
||||
case "stopCharge":
|
||||
handleStopCharge(parameter);
|
||||
break;
|
||||
case "restartPile":
|
||||
handleRestartPile(parameter);
|
||||
break;
|
||||
case "setPricing":
|
||||
handleSetPricing(parameter);
|
||||
break;
|
||||
case "setQrcode":
|
||||
handleSetQrcode(parameter);
|
||||
break;
|
||||
case "otaRequest":
|
||||
handleOtaRequest(parameter);
|
||||
break;
|
||||
case "offlineCardBalanceUpdate":
|
||||
handleOfflineCardBalanceUpdate(parameter);
|
||||
break;
|
||||
case "offlineCardSync":
|
||||
handleOfflineCardSync(parameter);
|
||||
break;
|
||||
case "offlineCardClear":
|
||||
handleOfflineCardClear(parameter);
|
||||
break;
|
||||
case "offlineCardQuery":
|
||||
handleOfflineCardQuery(parameter);
|
||||
break;
|
||||
case "timeSync":
|
||||
handleTimeSync(parameter);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的RPC方法: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理启动充电指令
|
||||
*/
|
||||
private void handleStartCharge(JsonNode parameter) {
|
||||
StartChargeDTO startChargeDto = JacksonUtil.fromJson(parameter, StartChargeDTO.class);
|
||||
pileProtocolService.startCharge(startChargeDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理停止充电指令
|
||||
*/
|
||||
private void handleStopCharge(JsonNode parameter) {
|
||||
StopChargeDTO stopChargeDto = JacksonUtil.fromJson(parameter, StopChargeDTO.class);
|
||||
pileProtocolService.stopCharge(stopChargeDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理重启充电桩指令
|
||||
*/
|
||||
private void handleRestartPile(JsonNode parameter) {
|
||||
RestartPileDTO restartPileDto = JacksonUtil.fromJson(parameter, RestartPileDTO.class);
|
||||
pileProtocolService.restartPile(restartPileDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理设置计费策略指令
|
||||
*/
|
||||
private void handleSetPricing(JsonNode parameter) {
|
||||
SetPricingDTO setPricingDto = JacksonUtil.fromJson(parameter, SetPricingDTO.class);
|
||||
pileProtocolService.setPricing(setPricingDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理设置二维码指令
|
||||
*/
|
||||
private void handleSetQrcode(JsonNode parameter) {
|
||||
SetQrcodeRequest setQrcodeRequest = JacksonUtil.fromJson(parameter, SetQrcodeRequest.class);
|
||||
pileProtocolService.setQrcode(setQrcodeRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理OTA升级指令
|
||||
*/
|
||||
private void handleOtaRequest(JsonNode parameter) {
|
||||
OtaRequest otaRequest = JacksonUtil.fromJson(parameter, OtaRequest.class);
|
||||
pileProtocolService.otaRequest(otaRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理离线卡余额更新指令
|
||||
*/
|
||||
private void handleOfflineCardBalanceUpdate(JsonNode parameter) {
|
||||
OfflineCardBalanceUpdateRequest request = JacksonUtil.fromJson(parameter, OfflineCardBalanceUpdateRequest.class);
|
||||
pileProtocolService.offlineCardBalanceUpdateRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理离线卡同步指令
|
||||
*/
|
||||
private void handleOfflineCardSync(JsonNode parameter) {
|
||||
OfflineCardSyncRequest request = JacksonUtil.fromJson(parameter, OfflineCardSyncRequest.class);
|
||||
pileProtocolService.offlineCardSyncRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理离线卡清除指令
|
||||
*/
|
||||
private void handleOfflineCardClear(JsonNode parameter) {
|
||||
OfflineCardClearRequest request = JacksonUtil.fromJson(parameter, OfflineCardClearRequest.class);
|
||||
pileProtocolService.offlineCardClearRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理离线卡查询指令
|
||||
*/
|
||||
private void handleOfflineCardQuery(JsonNode parameter) {
|
||||
OfflineCardQueryRequest request = JacksonUtil.fromJson(parameter, OfflineCardQueryRequest.class);
|
||||
pileProtocolService.offlineCardQueryRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理时间同步指令
|
||||
*/
|
||||
private void handleTimeSync(JsonNode parameter) {
|
||||
TimeSyncDTO timeSyncDto = JacksonUtil.fromJson(parameter, TimeSyncDTO.class);
|
||||
pileProtocolService.timeSync(timeSyncDto);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import sanbing.jcpp.app.adapter.dto.*;
|
||||
import sanbing.jcpp.app.service.PileProtocolService;
|
||||
import sanbing.jcpp.proto.gen.DownlinkProto.*;
|
||||
|
||||
@@ -39,8 +40,16 @@ public class TestController extends BaseController {
|
||||
String logicalCardNo = RandomStringUtils.secure().nextNumeric(12);
|
||||
String physicalCardNo = RandomStringUtils.secure().nextNumeric(12);
|
||||
|
||||
pileProtocolService.startCharge("20231212000010", "01", new BigDecimal("50"), orderNo,
|
||||
logicalCardNo, physicalCardNo, null);
|
||||
StartChargeDTO startChargeDto = new StartChargeDTO();
|
||||
startChargeDto.setPileCode("20231212000010");
|
||||
startChargeDto.setGunNo("01");
|
||||
startChargeDto.setLimitYuan(new BigDecimal("50"));
|
||||
startChargeDto.setOrderNo(orderNo);
|
||||
startChargeDto.setLogicalCardNo(logicalCardNo);
|
||||
startChargeDto.setPhysicalCardNo(physicalCardNo);
|
||||
startChargeDto.setParallelNo(null);
|
||||
|
||||
pileProtocolService.startCharge(startChargeDto);
|
||||
|
||||
return ResponseEntity.ok("success");
|
||||
}
|
||||
@@ -53,8 +62,16 @@ public class TestController extends BaseController {
|
||||
String physicalCardNo = RandomStringUtils.secure().nextNumeric(12);
|
||||
String parallelNo = RandomStringUtils.secure().nextNumeric(6);
|
||||
|
||||
pileProtocolService.startCharge("20231212000010", "01", new BigDecimal("100"),
|
||||
orderNo, logicalCardNo, physicalCardNo, parallelNo);
|
||||
StartChargeDTO startChargeDto = new StartChargeDTO();
|
||||
startChargeDto.setPileCode("20231212000010");
|
||||
startChargeDto.setGunNo("01");
|
||||
startChargeDto.setLimitYuan(new BigDecimal("100"));
|
||||
startChargeDto.setOrderNo(orderNo);
|
||||
startChargeDto.setLogicalCardNo(logicalCardNo);
|
||||
startChargeDto.setPhysicalCardNo(physicalCardNo);
|
||||
startChargeDto.setParallelNo(parallelNo);
|
||||
|
||||
pileProtocolService.startCharge(startChargeDto);
|
||||
|
||||
return ResponseEntity.ok("success");
|
||||
}
|
||||
@@ -62,7 +79,11 @@ public class TestController extends BaseController {
|
||||
@GetMapping("/stopCharge")
|
||||
public ResponseEntity<String> stopCharge() {
|
||||
|
||||
pileProtocolService.stopCharge("20231212000010", "01");
|
||||
StopChargeDTO stopChargeDto = new StopChargeDTO();
|
||||
stopChargeDto.setPileCode("20231212000010");
|
||||
stopChargeDto.setGunNo("01");
|
||||
|
||||
pileProtocolService.stopCharge(stopChargeDto);
|
||||
|
||||
return ResponseEntity.ok("success");
|
||||
}
|
||||
@@ -85,7 +106,11 @@ public class TestController extends BaseController {
|
||||
@GetMapping("/restartPile")
|
||||
public ResponseEntity<String> restartPile() {
|
||||
|
||||
pileProtocolService.restartPile("20231212000010", 1);
|
||||
RestartPileDTO restartPileDto = new RestartPileDTO();
|
||||
restartPileDto.setPileCode("20231212000010");
|
||||
restartPileDto.setType(1);
|
||||
|
||||
pileProtocolService.restartPile(restartPileDto);
|
||||
|
||||
return ResponseEntity.ok("success");
|
||||
}
|
||||
@@ -195,13 +220,16 @@ public class TestController extends BaseController {
|
||||
.setPeakValleyPricing(peakValleyPricing) // 设置峰谷计价配置
|
||||
.build();
|
||||
|
||||
pileProtocolService.setPricing(pileCode,
|
||||
SetPricingRequest.newBuilder()
|
||||
SetPricingDTO setPricingDto = new SetPricingDTO();
|
||||
setPricingDto.setPileCode(pileCode);
|
||||
setPricingDto.setSetPricingRequest(SetPricingRequest.newBuilder()
|
||||
.setPileCode(pileCode)
|
||||
.setPricingId(1000L)
|
||||
.setPricingModel(pricingModel)
|
||||
.build());
|
||||
|
||||
pileProtocolService.setPricing(setPricingDto);
|
||||
|
||||
return ResponseEntity.ok("success");
|
||||
}
|
||||
|
||||
@@ -274,13 +302,16 @@ public class TestController extends BaseController {
|
||||
.setTimePeriodPricing(timePeriodPricing) // 设置时段计价配置
|
||||
.build();
|
||||
|
||||
pileProtocolService.setPricing(pileCode,
|
||||
SetPricingRequest.newBuilder()
|
||||
SetPricingDTO setPricingDto = new SetPricingDTO();
|
||||
setPricingDto.setPileCode(pileCode);
|
||||
setPricingDto.setSetPricingRequest(SetPricingRequest.newBuilder()
|
||||
.setPileCode(pileCode)
|
||||
.setPricingId(2000L)
|
||||
.setPricingModel(pricingModel)
|
||||
.build());
|
||||
|
||||
pileProtocolService.setPricing(setPricingDto);
|
||||
|
||||
return ResponseEntity.ok("Time period pricing test success");
|
||||
}
|
||||
|
||||
@@ -309,7 +340,7 @@ public class TestController extends BaseController {
|
||||
pileProtocolService.offlineCardBalanceUpdateRequest(OfflineCardBalanceUpdateRequest.newBuilder()
|
||||
.setCardNo("1000000000123456")
|
||||
.setPileCode("20231212000010")
|
||||
.setGunCode("01")
|
||||
.setGunNo("01")
|
||||
.setLimitYuan("1000")
|
||||
.build());
|
||||
|
||||
@@ -334,7 +365,11 @@ public class TestController extends BaseController {
|
||||
|
||||
@GetMapping("/timeSync")
|
||||
public ResponseEntity<String> timeSync() {
|
||||
pileProtocolService.timeSync("20231212000010", LocalDateTime.now());
|
||||
TimeSyncDTO timeSyncDto = new TimeSyncDTO();
|
||||
timeSyncDto.setPileCode("20231212000010");
|
||||
timeSyncDto.setTime(LocalDateTime.now());
|
||||
|
||||
pileProtocolService.timeSync(timeSyncDto);
|
||||
return ResponseEntity.ok("success");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.app.adapter.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 重启充电桩DTO
|
||||
*
|
||||
* @author 九筒
|
||||
*/
|
||||
@Data
|
||||
public class RestartPileDTO {
|
||||
|
||||
/**
|
||||
* 充电桩编码
|
||||
*/
|
||||
@NotBlank(message = "充电桩编码不能为空")
|
||||
private String pileCode;
|
||||
|
||||
/**
|
||||
* 重启类型
|
||||
*/
|
||||
@NotNull(message = "重启类型不能为空")
|
||||
private Integer type;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.app.adapter.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import sanbing.jcpp.proto.gen.DownlinkProto.SetPricingRequest;
|
||||
|
||||
/**
|
||||
* 设置计费策略DTO
|
||||
*
|
||||
* @author 九筒
|
||||
*/
|
||||
@Data
|
||||
public class SetPricingDTO {
|
||||
|
||||
/**
|
||||
* 充电桩编码
|
||||
*/
|
||||
@NotBlank(message = "充电桩编码不能为空")
|
||||
private String pileCode;
|
||||
|
||||
/**
|
||||
* 计费策略请求
|
||||
*/
|
||||
@NotNull(message = "计费策略请求不能为空")
|
||||
private SetPricingRequest setPricingRequest;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.app.adapter.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 启动充电DTO
|
||||
*
|
||||
* @author 九筒
|
||||
*/
|
||||
@Data
|
||||
public class StartChargeDTO {
|
||||
|
||||
/**
|
||||
* 充电桩编码
|
||||
*/
|
||||
@NotBlank(message = "充电桩编码不能为空")
|
||||
private String pileCode;
|
||||
|
||||
/**
|
||||
* 充电枪编号
|
||||
*/
|
||||
@NotBlank(message = "充电枪编号不能为空")
|
||||
private String gunNo;
|
||||
|
||||
/**
|
||||
* 限制金额(元)
|
||||
*/
|
||||
@NotNull(message = "限制金额不能为空")
|
||||
private BigDecimal limitYuan;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
@NotBlank(message = "订单号不能为空")
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 逻辑卡号
|
||||
*/
|
||||
private String logicalCardNo;
|
||||
|
||||
/**
|
||||
* 物理卡号
|
||||
*/
|
||||
private String physicalCardNo;
|
||||
|
||||
/**
|
||||
* 并充序号(当不为空时,自动使用并充启机命令)
|
||||
*/
|
||||
private String parallelNo;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.app.adapter.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 停止充电DTO
|
||||
*
|
||||
* @author 九筒
|
||||
*/
|
||||
@Data
|
||||
public class StopChargeDTO {
|
||||
|
||||
/**
|
||||
* 充电桩编码
|
||||
*/
|
||||
@NotBlank(message = "充电桩编码不能为空")
|
||||
private String pileCode;
|
||||
|
||||
/**
|
||||
* 充电枪编号
|
||||
*/
|
||||
@NotBlank(message = "充电枪编号不能为空")
|
||||
private String gunNo;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.app.adapter.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 时间同步DTO
|
||||
*
|
||||
* @author 九筒
|
||||
*/
|
||||
@Data
|
||||
public class TimeSyncDTO {
|
||||
|
||||
/**
|
||||
* 充电桩编码
|
||||
*/
|
||||
@NotBlank(message = "充电桩编码不能为空")
|
||||
private String pileCode;
|
||||
|
||||
/**
|
||||
* 同步时间
|
||||
*/
|
||||
@NotNull(message = "同步时间不能为空")
|
||||
private LocalDateTime time;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 开源代码,仅供学习和交流研究使用,商用请联系三丙
|
||||
* 微信:mohan_88888
|
||||
* 抖音:程序员三丙
|
||||
* 付费课程知识星球:https://t.zsxq.com/aKtXo
|
||||
*/
|
||||
package sanbing.jcpp.app.adapter.request;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* RPC请求参数
|
||||
*
|
||||
* @author 九筒
|
||||
*/
|
||||
@Data
|
||||
public class RpcRequest {
|
||||
|
||||
/**
|
||||
* RPC方法名
|
||||
* 支持的方法包括:
|
||||
* - startCharge: 启动充电
|
||||
* - stopCharge: 停止充电
|
||||
* - restartPile: 重启充电桩
|
||||
* - setPricing: 设置计费策略
|
||||
* - setQrcode: 设置二维码
|
||||
* - otaRequest: OTA升级
|
||||
* - offlineCardBalanceUpdate: 离线卡余额更新
|
||||
* - offlineCardSync: 离线卡同步
|
||||
* - offlineCardClear: 离线卡清除
|
||||
* - offlineCardQuery: 离线卡查询
|
||||
* - timeSync: 时间同步
|
||||
*/
|
||||
@NotBlank(message = "方法名不能为空")
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 方法参数,JSON格式
|
||||
* 不同的方法需要不同的参数结构
|
||||
*/
|
||||
@NotNull(message = "参数不能为空")
|
||||
private JsonNode parameter;
|
||||
|
||||
/**
|
||||
* 超时时间(毫秒),仅用于双向RPC
|
||||
*/
|
||||
private Long timeoutMs;
|
||||
}
|
||||
@@ -21,7 +21,8 @@ import lombok.NoArgsConstructor;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ApiResponse<T> {
|
||||
|
||||
|
||||
private boolean success;
|
||||
private String errorCode;
|
||||
private String message;
|
||||
private T data;
|
||||
@@ -29,6 +30,7 @@ public class ApiResponse<T> {
|
||||
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
return ApiResponse.<T>builder()
|
||||
.success(true)
|
||||
.message("操作成功")
|
||||
.data(data)
|
||||
.timestamp(System.currentTimeMillis())
|
||||
@@ -37,6 +39,7 @@ public class ApiResponse<T> {
|
||||
|
||||
public static <T> ApiResponse<T> success(String message, T data) {
|
||||
return ApiResponse.<T>builder()
|
||||
.success(true)
|
||||
.message(message)
|
||||
.data(data)
|
||||
.timestamp(System.currentTimeMillis())
|
||||
@@ -45,6 +48,7 @@ public class ApiResponse<T> {
|
||||
|
||||
public static <T> ApiResponse<T> error(String errorCode, String message) {
|
||||
return ApiResponse.<T>builder()
|
||||
.success(false)
|
||||
.errorCode(errorCode)
|
||||
.message(message)
|
||||
.timestamp(System.currentTimeMillis())
|
||||
@@ -53,6 +57,7 @@ public class ApiResponse<T> {
|
||||
|
||||
public static <T> ApiResponse<T> error(ErrorCode errorCode, String message) {
|
||||
return ApiResponse.<T>builder()
|
||||
.success(false)
|
||||
.errorCode(errorCode.getCode())
|
||||
.message(message)
|
||||
.timestamp(System.currentTimeMillis())
|
||||
@@ -61,6 +66,7 @@ public class ApiResponse<T> {
|
||||
|
||||
public static <T> ApiResponse<T> error(ErrorCode errorCode) {
|
||||
return ApiResponse.<T>builder()
|
||||
.success(false)
|
||||
.errorCode(errorCode.getCode())
|
||||
.message(errorCode.getMessage())
|
||||
.timestamp(System.currentTimeMillis())
|
||||
|
||||
@@ -108,4 +108,9 @@ public class PileWithStatusResponse {
|
||||
* 最后活跃时间(13位时间戳)
|
||||
*/
|
||||
private Long lastActiveTime;
|
||||
|
||||
/**
|
||||
* 充电枪数量
|
||||
*/
|
||||
private Long gunCount;
|
||||
}
|
||||
|
||||
@@ -22,12 +22,15 @@ import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration(exclude = {RedisAutoConfiguration.class})
|
||||
@EnableTransactionManagement
|
||||
@MapperScan({"sanbing.jcpp.app.dal.mapper"})
|
||||
public class DalConfig {
|
||||
|
||||
@@ -44,13 +47,11 @@ public class DalConfig {
|
||||
return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public JdbcTemplate jdbcTemplate(@Qualifier("dataSource") DataSource dataSource) {
|
||||
return new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public NamedParameterJdbcTemplate namedParameterJdbcTemplate(@Qualifier("dataSource") DataSource dataSource) {
|
||||
return new NamedParameterJdbcTemplate(dataSource);
|
||||
@@ -58,7 +59,12 @@ public class DalConfig {
|
||||
|
||||
@Primary
|
||||
@Bean
|
||||
public TransactionTemplate transactionTemplate(DataSourceTransactionManager transactionManager) {
|
||||
public PlatformTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
|
||||
return new DataSourceTransactionManager(dataSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TransactionTemplate transactionTemplate(@Qualifier("transactionManager") PlatformTransactionManager transactionManager) {
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setIsolationLevel(TransactionTemplate.ISOLATION_READ_COMMITTED);
|
||||
transactionTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||
@@ -66,7 +72,6 @@ public class DalConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
|
||||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
|
||||
|
||||
@@ -20,11 +20,20 @@ import java.util.UUID;
|
||||
* @author 九筒
|
||||
*/
|
||||
public interface GunMapper extends BaseMapper<Gun> {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据充电桩编码和充电枪编码查询充电枪
|
||||
* 根据充电桩编码和充电枪编号查询充电枪
|
||||
* 充电桩上报的是 pile_code + gun_no 的组合,这个组合是唯一的
|
||||
*/
|
||||
Gun selectByPileCodeAndGunCode(@Param("pileCode") String pileCode, @Param("gunCode") String gunCode);
|
||||
Gun selectByPileCodeAndGunNo(@Param("pileCode") String pileCode, @Param("gunNo") String gunNo);
|
||||
|
||||
/**
|
||||
* 根据枪编号查询充电枪
|
||||
*/
|
||||
Gun selectByGunCode(@Param("gunCode") String gunCode);
|
||||
|
||||
GunWithStatusResponse selectGunWithStatusByCode(@Param("gunCode") String gunCode);
|
||||
|
||||
/**
|
||||
* 分页查询充电枪及其状态信息
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
package sanbing.jcpp.app.dal.repository;
|
||||
|
||||
import sanbing.jcpp.app.adapter.response.GunWithStatusResponse;
|
||||
import sanbing.jcpp.app.dal.entity.Gun;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -17,14 +18,26 @@ import java.util.UUID;
|
||||
*/
|
||||
public interface GunRepository {
|
||||
|
||||
|
||||
/**
|
||||
* 根据充电桩编码和充电枪编码查询充电枪
|
||||
* 根据充电桩编码和充电枪编号查询充电枪
|
||||
* 充电桩上报的是 pile_code + gun_no 的组合,这个组合是唯一的
|
||||
*
|
||||
* @param pileCode 充电桩编码
|
||||
* @param gunNo 充电枪编号 (如: "01", "02")
|
||||
* @return 充电枪实体,如果不存在返回null
|
||||
*/
|
||||
Gun findByPileCodeAndGunNo(String pileCode, String gunNo);
|
||||
|
||||
/**
|
||||
* 根据枪编号查询充电枪
|
||||
*
|
||||
* @param gunCode 充电枪编码
|
||||
* @return 充电枪实体,如果不存在返回null
|
||||
*/
|
||||
Gun findByPileCodeAndGunCode(String pileCode, String gunCode);
|
||||
Gun findByGunCode(String gunCode);
|
||||
|
||||
GunWithStatusResponse findGunWithStatusByCode(String gunCode);
|
||||
|
||||
/**
|
||||
* 根据充电枪ID查询充电枪
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
*/
|
||||
package sanbing.jcpp.app.dal.repository.impl;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
import sanbing.jcpp.app.adapter.response.GunWithStatusResponse;
|
||||
import sanbing.jcpp.app.dal.entity.Gun;
|
||||
import sanbing.jcpp.app.dal.mapper.GunMapper;
|
||||
import sanbing.jcpp.app.dal.repository.GunRepository;
|
||||
@@ -30,10 +31,10 @@ import static sanbing.jcpp.infrastructure.util.validation.Validator.validateStri
|
||||
*/
|
||||
@Repository
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class GunRepositoryImpl extends CachedVersionedEntityRepository<GunCacheKey, Gun, GunCacheEvictEvent> implements GunRepository {
|
||||
|
||||
@Resource
|
||||
GunMapper gunMapper;
|
||||
private final GunMapper gunMapper;
|
||||
|
||||
@TransactionalEventListener(classes = GunCacheEvictEvent.class)
|
||||
@Override
|
||||
@@ -45,22 +46,36 @@ public class GunRepositoryImpl extends CachedVersionedEntityRepository<GunCacheK
|
||||
if (event.getGunId() != null) {
|
||||
toEvict.add(new GunCacheKey(event.getGunId()));
|
||||
}
|
||||
|
||||
// 基于pileCode+gunCode的缓存key
|
||||
if (event.getPileCode() != null && event.getGunCode() != null) {
|
||||
toEvict.add(new GunCacheKey(event.getPileCode(), event.getGunCode()));
|
||||
|
||||
// 基于pileCode+gunNo的缓存key
|
||||
if (event.getPileCode() != null && event.getGunNo() != null) {
|
||||
toEvict.add(new GunCacheKey(event.getPileCode(), event.getGunNo()));
|
||||
}
|
||||
|
||||
// 基于gunCode的缓存key
|
||||
if (event.getGunCode() != null) {
|
||||
toEvict.add(new GunCacheKey(event.getGunCode()));
|
||||
}
|
||||
|
||||
cache.evict(toEvict);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Gun findByPileCodeAndGunCode(String pileCode, String gunCode) {
|
||||
public Gun findByPileCodeAndGunNo(String pileCode, String gunNo) {
|
||||
validateString(pileCode, code -> "无效的桩编号: " + pileCode);
|
||||
validateString(gunNo, no -> "无效的枪编号: " + gunNo);
|
||||
|
||||
return cache.get(new GunCacheKey(pileCode, gunNo),
|
||||
() -> gunMapper.selectByPileCodeAndGunNo(pileCode, gunNo));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Gun findByGunCode(String gunCode) {
|
||||
validateString(gunCode, code -> "无效的枪编号: " + gunCode);
|
||||
|
||||
return cache.get(new GunCacheKey(pileCode, gunCode),
|
||||
() -> gunMapper.selectByPileCodeAndGunCode(pileCode, gunCode));
|
||||
|
||||
return cache.get(new GunCacheKey(gunCode),
|
||||
() -> gunMapper.selectByGunCode(gunCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,4 +85,12 @@ public class GunRepositoryImpl extends CachedVersionedEntityRepository<GunCacheK
|
||||
return cache.get(new GunCacheKey(gunId),
|
||||
() -> gunMapper.selectById(gunId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GunWithStatusResponse findGunWithStatusByCode(String gunCode) {
|
||||
validateString(gunCode, code -> "无效的枪编号: " + gunCode);
|
||||
|
||||
// 这个方法不使用缓存,因为它包含状态信息,需要实时查询
|
||||
return gunMapper.selectGunWithStatusByCode(gunCode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
package sanbing.jcpp.app.dal.repository.impl;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
@@ -26,10 +26,10 @@ import static sanbing.jcpp.infrastructure.util.validation.Validator.validateStri
|
||||
*/
|
||||
@Repository
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PileRepositoryImpl extends CachedVersionedEntityRepository<PileCacheKey, Pile, PileCacheEvictEvent> implements PileRepository {
|
||||
|
||||
@Resource
|
||||
PileMapper pileMapper;
|
||||
private final PileMapper pileMapper;
|
||||
|
||||
@TransactionalEventListener(classes = PileCacheEvictEvent.class)
|
||||
@Override
|
||||
|
||||
@@ -42,11 +42,20 @@ public interface GunService {
|
||||
* 分页查询充电枪及状态信息
|
||||
*/
|
||||
PageResponse<GunWithStatusResponse> queryGunsWithStatus(GunQueryRequest request);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 根据充电桩编码和充电枪编码查询充电枪
|
||||
* 根据充电桩编码和充电枪编号查询充电枪
|
||||
* 充电桩上报的是 pile_code + gun_no 的组合,这个组合是唯一的
|
||||
*/
|
||||
Gun findByPileCodeAndGunCode(String pileCode, String gunCode);
|
||||
Gun findByPileCodeAndGunNo(String pileCode, String gunNo);
|
||||
|
||||
/**
|
||||
* 根据枪编号查询充电枪
|
||||
*/
|
||||
Gun findByGunCode(String gunCode);
|
||||
|
||||
GunWithStatusResponse findGunWithStatusByCode(String gunCode);
|
||||
|
||||
/**
|
||||
* 查询充电枪状态
|
||||
@@ -69,11 +78,11 @@ public interface GunService {
|
||||
* 处理充电枪状态上报
|
||||
*
|
||||
* @param pileCode 充电桩编码
|
||||
* @param gunCode 充电枪编码
|
||||
* @param gunNo 充电枪编号 (如: "01", "02")
|
||||
* @param protoStatus Proto状态
|
||||
* @param ts 时间戳
|
||||
* @return 是否需要更新充电桩状态
|
||||
*/
|
||||
boolean handleGunRunStatus(String pileCode, String gunCode, GunRunStatus protoStatus, long ts);
|
||||
boolean handleGunRunStatus(String pileCode, String gunNo, GunRunStatus protoStatus, long ts);
|
||||
|
||||
}
|
||||
|
||||
@@ -6,17 +6,14 @@
|
||||
*/
|
||||
package sanbing.jcpp.app.service;
|
||||
|
||||
import sanbing.jcpp.app.adapter.dto.*;
|
||||
import sanbing.jcpp.infrastructure.queue.Callback;
|
||||
import sanbing.jcpp.proto.gen.DownlinkProto;
|
||||
import sanbing.jcpp.proto.gen.DownlinkProto.OfflineCardBalanceUpdateRequest;
|
||||
import sanbing.jcpp.proto.gen.DownlinkProto.OfflineCardSyncRequest;
|
||||
import sanbing.jcpp.proto.gen.DownlinkProto.OtaRequest;
|
||||
import sanbing.jcpp.proto.gen.DownlinkProto.SetPricingRequest;
|
||||
import sanbing.jcpp.proto.gen.UplinkProto.UplinkQueueMessage;
|
||||
import sanbing.jcpp.proto.gen.DownlinkProto.SetQrcodeRequest;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import sanbing.jcpp.proto.gen.UplinkProto.UplinkQueueMessage;
|
||||
|
||||
/**
|
||||
* @author 九筒
|
||||
@@ -87,23 +84,22 @@ public interface PileProtocolService {
|
||||
* 启动充电(支持卡号和并充序号)
|
||||
* 当 parallelNo 不为空时,自动使用并充启机命令
|
||||
*/
|
||||
void startCharge(String pileCode, String gunCode, BigDecimal limitYuan, String orderNo,
|
||||
String logicalCardNo, String physicalCardNo, String parallelNo);
|
||||
void startCharge(StartChargeDTO startChargeDto);
|
||||
|
||||
/**
|
||||
* 停止充电
|
||||
*/
|
||||
void stopCharge(String pileCode, String gunCode);
|
||||
void stopCharge(StopChargeDTO stopChargeDto);
|
||||
|
||||
/**
|
||||
* 重启充电
|
||||
* 重启充电桩
|
||||
*/
|
||||
void restartPile(String pileCode, Integer type);
|
||||
void restartPile(RestartPileDTO restartPileDto);
|
||||
|
||||
/**
|
||||
* 下发计费
|
||||
* 下发计费策略
|
||||
*/
|
||||
void setPricing(String pileCode, SetPricingRequest setPricingRequest);
|
||||
void setPricing(SetPricingDTO setPricingDto);
|
||||
|
||||
/**
|
||||
* 充电桩与 BMS 充电错误上报
|
||||
@@ -179,7 +175,7 @@ public interface PileProtocolService {
|
||||
/**
|
||||
* 实时同步桩时间
|
||||
*/
|
||||
void timeSync(String pileCode, LocalDateTime time);
|
||||
void timeSync(TimeSyncDTO timeSyncDto);
|
||||
|
||||
/**
|
||||
* 实时同步桩时间应答
|
||||
|
||||
@@ -32,6 +32,11 @@ public interface PileService {
|
||||
* 根据ID查询充电桩
|
||||
*/
|
||||
Pile findById(UUID id);
|
||||
|
||||
/**
|
||||
* 根据桩编号查询充电桩
|
||||
*/
|
||||
Pile findByPileCode(String pileCode);
|
||||
|
||||
/**
|
||||
* 更新充电桩
|
||||
|
||||
@@ -15,5 +15,6 @@ public class GunCacheEvictEvent {
|
||||
|
||||
private UUID gunId;
|
||||
private String pileCode;
|
||||
private String gunNo;
|
||||
private String gunCode;
|
||||
}
|
||||
|
||||
@@ -13,27 +13,33 @@ import java.io.Serial;
|
||||
import java.util.UUID;
|
||||
|
||||
@Builder
|
||||
public record GunCacheKey(UUID gunId, String pileCode, String gunCode) implements VersionedCacheKey {
|
||||
public record GunCacheKey(UUID gunId, String pileCode, String gunNo, String gunCode) implements VersionedCacheKey {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public GunCacheKey(UUID gunId) {
|
||||
this(gunId, null, null);
|
||||
this(gunId, null, null, null);
|
||||
}
|
||||
|
||||
public GunCacheKey(String pileCode, String gunCode) {
|
||||
this(null, pileCode, gunCode);
|
||||
public GunCacheKey(String pileCode, String gunNo) {
|
||||
this(null, pileCode, gunNo, null);
|
||||
}
|
||||
|
||||
public GunCacheKey(String gunCode) {
|
||||
this(null, null, null, gunCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (gunId != null) {
|
||||
return gunId.toString();
|
||||
} else if (pileCode != null && gunCode != null) {
|
||||
return pileCode + ":" + gunCode;
|
||||
} else if (pileCode != null && gunNo != null) {
|
||||
return pileCode + ":" + gunNo;
|
||||
} else if (gunCode != null) {
|
||||
return "gunCode:" + gunCode;
|
||||
} else {
|
||||
throw new IllegalStateException("GunCacheKey 必须包含有效的 gunId 或者 pileCode+gunCode 组合");
|
||||
throw new IllegalStateException("GunCacheKey 必须包含有效的 gunId、pileCode+gunNo 组合或者 gunCode");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,9 +126,20 @@ public class DefaultGunService implements GunService {
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Gun findByPileCodeAndGunCode(String pileCode, String gunCode) {
|
||||
return gunRepository.findByPileCodeAndGunCode(pileCode, gunCode);
|
||||
public Gun findByPileCodeAndGunNo(String pileCode, String gunNo) {
|
||||
return gunRepository.findByPileCodeAndGunNo(pileCode, gunNo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Gun findByGunCode(String gunCode) {
|
||||
return gunRepository.findByGunCode(gunCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GunWithStatusResponse findGunWithStatusByCode(String gunCode) {
|
||||
return gunRepository.findGunWithStatusByCode(gunCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -167,33 +178,33 @@ public class DefaultGunService implements GunService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleGunRunStatus(String pileCode, String gunCode, GunRunStatus protoStatus, long ts) {
|
||||
log.info("处理充电枪状态上报: 桩编码={}, 枪编码={}, 状态={}", pileCode, gunCode, protoStatus);
|
||||
public boolean handleGunRunStatus(String pileCode, String gunNo, GunRunStatus protoStatus, long ts) {
|
||||
log.info("处理充电枪状态上报: 桩编码={}, 枪编号={}, 状态={}", pileCode, gunNo, protoStatus);
|
||||
|
||||
// 将Proto状态转换为数据库枚举
|
||||
GunRunStatusEnum dbStatus = convertProtoStatusToDbStatus(protoStatus);
|
||||
|
||||
if (dbStatus != null) {
|
||||
// 获取充电枪信息(使用缓存)
|
||||
Gun gun = findByPileCodeAndGunCode(pileCode, gunCode);
|
||||
Gun gun = findByPileCodeAndGunNo(pileCode, gunNo);
|
||||
if (gun != null) {
|
||||
// 检查状态是否真的发生了变化,避免重复保存
|
||||
String currentStatus = findGunStatus(gun.getId());
|
||||
if (dbStatus.name().equals(currentStatus)) {
|
||||
log.debug("充电枪状态未发生变化,跳过更新: 桩编码={}, 枪编码={}, 状态={}", pileCode, gunCode, dbStatus);
|
||||
log.debug("充电枪状态未发生变化,跳过更新: 桩编码={}, 枪编号={}, 状态={}", pileCode, gunNo, dbStatus);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存充电枪状态到属性表
|
||||
saveGunStatusChange(gun.getId(), dbStatus.name(), ts);
|
||||
|
||||
log.info("充电枪状态更新成功: 桩编码={}, 枪编码={}, 原状态={}, 新状态={}",
|
||||
pileCode, gunCode, currentStatus, dbStatus);
|
||||
|
||||
log.info("充电枪状态更新成功: 桩编码={}, 枪编号={}, 原状态={}, 新状态={}",
|
||||
pileCode, gunNo, currentStatus, dbStatus);
|
||||
|
||||
// 根据充电枪状态判断是否需要更新充电桩状态
|
||||
return shouldUpdatePileStatus(dbStatus);
|
||||
} else {
|
||||
log.warn("未找到充电枪: 桩编码={}, 枪编码={}", pileCode, gunCode);
|
||||
log.warn("未找到充电枪: 桩编码={}, 枪编号={}", pileCode, gunNo);
|
||||
}
|
||||
} else {
|
||||
log.warn("未知的充电枪状态: {}, 跳过更新", protoStatus);
|
||||
|
||||
@@ -13,6 +13,7 @@ import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import sanbing.jcpp.app.adapter.dto.*;
|
||||
import sanbing.jcpp.app.dal.config.ibatis.enums.GunRunStatusEnum;
|
||||
import sanbing.jcpp.app.dal.config.ibatis.enums.PileStatusEnum;
|
||||
import sanbing.jcpp.app.dal.entity.Gun;
|
||||
@@ -309,12 +310,12 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
try {
|
||||
GunRunStatusProto gunRunStatusProto = uplinkQueueMessage.getGunRunStatusProto();
|
||||
String pileCode = gunRunStatusProto.getPileCode();
|
||||
String gunCode = gunRunStatusProto.getGunCode();
|
||||
String gunNo = gunRunStatusProto.getGunNo();
|
||||
long ts = uplinkQueueMessage.getTs();
|
||||
GunRunStatus protoStatus = gunRunStatusProto.getGunRunStatus();
|
||||
|
||||
// 委托给 GunService 处理充电枪状态逻辑
|
||||
boolean needUpdatePileStatus = gunService.handleGunRunStatus(pileCode, gunCode, protoStatus, ts);
|
||||
boolean needUpdatePileStatus = gunService.handleGunRunStatus(pileCode, gunNo, protoStatus, ts);
|
||||
|
||||
// 如果需要,根据充电枪状态更新充电桩状态
|
||||
if (needUpdatePileStatus) {
|
||||
@@ -401,7 +402,7 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
|
||||
StartChargeRequest startChargeRequest = uplinkQueueMessage.getStartChargeRequest();
|
||||
String pileCode = startChargeRequest.getPileCode();
|
||||
String gunCode = startChargeRequest.getGunCode();
|
||||
String gunNo = startChargeRequest.getGunNo();
|
||||
// TODO 处理相关业务逻辑
|
||||
String orderNo = "ORD" + RandomStringUtils.secure().nextNumeric(20);
|
||||
String logicalCardNo = RandomStringUtils.secure().nextNumeric(12);
|
||||
@@ -413,29 +414,35 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
downlinkMessageBuilder.setStartChargeResponse(StartChargeResponse.newBuilder()
|
||||
.setTradeNo(orderNo)
|
||||
.setPileCode(startChargeRequest.getPileCode())
|
||||
.setGunCode(startChargeRequest.getGunCode())
|
||||
.setGunNo(startChargeRequest.getGunNo())
|
||||
.setLogicalCardNo(logicalCardNo)
|
||||
.setLimitYuan("50")
|
||||
.setAuthSuccess(true)
|
||||
.setFailReason(FailReason.ACCOUNT_NOT_ALLOWED_ON_PILE.name())
|
||||
);
|
||||
|
||||
log.info("业务[充电桩启动充电请求应答] 发送下行消息到充电桩: {}, 充电枪: {}", pileCode, gunCode);
|
||||
log.info("业务[充电桩启动充电请求应答] 发送下行消息到充电桩: {}, 充电枪: {}", pileCode, gunNo);
|
||||
downlinkCallService.sendDownlinkMessage(downlinkMessageBuilder,pileCode);
|
||||
|
||||
callback.onSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startCharge(String pileCode, String gunCode, BigDecimal limitYuan, String orderNo,
|
||||
String logicalCardNo, String physicalCardNo, String parallelNo) {
|
||||
public void startCharge(StartChargeDTO startChargeDto) {
|
||||
String pileCode = startChargeDto.getPileCode();
|
||||
String gunNo = startChargeDto.getGunNo();
|
||||
BigDecimal limitYuan = startChargeDto.getLimitYuan();
|
||||
String orderNo = startChargeDto.getOrderNo();
|
||||
String logicalCardNo = startChargeDto.getLogicalCardNo();
|
||||
String physicalCardNo = startChargeDto.getPhysicalCardNo();
|
||||
String parallelNo = startChargeDto.getParallelNo();
|
||||
|
||||
UUID messageId = UUID.randomUUID();
|
||||
UUID requestId = UUID.randomUUID();
|
||||
|
||||
RemoteStartChargingRequest.Builder requestBuilder = RemoteStartChargingRequest.newBuilder()
|
||||
.setPileCode(pileCode)
|
||||
.setGunCode(gunCode)
|
||||
.setGunNo(gunNo)
|
||||
.setLimitYuan(limitYuan.toPlainString())
|
||||
.setTradeNo(orderNo);
|
||||
|
||||
@@ -464,12 +471,14 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
.setDownlinkCmd(downlinkCmd.name())
|
||||
.setRemoteStartChargingRequest(requestBuilder.build());
|
||||
|
||||
log.info("业务[远程启动充电] 发送下行消息到充电桩: {}, 充电枪: {}, 订单号: {}", pileCode, gunCode, orderNo);
|
||||
log.info("业务[远程启动充电] 发送下行消息到充电桩: {}, 充电枪: {}, 订单号: {}", pileCode, gunNo, orderNo);
|
||||
downlinkCallService.sendDownlinkMessage(downlinkRequestMessageBuilder, pileCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopCharge(String pileCode, String gunCode) {
|
||||
public void stopCharge(StopChargeDTO stopChargeDto) {
|
||||
String pileCode = stopChargeDto.getPileCode();
|
||||
String gunNo = stopChargeDto.getGunNo();
|
||||
|
||||
UUID messageId = UUID.randomUUID();
|
||||
UUID requestId = UUID.randomUUID();
|
||||
@@ -483,20 +492,21 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
.setDownlinkCmd(DownlinkCmdEnum.REMOTE_STOP_CHARGING.name())
|
||||
.setRemoteStopChargingRequest(RemoteStopChargingRequest.newBuilder()
|
||||
.setPileCode(pileCode)
|
||||
.setGunCode(gunCode)
|
||||
.setGunNo(gunNo)
|
||||
.build());
|
||||
|
||||
log.info("业务[远程停止充电] 发送下行消息到充电桩: {}, 充电枪: {}", pileCode, gunCode);
|
||||
log.info("业务[远程停止充电] 发送下行消息到充电桩: {}, 充电枪: {}", pileCode, gunNo);
|
||||
downlinkCallService.sendDownlinkMessage(downlinkRequestMessageBuilder, pileCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restartPile(String pileCode, Integer type) {
|
||||
public void restartPile(RestartPileDTO restartPileDto) {
|
||||
String pileCode = restartPileDto.getPileCode();
|
||||
Integer type = restartPileDto.getType();
|
||||
|
||||
UUID messageId = UUID.randomUUID();
|
||||
UUID requestId = UUID.randomUUID();
|
||||
|
||||
|
||||
DownlinkRequestMessage.Builder downlinkRequestMessageBuilder = DownlinkRequestMessage.newBuilder()
|
||||
.setMessageIdMSB(messageId.getMostSignificantBits())
|
||||
.setMessageIdLSB(messageId.getLeastSignificantBits())
|
||||
@@ -514,7 +524,10 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPricing(String pileCode, SetPricingRequest setPricingRequest) {
|
||||
public void setPricing(SetPricingDTO setPricingDto) {
|
||||
String pileCode = setPricingDto.getPileCode();
|
||||
SetPricingRequest setPricingRequest = setPricingDto.getSetPricingRequest();
|
||||
|
||||
UUID messageId = UUID.randomUUID();
|
||||
UUID requestId = UUID.randomUUID();
|
||||
|
||||
@@ -557,9 +570,9 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
BmsChargingInfoProto bmsCharingInfoProto = uplinkQueueMessage.getBmsChargingInfoProto();
|
||||
String tradeNo = bmsCharingInfoProto.getTradeNo();
|
||||
String pileCode = bmsCharingInfoProto.getPileCode();
|
||||
String gunCode = bmsCharingInfoProto.getGunCode();
|
||||
String gunNo = bmsCharingInfoProto.getGunNo();
|
||||
String additionalInfo = bmsCharingInfoProto.getAdditionalInfo();
|
||||
log.info("BMS充电信息: 交易流水号: {}, 桩编码: {}, 枪号: {}, 附加信息: {}", tradeNo, pileCode, gunCode, additionalInfo);
|
||||
log.info("BMS充电信息: 交易流水号: {}, 桩编码: {}, 枪号: {}, 附加信息: {}", tradeNo, pileCode, gunNo, additionalInfo);
|
||||
// TODO 处理相关业务逻辑
|
||||
callback.onSuccess();
|
||||
}
|
||||
@@ -609,7 +622,7 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
BmsHandshakeProto bmsHandshakeProto = uplinkQueueMessage.getBmsHandshakeProto();
|
||||
String tradeNo = bmsHandshakeProto.getTradeNo();
|
||||
String pileCode = bmsHandshakeProto.getPileCode();
|
||||
String gunCode = bmsHandshakeProto.getGunCode();
|
||||
String gunNo = bmsHandshakeProto.getGunNo();
|
||||
String carVinCode = bmsHandshakeProto.getCarVinCode();
|
||||
String bmsProtocolVersion = bmsHandshakeProto.getBmsProtocolVersion();
|
||||
int bmsBatteryType = bmsHandshakeProto.getBmsBatteryType();
|
||||
@@ -617,8 +630,8 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
String additionalInfo = bmsHandshakeProto.getAdditionalInfo();
|
||||
|
||||
log.info("BMS充电握手信息: 交易流水号: {}, 桩编码: {}, 枪号: {}, 车辆VIN: {}, BMS协议版本: {}, " +
|
||||
"电池类型: {}, 电池容量: {}Ah, 附加信息: {}",
|
||||
tradeNo, pileCode, gunCode, carVinCode, bmsProtocolVersion,
|
||||
"电池类型: {}, 电池容量: {}Ah, 附加信息: {}",
|
||||
tradeNo, pileCode, gunNo, carVinCode, bmsProtocolVersion,
|
||||
bmsBatteryType, bmsPowerCapacity, additionalInfo);
|
||||
|
||||
// TODO 处理相关业务逻辑,比如保存握手信息到数据库
|
||||
@@ -627,7 +640,10 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timeSync(String pileCode, LocalDateTime time) {
|
||||
public void timeSync(TimeSyncDTO timeSyncDto) {
|
||||
String pileCode = timeSyncDto.getPileCode();
|
||||
LocalDateTime time = timeSyncDto.getTime();
|
||||
|
||||
UUID messageId = UUID.randomUUID();
|
||||
UUID requestId = UUID.randomUUID();
|
||||
DownlinkRequestMessage.Builder downlinkRequestMessageBuilder = DownlinkRequestMessage.newBuilder()
|
||||
@@ -689,32 +705,33 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
log.info("接收到地锁状态信息 {}", uplinkQueueMessage);
|
||||
GroundLockStatusProto groundLockStatusProto = uplinkQueueMessage.getGroundLockStatusProto();
|
||||
String pileCode = groundLockStatusProto.getPileCode();
|
||||
String gunCode = groundLockStatusProto.getGunCode();
|
||||
String gunNo = groundLockStatusProto.getGunNo();
|
||||
int lockStatus = groundLockStatusProto.getLockStatus();
|
||||
int parkStatus = groundLockStatusProto.getParkStatus();
|
||||
int lockBattery = groundLockStatusProto.getLockBattery();
|
||||
int alarmStatus = groundLockStatusProto.getAlarmStatus();
|
||||
|
||||
log.info("地锁状态信息: 桩编码: {}, 枪号: {}, 车位锁状态: {}, 车位状态: {}, 地锁电量: {}%, 报警状态: {}",
|
||||
pileCode, gunCode, lockStatus, parkStatus, lockBattery, alarmStatus);
|
||||
pileCode, gunNo, lockStatus, parkStatus, lockBattery, alarmStatus);
|
||||
|
||||
try {
|
||||
// 获取时间戳
|
||||
long ts = uplinkQueueMessage.getTs();
|
||||
|
||||
// 获取充电枪信息
|
||||
Gun gun = gunService.findByPileCodeAndGunCode(pileCode, gunCode);
|
||||
// 注意:充电桩上报的gunNo实际上是枪编号(gun_no),不是完整的枪编码(gun_code)
|
||||
Gun gun = gunService.findByPileCodeAndGunNo(pileCode, gunNo);
|
||||
if (gun != null) {
|
||||
// 保存地锁状态到属性表
|
||||
saveLockStatusToAttributes(gun.getId(), lockStatus, parkStatus, lockBattery, alarmStatus, ts);
|
||||
|
||||
log.info("地锁和车位状态已保存: 桩编码={}, 枪编码={}, 地锁状态={}, 车位状态={}",
|
||||
pileCode, gunCode, lockStatus, parkStatus);
|
||||
pileCode, gunNo, lockStatus, parkStatus);
|
||||
} else {
|
||||
log.warn("未找到充电枪,无法保存地锁状态: 桩编码={}, 枪编码={}", pileCode, gunCode);
|
||||
log.warn("未找到充电枪,无法保存地锁状态: 桩编码={}, 枪编码={}", pileCode, gunNo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("保存地锁状态失败: 桩编码={}, 枪编码={}", pileCode, gunCode, e);
|
||||
log.error("保存地锁状态失败: 桩编码={}, 枪编码={}", pileCode, gunNo, e);
|
||||
callback.onFailure(e);
|
||||
return;
|
||||
}
|
||||
@@ -855,11 +872,11 @@ public class DefaultPileProtocolService implements PileProtocolService {
|
||||
log.info("接收到充电过程BMS需求与充电机输出信息:{}", uplinkQueueMessage);
|
||||
BmsDemandChargerOutputProto bmsDemandChargerOutputProto = uplinkQueueMessage.getBmsDemandChargerOutputProto();
|
||||
String pileCode = bmsDemandChargerOutputProto.getPileCode();
|
||||
String gunCode = bmsDemandChargerOutputProto.getGunCode();
|
||||
String gunNo = bmsDemandChargerOutputProto.getGunNo();
|
||||
String tradeNo = bmsDemandChargerOutputProto.getTradeNo();
|
||||
String additionalInfo = bmsDemandChargerOutputProto.getAdditionalInfo();
|
||||
log.info("充电过程BMS需求与充电机输出信息: 桩编码: {}, 枪号: {}, 交易流水号: {}, 附加信息: {}",
|
||||
pileCode, gunCode, tradeNo, additionalInfo);
|
||||
pileCode, gunNo, tradeNo, additionalInfo);
|
||||
// TODO 处理相关业务逻辑
|
||||
callback.onSuccess();
|
||||
}
|
||||
|
||||
@@ -82,6 +82,11 @@ public class DefaultPileService implements PileService {
|
||||
return pileMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pile findByPileCode(String pileCode) {
|
||||
return pileRepository.findPileByCode(pileCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pile updatePile(UUID id, PileUpdateRequest request) throws JCPPException {
|
||||
Pile existingPile = findById(id);
|
||||
|
||||
@@ -98,11 +98,62 @@
|
||||
</choose>
|
||||
</select>
|
||||
|
||||
<!-- 根据充电桩编码和充电枪编码查询充电枪 -->
|
||||
<select id="selectByPileCodeAndGunCode" resultType="sanbing.jcpp.app.dal.entity.Gun">
|
||||
|
||||
<!-- 根据充电桩编码和充电枪编号查询充电枪 -->
|
||||
<select id="selectByPileCodeAndGunNo" resultType="sanbing.jcpp.app.dal.entity.Gun">
|
||||
SELECT g.* FROM t_gun g
|
||||
INNER JOIN t_pile p ON g.pile_id = p.id
|
||||
WHERE p.pile_code = #{pileCode} AND g.gun_code = #{gunCode}
|
||||
INNER JOIN t_pile p ON g.pile_id = p.id
|
||||
WHERE p.pile_code = #{pileCode}
|
||||
AND g.gun_no = #{gunNo}
|
||||
</select>
|
||||
|
||||
<!-- 根据枪编号查询充电枪 -->
|
||||
<select id="selectByGunCode" resultType="sanbing.jcpp.app.dal.entity.Gun">
|
||||
SELECT *
|
||||
FROM t_gun
|
||||
WHERE gun_code = #{gunCode}
|
||||
</select>
|
||||
|
||||
<!-- 根据枪编号查询充电枪详细信息(包含关联信息) -->
|
||||
<select id="selectGunWithStatusByCode" resultType="sanbing.jcpp.app.adapter.response.GunWithStatusResponse">
|
||||
SELECT
|
||||
<!-- 充电枪基本信息 -->
|
||||
g.id,
|
||||
g.created_time,
|
||||
g.updated_time,
|
||||
g.gun_name,
|
||||
g.gun_no,
|
||||
g.gun_code,
|
||||
g.station_id,
|
||||
g.pile_id,
|
||||
g.additional_info,
|
||||
g.version,
|
||||
|
||||
<!-- 充电站信息 -->
|
||||
s.station_name,
|
||||
|
||||
<!-- 充电桩信息 -->
|
||||
p.pile_name,
|
||||
p.pile_code,
|
||||
|
||||
<!-- 状态相关属性 -->
|
||||
a_run_status.str_v as run_status
|
||||
|
||||
FROM t_gun g
|
||||
|
||||
<!-- LEFT JOIN 获取充电站信息 -->
|
||||
LEFT JOIN t_station s ON g.station_id = s.id
|
||||
|
||||
<!-- LEFT JOIN 获取充电桩信息 -->
|
||||
LEFT JOIN t_pile p ON g.pile_id = p.id
|
||||
|
||||
<!-- LEFT JOIN 获取充电枪运行状态属性 -->
|
||||
LEFT JOIN t_attr a_run_status ON (
|
||||
a_run_status.entity_id = g.id AND
|
||||
a_run_status.attr_key = 'gunRunStatus'
|
||||
)
|
||||
|
||||
WHERE g.gun_code = #{gunCode}
|
||||
</select>
|
||||
|
||||
<!-- 统计充电桩下的充电枪数量 -->
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="sanbing.jcpp.app.dal.mapper.PileMapper">
|
||||
|
||||
<!-- 充电桩带状态信息的分页查询 -->
|
||||
<!-- 充电桩带状态信息的分页查询 - 性能优化版本 -->
|
||||
<select id="selectPileWithStatusPage" resultType="sanbing.jcpp.app.adapter.response.PileWithStatusResponse">
|
||||
SELECT
|
||||
<!-- 充电桩基本信息 -->
|
||||
@@ -27,38 +27,30 @@
|
||||
p.type,
|
||||
p.additional_info,
|
||||
p.version,
|
||||
|
||||
<!-- 状态相关属性 - 使用CASE WHEN优化多次JOIN -->
|
||||
MAX(CASE WHEN a.attr_key = #{statusKey.code} THEN a.str_v END) as status,
|
||||
MAX(CASE WHEN a.attr_key = #{connectedAtKey.code} THEN a.last_update_ts END) as connected_at,
|
||||
MAX(CASE WHEN a.attr_key = #{disconnectedAtKey.code} THEN a.last_update_ts END) as disconnected_at,
|
||||
MAX(CASE WHEN a.attr_key = #{lastActiveTimeKey.code} THEN a.last_update_ts END) as last_active_time,
|
||||
|
||||
<!-- 充电枪数量 - 使用LEFT JOIN优化子查询 -->
|
||||
COALESCE(g.gun_count, 0) as gun_count
|
||||
|
||||
<!-- 状态相关属性 -->
|
||||
a_status.str_v as status,
|
||||
a_connected.last_update_ts as connected_at,
|
||||
a_disconnected.last_update_ts as disconnected_at,
|
||||
a_last_active.last_update_ts as last_active_time
|
||||
|
||||
FROM t_pile p
|
||||
|
||||
<!-- LEFT JOIN 获取充电桩状态属性 -->
|
||||
LEFT JOIN t_attr a_status ON (
|
||||
a_status.entity_id = p.id AND
|
||||
a_status.attr_key = #{statusKey.code}
|
||||
)
|
||||
|
||||
<!-- LEFT JOIN 获取最后连接时间属性 -->
|
||||
LEFT JOIN t_attr a_connected ON (
|
||||
a_connected.entity_id = p.id AND
|
||||
a_connected.attr_key = #{connectedAtKey.code}
|
||||
)
|
||||
|
||||
<!-- LEFT JOIN 获取最后断开时间属性 -->
|
||||
LEFT JOIN t_attr a_disconnected ON (
|
||||
a_disconnected.entity_id = p.id AND
|
||||
a_disconnected.attr_key = #{disconnectedAtKey.code}
|
||||
)
|
||||
|
||||
<!-- LEFT JOIN 获取最后活跃时间属性 -->
|
||||
LEFT JOIN t_attr a_last_active ON (
|
||||
a_last_active.entity_id = p.id AND
|
||||
a_last_active.attr_key = #{lastActiveTimeKey.code}
|
||||
FROM t_pile p
|
||||
|
||||
<!-- 单次JOIN获取所有属性,避免多次JOIN -->
|
||||
LEFT JOIN t_attr a ON (
|
||||
a.entity_id = p.id AND
|
||||
a.attr_key IN (#{statusKey.code}, #{connectedAtKey.code}, #{disconnectedAtKey.code}, #{lastActiveTimeKey.code})
|
||||
)
|
||||
|
||||
<!-- LEFT JOIN 获取充电枪数量统计 -->
|
||||
LEFT JOIN (
|
||||
SELECT pile_id, COUNT(*) as gun_count
|
||||
FROM t_gun
|
||||
GROUP BY pile_id
|
||||
) g ON g.pile_id = p.id
|
||||
|
||||
<!-- 动态WHERE条件 -->
|
||||
<where>
|
||||
@@ -87,9 +79,18 @@
|
||||
AND p.type = #{request.type}
|
||||
</if>
|
||||
<if test="request.status != null">
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM t_attr a_status
|
||||
WHERE a_status.entity_id = p.id
|
||||
AND a_status.attr_key = #{statusKey.code}
|
||||
AND a_status.str_v = #{request.status.code}
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
|
||||
<!-- GROUP BY 子句 - 因为使用了聚合函数 -->
|
||||
GROUP BY p.id, p.created_time, p.updated_time, p.pile_name, p.pile_code, p.protocol,
|
||||
p.station_id, p.brand, p.model, p.manufacturer, p.type, p.additional_info, p.version, g.gun_count
|
||||
|
||||
<!-- 动态ORDER BY -->
|
||||
ORDER BY
|
||||
@@ -116,16 +117,19 @@
|
||||
p.type ${request.sortOrder}
|
||||
</when>
|
||||
<when test="request.sortField == 'status'">
|
||||
a_status.str_v ${request.sortOrder}
|
||||
MAX(CASE WHEN a.attr_key = #{statusKey.code} THEN a.str_v END) ${request.sortOrder}
|
||||
</when>
|
||||
<when test="request.sortField == 'connectedAt'">
|
||||
a_connected.last_update_ts ${request.sortOrder}
|
||||
MAX(CASE WHEN a.attr_key = #{connectedAtKey.code} THEN a.last_update_ts END) ${request.sortOrder}
|
||||
</when>
|
||||
<when test="request.sortField == 'disconnectedAt'">
|
||||
a_disconnected.last_update_ts ${request.sortOrder}
|
||||
MAX(CASE WHEN a.attr_key = #{disconnectedAtKey.code} THEN a.last_update_ts END) ${request.sortOrder}
|
||||
</when>
|
||||
<when test="request.sortField == 'lastActiveTime'">
|
||||
a_last_active.last_update_ts ${request.sortOrder}
|
||||
MAX(CASE WHEN a.attr_key = #{lastActiveTimeKey.code} THEN a.last_update_ts END) ${request.sortOrder}
|
||||
</when>
|
||||
<when test="request.sortField == 'gunCount'">
|
||||
COALESCE(g.gun_count, 0) ${request.sortOrder}
|
||||
</when>
|
||||
<when test="request.sortField == 'createdTime'">
|
||||
p.created_time ${request.sortOrder}
|
||||
|
||||
@@ -96,11 +96,13 @@ CREATE TABLE IF NOT EXISTS t_gun
|
||||
version int default 1
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_gun_pile_id
|
||||
on t_gun (pile_id);
|
||||
-- pile_id + gun_no 唯一索引,确保同一充电桩下枪号唯一
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uni_gun_pile_gun_no
|
||||
on t_gun (pile_id, gun_no);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_gun_pile_gun_code
|
||||
on t_gun (pile_id, gun_code);
|
||||
-- gun_code 唯一索引,确保充电枪编码全局唯一
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uni_gun_code
|
||||
on t_gun (gun_code);
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user