소스 검색

feat(price-policy): 新增价格策略管理及同步功能

- 新增第三方价格策略数据存储服务,实现价格策略数据的存储与更新逻辑
- 在充电业务服务中集成价格策略保存,保证查询价格策略时进行数据落地
- 添加定时任务,每日定时同步所有充电桩的价格策略信息并存入数据库
- 新增价格策略分页查询及修改接口,支持运营服务费修改及策略明细展示
- 实现价格策略分页查询的业务逻辑,支持充电站维度的条件筛选及数据返回
- 设计并实现价格策略分页返回数据结构及明细字段,清晰展现各时间段电价及服务费
- 增加价格策略更新流程,支持批量更新充电桩的运营服务费及销售合计价格
- 完善定时任务和服务层日志,增强系统可观测性及错误处理能力
SheepHy 3 일 전
부모
커밋
82839e3f90

+ 52 - 0
src/main/java/com/zsElectric/boot/business/controller/PricePolicyManagementController.java

@@ -0,0 +1,52 @@
+package com.zsElectric.boot.business.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zsElectric.boot.business.model.dto.UpdatePricePolicyDTO;
+import com.zsElectric.boot.business.model.query.PricePolicyPageQuery;
+import com.zsElectric.boot.business.model.vo.PricePolicyPageVO;
+import com.zsElectric.boot.business.service.PricePolicyManagementService;
+import com.zsElectric.boot.common.annotation.Log;
+import com.zsElectric.boot.common.enums.LogModuleEnum;
+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 lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 价格策略管理控制器
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@Tag(name = "价格策略管理")
+@RequestMapping("/dev/v1/price-policy")
+public class PricePolicyManagementController {
+
+    private final PricePolicyManagementService pricePolicyManagementService;
+
+    /**
+     * 分页查询价格策略列表
+     */
+    @Operation(summary = "分页查询价格策略列表")
+    @PostMapping("/page")
+    @Log(value = "分页查询价格策略列表", module = LogModuleEnum.PARKING_CALL, params = true, result = true)
+    public Result<Page<PricePolicyPageVO>> queryPricePolicyPage(@RequestBody PricePolicyPageQuery query) {
+        return Result.success(pricePolicyManagementService.queryPricePolicyPage(query));
+    }
+
+    /**
+     * 修改计费策略
+     */
+    @Operation(summary = "修改计费策略")
+    @PostMapping("/update")
+    @Log(value = "修改计费策略", module = LogModuleEnum.PARKING_CALL, params = true, result = true)
+    public Result<Void> updatePricePolicy(@RequestBody UpdatePricePolicyDTO dto) {
+        pricePolicyManagementService.updatePricePolicy(dto);
+        return Result.success();
+    }
+}

+ 29 - 0
src/main/java/com/zsElectric/boot/business/model/dto/UpdatePricePolicyDTO.java

@@ -0,0 +1,29 @@
+package com.zsElectric.boot.business.model.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 修改计费策略请求DTO
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Data
+@Schema(description = "修改计费策略请求")
+public class UpdatePricePolicyDTO {
+
+    @Schema(description = "电站ID", required = true, example = "193")
+    private String stationId;
+
+    @Schema(description = "电站名称", example = "轩宇智慧停车场")
+    private String stationName;
+
+    @Schema(description = "是否有合约服务费", required = true, example = "true")
+    private Boolean hasContractServiceFee;
+
+    @Schema(description = "合约服务费(元/度)", example = "0.1")
+    private BigDecimal contractServiceFee;
+}

+ 27 - 0
src/main/java/com/zsElectric/boot/business/model/query/PricePolicyPageQuery.java

@@ -0,0 +1,27 @@
+package com.zsElectric.boot.business.model.query;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 价格策略分页查询参数
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Data
+@Schema(description = "价格策略分页查询参数")
+public class PricePolicyPageQuery {
+
+    @Schema(description = "充电站ID", example = "193")
+    private String stationId;
+
+    @Schema(description = "充电站名称", example = "轩宇智慧停车场")
+    private String stationName;
+
+    @Schema(description = "页码", example = "1")
+    private Integer pageNo = 1;
+
+    @Schema(description = "每页数量", example = "10")
+    private Integer pageSize = 10;
+}

+ 75 - 0
src/main/java/com/zsElectric/boot/business/model/vo/PricePolicyPageVO.java

