longz
2026-07-29 45109a672b4946a059bac111d3a61a87e1886274
各类修复
8个文件已修改
256 ■■■■■ 已修改文件
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/FinancialSettlementController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/controller/FinancialSettlementControllerTest.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/PlatformRevenueAnalysisTest.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TChargingOrderServiceImplRefundableAmountTest.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java
@@ -447,17 +447,17 @@
    }
    
    
    @RequiresPermissions(value = {"/billingTemplateAudit/audit"}, logical = Logical.OR)
    @RequiresPermissions(value = {"/billingTemplateAudit/audit", "/billingTemplateAudit"}, logical = Logical.OR)
    @ApiOperation(tags = {"后台-申请表单-计费模板审核"},value = "审核")
    @PostMapping(value = "/auth/pass")
    @Log(title = "【计费模板审核】审核计费模板", businessType = BusinessType.OTHER,operatorType = OperatorType.MANAGE)
    public AjaxResult<PageInfo<TAccountingStrategyVO>> authPass(@RequestBody SteategyPassDto steategyPassDto) {
        TAccountingStrategy byId = accountingStrategyService.getById(steategyPassDto.getId());
        Long userId = SecurityUtils.getLoginUser().getUserid();
        if (byId.getAuditStatus()==1&&byId.getFirstUserId()!=userId){
        if (byId.getAuditStatus()==1 && !Objects.equals(byId.getFirstUserId(), userId)){
            return AjaxResult.error("您不是一级审核人员,无法审核");
        }
        if (byId.getAuditStatus()==2&&byId.getTwoUserId()!=userId){
        if (byId.getAuditStatus()==2 && !Objects.equals(byId.getTwoUserId(), userId)){
            return AjaxResult.error("您不是二级审核人员,无法审核");
        }
        if (steategyPassDto.getPass()==1){
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/FinancialSettlementController.java
@@ -35,7 +35,8 @@
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@@ -277,6 +278,7 @@
            tChargingOrderExports.add(tChargingOrderExport);
        }
        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), TChargingOrderExport.class, tChargingOrderExports);
        prependChargingPeriodSummary(workbook, buildChargingPeriodExportSummary(exportList));
        HttpServletResponse response = WebUtils.response();
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
@@ -353,6 +355,87 @@
        return nullToZero(amount).setScale(2, RoundingMode.HALF_UP).toPlainString();
    }
    static String buildChargingPeriodExportSummary(List<ChargingOrderListVO> list) {
        BigDecimal electrovalence = BigDecimal.ZERO;
        BigDecimal serviceCharge = BigDecimal.ZERO;
        BigDecimal paymentAmount = BigDecimal.ZERO;
        BigDecimal chargingCapacity = BigDecimal.ZERO;
        BigDecimal tripartiteCommission = BigDecimal.ZERO;
        BigDecimal balanceIncome = BigDecimal.ZERO;
        BigDecimal balanceGiftIncome = BigDecimal.ZERO;
        BigDecimal vipGiftAmount = BigDecimal.ZERO;
        long chargingSeconds = 0L;
        if (list != null) {
            for (ChargingOrderListVO item : list) {
                if (item == null) {
                    continue;
                }
                electrovalence = electrovalence.add(zero(item.getElectrovalence()));
                serviceCharge = serviceCharge.add(zero(item.getServiceCharge()));
                paymentAmount = paymentAmount.add(zero(item.getPaymentAmount()));
                chargingCapacity = chargingCapacity.add(zero(item.getElectricity()));
                tripartiteCommission = tripartiteCommission.add(zero(item.getSharingAmount()));
                balanceIncome = balanceIncome.add(zero(item.getBalanceIncome()));
                balanceGiftIncome = balanceGiftIncome.add(zero(item.getBalanceGiftIncome()));
                vipGiftAmount = vipGiftAmount.add(zero(item.getVipDiscountAmount()));
                chargingSeconds += item.getChargingSecond() == null ? 0L : item.getChargingSecond();
            }
        }
        int orderCount = list == null ? 0 : list.size();
        return "总计:" + orderCount + "条"
                + "  充电电费:" + money(electrovalence) + "元"
                + "  充电服务费:" + money(serviceCharge) + "元"
                + "  充电收入:" + money(paymentAmount) + "元"
                + "  充电电量:" + quantity(chargingCapacity) + "度"
                + "  充电时长:" + formatChargingDurationMinutes(chargingSeconds) + "分钟"
                + "  平台手续费:0.00元"
                + "  三方平台分佣:" + money(tripartiteCommission) + "元"
                + "  余额收入:" + money(balanceIncome) + "元"
                + "  余额赠送:" + money(balanceGiftIncome) + "元"
                + "  会员赠送:" + money(vipGiftAmount) + "元";
    }
    static void prependChargingPeriodSummary(Workbook workbook, String summary) {
        if (workbook == null || workbook.getNumberOfSheets() == 0) {
            return;
        }
        Sheet sheet = workbook.getSheetAt(0);
        int lastRowNum = sheet.getLastRowNum();
        if (sheet.getPhysicalNumberOfRows() > 0) {
            sheet.shiftRows(0, lastRowNum, 1, true, false);
        }
        Row summaryRow = sheet.createRow(0);
        summaryRow.setHeightInPoints(24);
        Cell summaryCell = summaryRow.createCell(0);
        summaryCell.setCellValue(summary == null ? "" : summary);
        CellStyle style = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setBold(true);
        style.setFont(font);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        style.setAlignment(HorizontalAlignment.LEFT);
        summaryCell.setCellStyle(style);
        Row headerRow = sheet.getRow(1);
        int lastColumn = headerRow == null ? 0 : headerRow.getLastCellNum() - 1;
        if (lastColumn > 0) {
            sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, lastColumn));
        }
    }
    private static BigDecimal zero(BigDecimal value) {
        return value == null ? BigDecimal.ZERO : value;
    }
    private static String money(BigDecimal value) {
        return zero(value).setScale(2, RoundingMode.HALF_UP).toPlainString();
    }
    private static String quantity(BigDecimal value) {
        return zero(value).setScale(3, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
    }
    static String formatChargingDurationMinutes(Long chargingSeconds) {
        if (chargingSeconds == null) {
            return "";
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java
@@ -97,7 +97,7 @@
    List<SixChargingDto> chargeSettlement(@Param("sixBefore") String sixBefore, @Param("siteIds") List<Integer> siteIds);
    List<SixChargingDto> chargeCurrentMonth(@Param("siteIds") List<Integer> siteIds);
    List<SixChargingDto> chargeCurrentMonth(@Param("siteIdsCsv") String siteIdsCsv);
    List<SixCircleDto> circle(@Param("siteIds")List<Integer> siteIds,@Param("sixBefore")LocalDate sixBefore, @Param("partnerView") Boolean partnerView);
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -3219,7 +3219,11 @@
            if (settlementIncome != null) {
                result.addAll(settlementIncome);
            }
            List<SixChargingDto> currentMonthIncome = this.baseMapper.chargeCurrentMonth(siteIds);
                String siteIdsCsv = siteIds == null ? null : siteIds.stream()
                        .filter(Objects::nonNull)
                        .map(String::valueOf)
                        .collect(Collectors.joining(","));
                List<SixChargingDto> currentMonthIncome = this.baseMapper.chargeCurrentMonth(siteIdsCsv);
            if (currentMonthIncome != null) {
                result.addAll(currentMonthIncome);
            }
@@ -4955,21 +4959,23 @@
        }
        mergeSuccessfulRefundAmounts(order);
        PayOrderInfoDto dto = new PayOrderInfoDto();
        BigDecimal thirdPayAmount = getRefundableThirdPayAmount(order);
        BigDecimal thirdRefundAmount = money(order.getThirdRefundAmount());
        dto.setOrderId(order.getId().toString());
        dto.setCode(order.getCode());
        dto.setTradeNo(order.getRechargeSerialNumber());
        dto.setPayType(order.getRechargePaymentType());
        dto.setPayAmount(money(order.getPaymentAmount()));
        dto.setPayAmount(thirdPayAmount);
        dto.setPayTime(order.getPayTime());
        dto.setRefundAmount(money(order.getRefundAmount()));
        dto.setRefundAmount(thirdRefundAmount);
        dto.setRechargeAmount(money(order.getRechargeAmount()));
        dto.setFinalAmount(money(order.getPaymentAmount()).subtract(money(order.getRefundAmount())).setScale(2, RoundingMode.HALF_UP));
        dto.setFinalAmount(thirdPayAmount.subtract(thirdRefundAmount).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP));
        dto.setPayMethod(order.getPayMethod());
        dto.setBalancePayAmount(order.getBalancePayAmount());
        dto.setThirdPayAmount(order.getThirdPayAmount());
        dto.setBalanceRefundAmount(order.getBalanceRefundAmount());
        dto.setThirdRefundAmount(order.getThirdRefundAmount());
        dto.setRefundableAmount(getManualRefundableAmount(order));
        dto.setBalancePayAmount(money(order.getBalancePayAmount()));
        dto.setThirdPayAmount(thirdPayAmount);
        dto.setBalanceRefundAmount(money(order.getBalanceRefundAmount()));
        dto.setThirdRefundAmount(thirdRefundAmount);
        dto.setRefundableAmount(getSpecialRefundableAmount(order));
        return R.ok(dto);
    }
