Kaynağa Gözat

Merge remote-tracking branch 'origin/main'

caotao 2 hafta önce
ebeveyn
işleme
6692cb13fd

+ 30 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/report/KanbanBoardController.java

@@ -191,4 +191,34 @@ public class KanbanBoardController {
                                                        HttpServletResponse response) throws IOException {
         voyageStockBoardService.exportConsumptionProfileDashboardExcel(reqVO, response);
     }
+
+    // ==================== 财务营收收款看板接口 ====================
+
+    /**
+     * 查询财务营收收款看板数据
+     *
+     * @param reqVO 查询条件
+     * @return 财务营收收款看板数据
+     */
+    @GetMapping("/financialRevenueDashboard")
+    @Operation(summary = "查询财务营收收款看板数据")
+    public CommonResult<FinancialRevenueDashboardRespVO> getFinancialRevenueDashboard(@Valid FinancialRevenueDashboardReqVO reqVO) {
+        FinancialRevenueDashboardRespVO respVO = voyageStockBoardService.getFinancialRevenueDashboard(reqVO);
+        return success(respVO);
+    }
+
+    /**
+     * 导出财务营收收款看板 Excel
+     *
+     * @param reqVO    查询条件
+     * @param response HTTP响应
+     * @throws IOException IO异常
+     */
+    @GetMapping("/financialRevenueDashboard/export-excel")
+    @Operation(summary = "导出财务营收收款看板 Excel")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportFinancialRevenueDashboardExcel(@Valid FinancialRevenueDashboardReqVO reqVO,
+                                                      HttpServletResponse response) throws IOException {
+        voyageStockBoardService.exportFinancialRevenueDashboardExcel(reqVO, response);
+    }
 }

+ 26 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/report/vo/FinancialRevenueDashboardReqVO.java

@@ -0,0 +1,26 @@
+package com.yc.ship.module.trade.controller.admin.report.vo;
+
+import com.yc.ship.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.List;
+
+/**
+ * 管理后台 - 财务营收收款看板 Request VO
+ */
+@Schema(description = "管理后台 - 财务营收收款看板 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class FinancialRevenueDashboardReqVO extends PageParam {
+
+    @Schema(description = "游轮ID列表(支持多选)", example = "[1,2]")
+    private List<Long> shipIds;
+
+    @Schema(description = "航次ID列表(支持多选)", example = "[100,101]")
+    private List<Long> voyageIds;
+
+}

+ 55 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/controller/admin/report/vo/FinancialRevenueDashboardRespVO.java

@@ -0,0 +1,55 @@
+package com.yc.ship.module.trade.controller.admin.report.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 管理后台 - 财务营收收款看板 Response VO
+ */
+@Schema(description = "管理后台 - 财务营收收款看板 Response VO")
+@Data
+public class FinancialRevenueDashboardRespVO {
+
+    @Schema(description = "已完成付款列表")
+    private List<AgentPaymentItemVO> paidList;
+
+    @Schema(description = "未付款列表")
+    private List<AgentPaymentItemVO> unpaidList;
+
+    @Schema(description = "部分付款列表")
+    private List<AgentPaymentItemVO> partialPaidList;
+
+    @Schema(description = "已完成付款合计")
+    private BigDecimal paidTotal;
+
+    @Schema(description = "未付款合计")
+    private BigDecimal unpaidTotal;
+
+    @Schema(description = "部分付款实际付款合计")
+    private BigDecimal partialPaidTotal;
+
+    @Schema(description = "部分付款差值合计")
+    private BigDecimal partialDiffTotal;
+
+    /**
+     * 代理商付款单项 VO
+     */
+    @Data
+    @Schema(description = "代理商付款单项 VO")
+    public static class AgentPaymentItemVO {
+
+        @Schema(description = "代理商名称", example = "重庆航邮寰球国际旅行社有限公司")
+        private String agentName;
+
+        @Schema(description = "金额", example = "8498.00")
+        private BigDecimal amount;
+
+        @Schema(description = "差值(仅部分付款使用)", example = "1000.00")
+        private BigDecimal diffAmount;
+
+    }
+
+}