@@ -0,0 +1,75 @@
+package com.zsElectric.boot.business.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 价格策略分页返回VO
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Data
+@Schema(description = "价格策略分页返回")
+public class PricePolicyPageVO {
+
+    @Schema(description = "充电站ID", example = "193")
+    private String stationId;
+
+    @Schema(description = "充电站名称", example = "轩宇智慧停车场")
+    private String stationName;
+
+    @Schema(description = "所在城市", example = "贵阳市")
+    private String city;
+
+    @Schema(description = "设备所属方", example = "贵阳智慧停车资产")
+    private String equipmentOwner;
+
+    @Schema(description = "详细地址", example = "贵州省贵阳市云岩区路189号")
+    private String address;
+
+    @Schema(description = "场所类型", example = "其他")
+    private String locationType;
+
+    @Schema(description = "充电终端数量", example = "24")
+    private Integer terminalCount;
+
+    @Schema(description = "站点状态", example = "未知")
+    private String stationStatus;
+
+    @Schema(description = "服务电话", example = "18108512304")
+    private String serviceTel;
+
+    @Schema(description = "更新时间", example = "2025-12-12 05:30")
+    private LocalDateTime updateTime;
+
+    @Schema(description = "价格策略明细列表")
+    private List<PricePolicyDetail> policyDetails;
+
+    @Data
+    @Schema(description = "价格策略明细")
+    public static class PricePolicyDetail {
+
+        @Schema(description = "时间段", example = "10:00-13:00")
+        private String timePeriod;
+
+        @Schema(description = "电费(元/度)", example = "0.87")
+        private BigDecimal elecPrice;
+
+        @Schema(description = "结算服务费(元)", example = "0.1")
+        private BigDecimal servicePrice;
+
+        @Schema(description = "结算费合计(元/度)", example = "0.97")
+        private BigDecimal settlementTotal;
+
+        @Schema(description = "运营服务费(元)", example = "0.09")
+        private BigDecimal operationServiceFee;
+
+        @Schema(description = "销售合计价格(元/度)", example = "1.06")
+        private BigDecimal totalSalesPrice;
+    }
+}

+ 30 - 0
src/main/java/com/zsElectric/boot/business/service/PricePolicyManagementService.java

@@ -0,0 +1,30 @@
+package com.zsElectric.boot.business.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zsElectric.boot.business.model.dto.UpdatePricePolicyDTO;
+import com.zsElectric.boot.business.model.query.PricePolicyPageQuery;
+import com.zsElectric.boot.business.model.vo.PricePolicyPageVO;
+
+/**
+ * 价格策略管理服务
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+public interface PricePolicyManagementService {
+
+    /**
+     * 分页查询价格策略列表
+     *
+     * @param query 查询参数
+     * @return 分页结果
+     */
+    Page<PricePolicyPageVO> queryPricePolicyPage(PricePolicyPageQuery query);
+
+    /**
+     * 修改计费策略(更新运营服务费)
+     *
+     * @param dto 修改参数
+     */
+    void updatePricePolicy(UpdatePricePolicyDTO dto);
+}

+ 312 - 0
src/main/java/com/zsElectric/boot/business/service/impl/PricePolicyManagementServiceImpl.java

