浏览代码

feat(policy-fee): 新增策略费用管理及查询功能

- 新增 AddPolicyFeeDTO 用于新增策略费用请求参数封装
- 修改 PolicyFee 实体,调整字段及注释以支持销售类型和渠道方ID
- 改造 PolicyFeeController,增加充电站信息分页查询、策略费用查询和新增/修改接口
- 在 PolicyFeeService 接口中添加根据站点查询策略费用及新增策略费用方法
- 实现 PolicyFeeServiceImpl,实现策略费用综合计算逻辑和新增/更新功能
- 新增 TimePeriodPriceVO 视图对象,封装时段价格及相关费用信息
- SecurityConfig 增加 WebSecurityCustomizer Bean,支持忽略安全路径校验,优化静态资源访问权限配置
SheepHy 1 天之前
父节点
当前提交
f9b4471bc2

+ 51 - 5
src/main/java/com/zsElectric/boot/business/controller/PolicyFeeController.java

@@ -1,16 +1,20 @@
 package com.zsElectric.boot.business.controller;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.zsElectric.boot.business.model.dto.AddPolicyFeeDTO;
+import com.zsElectric.boot.business.model.query.ThirdPartyStationInfoQuery;
 import com.zsElectric.boot.business.model.vo.PartyStationInfoVO;
+import com.zsElectric.boot.business.model.vo.ThirdPartyStationInfoVO;
+import com.zsElectric.boot.business.model.vo.TimePeriodPriceVO;
 import com.zsElectric.boot.business.service.PolicyFeeService;
 import com.zsElectric.boot.business.service.ThirdPartyChargingService;
 import com.zsElectric.boot.business.service.ThirdPartyInfoService;
+import com.zsElectric.boot.core.web.PageResult;
 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.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 
