Преглед изворни кода

feat(channel): 支持渠道方订单及相关业务逻辑

- 在AppInvokeChargeForm中新增渠道方订单相关字段(渠道方订单编号、用户手机号、预支付金额)
- ChargeOrderInfo及AppStopChargeForm调整支持渠道方订单类型
- AppletHomeService新增获取渠道方充电费用接口及实现
- AppletStationController添加渠道方实时费用查询接口
- ChargeOrderInfoServiceImpl新增渠道方启动充电逻辑及订单创建流程
- ChargingReceptionServiceImpl扩展充电状态推送支持渠道方异步通知机制
- 根据渠道方订单类型实现不同的订单结算和账户变动逻辑
- FirmAccountLog及相关处理去除无用字段并统一事件描述字段
- FirmInfo新增渠道方地址字段支持外部通知回调
- 代码中注释、日志及异常处理针对渠道方业务进行完善优化
- main函数中测试代码注释,新增BigDecimal计算示范,提升代码整洁性
wzq пре 2 недеља
родитељ
комит
d668f3129e
26 измењених фајлова са 464 додато и 199 уклоњено
  1. 13 4
      src/main/java/com/zsElectric/boot/business/controller/applet/AppletStationController.java
  2. 2 0
      src/main/java/com/zsElectric/boot/business/mapper/ThirdPartyStationInfoMapper.java
  3. 1 1
      src/main/java/com/zsElectric/boot/business/model/entity/ChargeOrderInfo.java
  4. 3 21
      src/main/java/com/zsElectric/boot/business/model/entity/FirmAccountLog.java
  5. 5 0
      src/main/java/com/zsElectric/boot/business/model/entity/FirmInfo.java
  6. 11 1
      src/main/java/com/zsElectric/boot/business/model/form/applet/AppInvokeChargeForm.java
  7. 3 3
      src/main/java/com/zsElectric/boot/business/model/form/applet/AppStopChargeForm.java
  8. 0 6
      src/main/java/com/zsElectric/boot/business/model/query/FirmAccountLogQuery.java
  9. 3 0
      src/main/java/com/zsElectric/boot/business/model/query/ThirdPartyStationInfoQuery.java
  10. 2 11
      src/main/java/com/zsElectric/boot/business/model/vo/FirmAccountLogVO.java
  11. 2 0
      src/main/java/com/zsElectric/boot/business/model/vo/FirmInfoVO.java
  12. 1 0
      src/main/java/com/zsElectric/boot/business/service/AppletHomeService.java
  13. 5 0
      src/main/java/com/zsElectric/boot/business/service/impl/AppletHomeServiceImpl.java
  14. 69 4
      src/main/java/com/zsElectric/boot/business/service/impl/ChargeOrderInfoServiceImpl.java
  15. 1 6
      src/main/java/com/zsElectric/boot/business/service/impl/FirmAccountLogServiceImpl.java
  16. 2 3
      src/main/java/com/zsElectric/boot/business/service/impl/FirmInfoServiceImpl.java
  17. 177 76
      src/main/java/com/zsElectric/boot/charging/service/impl/ChargingReceptionServiceImpl.java
  18. 6 0
      src/main/java/com/zsElectric/boot/common/constant/SystemConstants.java
  19. 61 53
      src/main/java/com/zsElectric/boot/common/util/AESCryptoUtils.java
  20. 1 1
      src/main/java/com/zsElectric/boot/core/filter/RateLimiterFilter.java
  21. 8 0
      src/main/java/com/zsElectric/boot/security/service/PermissionService.java
  22. 10 0
      src/main/java/com/zsElectric/boot/system/controller/MenuController.java
  23. 5 4
      src/main/java/com/zsElectric/boot/system/service/impl/MenuServiceImpl.java
  24. 11 3
      src/main/java/com/zsElectric/boot/system/service/impl/RoleMenuServiceImpl.java
  25. 1 2
      src/main/resources/mapper/business/FirmAccountLogMapper.xml
  26. 61 0
      src/main/resources/mapper/business/ThirdPartyStationInfoMapper.xml

+ 13 - 4
src/main/java/com/zsElectric/boot/business/controller/applet/AppletStationController.java

@@ -12,10 +12,7 @@ import com.zsElectric.boot.core.web.Result;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.math.BigDecimal;
 import java.util.List;
@@ -82,6 +79,18 @@ public class AppletStationController {
         return Result.success(appletHomeService.getCurrentChargingCost(orderNo));
     }
 
+    /**
+     * 获取当前充电订单实时费用
+     * 根据当前登录用户查询正在充电中的订单
+     *
+     * @return 实时充电费用信息,如果没有正在充电的订单则返回null
+     */
+    @Operation(summary = "渠道方获取当前充电订单实时费用")
+    @GetMapping("/chargingCost/{ChargeOrderNo}")
+    public Result<AppChargingCostVO> getChargingCost(@PathVariable("") String ChargeOrderNo) {
+        return Result.success(appletHomeService.getChargingCost(ChargeOrderNo));
+    }
+
     /**
      * 搜索充电站
      *

+ 2 - 0
src/main/java/com/zsElectric/boot/business/mapper/ThirdPartyStationInfoMapper.java

@@ -113,4 +113,6 @@ public interface ThirdPartyStationInfoMapper extends BaseMapper<ThirdPartyStatio
             @Param("longitude") java.math.BigDecimal longitude,
             @Param("latitude") java.math.BigDecimal latitude
     );
+
+    AppChargingCostVO selectChargingCost(String orderNo);
 }

+ 1 - 1
src/main/java/com/zsElectric/boot/business/model/entity/ChargeOrderInfo.java

@@ -27,7 +27,7 @@ public class ChargeOrderInfo extends BaseEntity {
      */
     private Long userId;
     /**
-     * 订单类型 0 个人订单 1 企业订单
+     * 订单类型 0 个人订单 1 企业订单 2 渠道方订单
      */
     private Integer orderType;
     /**

+ 3 - 21
src/main/java/com/zsElectric/boot/business/model/entity/FirmAccountLog.java

@@ -48,16 +48,10 @@ public class FirmAccountLog implements Serializable {
     private Integer firmType;
 
     /**
-     * 项目名称
+     * 事件描述
      */
-    @Schema(description = "项目名称")
-    private String projectName;
-
-    /**
-     * 交易名称
-     */
-    @Schema(description = "交易名称")
-    private String name;
+    @Schema(description = "事件描述")
+    private String eventDesc;
 
     /**
      * 流水号
@@ -89,18 +83,6 @@ public class FirmAccountLog implements Serializable {
     @Schema(description = "改变金额")
     private BigDecimal moneyChange;
 
-    /**
-     * 改变类型(1-充值 2-提现 3-签到 4-日利宝取出 5-团队升级 6-项目利息 7-下级返利 8-认购 9-日利宝存入 10-日利宝收益 11-红包奖励 12-项目到期返还本金 13-注册赠送)
-     */
-    @Schema(description = "改变类型(1-充值 2-提现 3-其他...)")
-    private Integer changeType;
-
-    /**
-     * 关联用户ID(当改变类型为下级分润的时候填写)
-     */
-    @Schema(description = "关联用户ID")
-    private Long childId;
-
     /**
      * 备注
      */

+ 5 - 0
src/main/java/com/zsElectric/boot/business/model/entity/FirmInfo.java