@@ -0,0 +1,312 @@
+package com.zsElectric.boot.business.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zsElectric.boot.business.model.dto.UpdatePricePolicyDTO;
+import com.zsElectric.boot.business.model.query.PricePolicyPageQuery;
+import com.zsElectric.boot.business.model.vo.PricePolicyPageVO;
+import com.zsElectric.boot.business.service.PricePolicyManagementService;
+import com.zsElectric.boot.charging.entity.ThirdPartyConnectorInfo;
+import com.zsElectric.boot.charging.entity.ThirdPartyEquipmentPricePolicy;
+import com.zsElectric.boot.charging.entity.ThirdPartyPolicyInfo;
+import com.zsElectric.boot.charging.entity.ThirdPartyStationInfo;
+import com.zsElectric.boot.charging.mapper.ThirdPartyConnectorInfoMapper;
+import com.zsElectric.boot.charging.mapper.ThirdPartyEquipmentPricePolicyMapper;
+import com.zsElectric.boot.charging.mapper.ThirdPartyPolicyInfoMapper;
+import com.zsElectric.boot.charging.mapper.ThirdPartyStationInfoMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 价格策略管理服务实现
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class PricePolicyManagementServiceImpl implements PricePolicyManagementService {
+
+    private final ThirdPartyStationInfoMapper stationInfoMapper;
+    private final ThirdPartyConnectorInfoMapper connectorInfoMapper;
+    private final ThirdPartyEquipmentPricePolicyMapper pricePolicyMapper;
+    private final ThirdPartyPolicyInfoMapper policyInfoMapper;
+
+    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
+
+    @Override
+    public Page<PricePolicyPageVO> queryPricePolicyPage(PricePolicyPageQuery query) {
+        // 查询充电站列表
+        LambdaQueryWrapper<ThirdPartyStationInfo> queryWrapper = Wrappers.lambdaQuery();
+        
+        if (StringUtils.hasText(query.getStationId())) {
+            queryWrapper.eq(ThirdPartyStationInfo::getStationId, query.getStationId());
+        }
+        if (StringUtils.hasText(query.getStationName())) {
+            queryWrapper.like(ThirdPartyStationInfo::getStationName, query.getStationName());
+        }
+        
+        Page<ThirdPartyStationInfo> stationPage = stationInfoMapper.selectPage(
+                new Page<>(query.getPageNo(), query.getPageSize()),
+                queryWrapper
+        );
+
+        // 转换为VO
+        Page<PricePolicyPageVO> resultPage = new Page<>(stationPage.getCurrent(), stationPage.getSize(), stationPage.getTotal());
+        List<PricePolicyPageVO> voList = stationPage.getRecords().stream()
+                .map(this::convertToVO)
+                .collect(Collectors.toList());
+        
+        resultPage.setRecords(voList);
+        return resultPage;
+    }
+
+    /**
+     * 转换为VO对象
+     */
+    private PricePolicyPageVO convertToVO(ThirdPartyStationInfo station) {
+        PricePolicyPageVO vo = new PricePolicyPageVO();
+        vo.setStationId(station.getStationId());
+        vo.setStationName(station.getStationName());
+        vo.setCity(getCity(station.getAreaCode()));
+        vo.setEquipmentOwner(station.getEquipmentOwnerId());
+        vo.setAddress(station.getAddress());
+        vo.setLocationType(getLocationType(station.getConstruction()));
+        vo.setTerminalCount(getTerminalCount(station.getStationId()));
+        vo.setStationStatus(getStationStatusText(station.getStationStatus()));
+        vo.setServiceTel(station.getServiceTel());
+        vo.setUpdateTime(station.getUpdateTime());
+
+        // 查询价格策略明细
+        vo.setPolicyDetails(queryPolicyDetails(station.getStationId()));
+
+        return vo;
+    }
+
+    /**
+     * 查询价格策略明细
+     */
+    private List<PricePolicyPageVO.PricePolicyDetail> queryPolicyDetails(String stationId) {
+        // 查询该充电站的所有充电桩接口
+        List<ThirdPartyConnectorInfo> connectors = connectorInfoMapper.selectList(
+                Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
+                        .eq(ThirdPartyConnectorInfo::getStationId, stationId)
+                        .last("LIMIT 1") // 取第一个充电桩的价格策略
+        );
+
+        if (CollectionUtils.isEmpty(connectors)) {
+            return new ArrayList<>();
+        }
+
+        String connectorId = connectors.get(0).getConnectorId();
+
+        // 查询最新的价格策略
+        List<ThirdPartyEquipmentPricePolicy> policies = pricePolicyMapper.selectList(
+                Wrappers.<ThirdPartyEquipmentPricePolicy>lambdaQuery()
+                        .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, connectorId)
+                        .orderByDesc(ThirdPartyEquipmentPricePolicy::getCreateTime)
+                        .last("LIMIT 1")
+        );
+
+        if (CollectionUtils.isEmpty(policies)) {
+            return new ArrayList<>();
+        }
+
+        Long policyId = policies.get(0).getId();
+
+        // 查询价格策略明细
+        List<ThirdPartyPolicyInfo> policyInfos = policyInfoMapper.selectList(
+                Wrappers.<ThirdPartyPolicyInfo>lambdaQuery()
+                        .eq(ThirdPartyPolicyInfo::getPricePolicyId, policyId)
+                        .orderBy(true, true, ThirdPartyPolicyInfo::getStartTime)
+        );
+
+        return convertToPolicyDetails(policyInfos);
+    }
+
+    /**
+     * 转换价格策略明细
+     */
+    private List<PricePolicyPageVO.PricePolicyDetail> convertToPolicyDetails(List<ThirdPartyPolicyInfo> policyInfos) {
+        List<PricePolicyPageVO.PricePolicyDetail> details = new ArrayList<>();
+
+        for (int i = 0; i < policyInfos.size(); i++) {
+            ThirdPartyPolicyInfo info = policyInfos.get(i);
+            PricePolicyPageVO.PricePolicyDetail detail = new PricePolicyPageVO.PricePolicyDetail();
+
+            // 时间段处理
+            String startTime = formatTime(info.getStartTime());
+            String endTime;
+            if (i < policyInfos.size() - 1) {
+                endTime = formatTime(policyInfos.get(i + 1).getStartTime());
+            } else {
+                endTime = formatTime(policyInfos.get(0).getStartTime());
+            }
+            detail.setTimePeriod(startTime + "-" + endTime);
+
+            detail.setElecPrice(info.getElecPrice());
+            detail.setServicePrice(info.getServicePrice());
+            
+            // 结算费合计 = 电价 + 结算服务费
+            BigDecimal settlementTotal = BigDecimal.ZERO;
+            if (info.getElecPrice() != null) {
+                settlementTotal = settlementTotal.add(info.getElecPrice());
+            }
+            if (info.getServicePrice() != null) {
+                settlementTotal = settlementTotal.add(info.getServicePrice());
+            }
+            detail.setSettlementTotal(settlementTotal);
+
+            detail.setOperationServiceFee(info.getOperationServiceFee());
+            detail.setTotalSalesPrice(info.getTotalSalesPrice());
+
+            details.add(detail);
+        }
+
+        return details;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updatePricePolicy(UpdatePricePolicyDTO dto) {
+        // 查询该充电站的所有充电桩接口
+        List<ThirdPartyConnectorInfo> connectors = connectorInfoMapper.selectList(
+                Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
+                        .eq(ThirdPartyConnectorInfo::getStationId, dto.getStationId())
+        );
+
+        if (CollectionUtils.isEmpty(connectors)) {
+            log.warn("未找到充电站的充电桩接口信息, stationId: {}", dto.getStationId());
+            return;
+        }
+
+        BigDecimal operationServiceFee = dto.getHasContractServiceFee() && dto.getContractServiceFee() != null 
+                ? dto.getContractServiceFee() 
+                : BigDecimal.ZERO;
+
+        // 更新所有充电桩的价格策略
+        for (ThirdPartyConnectorInfo connector : connectors) {
+            updateConnectorPricePolicy(connector.getConnectorId(), operationServiceFee);
+        }
+
+        log.info("更新充电站价格策略成功, stationId: {}, operationServiceFee: {}", dto.getStationId(), operationServiceFee);
+    }
+
+    /**
+     * 更新单个充电桩的价格策略
+     */
+    private void updateConnectorPricePolicy(String connectorId, BigDecimal operationServiceFee) {
+        // 查询最新的价格策略
+        List<ThirdPartyEquipmentPricePolicy> policies = pricePolicyMapper.selectList(
+                Wrappers.<ThirdPartyEquipmentPricePolicy>lambdaQuery()
+                        .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, connectorId)
+                        .orderByDesc(ThirdPartyEquipmentPricePolicy::getCreateTime)
+                        .last("LIMIT 1")
+        );
+
+        if (CollectionUtils.isEmpty(policies)) {
+            log.warn("未找到充电桩的价格策略, connectorId: {}", connectorId);
+            return;
+        }
+
+        Long policyId = policies.get(0).getId();
+
+        // 查询所有价格策略明细
+        List<ThirdPartyPolicyInfo> policyInfos = policyInfoMapper.selectList(
+                Wrappers.<ThirdPartyPolicyInfo>lambdaQuery()
+                        .eq(ThirdPartyPolicyInfo::getPricePolicyId, policyId)
+        );
+
+        // 更新每条明细的运营服务费和销售合计价格
+        for (ThirdPartyPolicyInfo info : policyInfos) {
+            info.setOperationServiceFee(operationServiceFee);
+            
+            // 重新计算销售合计价格
+            BigDecimal totalSalesPrice = calculateTotalSalesPrice(
+                    info.getElecPrice(),
+                    info.getServicePrice(),
+                    operationServiceFee
+            );
+            info.setTotalSalesPrice(totalSalesPrice);
+
+            policyInfoMapper.updateById(info);
+        }
+    }
+
+    /**
+     * 计算销售合计价格 = 电价 + 结算服务费 + 运营服务费
+     */
+    private BigDecimal calculateTotalSalesPrice(BigDecimal elecPrice, BigDecimal servicePrice, BigDecimal operationServiceFee) {
+        BigDecimal total = BigDecimal.ZERO;
+
+        if (elecPrice != null) {
+            total = total.add(elecPrice);
+        }
+        if (servicePrice != null) {
+            total = total.add(servicePrice);
+        }
+        if (operationServiceFee != null) {
+            total = total.add(operationServiceFee);
+        }
+
+        return total;
+    }
+
+    /**
+     * 格式化时间 HHmmss -> HH:mm
+     */
+    private String formatTime(String timeStr) {
+        if (timeStr == null || timeStr.length() < 4) {
+            return timeStr;
+        }
+        return timeStr.substring(0, 2) + ":" + timeStr.substring(2, 4);
+    }
+
+    /**
+     * 获取城市名称(简化处理)
+     */
+    private String getCity(String areaCode) {
+        // TODO: 根据区域码查询城市名称
+        return "贵阳市";
+    }
+
+    /**
+     * 获取场所类型文本
+     */
+    private String getLocationType(Integer construction) {
+        // TODO: 根据建设场所代码返回文本
+        return "其他";
+    }
+
+    /**
+     * 获取充电终端数量
+     */
+    private Integer getTerminalCount(String stationId) {
+        Long count = connectorInfoMapper.selectCount(
+                Wrappers.<ThirdPartyConnectorInfo>lambdaQuery()
+                        .eq(ThirdPartyConnectorInfo::getStationId, stationId)
+        );
+        return count != null ? count.intValue() : 0;
+    }
+
+    /**
+     * 获取站点状态文本
+     */
+    private String getStationStatusText(Integer status) {
+        // TODO: 根据状态代码返回文本
+        return "未知";
+    }
+}