@@ -35,7 +39,7 @@ public class PolicyFeeController {
      *
      */
     @Operation(summary = "获取充电桩信息集合(下拉使用)")
-    @PostMapping("/getPartyStationInfo")
+    @GetMapping("/getPartyStationInfo")
     public Result<List<PartyStationInfoVO>> getPartyStationInfo(){
         return Result.success(chargingService.getPartyStationInfo());
     }
@@ -44,8 +48,50 @@ public class PolicyFeeController {
      * 获取渠道方集合
      * */
     @Operation(summary = "获取渠道方集合(下拉使用)")
-    @PostMapping("/getPartyStationInfoList")
+    @GetMapping("/getPartyStationInfoList")
     public Result<List<PartyStationInfoVO>> getPartyStationInfoList(){
         return Result.success(thirdPartyInfoService.getPartyStationInfoList());
     }
+
+    /**
+     * 获取充电站信息分页列表(策略列表)
+     *
+     * @param queryParams 查询参数
+     * @return 充电站信息分页列表
+     */
+    @Operation(summary = "获取充电站信息分页列表(策略列表)")
+    @PostMapping("/getStationInfoPageByEquipment")
+    public PageResult<ThirdPartyStationInfoVO> getStationInfoPageByEquipment(@RequestBody ThirdPartyStationInfoQuery queryParams){
+        IPage<ThirdPartyStationInfoVO> stationInfoPageByEquipment = chargingService.getStationInfoPageByEquipment(queryParams);
+        return PageResult.success(stationInfoPageByEquipment);
+    }
+
+    /**
+     * 查询策略费用
+     *
+     * @param stationId 站点ID
+     * @param salesType 销售类型
+     * @param thirdPartyId 渠道方ID
+     * @return 策略费用列表
+     */
+    @Operation(summary = "查询策略费用")
+    @GetMapping("/getPolicyFee")
+    public Result<List<TimePeriodPriceVO>> getPolicyFee(
+            @RequestParam Long stationId,
+            @RequestParam Integer salesType,
+            @RequestParam(required = false, defaultValue = "0") Integer thirdPartyId) {
+        return Result.success(policyFeeService.getPolicyFee(stationId, salesType, thirdPartyId));
+    }
+
+    /**
+     * 新增/修改策略费用
+     *
+     * @param addPolicyFeeDTO 策略费用信息
+     * @return 操作结果
+     */
+    @Operation(summary = "新增/修改策略费用")
+    @PostMapping("/addPolicyFee")
+    public Result<Boolean> addPolicyFee(@RequestBody AddPolicyFeeDTO addPolicyFeeDTO) {
+        return Result.success(policyFeeService.addPolicyFee(addPolicyFeeDTO));
+    }
 }

+ 23 - 0
src/main/java/com/zsElectric/boot/business/model/dto/AddPolicyFeeDTO.java

@@ -0,0 +1,23 @@
+package com.zsElectric.boot.business.model.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+
+@Schema( description = "新增站点查询策略费用入参")
+@Data
+@Accessors(chain = true)
+public class AddPolicyFeeDTO {
+    @Schema(description = "时间段")
+    private String timePeriod;
+    @Schema(description = "运营服务费(元)")
+    private BigDecimal operationServiceFee;
+    @Schema(description = "站点信息ID(关联third_party_station_info表)")
+    private Long stationInfoId;
+    @Schema(description = "销售类型(0、平台 1、企业 2、渠道方)")
+    private Integer salesType;
+    @Schema(description = "渠道方ID(销售类型为1、2时必填)关联c_third_party_info")
+    private Long thirdPartyId;
+}

+ 3 - 6
src/main/java/com/zsElectric/boot/business/model/entity/PolicyFee.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableLogic;
 import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -39,11 +40,7 @@ public class PolicyFee implements Serializable {
      */
     private Long stationInfoId;
 
-    /**
-     * 策略明细ID(关联third_party_policy_info表)
-     */
-    private Long policyInfoId;
-
+    private String startTime;
     /**
      * 运营费
      */
@@ -60,7 +57,7 @@ public class PolicyFee implements Serializable {
     private Integer salesType;
 
     /**
-     * 渠道方ID(销售类型为2时必填)关联c_third_party_info
+     * 渠道方ID(销售类型为1、2时必填)关联c_third_party_info
      */
     private Long thirdPartyId;
 

+ 50 - 0
src/main/java/com/zsElectric/boot/business/model/vo/TimePeriodPriceVO.java

@@ -0,0 +1,50 @@
+
+package com.zsElectric.boot.business.model.vo;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * 时段价格视图对象
+ *
+ * @author zsElectric
+ * @since 2025-12-15
+ */
+@Data
+@Accessors(chain = true)
+@Schema(description = "时段价格视图对象")
+public class TimePeriodPriceVO implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    @Schema(description = "时间段")
+    private String timePeriod;
+
+    @Schema(description = "电费(元/度)")
+    private BigDecimal electricityPrice;
+
+    @Schema(description = "结算服务费(元)")
+    private BigDecimal settlementServiceFee;
+
+    @Schema(description = "结算费合计(元/度)")
+    private BigDecimal settlementTotalPrice;
+
+    @Schema(description = "运营服务费(元)")
+    private BigDecimal operationServiceFee;
+
+    @Schema(description = "增值费用(元)")
+    private BigDecimal valueAddedFees;
+
+    @Schema(description = "销售合计价格(元/度)")
+    private BigDecimal saleTotalPrice;
+
+    @Schema(description = "时段标志(1-尖,2-峰,3-平,4-谷)")
+    private Integer periodFlag;
+}

+ 15 - 0
src/main/java/com/zsElectric/boot/business/service/PolicyFeeService.java

@@ -1,5 +1,10 @@
 package com.zsElectric.boot.business.service;
 
+import com.zsElectric.boot.business.model.dto.AddPolicyFeeDTO;
+import com.zsElectric.boot.business.model.vo.TimePeriodPriceVO;
+
+import java.util.List;
+
 /**
  * 策略费用服务接口
  *
@@ -7,5 +12,15 @@ package com.zsElectric.boot.business.service;
  * @since 2025-12-15
  */
 public interface PolicyFeeService {
+    /**
+     * 根据站点查询策略费用
+     *
+     */
+    List<TimePeriodPriceVO> getPolicyFee(long stationId, int salesType, int thirdPartyId);
+
+    /**
+     * 新增站点查询策略费用
+     * */
+    boolean addPolicyFee(AddPolicyFeeDTO addPolicyFeeDTO);
 
 }

+ 149 - 0
src/main/java/com/zsElectric/boot/business/service/impl/PolicyFeeServiceImpl.java

@@ -1,11 +1,29 @@
 package com.zsElectric.boot.business.service.impl;
 
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.zsElectric.boot.business.mapper.PolicyFeeMapper;
+import com.zsElectric.boot.business.mapper.ThirdPartyEquipmentInfoMapper;
+import com.zsElectric.boot.business.model.dto.AddPolicyFeeDTO;
+import com.zsElectric.boot.business.model.entity.PolicyFee;
+import com.zsElectric.boot.business.model.vo.TimePeriodPriceVO;
 import com.zsElectric.boot.business.service.PolicyFeeService;
+import com.zsElectric.boot.charging.entity.ThirdPartyEquipmentInfo;
+import com.zsElectric.boot.charging.entity.ThirdPartyEquipmentPricePolicy;
+import com.zsElectric.boot.charging.entity.ThirdPartyPolicyInfo;
+import com.zsElectric.boot.charging.mapper.ThirdPartyEquipmentPricePolicyMapper;
+import com.zsElectric.boot.charging.mapper.ThirdPartyPolicyInfoMapper;
+import com.zsElectric.boot.system.model.entity.DictItem;
+import com.zsElectric.boot.system.service.DictItemService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
 /**
  * 策略费用服务实现
  *
@@ -17,6 +35,137 @@ import org.springframework.stereotype.Service;
 @RequiredArgsConstructor
 public class PolicyFeeServiceImpl implements PolicyFeeService {
 
+    /**
+     * 时段标志映射:1-尖, 2-峰, 3-平, 4-谷(固定常量,避免重复创建)
+     */
+    private static final Map<Integer, String> PERIOD_FLAG_LABEL_MAP = Map.of(
+            1, "尖",
+            2, "峰",
+            3, "平",
+            4, "谷"
+    );
+
     private final PolicyFeeMapper policyFeeMapper;
 
+    private final ThirdPartyEquipmentInfoMapper thirdPartyEquipmentInfoMapper;
+
+    private final ThirdPartyPolicyInfoMapper thirdPartyPolicyInfoMapper;
+
+    private final ThirdPartyEquipmentPricePolicyMapper thirdPartyEquipmentPricePolicyMapper;
+
+    private final DictItemService dictItemService;
+
+    @Override
+    public List<TimePeriodPriceVO> getPolicyFee(long stationId, int salesType, int thirdPartyId) {
+        List<TimePeriodPriceVO> timePeriodPriceVOS = new ArrayList<>();
+        
+        // 查询设备信息
+        ThirdPartyEquipmentInfo thirdPartyEquipmentInfo = thirdPartyEquipmentInfoMapper.selectOne(Wrappers.<ThirdPartyEquipmentInfo>lambdaQuery()
+                .eq(ThirdPartyEquipmentInfo::getStationId, stationId)
+                .eq(ThirdPartyEquipmentInfo::getIsDeleted, 0)
+                .last("limit 1"));
+        if (thirdPartyEquipmentInfo == null) {
+            return timePeriodPriceVOS;
+        }
+        
+        // 查询价格策略
+        ThirdPartyEquipmentPricePolicy pricePolicy = thirdPartyEquipmentPricePolicyMapper.selectOne(Wrappers.<ThirdPartyEquipmentPricePolicy>lambdaQuery()
+                .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, thirdPartyEquipmentInfo.getEquipmentId())
+                .eq(ThirdPartyEquipmentPricePolicy::getIsDeleted, 0)
+                .last("limit 1"));
+        if (pricePolicy == null) {
+            return timePeriodPriceVOS;
+        }
+
+        // 互联互通成本价
+        List<ThirdPartyPolicyInfo> thirdPartyPolicyInfos = thirdPartyPolicyInfoMapper.selectList(Wrappers.<ThirdPartyPolicyInfo>lambdaQuery()
+                .eq(ThirdPartyPolicyInfo::getPricePolicyId, pricePolicy.getId())
+                .eq(ThirdPartyPolicyInfo::getIsDeleted, 0));
+
+        // 运营费(按时段配置)
+        Map<String, PolicyFee> policyFeeMap = policyFeeMapper.selectList(Wrappers.<PolicyFee>lambdaQuery()
+                .eq(PolicyFee::getStationInfoId, stationId)
+                .eq(PolicyFee::getIsDeleted, 0)
+                .eq(PolicyFee::getSalesType, salesType)
+                .eq(salesType != 0, PolicyFee::getThirdPartyId, thirdPartyId))
+                .stream()
+                .collect(Collectors.toMap(
+                        PolicyFee::getStartTime,
+                        policyFee -> policyFee,
+                        (v1, v2) -> v1
+                ));
+
+        // 查询时段标志字典,获取增值服务费价格(label对应中文,value对应价格)
+        Map<String, BigDecimal> labelPriceMap = dictItemService.list(
+                        Wrappers.<DictItem>lambdaQuery()
+                                .eq(DictItem::getDictCode, "time_period_flag")
+                                .eq(DictItem::getStatus, 1)
+                ).stream()
+                .collect(Collectors.toMap(
+                        DictItem::getLabel,
+                        item -> new BigDecimal(item.getValue()),
+                        (v1, v2) -> v1
+                ));
+                
+        // 计算综合价格
+        for (ThirdPartyPolicyInfo policyInfo : thirdPartyPolicyInfos) {
+            // 获取增值服务费
+            BigDecimal valueAddedFees = labelPriceMap.getOrDefault(
+                    PERIOD_FLAG_LABEL_MAP.get(policyInfo.getPeriodFlag()),
+                    BigDecimal.ZERO
+            );
+            
+            // 根据时段获取对应的运营费配置
+            PolicyFee policyFee = policyFeeMap.get(policyInfo.getStartTime());
+            
+            TimePeriodPriceVO timePeriodPriceVO = new TimePeriodPriceVO();
+            timePeriodPriceVO.setTimePeriod(policyInfo.getStartTime())
+                    .setPeriodFlag(policyInfo.getPeriodFlag())
+                    .setElectricityPrice(policyInfo.getElecPrice())
+                    .setSettlementServiceFee(policyInfo.getServicePrice())
+                    .setSettlementTotalPrice(policyInfo.getElecPrice().add(policyInfo.getServicePrice()))
+                    .setOperationServiceFee(BigDecimal.ZERO)
+                    .setSaleTotalPrice(BigDecimal.ZERO)
+                    .setValueAddedFees(valueAddedFees);
+            
+            // 只有配置了该时段的运营费才计算销售价并返回
+            if (policyFee != null) {
+                timePeriodPriceVO.setOperationServiceFee(policyFee.getOpFee())
+                        .setSaleTotalPrice(policyInfo.getElecPrice()
+                                .add(policyInfo.getServicePrice())
+                                .add(policyFee.getOpFee())
+                                .add(valueAddedFees));
+                timePeriodPriceVOS.add(timePeriodPriceVO);
+            }
+        }
+        
+        return timePeriodPriceVOS;
+    }
+
+    @Override
+    public boolean addPolicyFee(AddPolicyFeeDTO addPolicyFeeDTO) {
+        // 根据站点+时段+销售类型+渠道方查询是否已存在
+        PolicyFee existPolicyFee = policyFeeMapper.selectOne(Wrappers.<PolicyFee>lambdaQuery()
+                .eq(PolicyFee::getStationInfoId, addPolicyFeeDTO.getStationInfoId())
+                .eq(PolicyFee::getStartTime, addPolicyFeeDTO.getTimePeriod())
+                .eq(PolicyFee::getSalesType, addPolicyFeeDTO.getSalesType())
+                .eq(addPolicyFeeDTO.getSalesType() != 0, PolicyFee::getThirdPartyId, addPolicyFeeDTO.getThirdPartyId())
+                .eq(PolicyFee::getIsDeleted, 0)
+                .last("limit 1"));
+        
+        if (existPolicyFee != null) {
+            // 已存在,执行更新
+            existPolicyFee.setOpFee(addPolicyFeeDTO.getOperationServiceFee());
+            return policyFeeMapper.updateById(existPolicyFee) > 0;
+        } else {
+            // 不存在,执行新增
+            PolicyFee policyFee = new PolicyFee();
+            policyFee.setStationInfoId(addPolicyFeeDTO.getStationInfoId());
+            policyFee.setStartTime(addPolicyFeeDTO.getTimePeriod());
+            policyFee.setOpFee(addPolicyFeeDTO.getOperationServiceFee());
+            policyFee.setSalesType(addPolicyFeeDTO.getSalesType());
+            policyFee.setThirdPartyId(addPolicyFeeDTO.getThirdPartyId());
+            return policyFeeMapper.insert(policyFee) > 0;
+        }
+    }
 }

