longz
2026-08-06 35d3dc45fbdc65eb23d5cc754134fbf240419f86
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java
@@ -4,7 +4,9 @@
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.account.api.feignClient.TAppUserSiteFavoriteClient;
import com.ruoyi.account.api.model.TAppUser;
import com.ruoyi.account.api.vo.SitePriceDropNotifyVO;
import com.ruoyi.chargingPile.api.dto.TAccountingStrategyDTO;
import com.ruoyi.chargingPile.api.model.*;
import com.ruoyi.chargingPile.api.query.BatchSetAccountingStrategy;
@@ -35,6 +37,8 @@
import com.ruoyi.system.api.domain.SysUser;
import com.ruoyi.system.api.feignClient.SysUserClient;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@@ -46,6 +50,7 @@
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeParseException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -65,6 +70,8 @@
@RestController
@RequestMapping("/t-accounting-strategy")
public class TAccountingStrategyController {
    private static final Logger log = LoggerFactory.getLogger(TAccountingStrategyController.class);
    private final TAccountingStrategyService accountingStrategyService;
    private final TAccountingStrategyDetailService accountingStrategyDetailService;
@@ -88,6 +95,9 @@
    @Resource
    private ChengDuJianGuanClient chengDuJianGuanClient;
    @Resource
    private TAppUserSiteFavoriteClient siteFavoriteClient;
    @Autowired
    private TokenService tokenService;
@@ -507,6 +517,8 @@
                            if(!siteIds.isEmpty()){
                                tcecClient.notificationStationChange(siteIds);
                            }
                            // 审批通过且新旧策略对比存在收费降低的时段时,向开启降价提醒的收藏用户推送降价消息
                            notifySubscribersOnPriceDrop(one, byId, list);
                        }
                        chargingGunService.update(Wrappers.<TChargingGun>lambdaUpdate()
                                .set(TChargingGun::getAccountingStrategyId, byId.getId())
@@ -547,6 +559,93 @@
    }
    /**
     * 新旧计费策略时段对比,存在收费降低的时段时向受影响站点的降价提醒订阅用户推送微信消息。
     * 推送为旁路操作,失败不影响审批主流程。
     */
    private void notifySubscribersOnPriceDrop(TAccountingStrategy oldStrategy, TAccountingStrategy newStrategy, List<Site> sites) {
        try {
            if (oldStrategy == null || newStrategy == null || sites == null || sites.isEmpty()) {
                return;
            }
            // 旧策略详情须在旧策略删除前查询
            List<TAccountingStrategyDetail> oldDetails = accountingStrategyDetailService.list(new LambdaQueryWrapper<TAccountingStrategyDetail>()
                    .eq(TAccountingStrategyDetail::getAccountingStrategyId, oldStrategy.getId())
                    .eq(TAccountingStrategyDetail::getDelFlag, 0));
            List<TAccountingStrategyDetail> newDetails = accountingStrategyDetailService.list(new LambdaQueryWrapper<TAccountingStrategyDetail>()
                    .eq(TAccountingStrategyDetail::getAccountingStrategyId, newStrategy.getId())
                    .eq(TAccountingStrategyDetail::getDelFlag, 0));
            if (!hasPriceDrop(oldDetails, newDetails)) {
                return;
            }
            BigDecimal minPrice = newDetails.stream()
                    .map(this::periodTotalPrice)
                    .filter(Objects::nonNull)
                    .min(BigDecimal::compareTo)
                    .orElse(null);
            if (minPrice == null) {
                return;
            }
            List<SitePriceDropNotifyVO> drops = sites.stream().map(site -> {
                SitePriceDropNotifyVO vo = new SitePriceDropNotifyVO();
                vo.setSiteId(site.getId());
                vo.setSiteName(site.getName());
                vo.setPrice(minPrice);
                return vo;
            }).collect(Collectors.toList());
            siteFavoriteClient.notifyPriceDrop(drops);
        } catch (Exception e) {
            log.error("计费策略审批通过后降价推送失败,oldStrategyId={}, newStrategyId={}",
                    oldStrategy == null ? null : oldStrategy.getId(), newStrategy == null ? null : newStrategy.getId(), e);
        }
    }
    /** 新旧时段按时间区间重叠对比,任一重叠时段新价低于旧价即视为降价(价格为 电价+服务费)。 */
    private boolean hasPriceDrop(List<TAccountingStrategyDetail> oldDetails, List<TAccountingStrategyDetail> newDetails) {
        for (TAccountingStrategyDetail oldDetail : oldDetails) {
            BigDecimal oldPrice = periodTotalPrice(oldDetail);
            LocalTime oldStart = parsePeriodTime(oldDetail.getStartTime(), false);
            LocalTime oldEnd = parsePeriodTime(oldDetail.getEndTime(), true);
            if (oldPrice == null || oldStart == null || oldEnd == null) {
                continue;
            }
            for (TAccountingStrategyDetail newDetail : newDetails) {
                BigDecimal newPrice = periodTotalPrice(newDetail);
                LocalTime newStart = parsePeriodTime(newDetail.getStartTime(), false);
                LocalTime newEnd = parsePeriodTime(newDetail.getEndTime(), true);
                if (newPrice == null || newStart == null || newEnd == null) {
                    continue;
                }
                if (oldStart.isBefore(newEnd) && newStart.isBefore(oldEnd) && newPrice.compareTo(oldPrice) < 0) {
                    return true;
                }
            }
        }
        return false;
    }
    private BigDecimal periodTotalPrice(TAccountingStrategyDetail detail) {
        if (detail.getElectrovalence() == null || detail.getServiceCharge() == null) {
            return null;
        }
        return detail.getElectrovalence().add(detail.getServiceCharge()).setScale(4, BigDecimal.ROUND_HALF_UP);
    }
    /** 时段时间解析,endTime 为 "00:00" 时视为 23:59:59(与 SiteController.parsePriceTime 语义一致)。 */
    private LocalTime parsePeriodTime(String value, boolean endTime) {
        if (value == null || value.isEmpty()) {
            return null;
        }
        if (endTime && "00:00".equals(value)) {
            return LocalTime.of(23, 59, 59);
        }
        try {
            return LocalTime.parse(value.length() == 5 ? value + ":00" : value);
        } catch (DateTimeParseException e) {
            return null;
        }
    }
    @ApiOperation(tags = {"后台-申请表单-计费模板审核"},value = "删除")
    @DeleteMapping(value = "/delete")
    @Log(title = "【计费模板审核】删除计费模板审核", businessType = BusinessType.DELETE)