@@ -5007,6 +5013,26 @@
                .setScale(2, RoundingMode.HALF_UP);
    }
    private BigDecimal getSpecialRefundableAmount(TChargingOrder order) {
        return calculateSpecialRefundableAmount(
                getManualRefundableAmount(order),
                getRefundableThirdPayAmount(order),
                money(order == null ? null : order.getThirdRefundAmount()));
    }
    static BigDecimal calculateSpecialRefundableAmount(BigDecimal manualRefundableAmount,
            BigDecimal thirdPayAmount, BigDecimal thirdRefundAmount) {
        BigDecimal normalizedManualAmount = manualRefundableAmount == null ? BigDecimal.ZERO : manualRefundableAmount;
        BigDecimal normalizedThirdPayAmount = thirdPayAmount == null ? BigDecimal.ZERO : thirdPayAmount;
        BigDecimal normalizedThirdRefundAmount = thirdRefundAmount == null ? BigDecimal.ZERO : thirdRefundAmount;
        BigDecimal remainingThirdAmount = normalizedThirdPayAmount
                .subtract(normalizedThirdRefundAmount)
                .max(BigDecimal.ZERO);
        return normalizedManualAmount
                .min(remainingThirdAmount)
                .setScale(2, RoundingMode.HALF_UP);
    }
    @Override
    @Transactional
    public R specialRefund(PayOrderRefundDto payOrderQueryDto) {
@@ -5032,7 +5058,7 @@
            }
            mergeSuccessfulRefundAmounts(order);
            BigDecimal refundAmount = money(payOrderQueryDto.getRefundAmount());
            BigDecimal refundableAmount = getManualRefundableAmount(order);
            BigDecimal refundableAmount = getSpecialRefundableAmount(order);
            if (refundAmount.compareTo(refundableAmount) > 0) {
                return R.fail("退款金额不能超过剩余可退金额");
            }
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml
@@ -449,28 +449,22 @@
    </select>
    <select id="chargeCurrentMonth" resultType="com.ruoyi.order.dto.SixChargingDto">
        SELECT EXTRACT(MONTH FROM charging_order.create_time) AS MONTH,
               SUM(IFNULL(accounting.period_electric_price, 0)) AS electrovalence,
               SUM(CASE
                       WHEN charging_order.order_source = 2
                           THEN IFNULL(accounting.period_service_price, 0) * 0.8
                       ELSE IFNULL(accounting.period_service_price, 0)
                   END) AS service_charge
        FROM t_charging_order_accounting_strategy accounting
        INNER JOIN t_charging_order charging_order
            ON accounting.charging_order_id = charging_order.id
        WHERE charging_order.del_flag = 0
          AND charging_order.status = 5
          AND charging_order.recharge_payment_status = 2
          AND charging_order.create_time &gt;= DATE_FORMAT(CURDATE(), '%Y-%m-01')
          AND charging_order.create_time &lt;= NOW()
        <if test="siteIds != null and siteIds.size() > 0">
            AND charging_order.site_id IN
            <foreach collection="siteIds" item="siteId" open="(" separator="," close=")">
                #{siteId}
            </foreach>
        SELECT DATE_FORMAT(CURDATE(), '%m') AS MONTH,
               IFNULL(SUM(IFNULL(electrovalence, 0)), 0) AS electrovalence,
               IFNULL(SUM(CASE
                       WHEN order_source = 2
                           THEN IFNULL(service_charge, 0) * 0.8
                       ELSE IFNULL(service_charge, 0)
                   END), 0) AS service_charge
        FROM t_charging_order
        WHERE del_flag = 0
          AND status = 5
          AND recharge_payment_status = 2
          AND create_time &gt;= DATE_FORMAT(CURDATE(), '%Y-%m-01')
          AND create_time &lt;= NOW()
        <if test="siteIdsCsv != null and siteIdsCsv != ''">
            AND site_id IN (${siteIdsCsv})
        </if>
        GROUP BY EXTRACT(MONTH FROM charging_order.create_time)
    </select>
    <select id="circle" resultType="com.ruoyi.order.dto.SixCircleDto">
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/controller/FinancialSettlementControllerTest.java
@@ -1,8 +1,16 @@
package com.ruoyi.order.controller;
import com.ruoyi.order.api.vo.ChargingOrderListVO;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class FinancialSettlementControllerTest {
@@ -12,4 +20,56 @@
        assertEquals("34.6", FinancialSettlementController.formatChargingDurationMinutes(2076L));
        assertEquals("", FinancialSettlementController.formatChargingDurationMinutes(null));
    }
    @Test
    void shouldBuildChargingPeriodExportSummaryFromExportRows() {
        ChargingOrderListVO first = new ChargingOrderListVO();
        first.setElectrovalence(new BigDecimal("10.12"));
        first.setServiceCharge(new BigDecimal("2.34"));
        first.setPaymentAmount(new BigDecimal("12.46"));
        first.setElectricity(new BigDecimal("8.1234"));
        first.setChargingSecond(90L);
        first.setSharingAmount(new BigDecimal("0.46"));
        first.setBalanceIncome(new BigDecimal("3.00"));
        first.setBalanceGiftIncome(new BigDecimal("1.00"));
        first.setVipDiscountAmount(new BigDecimal("0.50"));
        ChargingOrderListVO second = new ChargingOrderListVO();
        second.setElectrovalence(new BigDecimal("1.00"));
        second.setServiceCharge(new BigDecimal("1.00"));
        second.setPaymentAmount(new BigDecimal("2.00"));
        second.setElectricity(new BigDecimal("1.0000"));
        second.setChargingSecond(30L);
        String summary = FinancialSettlementController.buildChargingPeriodExportSummary(Arrays.asList(first, second));
        assertTrue(summary.contains("总计:2条"));
        assertTrue(summary.contains("充电电费:11.12元"));
        assertTrue(summary.contains("充电服务费:3.34元"));
        assertTrue(summary.contains("充电收入:14.46元"));
        assertTrue(summary.contains("充电电量:9.123度"));
        assertTrue(summary.contains("充电时长:2分钟"));
        assertTrue(summary.contains("三方平台分佣:0.46元"));
        assertTrue(summary.contains("余额收入:3.00元"));
        assertTrue(summary.contains("余额赠送:1.00元"));
        assertTrue(summary.contains("会员赠送:0.50元"));
    }
    @Test
    void shouldPrependMergedSummaryRowBeforeExportHeader() throws Exception {
        try (Workbook workbook = new HSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("充电时段统计");
            sheet.createRow(0).createCell(0).setCellValue("订单编号");
            sheet.getRow(0).createCell(1).setCellValue("电站名称");
            sheet.createRow(1).createCell(0).setCellValue("ORDER-1");
            FinancialSettlementController.prependChargingPeriodSummary(workbook, "总计:1条");
            assertEquals("总计:1条", sheet.getRow(0).getCell(0).getStringCellValue());
            assertEquals("订单编号", sheet.getRow(1).getCell(0).getStringCellValue());
            assertEquals("ORDER-1", sheet.getRow(2).getCell(0).getStringCellValue());
            assertEquals(1, sheet.getNumMergedRegions());
            assertEquals("A1:B1", sheet.getMergedRegion(0).formatAsString());
        }
    }
}
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/PlatformRevenueAnalysisTest.java
@@ -76,7 +76,7 @@
        currentMonth.setElectrovalence(new BigDecimal("3.456"));
        currentMonth.setServiceCharge(new BigDecimal("4.567"));
        when(mapper.chargeSettlement("2026-01-01", siteIds)).thenReturn(Arrays.asList(settlement));
        when(mapper.chargeCurrentMonth(siteIds)).thenReturn(Arrays.asList(currentMonth));
        when(mapper.chargeCurrentMonth("122,123")).thenReturn(Arrays.asList(currentMonth));
        List<SixChargingDto> result = service.charge(LocalDate.of(2026, 1, 1), siteIds, false);