+ 19 - 2
src/main/java/com/zsElectric/boot/config/SecurityConfig.java

@@ -30,6 +30,7 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider
 import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
 import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
 import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
 import org.springframework.security.config.http.SessionCreationPolicy;
@@ -118,14 +119,14 @@ public class SecurityConfig {
                             if (ArrayUtil.isNotEmpty(ignoreUrls)) {
                                 requestMatcherRegistry.requestMatchers(ignoreUrls).permitAll();
                             }
-                            
+
                             // 配置完全绕过安全检查的路径(原 unsecuredUrls)
                             String[] unsecuredUrls = securityProperties.getUnsecuredUrls();
                     log.info("非安全端点 unsecured-urls: {}", (Object) unsecuredUrls);
                             if (ArrayUtil.isNotEmpty(unsecuredUrls)) {
                                 requestMatcherRegistry.requestMatchers(unsecuredUrls).permitAll();
                             }
-                            
+
                             // 其他所有请求需登录后访问
                             requestMatcherRegistry.anyRequest().authenticated();
                         }
@@ -154,6 +155,22 @@ public class SecurityConfig {
                 .build();
     }
 
+    /**
+     * 配置Web安全自定义器,以忽略特定请求路径的安全性检查。
+     * <p>
+     * 该配置用于指定哪些请求路径不经过Spring Security过滤器链。通常用于静态资源文件。
+     * 注意:这些警告可以忽略,因为Swagger等静态资源需要完全绕过过滤器链才能正常访问。
+     */
+    @Bean
+    public WebSecurityCustomizer webSecurityCustomizer() {
+        return (web) -> {
+            String[] unsecuredUrls = securityProperties.getUnsecuredUrls();
+            if (ArrayUtil.isNotEmpty(unsecuredUrls)) {
+                web.ignoring().requestMatchers(unsecuredUrls);
+            }
+        };
+    }
+
     /**
      * 默认密码认证的 Provider
      */