+ 62 - 0
src/main/java/com/zsElectric/boot/charging/entity/ThirdPartyEquipmentPricePolicy.java

@@ -0,0 +1,62 @@
+package com.zsElectric.boot.charging.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+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.Data;
+import lombok.experimental.Accessors;
+
+import java.time.LocalDateTime;
+
+/**
+ * 第三方设备价格策略实体类
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Data
+@Accessors(chain = true)
+@TableName("third_party_equipment_price_policy")
+@Schema(description = "第三方设备价格策略")
+public class ThirdPartyEquipmentPricePolicy {
+
+    @Schema(description = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @Schema(description = "设备业务序列号")
+    @TableField("equip_biz_seq")
+    private String equipBizSeq;
+
+    @Schema(description = "充电桩ID")
+    @TableField("connector_id")
+    private String connectorId;
+
+    @Schema(description = "成功状态(0-成功,1-失败)")
+    @TableField("succ_stat")
+    private Integer succStat;
+
+    @Schema(description = "失败原因(0-无错误)")
+    @TableField("fail_reason")
+    private Integer failReason;
+
+    @Schema(description = "总时段数")
+    @TableField("sum_period")
+    private Integer sumPeriod;
+
+    @Schema(description = "创建时间")
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+    @Schema(description = "创建人ID")
+    @TableField("create_by")
+    private Long createBy;
+
+    @Schema(description = "逻辑删除(0-未删除 1-已删除)")
+    @TableLogic
+    @TableField("is_deleted")
+    private Integer isDeleted;
+}

+ 71 - 0
src/main/java/com/zsElectric/boot/charging/entity/ThirdPartyPolicyInfo.java

@@ -0,0 +1,71 @@
+package com.zsElectric.boot.charging.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+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.Data;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 第三方价格策略明细实体类
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Data
+@Accessors(chain = true)
+@TableName("third_party_policy_info")
+@Schema(description = "第三方价格策略明细")
+public class ThirdPartyPolicyInfo {
+
+    @Schema(description = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @Schema(description = "价格策略ID(关联third_party_equipment_price_policy表)")
+    @TableField("price_policy_id")
+    private Long pricePolicyId;
+
+    @Schema(description = "时段开始时间(HHmmss格式)")
+    @TableField("start_time")
+    private String startTime;
+
+    @Schema(description = "电价(元/度)")
+    @TableField("elec_price")
+    private BigDecimal elecPrice;
+
+    @Schema(description = "服务费(元/度)")
+    @TableField("service_price")
+    private BigDecimal servicePrice;
+
+    @Schema(description = "时段标志(1-尖,2-峰,3-平,4-谷)")
+    @TableField("period_flag")
+    private Integer periodFlag;
+
+    @Schema(description = "运营服务费(元/度)")
+    @TableField("operation_service_fee")
+    private BigDecimal operationServiceFee;
+
+    @Schema(description = "销售合计价格(元/度) = 电价 + 结算服务费 + 运营服务费")
+    @TableField("total_sales_price")
+    private BigDecimal totalSalesPrice;
+
+    @Schema(description = "创建时间")
+    @TableField("create_time")
+    private LocalDateTime createTime;
+
+    @Schema(description = "创建人ID")
+    @TableField("create_by")
+    private Long createBy;
+
+    @Schema(description = "逻辑删除(0-未删除 1-已删除)")
+    @TableLogic
+    @TableField("is_deleted")
+    private Integer isDeleted;
+}

+ 15 - 0
src/main/java/com/zsElectric/boot/charging/mapper/ThirdPartyEquipmentPricePolicyMapper.java

@@ -0,0 +1,15 @@
+package com.zsElectric.boot.charging.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zsElectric.boot.charging.entity.ThirdPartyEquipmentPricePolicy;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 第三方设备价格策略Mapper接口
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Mapper
+public interface ThirdPartyEquipmentPricePolicyMapper extends BaseMapper<ThirdPartyEquipmentPricePolicy> {
+}

+ 15 - 0
src/main/java/com/zsElectric/boot/charging/mapper/ThirdPartyPolicyInfoMapper.java

@@ -0,0 +1,15 @@
+package com.zsElectric.boot.charging.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zsElectric.boot.charging.entity.ThirdPartyPolicyInfo;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 第三方价格策略明细Mapper接口
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Mapper
+public interface ThirdPartyPolicyInfoMapper extends BaseMapper<ThirdPartyPolicyInfo> {
+}

+ 67 - 0
src/main/java/com/zsElectric/boot/charging/quartz/ChargingJob.java

@@ -1,15 +1,20 @@
 package com.zsElectric.boot.charging.quartz;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
+import com.zsElectric.boot.charging.entity.ThirdPartyConnectorInfo;
+import com.zsElectric.boot.charging.mapper.ThirdPartyConnectorInfoMapper;
 import com.zsElectric.boot.charging.service.ChargingBusinessService;
+import com.zsElectric.boot.charging.vo.ChargingPricePolicyVO;
 import com.zsElectric.boot.charging.vo.QueryStationsInfoVO;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
 
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.List;
 
 /**
  * 充电站信息同步定时任务
@@ -23,6 +28,7 @@ import java.time.format.DateTimeFormatter;
 public class ChargingJob {
 
     private final ChargingBusinessService chargingBusinessService;
+    private final ThirdPartyConnectorInfoMapper connectorInfoMapper;
 
     private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 
@@ -62,4 +68,65 @@ public class ChargingJob {
         
         log.info("充电站信息同步定时任务执行结束");
     }
+
+    /**
+     * 定时同步设备价格策略信息
+     * 每天凌晨5:30执行,查询所有充电桩的价格策略并存储到数据库
+     * cron表达式: 0 30 5 * * ? 表示每天凌晨5点30分执行
+     */
+    @Scheduled(cron = "0 30 5 * * ?")
+    public void syncEquipmentPricePolicy() {
+        log.info("开始执行设备价格策略同步定时任务");
+        
+        try {
+            // 查询所有充电桩接口信息
+            List<ThirdPartyConnectorInfo> connectorList = connectorInfoMapper.selectList(null);
+            
+            if (CollectionUtils.isEmpty(connectorList)) {
+                log.warn("未查询到充电桩接口信息,跳过价格策略同步");
+                return;
+            }
+            
+            log.info("开始同步价格策略,总共 {} 个充电桩接口", connectorList.size());
+            
+            int successCount = 0;
+            int failCount = 0;
+            
+            // 遍历每个充电桩接口,查询价格策略
+            for (ThirdPartyConnectorInfo connector : connectorList) {
+                try {
+                    String connectorId = connector.getConnectorId();
+                    String equipmentId = connector.getEquipmentId();
+                    
+                    log.info("查询价格策略 - connectorId: {}, equipmentId: {}", connectorId, equipmentId);
+                    
+                    // 调用业务服务查询价格策略(会自动保存到数据库)
+                    ChargingPricePolicyVO result = chargingBusinessService.queryEquipBusinessPolicy(equipmentId, connectorId);
+                    
+                    if (result != null && result.getSuccStat() != null && result.getSuccStat() == 0) {
+                        successCount++;
+                        log.info("价格策略同步成功 - connectorId: {}, 时段数: {}", connectorId, result.getSumPeriod());
+                    } else {
+                        failCount++;
+                        log.warn("价格策略同步失败 - connectorId: {}, 失败原因: {}", connectorId, 
+                                result != null ? result.getFailReason() : "返回结果为空");
+                    }
+                    
+                    // 适当延迟,避免频繁调用第三方接口
+                    Thread.sleep(500);
+                    
+                } catch (Exception e) {
+                    failCount++;
+                    log.error("同步价格策略失败 - connectorId: {}", connector.getConnectorId(), e);
+                }
+            }
+            
+            log.info("设备价格策略同步完成 - 成功: {}, 失败: {}, 总数: {}", successCount, failCount, connectorList.size());
+            
+        } catch (Exception e) {
+            log.error("设备价格策略同步定时任务执行异常", e);
+        }
+        
+        log.info("设备价格策略同步定时任务执行结束");
+    }
 }

