longz
2026-07-29 27021fdc85fee6e7539c25d7b498537cc4736b72
各类修复
6个文件已修改
248 ■■■■■ 已修改文件
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/FinancialSettlementController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/controller/FinancialSettlementControllerTest.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/mapper/EnterpriseChargingDetailMapperTest.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/FinancialSettlementController.java
@@ -361,6 +361,8 @@
        BigDecimal paymentAmount = BigDecimal.ZERO;
        BigDecimal chargingCapacity = BigDecimal.ZERO;
        BigDecimal tripartiteCommission = BigDecimal.ZERO;
        BigDecimal platformIncome = BigDecimal.ZERO;
        BigDecimal partnerIncome = BigDecimal.ZERO;
        BigDecimal balanceIncome = BigDecimal.ZERO;
        BigDecimal balanceGiftIncome = BigDecimal.ZERO;
        BigDecimal vipGiftAmount = BigDecimal.ZERO;
@@ -375,6 +377,8 @@
                paymentAmount = paymentAmount.add(zero(item.getPaymentAmount()));
                chargingCapacity = chargingCapacity.add(zero(item.getElectricity()));
                tripartiteCommission = tripartiteCommission.add(zero(item.getSharingAmount()));
                platformIncome = platformIncome.add(zero(item.getPlatformIncome()));
                partnerIncome = partnerIncome.add(zero(item.getPartnerIncome()));
                balanceIncome = balanceIncome.add(zero(item.getBalanceIncome()));
                balanceGiftIncome = balanceGiftIncome.add(zero(item.getBalanceGiftIncome()));
                vipGiftAmount = vipGiftAmount.add(zero(item.getVipDiscountAmount()));
