Ver código fonte

预约充电

skyline 2 anos atrás
pai
commit
9a0ab53ecb

+ 1 - 0
admin-web/src/views/admin/station/endpoint/index.vue

@@ -169,6 +169,7 @@ const state = reactive({
       {label: '站点编号', prop: 'stationNo', resizable: true, width: 100, fixed: 'left'},
       {label: '站点名称', prop: 'stationName', resizable: true, width: 200, fixed: 'left'},
       {label: '充电桩编号', prop: 'shortId', resizable: true, width: 110, fixed: 'left'},
+      {label: '车位编号', prop: 'parkingNo', resizable: true, width: 90, fixed: 'left'},
       {label: '充电桩序列号', prop: 'equipmentId', width: 180, resizable: true},
       {label: '设备型号', prop: 'equipmentModel', width: 150, resizable: true},
       {label: '服务状态', prop: 'serviceStatus', resizable: true, width: 130},

+ 7 - 2
admin/src/main/java/com/kym/admin/controller/FinanceController.java

@@ -25,7 +25,12 @@ public class FinanceController {
         this.refundLogService = refundLogService;
     }
 
-    @SysLog("退款申请列表")
+    /**
+     * 退款申请列表
+     *
+     * @param params
+     * @return
+     */
     @GetMapping("/listRefundLog")
     R<?> listRefundLog(@ModelAttribute CommonQueryParam params) {
         return R.success(refundLogService.listRefundLog(params));
@@ -39,7 +44,7 @@ public class FinanceController {
     }
 
     @GetMapping("handleInvoice")
-    R<?> handleInvoice(){
+    R<?> handleInvoice() {
         return R.success(wxPayService.baseInformation());
     }
 

+ 3 - 1
entity/src/main/java/com/kym/entity/BaseEntity.java

@@ -3,6 +3,7 @@ package com.kym.entity;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
+import java.io.Serializable;
 import java.time.LocalDateTime;
 
 /**
@@ -11,7 +12,8 @@ import java.time.LocalDateTime;
  * @date 2023-08-14 19:00
  */
 @Data
-public class BaseEntity {
+public class BaseEntity implements Serializable {
+    private static final long serialVersionUID = 1L;
     /**
      * ID
      */

+ 6 - 5
entity/src/main/java/com/kym/entity/admin/EquipmentInfo.java

@@ -19,11 +19,12 @@ import lombok.Data;
 @TableName("t_equipment_info")
 public class EquipmentInfo extends BaseEntity {
 
-    private static final long serialVersionUID = 1L;
-
-    private static int SERVICE_STATUS_空闲 = 0;
-    private static int SERVICE_STATUS_已连接 = 1;
-    private static int SERVICE_STATUS_充电中 = 2;
+    public static int SERVICE_STATUS_离线 = 0;
+    public static int SERVICE_STATUS_空闲 = 1;
+    public static int SERVICE_STATUS_已连接 = 2;
+    public static int SERVICE_STATUS_充电中 = 3;
+    public static int SERVICE_STATUS_预约中 = 4;
+    public static int SERVICE_STATUS_故障 = 255;
 
     /**
      * 公司id

+ 1 - 0
entity/src/main/java/com/kym/entity/miniapp/ChargeOrder.java

@@ -29,6 +29,7 @@ public class ChargeOrder extends BaseEntity implements Serializable {
     public static int ORDER_STATUS_失败 = 2;
 
 
+    public static int CHARGE_STATUS_预约中 = 0;
     public static int CHARGE_STATUS_启动中 = 1;
     public static int CHARGE_STATUS_充电中 = 2;
     public static int CHARGE_STATUS_停止中 = 3;

+ 6 - 3
miniapp/src/main/java/com/kym/miniapp/controller/ChargerController.java

@@ -14,6 +14,7 @@ import com.kym.service.miniapp.ChargeService;
 import lombok.SneakyThrows;
 import org.springframework.web.bind.annotation.*;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 
@@ -79,9 +80,11 @@ public class ChargerController {
     }
 
     @ApiLog("启动充电")
-    @GetMapping("/startCharge/{connectorId}")
-    R startCharge(@PathVariable("connectorId") String connectorId) {
-        return R.success(chargeService.queryStartCharge(connectorId));
+    @GetMapping({"/startCharge/{connectorId}", "/startCharge/{connectorId}"})
+    R startCharge(@PathVariable("connectorId") String connectorId,
+                  @RequestParam(value = "isBooking", required = false) Boolean isBooking,
+                  @RequestParam(value = "startTime", required = false) LocalDateTime startTime) {
+        return R.success(chargeService.queryStartCharge(connectorId, isBooking,startTime));
     }
 
     @ApiLog("停止充电")

+ 2 - 1
service/src/main/java/com/kym/service/miniapp/ChargeService.java

@@ -3,6 +3,7 @@ package com.kym.service.miniapp;
 import com.kym.entity.enplus.response.EnBusinessPolicy;
 import com.kym.entity.miniapp.ChargeOrder;
 
+import java.time.LocalDateTime;
 import java.util.Map;
 
 /**
@@ -12,7 +13,7 @@ import java.util.Map;
  */
 public interface ChargeService {
 
-    Map queryStartCharge(String connectorId);
+    Map queryStartCharge(String connectorId, Boolean isBooking, LocalDateTime startTime);
 
     ChargeOrder queryEquipChargeStatus();
 

+ 3 - 2
service/src/main/java/com/kym/service/miniapp/impl/ChargeOrderServiceImpl.java

@@ -43,8 +43,8 @@ import java.util.stream.Collectors;
 @DS("db-miniapp")
 public class ChargeOrderServiceImpl extends ServiceImpl<ChargeOrderMapper, ChargeOrder> implements ChargeOrderService {
 
-    private final KymCache kymCache;
 
+    private final KymCache kymCache;
     private final ExportService exportService;
 
     public ChargeOrderServiceImpl(KymCache kymCache, ExportService exportService) {
@@ -52,6 +52,7 @@ public class ChargeOrderServiceImpl extends ServiceImpl<ChargeOrderMapper, Charg
         this.exportService = exportService;
     }
 
+
     @Override
     public ChargeOrder getChargingOrderByUserId(Long userId) {
         return lambdaQuery()
@@ -173,7 +174,7 @@ public class ChargeOrderServiceImpl extends ServiceImpl<ChargeOrderMapper, Charg
             item.setServiceMoneyPercent(BigDecimal.ONE.subtract(item.getElecMoneyPercent()));
         }).collect(Collectors.toList());
         var map = Map.of(
-                "totalPower", BigDecimal.valueOf(res.stream().mapToDouble(StationStatVo::getTotalPower).sum()).setScale(2,RoundingMode.HALF_UP).doubleValue(),
+                "totalPower", BigDecimal.valueOf(res.stream().mapToDouble(StationStatVo::getTotalPower).sum()).setScale(2, RoundingMode.HALF_UP).doubleValue(),
                 "serviceMoney", res.stream().mapToInt(StationStatVo::getServiceMoney).sum()
         );
         return new PageBean<>(res).setExtraData(map);

+ 55 - 9
service/src/main/java/com/kym/service/miniapp/impl/ChargeServiceImpl.java

@@ -6,18 +6,25 @@ import com.kym.common.config.EnPlusConfig;
 import com.kym.common.constant.ResponseEnum;
 import com.kym.common.exception.BusinessException;
 import com.kym.common.utils.OrderUtils;
+import com.kym.entity.admin.EquipmentInfo;
 import com.kym.entity.enplus.response.EnBusinessPolicy;
 import com.kym.entity.miniapp.ChargeOrder;
+import com.kym.service.admin.EquipmentInfoService;
 import com.kym.service.admin.EquipmentRelationService;
 import com.kym.service.enplus.EnPlusService;
 import com.kym.service.miniapp.AccountService;
 import com.kym.service.miniapp.ChargeOrderService;
 import com.kym.service.miniapp.ChargeService;
+import com.kym.service.queue.DelayedItem;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDateTime;
 import java.util.Map;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 /**
  * @author skyline
@@ -31,6 +38,8 @@ public class ChargeServiceImpl implements ChargeService {
 
     private final EquipmentRelationService equipmentRelationService;
 
+    private final EquipmentInfoService equipmentInfoService;
+
     private final ChargeOrderService chargeOrderService;
 
     private final AccountService accountService;
@@ -39,8 +48,16 @@ public class ChargeServiceImpl implements ChargeService {
 
     private final EnPlusConfig enPlusConfig;
 
-    public ChargeServiceImpl(EquipmentRelationService equipmentRelationService, ChargeOrderService chargeOrderService, AccountService accountService, EnPlusService enPlusService, EnPlusConfig enPlusConfig) {
+    /**
+     * 预约充电队列
+     */
+    DelayQueue<DelayedItem> delayQueue = new DelayQueue<>();
+
+    private ExecutorService executorService = Executors.newFixedThreadPool(2);
+
+    public ChargeServiceImpl(EquipmentRelationService equipmentRelationService, EquipmentInfoService equipmentInfoService, ChargeOrderService chargeOrderService, AccountService accountService, EnPlusService enPlusService, EnPlusConfig enPlusConfig) {
         this.equipmentRelationService = equipmentRelationService;
+        this.equipmentInfoService = equipmentInfoService;
         this.chargeOrderService = chargeOrderService;
         this.accountService = accountService;
         this.enPlusService = enPlusService;
@@ -51,17 +68,26 @@ public class ChargeServiceImpl implements ChargeService {
      * 启动充电
      *
      * @param connectorId
+     * @param isBooking
+     * @param startTime
      * @return
      */
     @Override
-    public Map<String, String> queryStartCharge(String connectorId) {
+    public Map<String, String> queryStartCharge(String connectorId, Boolean isBooking, LocalDateTime startTime) {
         var userId = StpUtil.getLoginIdAsLong();
+//        if (isBooking) {
+//            // 预约充电通过connectorId查询预约中的订单
+//            var bookingOrder = chargeOrderService.lambdaQuery()
+//                    .eq(ChargeOrder::getConnectorId, connectorId).eq(ChargeOrder::getChargeStatus, ChargeOrder.CHARGE_STATUS_预约中).one();
+//            userId =bookingOrder.getUserId();
+//        }
         var map = getConnectorIdAndStationId(connectorId);
         connectorId = map.get("connectorId");
         var stationId = map.get("stationId");
         LOGGER.info("用户:{},设备:{}请求启动充电", userId, connectorId);
         // 二维码文本
         var qrCode = "";
+
         // 当前设备是是否有正在进行中的订单
         var chargeOrder = chargeOrderService.getChargingOrderByUserId(userId);
         if (chargeOrder != null) {
@@ -83,18 +109,38 @@ public class ChargeServiceImpl implements ChargeService {
         // 请求设备认证
         var equipAuth = enPlusService.queryEquipAuth(connectorId, startChargeSeq);
 
+        // 组装订单数据
+        var order = new ChargeOrder();
+        order.setUserId(userId);
+        order.setStationId(stationId);
+        order.setStartChargeSeq(startChargeSeq);
+        order.setConnectorId(connectorId);
+        order.setOrderStatus(ChargeOrder.ORDER_STATUS_未知);
+
+        // 如果是预约订单,则将订单放入预约充电延迟队列
+        if (isBooking) {
+            order.setChargeStatus(ChargeOrder.CHARGE_STATUS_预约中);
+            chargeOrderService.save(order);
+            var flag = delayQueue.offer(new DelayedItem<ChargeOrder>(order, startTime));
+            if (flag) {
+                // 修改设备状态为预约中
+                equipmentInfoService.lambdaUpdate()
+                        .set(EquipmentInfo::getServiceStatus, EquipmentInfo.SERVICE_STATUS_预约中)
+                        .eq(EquipmentInfo::getEquipmentId, connectorId.substring(0, 16))
+                        .update();
+                return Map.of("startChargeSeq", startChargeSeq);
+            } else {
+                throw new BusinessException("充电预约失败");
+            }
+        }
+
+        chargeOrderService.save(order);
+
         if (equipAuth.containsKey("SuccStat") && equipAuth.getIntValue("SuccStat") == 0) {
-            // TODO 查询业务策略信息(计费信息),目前计费在EN+完成,后续自主计费需要开发
             // 启动充电
             var startCharge = enPlusService.queryStartCharge(startChargeSeq, connectorId, qrCode, amount);
             if (startCharge.containsKey("SuccStat") && startCharge.getIntValue("SuccStat") == 0) {
                 // 启动成功,生成充电订单
-                var order = new ChargeOrder();
-                order.setUserId(userId);
-                order.setStationId(stationId);
-                order.setStartChargeSeq(startChargeSeq);
-                order.setConnectorId(connectorId);
-                order.setOrderStatus(ChargeOrder.ORDER_STATUS_未知);
                 order.setChargeStatus(startCharge.getIntValue("StartChargeSeqStat"));
                 chargeOrderService.save(order);
                 return Map.of("startChargeSeq", startChargeSeq);

+ 14 - 0
service/src/main/java/com/kym/service/queue/ChargeDelayConsumer.java

@@ -0,0 +1,14 @@
+package com.kym.service.queue;
+
+/**
+ * @author skyline
+ * @description
+ * @date 2023-10-08 19:20
+ */
+
+public class ChargeDelayConsumer implements Runnable{
+    @Override
+    public void run() {
+
+    }
+}

+ 32 - 0
service/src/main/java/com/kym/service/queue/DelayService.java

@@ -0,0 +1,32 @@
+package com.kym.service.queue;
+
+/**
+ * @author skyline
+ * @description
+ * @date 2023-10-08 21:41
+ */
+public interface DelayService<T> {
+    /**
+     * 添加延迟对象到延时队列
+     *
+     * @param delayedItem 延迟对象
+     * @return boolean
+     */
+    boolean addToOrderDelayQueue(DelayedItem<T> delayedItem);
+
+    /**
+     * 根据对象添加到指定延时队列
+     *
+     * @param data 数据对象
+     * @return boolean
+     */
+    boolean addToDelayQueue(T data);
+
+    /**
+     * 移除指定的延迟对象从延时队列中
+     *
+     * @param data
+     */
+    void removeToOrderDelayQueue(T data);
+
+}

+ 85 - 0
service/src/main/java/com/kym/service/queue/DelayServiceImpl.java

@@ -0,0 +1,85 @@
+package com.kym.service.queue;
+
+import com.kym.entity.miniapp.ChargeOrder;
+import com.kym.service.miniapp.ChargeOrderService;
+import com.kym.service.miniapp.ChargeService;
+import jakarta.annotation.PostConstruct;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * @author skyline
+ * @description
+ * @date 2023-10-08 22:11
+ */
+@Service
+@Slf4j
+public class DelayServiceImpl implements DelayService<ChargeOrder> {
+
+    /**
+     * 预约订单队列
+     */
+    private final static DelayQueue<DelayedItem<ChargeOrder>> DELAY_QUEUE = new DelayQueue<>();
+    private final ChargeOrderService chargeOrderService;
+    private final ChargeService chargeService;
+    /**
+     * 线程池
+     */
+    private final ExecutorService executor = Executors.newFixedThreadPool(2);
+
+    public DelayServiceImpl(ChargeOrderService chargeOrderService, ChargeService chargeService) {
+        this.chargeOrderService = chargeOrderService;
+        this.chargeService = chargeService;
+    }
+
+    @PostConstruct
+    public void init() {
+        // 队列加载所有充电状态为预约中的订单,按照开始时间排序
+        var orderList = chargeOrderService.lambdaQuery()
+                .eq(ChargeOrder::getChargeStatus, ChargeOrder.CHARGE_STATUS_预约中)
+                .orderByAsc(ChargeOrder::getStartTime)
+                .list();
+        DELAY_QUEUE.addAll(orderList.stream().map(order -> new DelayedItem<>(order, order.getStartTime())).toList());
+
+        // 开启线程处理队列消息
+        executor.execute(() -> {
+            log.info("启动预约充电订单处理线程:{}", Thread.currentThread().getName());
+            DelayedItem<ChargeOrder> delayedItem;
+            while (true) {
+                try {
+                    delayedItem = DELAY_QUEUE.take();
+                    // 启动充电
+//                    chargeService.queryStartCharge(delayedItem.data.getConnectorId())
+                } catch (InterruptedException e) {
+                    log.error("预约充电队列take异常", e);
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+
+    }
+
+
+    @Override
+    public boolean addToOrderDelayQueue(DelayedItem<ChargeOrder> delayedItem) {
+        return DELAY_QUEUE.add(delayedItem);
+    }
+
+    @Override
+    public boolean addToDelayQueue(ChargeOrder chargeOrder) {
+        DelayedItem<ChargeOrder> orderDelayed = new DelayedItem<>(chargeOrder, chargeOrder.getStartTime());
+        return DELAY_QUEUE.add(orderDelayed);
+    }
+
+    @Override
+    public void removeToOrderDelayQueue(ChargeOrder chargeOrder) {
+        if (chargeOrder == null) {
+            return;
+        }
+        DELAY_QUEUE.removeIf(queue -> queue.data.getStartChargeSeq().equals(chargeOrder.getStartChargeSeq()));
+    }
+}

+ 41 - 0
service/src/main/java/com/kym/service/queue/DelayedItem.java

@@ -0,0 +1,41 @@
+package com.kym.service.queue;
+
+import jakarta.validation.constraints.NotNull;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author skyline
+ * @description 延迟执行
+ * @date 2023-10-08 21:43
+ */
+public class DelayedItem<T> implements Delayed {
+    /**
+     * 业务对象
+     */
+    final T data;
+
+    /**
+     * 预约启动时间
+     */
+    final LocalDateTime startTime;
+
+    public DelayedItem(T data, LocalDateTime startTime) {
+        this.data = data;
+        this.startTime = startTime;
+    }
+
+
+    @Override
+    public long getDelay(@NotNull TimeUnit unit) {
+        return unit.convert(Duration.between(startTime, LocalDateTime.now()));
+    }
+
+    @Override
+    public int compareTo(@NotNull Delayed o) {
+        return (int) (getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS));
+    }
+}