+ 19 - 0
src/main/java/com/zsElectric/boot/charging/service/ThirdPartyPricePolicyDataService.java

@@ -0,0 +1,19 @@
+package com.zsElectric.boot.charging.service;
+
+import com.zsElectric.boot.charging.vo.ChargingPricePolicyVO;
+
+/**
+ * 第三方价格策略数据存储服务
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+public interface ThirdPartyPricePolicyDataService {
+
+    /**
+     * 保存价格策略信息
+     *
+     * @param pricePolicyVO 价格策略信息
+     */
+    void savePricePolicyInfo(ChargingPricePolicyVO pricePolicyVO);
+}

+ 12 - 1
src/main/java/com/zsElectric/boot/charging/service/impl/ChargingBusinessServiceImpl.java

@@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.zsElectric.boot.charging.dto.StartChargingRequestDTO;
 import com.zsElectric.boot.charging.dto.StartChargingResponseVO;
 import com.zsElectric.boot.charging.service.ChargingBusinessService;
+import com.zsElectric.boot.charging.service.ThirdPartyPricePolicyDataService;
 import com.zsElectric.boot.charging.service.ThirdPartyStationDataService;
 import com.zsElectric.boot.charging.vo.*;
 import com.zsElectric.boot.common.constant.ConnectivityConstants;