@@ -2,6 +2,7 @@ package com.zsElectric.boot.business.model.entity;
 
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.zsElectric.boot.common.base.BaseEntity;
+import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -53,6 +54,10 @@ public class FirmInfo extends BaseEntity {
      * 累计盈利
      */
     private BigDecimal totalProfit;
+    /**
+     * 渠道方地址
+     */
+    private String channelUrl;
     /**
      * 创建人
      */

+ 11 - 1
src/main/java/com/zsElectric/boot/business/model/form/applet/AppInvokeChargeForm.java

@@ -7,6 +7,7 @@ import lombok.Setter;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.math.BigDecimal;
 
 @Getter
 @Setter
@@ -16,7 +17,7 @@ public class AppInvokeChargeForm implements Serializable {
     @Serial
     private static final long serialVersionUID = 1L;
 
-    @Schema(description = "订单类型 0-平台 1-企业")
+    @Schema(description = "订单类型 0-平台 1-企业 2-渠道方")
     private Integer orderType;
 
     @Schema(description = "充电桩编号")
@@ -36,4 +37,13 @@ public class AppInvokeChargeForm implements Serializable {
     @Schema(description = "充电设备接口编码")
     @NotBlank(message = "充电设备接口编码不能为空")
     private String connectorId;
+
+    @Schema(description = "渠道方订单编号")
+    private String channelOrderNo;
+
+    @Schema(description = "渠道方用户手机号")
+    private String channelUserPhone;
+
+    @Schema(description = "渠道方预支付金额")
+    private BigDecimal channelPreAmt;
 }

+ 3 - 3
src/main/java/com/zsElectric/boot/business/model/form/applet/AppStopChargeForm.java

@@ -10,13 +10,13 @@ import java.io.Serializable;
 
 @Getter
 @Setter
-@Schema(description = "用户启用充电表单对象")
+@Schema(description = "用户停止充电表单对象")
 public class AppStopChargeForm implements Serializable {
 
     @Serial
     private static final long serialVersionUID = 1L;
 
-    @Schema(description = "充电订单ID")
-    @NotBlank(message = "充电订单ID不能为空")
+    @Schema(description = "充电订单编号")
+    @NotBlank(message = "充电订单编号不能为空")
     private String chargeOrderNo;
 }

+ 0 - 6
src/main/java/com/zsElectric/boot/business/model/query/FirmAccountLogQuery.java

@@ -27,15 +27,9 @@ public class FirmAccountLogQuery extends BasePageQuery {
     @Schema(description = "收支类型(1-转入/上账 2-转出/下账)")
     private Integer incomeType;
 
-    @Schema(description = "改变类型")
-    private Integer changeType;
-
     @Schema(description = "流水号")
     private String serialNo;
 
-    @Schema(description = "交易名称")
-    private String name;
-
     @Schema(description = "开始时间")
     private LocalDateTime startTime;
 

+ 3 - 0
src/main/java/com/zsElectric/boot/business/model/query/ThirdPartyStationInfoQuery.java

@@ -36,4 +36,7 @@ public class ThirdPartyStationInfoQuery extends BasePageQuery {
 
     @Schema(description = "服务电话")
     private String serviceTel;
+
+    @Schema(description = "收费类型(0-平台 1-企业)")
+    private Integer salesType;
 }

+ 2 - 11
src/main/java/com/zsElectric/boot/business/model/vo/FirmAccountLogVO.java

@@ -35,11 +35,8 @@ public class FirmAccountLogVO implements Serializable {
     @Schema(description = "企业名称")
     private String firmName;
 
-    @Schema(description = "项目名称")
-    private String projectName;
-
-    @Schema(description = "交易名称")
-    private String name;
+    @Schema(description = "事件描述")
+    private String eventDesc;
 
     @Schema(description = "流水号")
     private String serialNo;
@@ -56,12 +53,6 @@ public class FirmAccountLogVO implements Serializable {
     @Schema(description = "改变金额")
     private BigDecimal moneyChange;
 
-    @Schema(description = "改变类型")
-    private Integer changeType;
-
-    @Schema(description = "关联用户ID")
-    private Long childId;
-
     @Schema(description = "备注")
     private String remark;
 

+ 2 - 0
src/main/java/com/zsElectric/boot/business/model/vo/FirmInfoVO.java

@@ -42,6 +42,8 @@ public class FirmInfoVO implements Serializable {
     private BigDecimal currentProfit;
     @Schema(description = "累计盈利")
     private BigDecimal totalProfit;
+    @Schema(description = "渠道方地址")
+    private String channelUrl;
     @Schema(description = "创建时间")
     private LocalDateTime createTime;
     @Schema(description = "创建人")

+ 1 - 0
src/main/java/com/zsElectric/boot/business/service/AppletHomeService.java

@@ -94,4 +94,5 @@ public interface AppletHomeService {
      */
     BigDecimal calculateAvailableChargingAmount(String connectorCode);
 
+    AppChargingCostVO getChargingCost(String orderNo);
 }

+ 5 - 0
src/main/java/com/zsElectric/boot/business/service/impl/AppletHomeServiceImpl.java

@@ -1006,6 +1006,11 @@ public class AppletHomeServiceImpl implements AppletHomeService {
         return availableAmount;
     }
 
+    @Override
+    public AppChargingCostVO getChargingCost(String orderNo) {
+        return thirdPartyStationInfoMapper.selectChargingCost(orderNo);
+    }
+
     /**
      * 计算充电费用(含运营费)
      * 根据 charge_details 是否有数据决定计算方式:

+ 69 - 4
src/main/java/com/zsElectric/boot/business/service/impl/ChargeOrderInfoServiceImpl.java

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.toolkit.Assert;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.zsElectric.boot.business.converter.ChargeOrderInfoConverter;
 import com.zsElectric.boot.business.mapper.*;
 import com.zsElectric.boot.business.model.dto.ChargeOrderInfoExportDTO;
@@ -17,7 +18,6 @@ import com.zsElectric.boot.business.model.form.applet.AppStopChargeForm;
 import com.zsElectric.boot.business.model.query.ChargeOrderInfoQuery;
 import com.zsElectric.boot.business.model.query.applet.AppChargeOrderInfoQuery;
 import com.zsElectric.boot.business.model.vo.ChargeOrderInfoVO;
-import com.zsElectric.boot.business.model.vo.UserAccountVO;
 import com.zsElectric.boot.business.model.vo.applet.AppChargeVO;
 import com.zsElectric.boot.business.model.vo.applet.AppUserInfoVO;
 import com.zsElectric.boot.business.service.AppletHomeService;
@@ -25,7 +25,6 @@ import com.zsElectric.boot.business.service.ChargeOrderInfoService;
 import com.zsElectric.boot.business.service.UserAccountService;
 import com.zsElectric.boot.charging.dto.StartChargingRequestDTO;
 import com.zsElectric.boot.charging.dto.StartChargingResponseVO;
-import com.zsElectric.boot.charging.entity.ThirdPartyStationInfo;
 import com.zsElectric.boot.charging.service.ChargingBusinessService;
 import com.zsElectric.boot.charging.vo.EquipmentAuthResponseVO;
 import com.zsElectric.boot.charging.vo.StopChargingOperationResponseVO;
@@ -33,8 +32,6 @@ import com.zsElectric.boot.common.constant.ConnectivityConstants;
 import com.zsElectric.boot.common.constant.SystemConstants;
 import com.zsElectric.boot.core.exception.BusinessException;
 import com.zsElectric.boot.security.util.SecurityUtils;
-import com.zsElectric.boot.system.mapper.DictItemMapper;
-import com.zsElectric.boot.system.model.entity.DictItem;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -73,6 +70,8 @@ public class ChargeOrderInfoServiceImpl extends ServiceImpl<ChargeOrderInfoMappe
 
     private final AppletHomeService appletHomeService;
 
+    private final UserFirmMapper userFirmMapper;
+
     //充电订单号前缀
     private final String ORDER_NO_PREFIX = "CD";
     //设备流水号前缀
@@ -165,6 +164,14 @@ public class ChargeOrderInfoServiceImpl extends ServiceImpl<ChargeOrderInfoMappe
         try {
             AppChargeVO appInvokeChargeVO = new AppChargeVO();
 
+            //渠道方启动充电
+            if (Objects.equals(formData.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
+
+                String orderNo = channelInvokeCharge(formData);
+                appInvokeChargeVO.setChargeOrderNo(orderNo);
+                return appInvokeChargeVO;
+            }
+
             //必要校验
             Long userId = SecurityUtils.getUserId();
             Assert.isTrue(userId != null, "用户ID不能为空");
@@ -251,6 +258,64 @@ public class ChargeOrderInfoServiceImpl extends ServiceImpl<ChargeOrderInfoMappe
         }
     }
 
+    /**
+     * 渠道方启动充电
+     *
+     * @param formData
+     * @return
+     */
+    public String channelInvokeCharge(AppInvokeChargeForm formData) throws JsonProcessingException {
+
+        String seq = ConnectivityConstants.OPERATOR_ID + formData.getChannelOrderNo();
+
+        //请求设备认证
+        EquipmentAuthResponseVO equipmentAuthResponseVO = chargingBusinessService.queryEquipAuth(seq,
+                formData.getConnectorId());
+
+        if (Objects.equals(equipmentAuthResponseVO.getSuccStat(), SystemConstants.STATUS_ONE)) {
+            throw new BusinessException("设备认证失败,请检查枪是否正确接入充电槽!");
+        } else {
+            log.info("设备认证成功,设备认证流水号:{}", equipmentAuthResponseVO.getEquipAuthSeq());
+        }
+
+        Long userId = SecurityUtils.getUserId();
+        UserFirm userFirm = userFirmMapper.selectOne(Wrappers.lambdaQuery(UserFirm.class).eq(UserFirm::getUserId, userId).last("limit 1"));
+
+        //创建订单
+        ChargeOrderInfo chargeOrderInfo = new ChargeOrderInfo();
+        chargeOrderInfo.setUserId(userId);
+        chargeOrderInfo.setFirmId(userFirm.getFirmId());
+        chargeOrderInfo.setOrderType(SystemConstants.CHARGE_ORDER_TYPE_CHANNEL);
+        chargeOrderInfo.setConnectorId(formData.getConnectorId());
+        chargeOrderInfo.setEquipmentId(formData.getEquipmentId());
+        chargeOrderInfo.setEquipAuthSeq(seq);
+        chargeOrderInfo.setChargeOrderNo(formData.getChannelOrderNo());
+        chargeOrderInfo.setStartChargeSeq(seq);
+        chargeOrderInfo.setPhoneNum(formData.getChannelUserPhone());
+        chargeOrderInfo.setThirdPartyStationId(formData.getStationId());
+        //预支付金额
+        chargeOrderInfo.setPreAmt(formData.getChannelPreAmt());
+
+        //启动充电
+        StartChargingRequestDTO requestDTO = new StartChargingRequestDTO();
+        requestDTO
+                .setStartChargeSeq(seq)
+                .setConnectorID(formData.getConnectorId())
+                .setPhoneNum(formData.getChannelUserPhone())
+                //预支付金额
+                .setChargingAmt(formData.getChannelPreAmt().toString())
+        ;
+        StartChargingResponseVO startChargingResponseVO = chargingBusinessService.startCharging(requestDTO);
+        if (!Objects.equals(startChargingResponseVO.getSuccStat(), SystemConstants.STATUS_ZERO)) {
+            throw new BusinessException(startChargingResponseVO.getFailReasonMsg());
+        }
+
+        //保存订单
+        this.save(chargeOrderInfo);
+
+        return chargeOrderInfo.getChargeOrderNo();
+    }
+
     @Override
     public AppChargeVO stopCharge(AppStopChargeForm formData) {
         try {

+ 1 - 6
src/main/java/com/zsElectric/boot/business/service/impl/FirmAccountLogServiceImpl.java

@@ -55,9 +55,7 @@ public class FirmAccountLogServiceImpl extends ServiceImpl<FirmAccountLogMapper,
         queryWrapper.eq(queryParams.getFirmId() != null, FirmAccountLog::getFirmId, queryParams.getFirmId())
                 .eq(queryParams.getFirmType() != null, FirmAccountLog::getFirmType, queryParams.getFirmType())
                 .eq(queryParams.getIncomeType() != null, FirmAccountLog::getIncomeType, queryParams.getIncomeType())
-                .eq(queryParams.getChangeType() != null, FirmAccountLog::getChangeType, queryParams.getChangeType())
                 .like(StrUtil.isNotBlank(queryParams.getSerialNo()), FirmAccountLog::getSerialNo, queryParams.getSerialNo())
-                .like(StrUtil.isNotBlank(queryParams.getName()), FirmAccountLog::getName, queryParams.getName())
                 .orderByDesc(FirmAccountLog::getCreateTime);
 
         IPage<FirmAccountLog> entityPage = this.page(page, queryWrapper);
@@ -67,15 +65,12 @@ public class FirmAccountLogServiceImpl extends ServiceImpl<FirmAccountLogMapper,
             FirmAccountLogVO vo = new FirmAccountLogVO();
             vo.setId(entity.getId());
             vo.setFirmId(entity.getFirmId());
-            vo.setProjectName(entity.getProjectName());
-            vo.setName(entity.getName());
+            vo.setEventDesc(entity.getEventDesc());
             vo.setSerialNo(entity.getSerialNo());
             vo.setIncomeType(entity.getIncomeType());
             vo.setBeforeChange(entity.getBeforeChange());
             vo.setAfterChange(entity.getAfterChange());
             vo.setMoneyChange(entity.getMoneyChange());
-            vo.setChangeType(entity.getChangeType());
-            vo.setChildId(entity.getChildId());
             vo.setRemark(entity.getRemark());
             vo.setCreateTime(entity.getCreateTime());
             return vo;

+ 2 - 3
src/main/java/com/zsElectric/boot/business/service/impl/FirmInfoServiceImpl.java

@@ -202,14 +202,13 @@ public class FirmInfoServiceImpl extends ServiceImpl<FirmInfoMapper, FirmInfo> i
         FirmAccountLog accountLog = new FirmAccountLog();
         accountLog.setFirmId(form.getFirmId());
         accountLog.setFirmType(firmInfo.getFirmType()); // 设置类型(1-企业 2-渠道)
-        accountLog.setProjectName("企业账户");
-        accountLog.setName(form.getIncomeType() == 1 ? "上账" : "下账");
+        accountLog.setEventDesc(form.getIncomeType() == 1 ? "上账" : "下账");
         accountLog.setSerialNo(generateSerialNo());
         accountLog.setIncomeType(form.getIncomeType());
         accountLog.setBeforeChange(beforeBalance);
         accountLog.setAfterChange(afterBalance);
         accountLog.setMoneyChange(changeAmount);
-        accountLog.setChangeType(form.getChangeType() != null ? form.getChangeType() : (form.getIncomeType() == 1 ? 1 : 2));
+//        accountLog.setChangeType(form.getChangeType() != null ? form.getChangeType() : (form.getIncomeType() == 1 ? 1 : 2));
         accountLog.setRemark(form.getRemark());
         accountLog.setCreateTime(LocalDateTime.now());
         firmAccountLogMapper.insert(accountLog);

+ 177 - 76
src/main/java/com/zsElectric/boot/charging/service/impl/ChargingReceptionServiceImpl.java

@@ -1,5 +1,6 @@
 package com.zsElectric.boot.charging.service.impl;
 
+import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -20,6 +21,7 @@ import com.zsElectric.boot.charging.vo.QueryStationStatusVO;
 import com.zsElectric.boot.common.constant.ConnectivityConstants;
 import com.zsElectric.boot.common.constant.SystemConstants;
 import com.zsElectric.boot.common.util.DateUtils;
+import com.zsElectric.boot.common.util.OkHttpUtil;
 import com.zsElectric.boot.common.util.electric.ChargingUtil;
 import com.zsElectric.boot.common.util.electric.RequestParmsEntity;
 import com.zsElectric.boot.common.util.electric.ResponseParmsEntity;
@@ -30,6 +32,9 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 
@@ -41,6 +46,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
@@ -68,6 +74,7 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
     private final RedissonClient redissonClient;
     private final UserAccountMapper userAccountMapper;
     private final UserFirmMapper userFirmMapper;
+    private final FirmInfoMapper firmInfoMapper;
     private final PolicyFeeMapper policyFeeMapper;
     private final DictItemMapper dictItemMapper;
     private final ThirdPartyEquipmentInfoMapper thirdPartyEquipmentInfoMapper;
@@ -75,7 +82,8 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
     private final ThirdPartyStationInfoMapper thirdPartyStationInfoMapper;
     private final ThirdPartyPolicyInfoMapper thirdPartyPolicyInfoMapper;
     private final DiscountsActivityMapper discountsActivityMapper;
-
+    private final FirmAccountLogMapper firmAccountLogMapper;
+    private final OkHttpUtil okHttpUtil;
 
     /**
      * 熔断检查锁前缀
@@ -151,91 +159,134 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
             ChargeOrderInfo chargeOrderInfo = chargeOrderInfoService.getOne(new LambdaQueryWrapper<ChargeOrderInfo>()
                     .eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("LIMIT 1"));
 
-            //推送订单明细
-            chargeOrderInfo.setChargeDetails(jsonNode.toString());
-
-            //优惠单价
-            BigDecimal discountPrice = BigDecimal.ZERO;
-
-            //判断当前订单是否为首单
-            Long userId = chargeOrderInfo.getUserId();
-            List<ChargeOrderInfo> list = chargeOrderInfoService.list(Wrappers.<ChargeOrderInfo>lambdaQuery().eq(ChargeOrderInfo::getUserId, userId).eq(ChargeOrderInfo::getStatus,
-                    SystemConstants.STATUS_THREE));
-            if(ObjectUtil.isEmpty(list)){
-                DiscountsActivity discountsActivity = discountsActivityMapper.selectOne(Wrappers.<DiscountsActivity>lambdaQuery()
-                        .eq(DiscountsActivity::getType, SystemConstants.STATUS_ONE)
-                        .eq(DiscountsActivity::getStatus, SystemConstants.STATUS_ONE)
-                        .last("LIMIT 1"));
-                if(ObjectUtil.isNotEmpty(discountsActivity)){
-                    chargeOrderInfo.setDiscountInfoId(discountsActivity.getId());
-                    chargeOrderInfo.setDiscountDesc(discountsActivity.getActivityDesc());
-                    discountPrice = discountsActivity.getDiscount();
+            if (ObjectUtil.isNotEmpty(chargeOrderInfo)) {
+                if (Objects.equals(chargeOrderInfo.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
+                    //订单信息渠道方推送
+                    Map<String, Object> map = objectMapper.convertValue(jsonNode, Map.class);
+                    map.put("chargeOrderNo", chargeOrderInfo.getChargeOrderNo());
+                    FirmInfo firmInfo = firmInfoMapper.selectById(chargeOrderInfo.getFirmId());
+                    if (ObjectUtil.isNotNull(firmInfo)) {
+                        okHttpUtil.doPostForm(firmInfo.getChannelUrl() + "/notification_charge_order_info", map, null);
+                    }
+                }
+                //推送订单明细
+                chargeOrderInfo.setChargeDetails(jsonNode.toString());
+
+                //优惠单价
+                BigDecimal discountPrice = BigDecimal.ZERO;
+
+                if (Objects.equals(chargeOrderInfo.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_PLATFORM)) {
+                    //判断当前订单是否为首单
+                    Long userId = chargeOrderInfo.getUserId();
+                    List<ChargeOrderInfo> list = chargeOrderInfoService.list(Wrappers.<ChargeOrderInfo>lambdaQuery().eq(ChargeOrderInfo::getUserId, userId).eq(ChargeOrderInfo::getStatus,
+                            SystemConstants.STATUS_THREE));
+                    if (ObjectUtil.isEmpty(list)) {
+                        DiscountsActivity discountsActivity = discountsActivityMapper.selectOne(Wrappers.<DiscountsActivity>lambdaQuery()
+                                .eq(DiscountsActivity::getType, SystemConstants.STATUS_ONE)
+                                .eq(DiscountsActivity::getStatus, SystemConstants.STATUS_ONE)
+                                .last("LIMIT 1"));
+                        if (ObjectUtil.isNotEmpty(discountsActivity)) {
+                            chargeOrderInfo.setDiscountInfoId(discountsActivity.getId());
+                            chargeOrderInfo.setDiscountDesc(discountsActivity.getActivityDesc());
+                            discountPrice = discountsActivity.getDiscount();
+                        }
+                    }
                 }
-            }
 
-            chargeOrderInfo.setStopReason(stopReason);
-            chargeOrderInfo.setStartTime(startTime);
-            chargeOrderInfo.setEndTime(endTime);
-            chargeOrderInfo.setTotalCharge(new BigDecimal(totalPower));
-
-            chargeOrderInfo.setThirdPartyTotalCost(new BigDecimal(totalMoney));
-            chargeOrderInfo.setThirdPartyServerfee(new  BigDecimal(totalSeviceMoney));
-            chargeOrderInfo.setThirdPartyElecfee(new BigDecimal(totalElecMoney));
-
-            ThirdPartyConnectorInfo thirdPartyConnectorInfo = connectorInfoMapper.selectOne(Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
-                    .eq(ThirdPartyConnectorInfo::getConnectorId, connectorID).last("LIMIT 1"));
-            String stationId = thirdPartyConnectorInfo.getStationId();
-            ThirdPartyStationInfo thirdPartyStationInfo = thirdPartyStationInfoMapper.selectOne(Wrappers.<ThirdPartyStationInfo>lambdaQuery()
-                    .eq(ThirdPartyStationInfo::getStationId, stationId).last("LIMIT 1"));
-            if (ObjectUtil.isEmpty(thirdPartyConnectorInfo)) {
-                log.error("thirdPartyConnectorInfo" +"为空===============================================");
-            }
+                chargeOrderInfo.setStopReason(stopReason);
+                chargeOrderInfo.setStartTime(startTime);
+                chargeOrderInfo.setEndTime(endTime);
+                chargeOrderInfo.setTotalCharge(new BigDecimal(totalPower));
+
+                chargeOrderInfo.setThirdPartyTotalCost(new BigDecimal(totalMoney));
+                chargeOrderInfo.setThirdPartyServerfee(new BigDecimal(totalSeviceMoney));
+                chargeOrderInfo.setThirdPartyElecfee(new BigDecimal(totalElecMoney));
+
+                ThirdPartyConnectorInfo thirdPartyConnectorInfo = connectorInfoMapper.selectOne(Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
+                        .eq(ThirdPartyConnectorInfo::getConnectorId, connectorID).last("LIMIT 1"));
+                String stationId = thirdPartyConnectorInfo.getStationId();
+                ThirdPartyStationInfo thirdPartyStationInfo = thirdPartyStationInfoMapper.selectOne(Wrappers.<ThirdPartyStationInfo>lambdaQuery()
+                        .eq(ThirdPartyStationInfo::getStationId, stationId).last("LIMIT 1"));
+                if (ObjectUtil.isEmpty(thirdPartyConnectorInfo)) {
+                    log.error("thirdPartyConnectorInfo" + "为空===============================================");
+                }
 
-            //平台服务费
-            BigDecimal serviceFee = BigDecimal.ZERO;
-            //优惠金额
-            BigDecimal discountFee = BigDecimal.ZERO;
-            JsonNode chargeDetails = jsonNode.get("ChargeDetails");
-            if (ObjectUtil.isNotEmpty(chargeDetails)) {
-                for (JsonNode node : chargeDetails) {
-                    //提取字段值
-                    String itemFlag = node.get("ItemFlag").asText();
-                    node.get("DetailPower").asText();
-                    BigDecimal detailPower = new BigDecimal(node.get("DetailPower").asText());
-                    PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.<PolicyFee>lambdaQuery()
-                            .eq(PolicyFee::getStationInfoId, thirdPartyStationInfo.getId())
-                            .eq(PolicyFee::getPeriodFlag, Integer.parseInt(itemFlag))
-                            .last("LIMIT 1"));
-                    if (ObjectUtil.isNotEmpty(policyFee)) {
-                        BigDecimal opFee = policyFee.getOpFee();
-                        if(ObjectUtil.isNotEmpty(chargeOrderInfo.getDiscountInfoId())){
-                            opFee = opFee.subtract(discountPrice);
+                //平台服务费
+                BigDecimal serviceFee = BigDecimal.ZERO;
+                //优惠金额
+                BigDecimal discountFee = BigDecimal.ZERO;
+                JsonNode chargeDetails = jsonNode.get("ChargeDetails");
+                if (ObjectUtil.isNotEmpty(chargeDetails)) {
+                    for (JsonNode node : chargeDetails) {
+                        //提取字段值
+                        String itemFlag = node.get("ItemFlag").asText();
+                        node.get("DetailPower").asText();
+                        BigDecimal detailPower = new BigDecimal(node.get("DetailPower").asText());
+                        PolicyFee policyFee = policyFeeMapper.selectOne(Wrappers.<PolicyFee>lambdaQuery()
+                                .eq(PolicyFee::getStationInfoId, thirdPartyStationInfo.getId())
+                                .eq(PolicyFee::getPeriodFlag, Integer.parseInt(itemFlag))
+                                .last("LIMIT 1"));
+                        if (ObjectUtil.isNotEmpty(policyFee)) {
+                            BigDecimal opFee = policyFee.getOpFee();
+                            if (ObjectUtil.isNotEmpty(chargeOrderInfo.getDiscountInfoId())) {
+                                opFee = opFee.subtract(discountPrice);
+                            }
+                            log.info("策略费用:{}", opFee);
+                            serviceFee = serviceFee.add(opFee.multiply(detailPower));
+                            discountFee = discountFee.add(discountPrice.multiply(detailPower));
                         }
-                        log.info("策略费用:{}",opFee);
-                        serviceFee = serviceFee.add(opFee.multiply(detailPower));
-                        discountFee = discountFee.add(discountPrice.multiply(detailPower));
                     }
                 }
-            }
 
-            log.info("计算后的平台服务费:{}", serviceFee);
-            chargeOrderInfo.setDiscountMoney(discountFee);
-            chargeOrderInfo.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP));
-            //订单结算:平台实际收取金额 = 互联互通金额 + 中数电动金额(平台总服务费)
-            chargeOrderInfo.setRealCost(chargeOrderInfo.getRealServiceCost().add(chargeOrderInfo.getThirdPartyTotalCost()));
+                log.info("计算后的平台服务费:{}", serviceFee);
+                if (chargeOrderInfo.getOrderType().equals(SystemConstants.CHARGE_ORDER_TYPE_PLATFORM)) {
+                    chargeOrderInfo.setDiscountMoney(discountFee);
+                    chargeOrderInfo.setRealServiceCost(serviceFee.setScale(2, RoundingMode.HALF_UP));
+                    //订单结算:平台实际收取金额 = 互联互通金额 + 中数电动金额(平台总服务费)
+                    chargeOrderInfo.setRealCost(chargeOrderInfo.getRealServiceCost().add(chargeOrderInfo.getThirdPartyTotalCost()));
 
-            //订单状态->已完成
-            chargeOrderInfo.setStatus(SystemConstants.STATUS_THREE);
+                    //订单状态->已完成
+                    chargeOrderInfo.setStatus(SystemConstants.STATUS_THREE);
 
-            //计算充电时间
-            chargeOrderInfo.setChargeTime(DateUtils.getDuration(chargeOrderInfo.getStartTime(), chargeOrderInfo.getEndTime()));
+                    //计算充电时间
+                    chargeOrderInfo.setChargeTime(DateUtils.getDuration(chargeOrderInfo.getStartTime(), chargeOrderInfo.getEndTime()));
 
-            //修改订单
-            chargeOrderInfoService.updateById(chargeOrderInfo);
+                    //修改订单
+                    chargeOrderInfoService.updateById(chargeOrderInfo);
 
-            //账户余额扣减(积分增加)
-            log.info("执行账户余额扣减(积分增加)");
-            chargeOrderInfoService.orderSettlement(chargeOrderInfo.getId());
+                    //账户余额扣减(积分增加)
+                    log.info("执行账户余额扣减(积分增加)");
+                    chargeOrderInfoService.orderSettlement(chargeOrderInfo.getId());
+                }
+                if (chargeOrderInfo.getOrderType().equals(SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
+                    chargeOrderInfo.setRealServiceCost(BigDecimal.ZERO);
+                    //订单结算:平台实际收取金额 = 互联互通金额 + 中数电动金额(平台总服务费)
+                    chargeOrderInfo.setRealCost(chargeOrderInfo.getRealServiceCost().add(chargeOrderInfo.getThirdPartyTotalCost()));
+                    //订单状态->已完成
+                    chargeOrderInfo.setStatus(SystemConstants.STATUS_THREE);
+                    //计算充电时间
+                    chargeOrderInfo.setChargeTime(DateUtils.getDuration(chargeOrderInfo.getStartTime(), chargeOrderInfo.getEndTime()));
+                    //修改订单
+                    chargeOrderInfoService.updateById(chargeOrderInfo);
+                    //渠道方账户余额扣减
+                    BigDecimal cost = chargeOrderInfo.getRealCost();
+                    FirmInfo firmInfo = firmInfoMapper.selectById(chargeOrderInfo.getFirmId());
+                    FirmAccountLog accountLog = new FirmAccountLog();
+                    accountLog.setFirmId(firmInfo.getId());
+                    accountLog.setFirmType(firmInfo.getFirmType());
+                    accountLog.setEventDesc("渠道方充电订单下账");
+                    accountLog.setSerialNo(chargeOrderInfo.getChargeOrderNo());
+                    accountLog.setIncomeType(2);
+                    accountLog.setBeforeChange(firmInfo.getBalance());
+                    accountLog.setAfterChange(firmInfo.getBalance().subtract(cost));
+                    accountLog.setMoneyChange(cost);
+                    firmAccountLogMapper.insert(accountLog);
+
+                    // 渠道方账户余额修改
+                    firmInfo.setBalance(firmInfo.getBalance().subtract(cost));
+                    firmInfoMapper.updateById(firmInfo);
+                }
+            }
 
             // 执行业务处理
             businessHandler.accept(jsonNode);
@@ -277,6 +328,16 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
                                 chargeOrderInfo.setStatus(SystemConstants.STATUS_ONE);
                                 chargeOrderInfo.setStartTime(startTime);
                                 chargeOrderInfoService.updateById(chargeOrderInfo);
+
+                                // 推送渠道方启动充电结果
+                                FirmInfo firmInfo = firmInfoMapper.selectById(chargeOrderInfo.getFirmId());
+                                if (ObjectUtil.isNotNull(firmInfo)) {
+                                    HashMap<String, Object> map = new HashMap<>();
+                                    map.put("chargeOrderNo", chargeOrderInfo.getChargeOrderNo());
+                                    map.put("status", SystemConstants.STATUS_ONE);
+                                    map.put("startTime", startTime);
+                                    okHttpUtil.doPostForm(firmInfo.getChannelUrl() + "/notification_start_charge_result", map, null);
+                                }
                             }
                         }
                     }
@@ -330,6 +391,15 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
                             chargeOrderInfo.setStatus(SystemConstants.STATUS_TWO);
                             chargeOrderInfoService.updateById(chargeOrderInfo);
                             log.info("更新订单状态为结算中 - orderId: {}", chargeOrderInfo.getId());
+
+                            // 推送停止充电订单结果
+                            FirmInfo firmInfo = firmInfoMapper.selectById(chargeOrderInfo.getFirmId());
+                            if (ObjectUtil.isNotNull(firmInfo)) {
+                                HashMap<String, Object> map = new HashMap<>();
+                                map.put("chargeOrderNo", chargeOrderInfo.getChargeOrderNo());
+                                map.put("status", SystemConstants.STATUS_TWO);
+                                okHttpUtil.doPostForm(firmInfo.getChannelUrl() + "/notification_stop_charge_result", map, null);
+                            }
                         }
                     }
                     case 5 -> log.info("未知 - StartChargeSeq: {}", startChargeSeq);
@@ -578,13 +648,44 @@ public class ChargingReceptionServiceImpl implements ChargingReceptionService {
                 log.info("新增充电状态成功 - startChargeSeq: {}", startChargeSeq);
             }
 
+            //异步推送渠道方充电状态
+            pushChargeStatusTask(startChargeSeq, chargeStatus);
+
             // 熔断保护 - 余额不足判断
             isNeedBreak(chargeStatus, chargeOrderInfo);
         } catch (Exception e) {
             log.error("保存充电状态数据失败", e);
         }
     }
-    
+
+    @Autowired
+    @Qualifier("businessTaskExecutor")
+    private ThreadPoolTaskExecutor businessTaskExecutor;
+
+    /**
+     * 异步推送渠道方充电状态
+     */
+    public CompletableFuture<Void> pushChargeStatusTask(String startChargeSeq, ThirdPartyChargeStatus chargeStatus) {
+        return CompletableFuture.runAsync(() -> {
+            log.info("异步推送渠道方充电状态 - {}", startChargeSeq);
+            //通过startChargeSeq查询订单详情
+            ChargeOrderInfo chargeOrderInfo =
+                    chargeOrderInfoService.getOne(Wrappers.<ChargeOrderInfo>lambdaQuery().eq(ChargeOrderInfo::getStartChargeSeq, startChargeSeq).last("limit 1"));
+            if (ObjUtil.isNotEmpty(chargeOrderInfo)) {
+                if (Objects.equals(chargeOrderInfo.getOrderType(), SystemConstants.CHARGE_ORDER_TYPE_CHANNEL)) {
+
+                    Long firmId = chargeOrderInfo.getFirmId();
+                    FirmInfo firmInfo = firmInfoMapper.selectById(firmId);
+                    String channelUrl = firmInfo.getChannelUrl() + "/notification_equip_charge_status";
+
+                    // 推送充电状态
+                    okHttpUtil.doPostForm(channelUrl, BeanUtil.beanToMap(chargeStatus), null);
+
+                }
+            }
+        }, businessTaskExecutor);
+    }
+
     /**
      * 计算平台实际收取金额
      * 根据充电度数 * 当前时段的平台价格策略计算(支持跨时段计费)

+ 6 - 0
src/main/java/com/zsElectric/boot/common/constant/SystemConstants.java

@@ -66,4 +66,10 @@ public interface SystemConstants {
     Integer ACCOUNT_TYPE_PERSONAL = 1;
     Integer ACCOUNT_TYPE_COMPANY = 2;
 
+    /**
+     * 充电订单类型 0-平台 1-企业 2-渠道
+     */
+    Integer CHARGE_ORDER_TYPE_PLATFORM = 0;
+    Integer CHARGE_ORDER_TYPE_FIRM = 1;
+    Integer CHARGE_ORDER_TYPE_CHANNEL = 2;
 }

+ 61 - 53
src/main/java/com/zsElectric/boot/common/util/AESCryptoUtils.java

@@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
 import javax.crypto.Cipher;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
+import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
 
@@ -147,58 +148,65 @@ public class AESCryptoUtils {
      */
     public static void main(String[] args) throws Exception {
 
-        try {
-            // 测试数据
-            String originalData = "{\"OperatorID\":\"MA6DP6BE7\",\"SuccStat\":0,\"AccessToken\":\"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJNQTZEUDZCRTciLCJpYXQiOjE3NjQ1ODE0NzQsImV4cCI6MTc2NDU4ODY3NH0.mfXp_Wly4QiVYILK6hNSOEGSU7XejlYZRing0CRc8MQW1xuUxrQQwOxeCAwI_Bnay5WzqaYYFQeVuNeignxAuQ\",\"TokenAvailableTime\":6885,\"FailReason\":0}";
-            String key = DATA_SECRET;   // 16字节密钥
-            String iv = DATA_SECRET_IV;   // 16字节初始化向量
-
-            System.out.println("=== AES-128-CBC-PKCS5Padding 加解密测试 ===");
-            System.out.println("原始数据: " + originalData);
-            System.out.println("密钥: " + key);
-            System.out.println("初始化向量: " + iv);
-
-            // 加密
-            long startTime = System.currentTimeMillis();
-            String encryptedData = encrypt(originalData, key, iv);
-            long encryptTime = System.currentTimeMillis() - startTime;
-            System.out.println("加密结果: " + encryptedData);
-            System.out.println("加密耗时: " + encryptTime + "ms");
-
-            String data = "KYWxoKWK3w8a8867aXCha+tgVE2cbZ4eR1Dc1YExri06DfZWBpUMAzlhY7rWR5SeU+xCVOauk4F7MxCJLN+5aJCBENCOAZtUksMM7VgsOz0=";
-            System.out.println("测试的加密数据:"+data);
-
-            String key1 = DATA_SECRET;   // 16字节密钥
-            String iv1 = DATA_SECRET_IV;
-            System.out.println("密钥: " + key);
-            System.out.println("初始化向量: " + iv);
-            String string = decrypt(data, key1, iv1);
-            System.out.println("测试的解密:"+string);
-            // 解密
-            startTime = System.currentTimeMillis();
-            String decryptedData = decrypt(encryptedData, key, iv);
-            long decryptTime = System.currentTimeMillis() - startTime;
-            System.out.println("解密结果: " + decryptedData);
-            System.out.println("解密耗时: " + decryptTime + "ms");
-
-            // 验证加解密一致性
-            boolean isSuccess = originalData.equals(decryptedData);
-            System.out.println("加解密验证: " + (isSuccess ? "成功" : "失败"));
-
-            // 测试随机密钥生成
-            System.out.println("\n=== 随机密钥生成测试 ===");
-            String randomKey = generateRandomKey();
-            String randomIV = generateRandomIV();
-            System.out.println("随机密钥: " + randomKey);
-            System.out.println("随机IV: " + randomIV);
-
-            // 使用随机密钥进行加解密测试
-            String testEncrypted = encrypt("测试数据", randomKey, randomIV);
-            String testDecrypted = decrypt(testEncrypted, randomKey, randomIV);
-            System.out.println("随机密钥加解密测试: " + ("测试数据".equals(testDecrypted) ? "成功" : "失败"));
-
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
+        BigDecimal a = new BigDecimal("6.099");
+        BigDecimal b = new BigDecimal("0.72");
+
+        System.out.println(a.multiply(b));
+        System.out.println((a.multiply(b)).setScale(2, BigDecimal.ROUND_HALF_UP));
+
+
+//        try {
+//            // 测试数据
+//            String originalData = "{\"OperatorID\":\"MA6DP6BE7\",\"SuccStat\":0,\"AccessToken\":\"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJNQTZEUDZCRTciLCJpYXQiOjE3NjQ1ODE0NzQsImV4cCI6MTc2NDU4ODY3NH0.mfXp_Wly4QiVYILK6hNSOEGSU7XejlYZRing0CRc8MQW1xuUxrQQwOxeCAwI_Bnay5WzqaYYFQeVuNeignxAuQ\",\"TokenAvailableTime\":6885,\"FailReason\":0}";
+//            String key = DATA_SECRET;   // 16字节密钥
+//            String iv = DATA_SECRET_IV;   // 16字节初始化向量
+//
+//            System.out.println("=== AES-128-CBC-PKCS5Padding 加解密测试 ===");
+//            System.out.println("原始数据: " + originalData);
+//            System.out.println("密钥: " + key);
+//            System.out.println("初始化向量: " + iv);
+//
+//            // 加密
+//            long startTime = System.currentTimeMillis();
+//            String encryptedData = encrypt(originalData, key, iv);
+//            long encryptTime = System.currentTimeMillis() - startTime;
+//            System.out.println("加密结果: " + encryptedData);
+//            System.out.println("加密耗时: " + encryptTime + "ms");
+//
+//            String data = "KYWxoKWK3w8a8867aXCha+tgVE2cbZ4eR1Dc1YExri06DfZWBpUMAzlhY7rWR5SeU+xCVOauk4F7MxCJLN+5aJCBENCOAZtUksMM7VgsOz0=";
+//            System.out.println("测试的加密数据:"+data);
+//
+//            String key1 = DATA_SECRET;   // 16字节密钥
+//            String iv1 = DATA_SECRET_IV;
+//            System.out.println("密钥: " + key);
+//            System.out.println("初始化向量: " + iv);
+//            String string = decrypt(data, key1, iv1);
+//            System.out.println("测试的解密:"+string);
+//            // 解密
+//            startTime = System.currentTimeMillis();
+//            String decryptedData = decrypt(encryptedData, key, iv);
+//            long decryptTime = System.currentTimeMillis() - startTime;
+//            System.out.println("解密结果: " + decryptedData);
+//            System.out.println("解密耗时: " + decryptTime + "ms");
+//
+//            // 验证加解密一致性
+//            boolean isSuccess = originalData.equals(decryptedData);
+//            System.out.println("加解密验证: " + (isSuccess ? "成功" : "失败"));
+//
+//            // 测试随机密钥生成
+//            System.out.println("\n=== 随机密钥生成测试 ===");
+//            String randomKey = generateRandomKey();
+//            String randomIV = generateRandomIV();
+//            System.out.println("随机密钥: " + randomKey);
+//            System.out.println("随机IV: " + randomIV);
+//
+//            // 使用随机密钥进行加解密测试
+//            String testEncrypted = encrypt("测试数据", randomKey, randomIV);
+//            String testDecrypted = decrypt(testEncrypted, randomKey, randomIV);
+//            System.out.println("随机密钥加解密测试: " + ("测试数据".equals(testDecrypted) ? "成功" : "失败"));
+//
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//        }
     }
 }

+ 1 - 1
src/main/java/com/zsElectric/boot/core/filter/RateLimiterFilter.java

@@ -32,7 +32,7 @@ public class RateLimiterFilter extends OncePerRequestFilter {
     private final RedisTemplate<String, Object> redisTemplate;
     private final ConfigService configService;
 
-    private static final long DEFAULT_IP_LIMIT = 10L; // 默认 IP 限流阈值
+    private static final long DEFAULT_IP_LIMIT = 1000L; // 默认 IP 限流阈值
 
     public RateLimiterFilter(RedisTemplate<String, Object> redisTemplate, ConfigService configService) {
         this.redisTemplate = redisTemplate;

+ 8 - 0
src/main/java/com/zsElectric/boot/security/service/PermissionService.java

@@ -32,24 +32,32 @@ public class PermissionService {
      * @return 是否有权限
      */
     public boolean hasPerm(String requiredPerm) {
+        log.info("========== 权限校验开始 ==========");
+        log.info("所需权限: {}", requiredPerm);
 
         if (StrUtil.isBlank(requiredPerm)) {
+            log.warn("权限标识为空,拒绝访问");
             return false;
         }
         // 超级管理员放行
         if (SecurityUtils.isRoot()) {
+            log.info("超级管理员,直接放行");
             return true;
         }
 
         // 获取当前登录用户的角色编码集合
         Set<String> roleCodes = SecurityUtils.getRoles();
+        log.info("当前用户角色: {}", roleCodes);
         if (CollectionUtil.isEmpty(roleCodes)) {
+            log.warn("用户角色为空,拒绝访问");
             return false;
         }
 
         // 获取当前登录用户的所有角色的权限列表
         Set<String> rolePerms = this.getRolePermsFormCache(roleCodes);
+        log.info("角色拥有的权限列表: {}", rolePerms);
         if (CollectionUtil.isEmpty(rolePerms)) {
+            log.warn("角色权限为空(Redis缓存中无数据),拒绝访问");
             return false;
         }
         // 判断当前登录用户的所有角色的权限列表中是否包含所需权限

+ 10 - 0
src/main/java/com/zsElectric/boot/system/controller/MenuController.java

@@ -10,6 +10,7 @@ import com.zsElectric.boot.system.model.query.MenuQuery;
 import com.zsElectric.boot.system.model.vo.MenuVO;
 import com.zsElectric.boot.system.model.vo.RouteVO;
 import com.zsElectric.boot.system.service.MenuService;
+import com.zsElectric.boot.system.service.RoleMenuService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -34,6 +35,7 @@ import java.util.List;
 public class MenuController {
 
     private final MenuService menuService;
+    private final RoleMenuService roleMenuService;
 
     @Operation(summary = "菜单列表")
     @GetMapping
@@ -111,5 +113,13 @@ public class MenuController {
         return Result.judge(result);
     }
 
+    @Operation(summary = "刷新权限缓存")
+    @PostMapping("/refresh-perms")
+    @PreAuthorize("@ss.hasPerm('sys:menu:edit')")
+    public Result<?> refreshPermsCache() {
+        roleMenuService.refreshRolePermsCache();
+        return Result.success("权限缓存刷新成功");
+    }
+
 }
 

+ 5 - 4
src/main/java/com/zsElectric/boot/system/service/impl/MenuServiceImpl.java

@@ -293,10 +293,8 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements Me
 
         boolean result = this.saveOrUpdate(entity);
         if (result) {
-            // 编辑刷新角色权限缓存
-            if (menuForm.getId() != null) {
-                roleMenuService.refreshRolePermsCache();
-            }
+            // 新增/编辑后刷新角色权限缓存
+            roleMenuService.refreshRolePermsCache();
         }
         // 修改菜单如果有子菜单,则更新子菜单的树路径
         updateChildrenTreePath(entity.getId(), treePath);
@@ -479,6 +477,9 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements Me
                 button.setTreePath(treePath + "," + button.getId());
                 this.updateById(button);
             }
+
+            // 代码生成后刷新角色权限缓存
+            roleMenuService.refreshRolePermsCache();
         }
     }
 

+ 11 - 3
src/main/java/com/zsElectric/boot/system/service/impl/RoleMenuServiceImpl.java

@@ -34,8 +34,9 @@ public class RoleMenuServiceImpl extends ServiceImpl<RoleMenuMapper, RoleMenu> i
      */
     @PostConstruct
     public void initRolePermsCache() {
-        log.info("初始化权限缓存... ");
+        log.info("========== 初始化权限缓存开始 ==========");
         refreshRolePermsCache();
+        log.info("========== 初始化权限缓存完成 ==========");
     }
 
     /**
@@ -43,18 +44,25 @@ public class RoleMenuServiceImpl extends ServiceImpl<RoleMenuMapper, RoleMenu> i
      */
     @Override
     public void refreshRolePermsCache() {
-        // 清理权限缓存
-        redisTemplate.opsForHash().delete(RedisConstants.System.ROLE_PERMS, "*");
+        // 清理权限缓存(删除整个 hash key)
+        redisTemplate.delete(RedisConstants.System.ROLE_PERMS);
+        log.info("已清理Redis权限缓存, key: {}", RedisConstants.System.ROLE_PERMS);
 
         List<RolePermsBO> list = this.baseMapper.getRolePermsList(null);
+        log.info("从数据库查询到的角色权限数据: {} 条", list != null ? list.size() : 0);
+
         if (CollectionUtil.isNotEmpty(list)) {
             list.forEach(item -> {
                 String roleCode = item.getRoleCode();
                 Set<String> perms = item.getPerms();
+                log.info("角色[{}]的权限列表: {}", roleCode, perms);
                 if (CollectionUtil.isNotEmpty(perms)) {
                     redisTemplate.opsForHash().put(RedisConstants.System.ROLE_PERMS, roleCode, perms);
+                    log.info("已缓存角色[{}]的权限到Redis, 权限数量: {}", roleCode, perms.size());
                 }
             });
+        } else {
+            log.warn("数据库中未查询到任何角色权限数据!请检查 sys_role、sys_menu、sys_role_menu 表数据");
         }
     }
 

+ 1 - 2
src/main/resources/mapper/business/FirmAccountLogMapper.xml

@@ -9,8 +9,7 @@
             a.firm_id,
             IFNULL(b.firm_type, IFNULL(a.firm_type, 1)) AS firmType,
             b.name AS firmName,
-            a.project_name,
-            a.name,
+            a.event_desc,
             a.serial_no,
             a.income_type,
             a.before_change,

+ 61 - 0
src/main/resources/mapper/business/ThirdPartyStationInfoMapper.xml

@@ -129,6 +129,15 @@
         <if test="query.serviceTel != null and query.serviceTel != ''">
             AND tpsi.service_tel = #{query.serviceTel}
         </if>
+        <if test="query.salesType != null and query.salesType == 0">
+            AND pf.firm_id IS NULL AND pf.third_party_id IS NULL
+        </if>
+        <if test="query.salesType != null and query.salesType == 1">
+            AND pf.firm_id IS NOT NULL
+        </if>
+        <if test="query.salesType != null and query.salesType == 2">
+            AND pf.third_party_id IS NOT NULL AND pf.firm_id IS NULL
+        </if>
         GROUP BY tpsi.id, pf.firm_id, pf.third_party_id
         ORDER BY tpsi.update_time DESC
     </select>
@@ -704,5 +713,57 @@
         </if>
         LIMIT 50
     </select>
+    <select id="selectChargingCost" resultType="com.zsElectric.boot.business.model.vo.applet.AppChargingCostVO">
+        SELECT
+            coi.charge_order_no,
+            tpsi.station_name,
+            coi.status,
+            tpci.connector_name,
+            tcs.start_charge_seq_stat AS order_status,
+            CASE tcs.start_charge_seq_stat
+                WHEN 1 THEN '启动中'
+                WHEN 2 THEN '充电中'
+                WHEN 3 THEN '停止中'
+                WHEN 4 THEN '已结束'
+                WHEN 5 THEN '未知'
+                ELSE '未知'
+                END AS order_status_desc,
+            TIMESTAMPDIFF(SECOND, tcs.start_time, tcs.end_time) AS charging_duration,
+            CONCAT(
+                    LPAD(FLOOR(TIMESTAMPDIFF(SECOND, tcs.start_time, tcs.end_time) / 3600), 2, '0'), ':',
+                    LPAD(FLOOR(MOD(TIMESTAMPDIFF(SECOND, tcs.start_time, tcs.end_time), 3600) / 60), 2, '0'), ':',
+                    LPAD(MOD(TIMESTAMPDIFF(SECOND, tcs.start_time, tcs.end_time), 60), 2, '0')
+            ) AS charging_duration_desc,
+            IFNULL(tcs.total_power, 0) AS total_power,
+            IFNULL(tcs.elec_money, 0) AS elec_money,
+            IFNULL(tcs.service_money, 0) AS service_money,
+            IFNULL(tcs.total_money, 0) AS total_money,
+            tcs.charge_details,
+            tcs.soc,
+            COALESCE(tcs.current_a, tcs.current_b, tcs.current_c) AS `current`,
+            COALESCE(tcs.voltage_a, tcs.voltage_b, tcs.voltage_c) AS voltage,
+            ROUND(
+                    COALESCE(tcs.voltage_a, tcs.voltage_b, tcs.voltage_c) *
+                    COALESCE(tcs.current_a, tcs.current_b, tcs.current_c) / 1000,
+                    2
+            ) AS power,
+            DATE_FORMAT(tcs.start_time, '%Y-%m-%d %H:%i:%s') AS start_time,
+            DATE_FORMAT(tcs.update_time, '%Y-%m-%d %H:%i:%s') AS last_update_time,
+            tpci.connector_id AS connector_code
+        FROM c_charge_order_info coi
+                 INNER JOIN third_party_charge_status tcs
+                            ON coi.start_charge_seq = tcs.start_charge_seq
+                 INNER JOIN third_party_connector_info tpci
+                            ON tcs.connector_id = tpci.connector_id
+                                AND tpci.is_deleted = 0
+                 INNER JOIN third_party_station_info tpsi
+                            ON tpci.station_id = tpsi.station_id
+                                AND tpsi.is_deleted = 0
+        WHERE
+            coi.charge_order_no = #{orderNo}
+            AND coi.is_deleted = 0
+        ORDER BY tcs.update_time DESC
+            LIMIT 1
+    </select>
 
 </mapper>