+ 24 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/dal/mysql/report/VoyageStockBoardMapper.java

@@ -92,4 +92,28 @@ public interface VoyageStockBoardMapper {
     List<ConsumptionProfileRawVO> selectConsumptionProfileList(@Param("shipId") Long shipId,
                                                                @Param("startDate") String startDate,
                                                                @Param("endDate") String endDate);
+
+    /**
+     * 查询已完成付款的代理商列表
+     *
+     * @param reqVO 查询条件
+     * @return 代理商付款列表
+     */
+    List<FinancialRevenueDashboardRespVO.AgentPaymentItemVO> selectPaidAgentList(@Param("vo") FinancialRevenueDashboardReqVO reqVO);
+
+    /**
+     * 查询未付款的代理商列表
+     *
+     * @param reqVO 查询条件
+     * @return 代理商付款列表
+     */
+    List<FinancialRevenueDashboardRespVO.AgentPaymentItemVO> selectUnpaidAgentList(@Param("vo") FinancialRevenueDashboardReqVO reqVO);
+
+    /**
+     * 查询部分付款的代理商列表
+     *
+     * @param reqVO 查询条件
+     * @return 代理商付款列表
+     */
+    List<FinancialRevenueDashboardRespVO.AgentPaymentItemVO> selectPartialPaidAgentList(@Param("vo") FinancialRevenueDashboardReqVO reqVO);
 }

+ 1 - 1
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/pay/impl/TradeOrderPayServiceImpl.java

@@ -978,7 +978,7 @@ public class TradeOrderPayServiceImpl implements TradeOrderPayService {
             TradeDetailDO tradeDetailDO = tradeOrderRepositoryService.getOrderAnyDetail(tradeOrderPayDO.getOrderId());
             PayOrderReqVO.PayParams payParams = payOrderReqVO.getPayParams();
             payParams.setParam(tradeDetailDO.getProductName());
-            payParams.setSubject(payOrderReqVO.getAccountName());
+            payParams.setSubject(payOrderReqVO.getAccountName()==null?"用户":payOrderReqVO.getAccountName());
             payParams.setBody(tradeDetailDO.getProductName());
             payParams.setProductId(tradeDetailDO.getProductId().toString());
         }

+ 17 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/report/VoyageStockBoardService.java

@@ -102,4 +102,21 @@ public interface VoyageStockBoardService {
      * @throws IOException IO异常
      */
     void exportConsumptionProfileDashboardExcel(@Valid ConsumptionProfileDashboardReqVO reqVO, HttpServletResponse response) throws IOException;
+
+    /**
+     * 查询财务营收收款看板数据
+     *
+     * @param reqVO 查询条件
+     * @return 财务营收收款看板数据
+     */
+    FinancialRevenueDashboardRespVO getFinancialRevenueDashboard(@Valid FinancialRevenueDashboardReqVO reqVO);
+
+    /**
+     * 导出财务营收收款看板 Excel
+     *
+     * @param reqVO    查询条件
+     * @param response HTTP响应
+     * @throws IOException IO异常
+     */
+    void exportFinancialRevenueDashboardExcel(@Valid FinancialRevenueDashboardReqVO reqVO, HttpServletResponse response) throws IOException;
 }

+ 188 - 0
ship-module-trade/ship-module-trade-biz/src/main/java/com/yc/ship/module/trade/service/report/impl/VoyageStockBoardServiceImpl.java