@@ -388,7 +392,8 @@
                + "  充电收入:" + money(paymentAmount) + "元"
                + "  充电电量:" + quantity(chargingCapacity) + "度"
                + "  充电时长:" + formatChargingDurationMinutes(chargingSeconds) + "分钟"
                + "  平台手续费:0.00元"
                + "  平台收入:" + money(platformIncome) + "元"
                + "  合作商分成:" + money(partnerIncome) + "元"
                + "  三方平台分佣:" + money(tripartiteCommission) + "元"
                + "  余额收入:" + money(balanceIncome) + "元"
                + "  余额赠送:" + money(balanceGiftIncome) + "元"
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java
@@ -8,6 +8,7 @@
import com.ruoyi.order.api.model.ChargingListQuery;
import com.ruoyi.order.api.model.TChargingOrder;
import com.ruoyi.order.api.model.TChargingOrderRefund;
import com.ruoyi.order.api.model.TOrderInvoiceDetail;
import com.ruoyi.order.api.query.ChargingOrderQuery;
import com.ruoyi.order.api.vo.ChargingOrderListVO;
import com.ruoyi.order.api.vo.ChargingOrderVO;
@@ -84,10 +85,17 @@
    List<Map<String, Object>> enterpriseChargingTimesByCompanies(@Param("data") EnterpriseChargingAnalysisQuery query,
                                                                  @Param("companyNames") List<String> companyNames);
    List<EnterpriseChargingDetailVO> enterpriseChargingDetail(@Param("pageInfo") PageInfo<EnterpriseChargingDetailVO> pageInfo,
    List<Long> enterpriseInvoiceIds(@Param("data") EnterpriseChargingAnalysisQuery query);
    List<TOrderInvoiceDetail> enterpriseInvoiceChargingDetails(@Param("invoiceIds") List<Long> invoiceIds);
    Long enterpriseChargingDetailCount(@Param("orderIds") List<Long> orderIds,
                                                              @Param("data") EnterpriseChargingAnalysisQuery query);
    List<EnterpriseChargingDetailVO> enterpriseChargingDetailExport(@Param("data") EnterpriseChargingAnalysisQuery query);
    List<EnterpriseChargingDetailVO> enterpriseChargingDetail(@Param("orderIds") List<Long> orderIds,
                                                              @Param("data") EnterpriseChargingAnalysisQuery query,
                                                              @Param("offset") long offset,
                                                              @Param("pageSize") long pageSize);
    List<Map<String, Object>> enterpriseChargingPeriodCounts(@Param("orderIds") List<Long> orderIds);
ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -141,6 +141,8 @@
    private static final String SPECIAL_REFUND_TITLE = "特殊退款处理";
    private static final int RESERVATION_RECOMMENDATION_HISTORY_DAYS = 30;
    private static final int ENTERPRISE_DETAIL_PERIOD_BATCH_SIZE = 500;
    private static final int ENTERPRISE_INVOICE_BATCH_SIZE = 500;
    private static final int ENTERPRISE_ORDER_ID_BATCH_SIZE = 500;
    @Override
    public ReservationRecommendationVO getReservationRecommendation(Integer siteId) {
@@ -3139,7 +3141,22 @@
    @Override
    public R<PageInfo<EnterpriseChargingDetailVO>> enterpriseChargingDetail(EnterpriseChargingAnalysisQuery query) {
        PageInfo<EnterpriseChargingDetailVO> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize());
        List<EnterpriseChargingDetailVO> list = this.baseMapper.enterpriseChargingDetail(pageInfo, query);
        Map<Long, TOrderInvoiceDetail> invoiceDetailMap = getEnterpriseInvoiceDetailMap(query);
        if (invoiceDetailMap.isEmpty()) {
            pageInfo.setTotal(0L);
            pageInfo.setRecords(Collections.emptyList());
            return R.ok(pageInfo);
        }
        List<Long> orderIds = new ArrayList<>(invoiceDetailMap.keySet());
        long total = countEnterpriseChargingDetails(orderIds, query);
        pageInfo.setTotal(total);
        if (total == 0L) {
            pageInfo.setRecords(Collections.emptyList());
            return R.ok(pageInfo);
        }
        List<EnterpriseChargingDetailVO> list = queryEnterpriseChargingDetailPage(
                orderIds, query, pageInfo.offset(), pageInfo.getSize());
        fillEnterpriseInvoiceAmounts(list, invoiceDetailMap);
        fillEnterpriseChargingPeriodCounts(list);
        pageInfo.setRecords(list);
        return R.ok(pageInfo);
@@ -3147,11 +3164,95 @@
    @Override
    public List<EnterpriseChargingDetailVO> enterpriseChargingDetailExport(EnterpriseChargingAnalysisQuery query) {
        List<EnterpriseChargingDetailVO> list = this.baseMapper.enterpriseChargingDetailExport(query);
        Map<Long, TOrderInvoiceDetail> invoiceDetailMap = getEnterpriseInvoiceDetailMap(query);
        if (invoiceDetailMap.isEmpty()) {
            return Collections.emptyList();
        }
        List<EnterpriseChargingDetailVO> list = queryEnterpriseChargingDetailPage(
                new ArrayList<>(invoiceDetailMap.keySet()), query, 0L, Long.MAX_VALUE);
        fillEnterpriseInvoiceAmounts(list, invoiceDetailMap);
        fillEnterpriseChargingPeriodCounts(list);
        return list;
    }
    private long countEnterpriseChargingDetails(List<Long> orderIds, EnterpriseChargingAnalysisQuery query) {
        long total = 0L;
        for (int start = 0; start < orderIds.size(); start += ENTERPRISE_ORDER_ID_BATCH_SIZE) {
            int end = Math.min(start + ENTERPRISE_ORDER_ID_BATCH_SIZE, orderIds.size());
            Long batchTotal = this.baseMapper.enterpriseChargingDetailCount(orderIds.subList(start, end), query);
            if (batchTotal != null) {
                total += batchTotal;
            }
        }
        return total;
    }
    private List<EnterpriseChargingDetailVO> queryEnterpriseChargingDetailPage(List<Long> orderIds,
            EnterpriseChargingAnalysisQuery query, long offset, long pageSize) {
        if (orderIds == null || orderIds.isEmpty() || pageSize <= 0L) {
            return Collections.emptyList();
        }
        long requiredRows = pageSize == Long.MAX_VALUE || offset > Long.MAX_VALUE - pageSize
                ? Long.MAX_VALUE : offset + pageSize;
        List<EnterpriseChargingDetailVO> candidates = new ArrayList<>();
        for (int start = 0; start < orderIds.size(); start += ENTERPRISE_ORDER_ID_BATCH_SIZE) {
            int end = Math.min(start + ENTERPRISE_ORDER_ID_BATCH_SIZE, orderIds.size());
            List<Long> batchOrderIds = orderIds.subList(start, end);
            long batchLimit = Math.min(requiredRows, batchOrderIds.size());
            candidates.addAll(this.baseMapper.enterpriseChargingDetail(batchOrderIds, query, 0L, batchLimit));
        }
        candidates.sort(Comparator
                .comparing(EnterpriseChargingDetailVO::getCreateTime,
                        Comparator.nullsLast(Comparator.reverseOrder()))
                .thenComparing(EnterpriseChargingDetailVO::getId,
                        Comparator.nullsLast(Comparator.reverseOrder())));
        int fromIndex = (int) Math.min(offset, candidates.size());
        long requestedEnd = pageSize == Long.MAX_VALUE || fromIndex > Long.MAX_VALUE - pageSize
                ? Long.MAX_VALUE : fromIndex + pageSize;
        int toIndex = (int) Math.min(requestedEnd, candidates.size());
        return new ArrayList<>(candidates.subList(fromIndex, toIndex));
    }
    private Map<Long, TOrderInvoiceDetail> getEnterpriseInvoiceDetailMap(EnterpriseChargingAnalysisQuery query) {
        List<Long> invoiceIds = this.baseMapper.enterpriseInvoiceIds(query);
        if (invoiceIds == null || invoiceIds.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<Long, TOrderInvoiceDetail> detailMap = new LinkedHashMap<>();
        for (int start = 0; start < invoiceIds.size(); start += ENTERPRISE_INVOICE_BATCH_SIZE) {
            int end = Math.min(start + ENTERPRISE_INVOICE_BATCH_SIZE, invoiceIds.size());
            List<TOrderInvoiceDetail> details = this.baseMapper.enterpriseInvoiceChargingDetails(invoiceIds.subList(start, end));
            if (details == null) {
                continue;
            }
            for (TOrderInvoiceDetail detail : details) {
                if (detail == null || detail.getOrderId() == null) {
                    continue;
                }
                detailMap.merge(detail.getOrderId(), detail, this::mergeEnterpriseInvoiceDetail);
            }
        }
        return detailMap;
    }
    private TOrderInvoiceDetail mergeEnterpriseInvoiceDetail(TOrderInvoiceDetail current, TOrderInvoiceDetail addition) {
        current.setElectrovalence(nullToZero(current.getElectrovalence()).add(nullToZero(addition.getElectrovalence())));
        current.setServiceCharge(nullToZero(current.getServiceCharge()).add(nullToZero(addition.getServiceCharge())));
        return current;
    }
    private void fillEnterpriseInvoiceAmounts(List<EnterpriseChargingDetailVO> list,
            Map<Long, TOrderInvoiceDetail> invoiceDetailMap) {
        if (list == null || list.isEmpty()) {
            return;
        }
        for (EnterpriseChargingDetailVO detail : list) {
            TOrderInvoiceDetail invoiceDetail = invoiceDetailMap.get(detail.getId());
            detail.setElectrovalence(invoiceDetail == null ? BigDecimal.ZERO : nullToZero(invoiceDetail.getElectrovalence()));
            detail.setServiceCharge(invoiceDetail == null ? BigDecimal.ZERO : nullToZero(invoiceDetail.getServiceCharge()));
        }
    }
    private void fillEnterpriseChargingPeriodCounts(List<EnterpriseChargingDetailVO> list) {
        if (list == null || list.isEmpty()) {
            return;
ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml
@@ -2874,8 +2874,56 @@
        ORDER BY toi.name, co.start_time
    </select>
    <sql id="enterpriseChargingDetailWhere">
        <include refid="enterpriseChargingBaseWhere" />
    <select id="enterpriseInvoiceIds" resultType="long">
        SELECT id
        FROM t_order_invoice
        <where>
            invoicing_object_type = 2
            AND status IN (2, 3)
            <if test="data.exactCompanyName != null and data.exactCompanyName != ''">
                AND name = #{data.exactCompanyName}
            </if>
            <if test="(data.exactCompanyName == null or data.exactCompanyName == '') and data.companyName != null and data.companyName != ''">
                AND name LIKE CONCAT('%', #{data.companyName}, '%')
            </if>
            <if test="data.socialCreditCode != null and data.socialCreditCode != ''">
                AND IFNULL(tax_identification_number, '') = #{data.socialCreditCode}
            </if>
        </where>
    </select>
    <select id="enterpriseInvoiceChargingDetails" resultType="com.ruoyi.order.api.model.TOrderInvoiceDetail">
        SELECT order_id AS orderId,
               electrovalence,
               service_charge AS serviceCharge
        FROM t_order_invoice_detail
        WHERE order_type = 1
          AND order_invoice_id IN
        <foreach collection="invoiceIds" item="invoiceId" open="(" separator="," close=")">
            #{invoiceId}
        </foreach>
    </select>
    <sql id="enterpriseChargingOrderWhere">
        co.id IN
        <foreach collection="orderIds" item="orderId" open="(" separator="," close=")">
            #{orderId}
        </foreach>
        <if test="data.siteType != null">
            AND s.site_type = #{data.siteType}
        </if>
        <if test="data.partnerId != null">
            AND s.partner_id = #{data.partnerId}
        </if>
        <if test="data.siteId != null">
            AND co.site_id = #{data.siteId}
        </if>
        <if test="data.createTime1 != null">
            AND co.create_time &gt;= #{data.createTime1}
        </if>
        <if test="data.createTime2 != null">
            AND co.create_time &lt;= #{data.createTime2}
        </if>
        <if test="data.code != null and data.code != ''">
            AND co.code LIKE CONCAT('%', #{data.code}, '%')
        </if>
@@ -2908,6 +2956,23 @@
        </if>
    </sql>
    <select id="enterpriseChargingDetailCount" resultType="long">
        SELECT COUNT(*)
        FROM t_charging_order co
        <if test="data.siteType != null or data.partnerId != null or (data.partnerName != null and data.partnerName != '')">
            INNER JOIN charging_pile_service.t_site s ON s.id = co.site_id
        </if>
        <if test="data.partnerName != null and data.partnerName != ''">
            INNER JOIN charging_pile_service.t_partner p ON p.id = s.partner_id
        </if>
        <if test="data.phone != null and data.phone != ''">
            INNER JOIN charging_pile_account.t_app_user au ON au.id = co.app_user_id
        </if>
        <where>
            <include refid="enterpriseChargingOrderWhere" />
        </where>
    </select>
    <sql id="enterpriseChargingDetailSelect">
        SELECT
            co.id,
@@ -2928,16 +2993,12 @@
            au.phone AS phone,
            car.license_plate AS licensePlate,
            co.charging_capacity AS chargingCapacity,
            IFNULL(tod.electrovalence, 0) AS electrovalence,
            IFNULL(tod.service_charge, 0) AS serviceCharge,
            co.end_mode AS endMode,
            co.create_time AS createTime,
            co.start_time AS startTime,
            co.end_time AS endTime,
            co.pay_time AS payTime
        FROM t_order_invoice toi
        INNER JOIN t_order_invoice_detail tod ON tod.order_invoice_id = toi.id
        INNER JOIN t_charging_order co ON co.id = tod.order_id
        FROM t_charging_order co
        LEFT JOIN charging_pile_service.t_site s ON s.id = co.site_id
        LEFT JOIN charging_pile_service.t_partner p ON p.id = s.partner_id
        LEFT JOIN charging_pile_service.t_charging_pile cp ON cp.id = co.charging_pile_id
@@ -2949,17 +3010,10 @@
    <select id="enterpriseChargingDetail" resultType="com.ruoyi.order.dto.EnterpriseChargingDetailVO">
        <include refid="enterpriseChargingDetailSelect" />
        <where>
            <include refid="enterpriseChargingDetailWhere" />
            <include refid="enterpriseChargingOrderWhere" />
        </where>
        ORDER BY co.create_time DESC
    </select>
    <select id="enterpriseChargingDetailExport" resultType="com.ruoyi.order.dto.EnterpriseChargingDetailVO">
        <include refid="enterpriseChargingDetailSelect" />
        <where>
            <include refid="enterpriseChargingDetailWhere" />
        </where>
        ORDER BY co.create_time DESC
        LIMIT #{offset}, #{pageSize}
    </select>
    <select id="enterpriseChargingPeriodCounts" resultType="map">
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/controller/FinancialSettlementControllerTest.java
@@ -30,6 +30,8 @@
        first.setElectricity(new BigDecimal("8.1234"));
        first.setChargingSecond(90L);
        first.setSharingAmount(new BigDecimal("0.46"));
        first.setPlatformIncome(new BigDecimal("1.24"));
        first.setPartnerIncome(new BigDecimal("0.64"));
        first.setBalanceIncome(new BigDecimal("3.00"));
        first.setBalanceGiftIncome(new BigDecimal("1.00"));
        first.setVipDiscountAmount(new BigDecimal("0.50"));
@@ -40,6 +42,8 @@
        second.setPaymentAmount(new BigDecimal("2.00"));
        second.setElectricity(new BigDecimal("1.0000"));
        second.setChargingSecond(30L);
        second.setPlatformIncome(new BigDecimal("0.76"));
        second.setPartnerIncome(new BigDecimal("0.36"));
        String summary = FinancialSettlementController.buildChargingPeriodExportSummary(Arrays.asList(first, second));
@@ -49,6 +53,8 @@
        assertTrue(summary.contains("充电收入:14.46元"));
        assertTrue(summary.contains("充电电量:9.123度"));
        assertTrue(summary.contains("充电时长:2分钟"));
        assertTrue(summary.contains("平台收入:2.00元"));
        assertTrue(summary.contains("合作商分成:1.00元"));
        assertTrue(summary.contains("三方平台分佣:0.46元"));
        assertTrue(summary.contains("余额收入:3.00元"));
        assertTrue(summary.contains("余额赠送:1.00元"));
ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/mapper/EnterpriseChargingDetailMapperTest.java
@@ -35,16 +35,40 @@
        assertFalse(detailSelect.contains("SELECT DISTINCT"));
        assertFalse(detailSelect.contains("FROM t_charging_order_accounting_strategy"));
        assertTrue(detailSelect.contains("IFNULL(tod.electrovalence, 0) AS electrovalence"));
        assertTrue(detailSelect.contains("IFNULL(tod.service_charge, 0) AS serviceCharge"));
        assertFalse(detailSelect.contains("t_order_invoice"));
        assertFalse(detailSelect.contains("t_order_invoice_detail"));
        assertTrue(detailSelect.contains("AS licensePlate"));
        assertTrue(detailSelect.contains("AS chargingCapacity"));
        assertTrue(detailSelect.contains("AS electrovalence"));
        assertTrue(detailSelect.contains("AS serviceCharge"));
        assertFalse(detailSelect.contains("AS electrovalence"));
        assertFalse(detailSelect.contains("AS serviceCharge"));
        assertTrue(detailSelect.contains("AS endMode"));
        assertTrue(baseWhere.contains("AND toi.status IN (2, 3)"));
        assertFalse(baseWhere.contains("AND toi.status IN (1, 2, 3)"));
        String countSelect = mapper.substring(
                mapper.indexOf("<select id=\"enterpriseChargingDetailCount\""),
                mapper.indexOf("<sql id=\"enterpriseChargingDetailSelect\""));
        assertFalse(countSelect.contains("t_order_invoice"));
        assertFalse(countSelect.contains("t_order_invoice_detail"));
        assertFalse(countSelect.contains("t_charging_pile cp"));
        assertFalse(countSelect.contains("t_charging_gun cg"));
        assertFalse(countSelect.contains("t_app_user_car"));
        assertTrue(countSelect.contains("FROM t_charging_order co"));
        assertTrue(countSelect.contains("<include refid=\"enterpriseChargingOrderWhere\""));
        String invoiceIds = mapper.substring(
                mapper.indexOf("<select id=\"enterpriseInvoiceIds\""),
                mapper.indexOf("<select id=\"enterpriseInvoiceChargingDetails\""));
        assertTrue(invoiceIds.contains("FROM t_order_invoice"));
        assertFalse(invoiceIds.contains("JOIN"));
        String invoiceDetails = mapper.substring(
                mapper.indexOf("<select id=\"enterpriseInvoiceChargingDetails\""),
                mapper.indexOf("<sql id=\"enterpriseChargingOrderWhere\""));
        assertTrue(invoiceDetails.contains("FROM t_order_invoice_detail"));
        assertTrue(invoiceDetails.contains("order_invoice_id IN"));
        assertFalse(invoiceDetails.contains("JOIN"));
        assertTrue(mapper.contains("<select id=\"enterpriseChargingPeriodCounts\""));
        String periodCounts = mapper.substring(
                mapper.indexOf("<select id=\"enterpriseChargingPeriodCounts\""),