@@ -32,6 +33,7 @@ public class ChargingBusinessServiceImpl implements ChargingBusinessService {
 
     private final ChargingUtil chargingUtil;
     private final ThirdPartyStationDataService thirdPartyStationDataService;
+    private final ThirdPartyPricePolicyDataService thirdPartyPricePolicyDataService;
 
     private final ObjectMapper objectMapper = new ObjectMapper();
 
@@ -107,7 +109,16 @@ public class ChargingBusinessServiceImpl implements ChargingBusinessService {
         log.info("查询设备价格策略返回结果:{}", jsonObject);
         JsonNode responseDecode = chargingUtil.responseDecode(jsonObject);
         log.info("查询设备价格策略返回结果解密后:{}", responseDecode);
-        return objectMapper.readValue(responseDecode.toString(), ChargingPricePolicyVO.class);
+        ChargingPricePolicyVO result = objectMapper.readValue(responseDecode.toString(), ChargingPricePolicyVO.class);
+        
+        // 保存价格策略信息到数据库
+        try {
+            thirdPartyPricePolicyDataService.savePricePolicyInfo(result);
+        } catch (Exception e) {
+            log.error("保存价格策略信息到数据库失败", e);
+        }
+        
+        return result;
     }
 
     @Override

+ 231 - 0
src/main/java/com/zsElectric/boot/charging/service/impl/ThirdPartyPricePolicyDataServiceImpl.java

@@ -0,0 +1,231 @@
+package com.zsElectric.boot.charging.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+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.charging.service.ThirdPartyPricePolicyDataService;
+import com.zsElectric.boot.charging.vo.ChargingPricePolicyVO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * 第三方价格策略数据存储服务实现
+ *
+ * @author system
+ * @since 2025-12-12
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class ThirdPartyPricePolicyDataServiceImpl implements ThirdPartyPricePolicyDataService {
+
+    private final ThirdPartyEquipmentPricePolicyMapper pricePolicyMapper;
+    private final ThirdPartyPolicyInfoMapper policyInfoMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void savePricePolicyInfo(ChargingPricePolicyVO pricePolicyVO) {
+        if (pricePolicyVO == null) {
+            log.warn("价格策略信息为空,跳过存储");
+            return;
+        }
+
+        try {
+            // 查询最新的价格策略记录
+            ThirdPartyEquipmentPricePolicy latestPolicy = getLatestPricePolicy(
+                    pricePolicyVO.getEquipBizSeq(), 
+                    pricePolicyVO.getConnectorID()
+            );
+
+            // 如果数据完全相同,不做任何操作
+            if (latestPolicy != null && isPolicySame(latestPolicy, pricePolicyVO)) {
+                log.info("价格策略数据未发生变化,跳过保存 - equipBizSeq: {}, connectorId: {}", 
+                        pricePolicyVO.getEquipBizSeq(), pricePolicyVO.getConnectorID());
+                return;
+            }
+
+            // 数据发生变化,插入新记录
+            Long policyId = insertNewPricePolicy(pricePolicyVO);
+
+            // 保存价格策略明细
+            if (!CollectionUtils.isEmpty(pricePolicyVO.getPolicyInfos())) {
+                for (ChargingPricePolicyVO.PolicyInfo policyInfo : pricePolicyVO.getPolicyInfos()) {
+                    savePolicyInfoDetail(policyInfo, policyId);
+                }
+            }
+
+            log.info("价格策略信息保存成功(新增记录) - equipBizSeq: {}, connectorId: {}, policyId: {}", 
+                    pricePolicyVO.getEquipBizSeq(), pricePolicyVO.getConnectorID(), policyId);
+
+        } catch (Exception e) {
+            log.error("保存价格策略信息失败 - equipBizSeq: {}, connectorId: {}", 
+                    pricePolicyVO.getEquipBizSeq(), pricePolicyVO.getConnectorID(), e);
+            throw new RuntimeException("保存价格策略信息失败", e);
+        }
+    }
+
+    /**
+     * 获取最新的价格策略记录
+     */
+    private ThirdPartyEquipmentPricePolicy getLatestPricePolicy(String equipBizSeq, String connectorId) {
+        List<ThirdPartyEquipmentPricePolicy> policies = pricePolicyMapper.selectList(
+                Wrappers.<ThirdPartyEquipmentPricePolicy>lambdaQuery()
+                        .eq(ThirdPartyEquipmentPricePolicy::getEquipBizSeq, equipBizSeq)
+                        .eq(ThirdPartyEquipmentPricePolicy::getConnectorId, connectorId)
+                        .orderByDesc(ThirdPartyEquipmentPricePolicy::getCreateTime)
+                        .last("LIMIT 1")
+        );
+        
+        return CollectionUtils.isEmpty(policies) ? null : policies.get(0);
+    }
+
+    /**
+     * 判断价格策略是否相同(比较主表和明细表)
+     */
+    private boolean isPolicySame(ThirdPartyEquipmentPricePolicy latestPolicy, ChargingPricePolicyVO newPolicy) {
+        // 比较主表字段
+        if (!Objects.equals(latestPolicy.getSuccStat(), newPolicy.getSuccStat()) ||
+            !Objects.equals(latestPolicy.getFailReason(), newPolicy.getFailReason()) ||
+            !Objects.equals(latestPolicy.getSumPeriod(), newPolicy.getSumPeriod())) {
+            return false;
+        }
+
+        // 查询明细表数据
+        List<ThirdPartyPolicyInfo> existingDetails = policyInfoMapper.selectList(
+                Wrappers.<ThirdPartyPolicyInfo>lambdaQuery()
+                        .eq(ThirdPartyPolicyInfo::getPricePolicyId, latestPolicy.getId())
+                        .orderBy(true, true, ThirdPartyPolicyInfo::getStartTime)
+        );
+
+        // 比较明细表
+        return isPolicyDetailsSame(existingDetails, newPolicy.getPolicyInfos());
+    }
+
+    /**
+     * 比较价格策略明细是否相同
+     */
+    private boolean isPolicyDetailsSame(List<ThirdPartyPolicyInfo> existingDetails, 
+                                        List<ChargingPricePolicyVO.PolicyInfo> newDetails) {
+        if (CollectionUtils.isEmpty(existingDetails) && CollectionUtils.isEmpty(newDetails)) {
+            return true;
+        }
+        
+        if (CollectionUtils.isEmpty(existingDetails) || CollectionUtils.isEmpty(newDetails)) {
+            return false;
+        }
+        
+        if (existingDetails.size() != newDetails.size()) {
+            return false;
+        }
+
+        // 按 StartTime 排序后比较
+        List<ChargingPricePolicyVO.PolicyInfo> sortedNewDetails = newDetails.stream()
+                .sorted(Comparator.comparing(ChargingPricePolicyVO.PolicyInfo::getStartTime))
+                .collect(Collectors.toList());
+
+        for (int i = 0; i < existingDetails.size(); i++) {
+            ThirdPartyPolicyInfo existing = existingDetails.get(i);
+            ChargingPricePolicyVO.PolicyInfo newDetail = sortedNewDetails.get(i);
+
+            if (!Objects.equals(existing.getStartTime(), newDetail.getStartTime()) ||
+                !isBigDecimalEqual(existing.getElecPrice(), newDetail.getElecPrice()) ||
+                !isBigDecimalEqual(existing.getServicePrice(), newDetail.getServicePrice()) ||
+                !Objects.equals(existing.getPeriodFlag(), newDetail.getPeriodFlag())) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * 比较 BigDecimal 是否相等(处理 null 情况)
+     */
+    private boolean isBigDecimalEqual(BigDecimal a, BigDecimal b) {
+        if (a == null && b == null) {
+            return true;
+        }
+        if (a == null || b == null) {
+            return false;
+        }
+        return a.compareTo(b) == 0;
+    }
+
+    /**
+     * 插入新的价格策略记录
+     */
+    private Long insertNewPricePolicy(ChargingPricePolicyVO pricePolicyVO) {
+        ThirdPartyEquipmentPricePolicy entity = new ThirdPartyEquipmentPricePolicy();
+        
+        // 设置字段值
+        entity.setEquipBizSeq(pricePolicyVO.getEquipBizSeq());
+        entity.setConnectorId(pricePolicyVO.getConnectorID());
+        entity.setSuccStat(pricePolicyVO.getSuccStat());
+        entity.setFailReason(pricePolicyVO.getFailReason());
+        entity.setSumPeriod(pricePolicyVO.getSumPeriod());
+        entity.setCreateTime(LocalDateTime.now());
+
+        // 插入新记录
+        pricePolicyMapper.insert(entity);
+
+        return entity.getId();
+    }
+
+    /**
+     * 保存价格策略明细
+     */
+    private void savePolicyInfoDetail(ChargingPricePolicyVO.PolicyInfo policyInfo, Long policyId) {
+        ThirdPartyPolicyInfo entity = new ThirdPartyPolicyInfo();
+        entity.setPricePolicyId(policyId);
+        entity.setStartTime(policyInfo.getStartTime());
+        entity.setElecPrice(policyInfo.getElecPrice());
+        entity.setServicePrice(policyInfo.getServicePrice());
+        entity.setPeriodFlag(policyInfo.getPeriodFlag());
+        
+        // 设置运营服务费默认值为0
+        entity.setOperationServiceFee(BigDecimal.ZERO);
+        
+        // 计算销售合计价格 = 电价 + 结算服务费 + 运营服务费
+        entity.setTotalSalesPrice(calculateTotalSalesPrice(
+                policyInfo.getElecPrice(), 
+                policyInfo.getServicePrice(), 
+                BigDecimal.ZERO
+        ));
+        
+        entity.setCreateTime(LocalDateTime.now());
+
+        policyInfoMapper.insert(entity);
+    }
+    
+    /**
+     * 计算销售合计价格 = 电价 + 结算服务费 + 运营服务费
+     */
+    private BigDecimal calculateTotalSalesPrice(BigDecimal elecPrice, BigDecimal servicePrice, BigDecimal operationServiceFee) {
+        BigDecimal total = BigDecimal.ZERO;
+        
+        if (elecPrice != null) {
+            total = total.add(elecPrice);
+        }
+        if (servicePrice != null) {
+            total = total.add(servicePrice);
+        }
+        if (operationServiceFee != null) {
+            total = total.add(operationServiceFee);
+        }
+        
+        return total;
+    }
+}