@@ -1217,4 +1217,192 @@ public class VoyageStockBoardServiceImpl implements VoyageStockBoardService {
         if (val == null) return "0.0";
         return val.setScale(1, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
     }
+
+    // ==================== 财务营收收款看板 ====================
+
+    @Override
+    public FinancialRevenueDashboardRespVO getFinancialRevenueDashboard(FinancialRevenueDashboardReqVO reqVO) {
+        FinancialRevenueDashboardRespVO respVO = new FinancialRevenueDashboardRespVO();
+
+        // 1. 查询已完成付款列表
+        List<FinancialRevenueDashboardRespVO.AgentPaymentItemVO> paidList =
+                voyageStockBoardMapper.selectPaidAgentList(reqVO);
+        // 2. 查询未付款列表
+        List<FinancialRevenueDashboardRespVO.AgentPaymentItemVO> unpaidList =
+                voyageStockBoardMapper.selectUnpaidAgentList(reqVO);
+        // 3. 查询部分付款列表
+        List<FinancialRevenueDashboardRespVO.AgentPaymentItemVO> partialPaidList =
+                voyageStockBoardMapper.selectPartialPaidAgentList(reqVO);
+
+        respVO.setPaidList(paidList != null ? paidList : Collections.emptyList());
+        respVO.setUnpaidList(unpaidList != null ? unpaidList : Collections.emptyList());
+        respVO.setPartialPaidList(partialPaidList != null ? partialPaidList : Collections.emptyList());
+
+        // 计算合计
+        respVO.setPaidTotal(sumAmount(respVO.getPaidList()));
+        respVO.setUnpaidTotal(sumAmount(respVO.getUnpaidList()));
+        respVO.setPartialPaidTotal(sumAmount(respVO.getPartialPaidList()));
+        respVO.setPartialDiffTotal(sumDiffAmount(respVO.getPartialPaidList()));
+
+        return respVO;
+    }
+
+    /**
+     * 汇总金额
+     */
+    private BigDecimal sumAmount(List<FinancialRevenueDashboardRespVO.AgentPaymentItemVO> list) {
+        if (CollUtil.isEmpty(list)) {
+            return BigDecimal.ZERO;
+        }
+        return list.stream()
+                .map(item -> item.getAmount() != null ? item.getAmount() : BigDecimal.ZERO)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    /**
+     * 汇总差值金额
+     */
+    private BigDecimal sumDiffAmount(List<FinancialRevenueDashboardRespVO.AgentPaymentItemVO> list) {
+        if (CollUtil.isEmpty(list)) {
+            return BigDecimal.ZERO;
+        }
+        return list.stream()
+                .map(item -> item.getDiffAmount() != null ? item.getDiffAmount() : BigDecimal.ZERO)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    @Override
+    public void exportFinancialRevenueDashboardExcel(FinancialRevenueDashboardReqVO reqVO, HttpServletResponse response) throws IOException {
+        FinancialRevenueDashboardRespVO data = getFinancialRevenueDashboard(reqVO);
+
+        response.addHeader("Content-Disposition",
+                "attachment;filename=" + URLEncoder.encode("收款情况.xlsx", StandardCharsets.UTF_8.name()));
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
+
+        // 构建自定义表头(匹配图片格式)
+        List<List<String>> head = buildFinancialRevenueHead();
+        // 构建导出数据
+        List<List<Object>> exportData = buildFinancialRevenueExportData(data);
+
+        if (CollUtil.isEmpty(exportData)) {
+            ExcelUtils.exportEmpty(response, "收款情况");
+            return;
+        }
+
+        EasyExcel.write(response.getOutputStream())
+                .head(head)
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                .sheet("收款情况")
+                .doWrite(exportData);
+    }
+
+    /**
+     * 构建财务营收收款看板导出表头
+     */
+    private List<List<String>> buildFinancialRevenueHead() {
+        List<List<String>> head = new ArrayList<>();
+        head.add(Collections.singletonList("代理商名称"));
+        head.add(Collections.singletonList("分类/说明"));
+        head.add(Collections.singletonList("金额"));
+        head.add(Collections.singletonList("差值"));
+        return head;
+    }
+
+    /**
+     * 构建财务营收收款看板导出数据
+     */
+    private List<List<Object>> buildFinancialRevenueExportData(FinancialRevenueDashboardRespVO data) {
+        List<List<Object>> result = new ArrayList<>();
+
+        // 标题行
+        List<Object> titleRow = new ArrayList<>();
+        titleRow.add("");
+        titleRow.add("收款情况");
+        titleRow.add("");
+        titleRow.add("");
+        result.add(titleRow);
+
+        // 1. 已完成付款
+        if (CollUtil.isNotEmpty(data.getPaidList())) {
+            List<Object> categoryRow = new ArrayList<>();
+            categoryRow.add("");
+            categoryRow.add("1. 已完成付款");
+            categoryRow.add("实际付款金额");
+            categoryRow.add("");
+            result.add(categoryRow);
+
+            for (FinancialRevenueDashboardRespVO.AgentPaymentItemVO item : data.getPaidList()) {
+                List<Object> row = new ArrayList<>();
+                row.add(item.getAgentName() != null ? item.getAgentName() : "");
+                row.add("");
+                row.add(item.getAmount() != null ? item.getAmount() : BigDecimal.ZERO);
+                row.add("");
+                result.add(row);
+            }
+
+            // 小计
+            List<Object> subtotalRow = new ArrayList<>();
+            subtotalRow.add("");
+            subtotalRow.add("小计");
+            subtotalRow.add(data.getPaidTotal() != null ? data.getPaidTotal() : BigDecimal.ZERO);
+            subtotalRow.add("");
+            result.add(subtotalRow);
+        }
+
+        // 2. 未付款
+        if (CollUtil.isNotEmpty(data.getUnpaidList())) {
+            List<Object> categoryRow = new ArrayList<>();
+            categoryRow.add("");
+            categoryRow.add("2. 未付款");
+            categoryRow.add("未付款金额");
+            categoryRow.add("");
+            result.add(categoryRow);
+
+            for (FinancialRevenueDashboardRespVO.AgentPaymentItemVO item : data.getUnpaidList()) {
+                List<Object> row = new ArrayList<>();
+                row.add(item.getAgentName() != null ? item.getAgentName() : "");
+                row.add("");
+                row.add(item.getAmount() != null ? item.getAmount() : BigDecimal.ZERO);
+                row.add("");
+                result.add(row);
+            }
+
+            // 小计
+            List<Object> subtotalRow = new ArrayList<>();
+            subtotalRow.add("");
+            subtotalRow.add("小计");
+            subtotalRow.add(data.getUnpaidTotal() != null ? data.getUnpaidTotal() : BigDecimal.ZERO);
+            subtotalRow.add("");
+            result.add(subtotalRow);
+        }
+
+        // 3. 部分付款
+        if (CollUtil.isNotEmpty(data.getPartialPaidList())) {
+            List<Object> categoryRow = new ArrayList<>();
+            categoryRow.add("");
+            categoryRow.add("3. 部分付款");
+            categoryRow.add("实际付款金额");
+            categoryRow.add("差值");
+            result.add(categoryRow);
+
+            for (FinancialRevenueDashboardRespVO.AgentPaymentItemVO item : data.getPartialPaidList()) {
+                List<Object> row = new ArrayList<>();
+                row.add(item.getAgentName() != null ? item.getAgentName() : "");
+                row.add("");
+                row.add(item.getAmount() != null ? item.getAmount() : BigDecimal.ZERO);
+                row.add(item.getDiffAmount() != null ? item.getDiffAmount() : BigDecimal.ZERO);
+                result.add(row);
+            }
+
+            // 小计
+            List<Object> subtotalRow = new ArrayList<>();
+            subtotalRow.add("");
+            subtotalRow.add("小计");
+            subtotalRow.add(data.getPartialPaidTotal() != null ? data.getPartialPaidTotal() : BigDecimal.ZERO);
+            subtotalRow.add(data.getPartialDiffTotal() != null ? data.getPartialDiffTotal() : BigDecimal.ZERO);
+            result.add(subtotalRow);
+        }
+
+        return result;
+    }
 }

+ 80 - 0
ship-module-trade/ship-module-trade-biz/src/main/resources/mapper/report/VoyageStockBoardMapper.xml

@@ -285,4 +285,84 @@
         ORDER BY pv.start_time
     </select>
 
+    <!-- 财务营收收款看板:公共查询条件 -->
+    <sql id="financialRevenueQueryConditions">
+        <if test="vo.shipIds != null and vo.shipIds.size() > 0">
+            AND v.ship_id IN
+            <foreach collection="vo.shipIds" item="shipId" open="(" separator="," close=")">
+                #{shipId}
+            </foreach>
+        </if>
+        <if test="vo.voyageIds != null and vo.voyageIds.size() > 0">
+            AND o.voyage_id IN
+            <foreach collection="vo.voyageIds" item="voyageId" open="(" separator="," close=")">
+                #{voyageId}
+            </foreach>
+        </if>
+    </sql>
+
+    <!-- 查询已完成付款的代理商列表 -->
+    <select id="selectPaidAgentList"
+            resultType="com.yc.ship.module.trade.controller.admin.report.vo.FinancialRevenueDashboardRespVO$AgentPaymentItemVO">
+        SELECT
+            o.source_name AS agentName,
+            SUM(IFNULL(pay_sum.totalPaid, 0)) AS amount,
+            NULL AS diffAmount
+        FROM trade_order o
+        INNER JOIN product_voyage v ON o.voyage_id = v.id AND v.deleted = 0
+        LEFT JOIN (
+            SELECT order_id, SUM(pay_amount) AS totalPaid
+            FROM trade_order_pay
+            WHERE deleted = 0 AND pay_status = 1
+            GROUP BY order_id
+        ) pay_sum ON o.id = pay_sum.order_id
+        WHERE o.deleted = 0
+        AND o.is_full_pay = 1 AND o.order_status IN (15, 14, 13, 10, 12, 9, 8, 7, 6, 5, 4, 3, 1, 0)
+        <include refid="financialRevenueQueryConditions"/>
+        GROUP BY o.source_name
+        HAVING amount > 0
+        ORDER BY amount DESC
+    </select>
+
+    <!-- 查询未付款的代理商列表 -->
+    <select id="selectUnpaidAgentList"
+            resultType="com.yc.ship.module.trade.controller.admin.report.vo.FinancialRevenueDashboardRespVO$AgentPaymentItemVO">
+        SELECT
+            o.source_name AS agentName,
+            SUM(IFNULL(o.pay_amount, 0)) AS amount,
+            NULL AS diffAmount
+        FROM trade_order o
+        INNER JOIN product_voyage v ON o.voyage_id = v.id AND v.deleted = 0
+        WHERE o.deleted = 0
+        AND o.pay_status IN (0, 2) AND o.order_status IN (15, 14, 13, 10, 12, 9, 8, 7, 6, 5, 4, 3, 1, 0)
+        <include refid="financialRevenueQueryConditions"/>
+        GROUP BY o.source_name
+        HAVING amount > 0
+        ORDER BY amount DESC
+    </select>
+
+    <!-- 查询部分付款的代理商列表 -->
+    <select id="selectPartialPaidAgentList"
+            resultType="com.yc.ship.module.trade.controller.admin.report.vo.FinancialRevenueDashboardRespVO$AgentPaymentItemVO">
+        SELECT
+            o.source_name AS agentName,
+            SUM(IFNULL(pay_sum.totalPaid, 0)) AS amount,
+            SUM(IFNULL(o.pay_amount, o.amount) - IFNULL(pay_sum.totalPaid, 0)) - SUM(IFNULL(o.deposi, 0)) AS diffAmount
+        FROM trade_order o
+        INNER JOIN product_voyage v ON o.voyage_id = v.id AND v.deleted = 0
+        LEFT JOIN (
+            SELECT order_id, SUM(pay_amount) AS totalPaid
+            FROM trade_order_pay
+            WHERE deleted = 0 AND pay_status = 1
+            GROUP BY order_id
+        ) pay_sum ON o.id = pay_sum.order_id
+        WHERE o.deleted = 0
+         AND o.order_status IN (15, 14, 13, 10, 12, 9, 8, 7, 6, 5, 4, 3, 1, 0)
+        AND IFNULL(pay_sum.totalPaid, 0) > 0
+        <include refid="financialRevenueQueryConditions"/>
+        GROUP BY o.source_name
+        HAVING diffAmount > 0
+        ORDER BY amount DESC
+    </select>
+
 </mapper>