@@ -87,6 +87,7 @@
        assertEquals("07", result.get(1).getMonth());
        assertEquals(new BigDecimal("3.46"), result.get(1).getElectrovalence());
        assertEquals(new BigDecimal("4.57"), result.get(1).getServiceCharge());
        verify(mapper).chargeCurrentMonth("122,123");
    }
    @Test
@@ -99,7 +100,7 @@
        assertEquals("07", result.get(0).getMonth());
        verify(mapper, never()).chargeSettlement("2026-01-01", siteIds);
        verify(mapper, never()).chargeCurrentMonth(siteIds);
        verify(mapper, never()).chargeCurrentMonth("122,123");
    }
    @Test
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TChargingOrderServiceImplRefundableAmountTest.java
@@ -32,6 +32,22 @@
        assertEquals(new BigDecimal("21.75"), refundableAmount);
    }
    @Test
    void specialRefundForCombinedPaymentOnlyUsesThirdPartyAmount() {
        BigDecimal refundableAmount = TChargingOrderServiceImpl.calculateSpecialRefundableAmount(
                new BigDecimal("4.00"), new BigDecimal("2.00"), BigDecimal.ZERO);
        assertEquals(new BigDecimal("2.00"), refundableAmount);
    }
    @Test
    void previousThirdPartyRefundReducesSpecialRefundableAmount() {
        BigDecimal refundableAmount = TChargingOrderServiceImpl.calculateSpecialRefundableAmount(
                new BigDecimal("4.00"), new BigDecimal("2.00"), new BigDecimal("0.50"));
        assertEquals(new BigDecimal("1.50"), refundableAmount);
    }
    private TChargingOrderRefund refund(String title, String amount) {
        TChargingOrderRefund refund = new TChargingOrderRefund();
        refund.setRefundTitle(title);