Sfoglia il codice sorgente

集成EasyExcel 4.0.3
开发导出所有律所的对账统计列表到Excel(基于/getAllLawFirmList接口数据)
开发导出律师对账统计列表到Excel(基于/getLawyerListWithName接口数据)

LuTong 3 settimane fa
parent
commit
0466972fd5

+ 6 - 0
alien-entity/pom.xml

@@ -76,6 +76,12 @@
             <version>2.0.53</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>4.0.3</version>
+            <scope>compile</scope>
+        </dependency>
 
     </dependencies>
 

+ 43 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LawFirmExportResponseVO.java

@@ -0,0 +1,43 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 律所对账导出响应对象
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel(value = "LawFirmExportResponseVO对象", description = "律所对账导出响应对象")
+public class LawFirmExportResponseVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "导出状态:true-成功,false-失败")
+    private Boolean success;
+
+    @ApiModelProperty(value = "导出消息")
+    private String message;
+
+    @ApiModelProperty(value = "查询到的总数据条数")
+    private Integer totalCount;
+
+    @ApiModelProperty(value = "实际导出的数据条数")
+    private Integer exportCount;
+
+    @ApiModelProperty(value = "文件名")
+    private String fileName;
+
+    @ApiModelProperty(value = "是否导出全部数据:true-全部,false-本页")
+    private Boolean exportAll;
+}
+

+ 46 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LawFirmListVO.java

@@ -0,0 +1,46 @@
+package shop.alien.entity.store.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 律所对账列表视图对象
+ * 用于 /getAllLawFirmList 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "LawFirmListVO对象", description = "律所对账列表视图对象")
+public class LawFirmListVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "律所ID")
+    private Integer firmId;
+
+    @ExcelProperty(value = "律所名称", index = 0)
+    @ApiModelProperty(value = "律所名称")
+    private String firmName;
+
+    @ExcelProperty(value = "总订单数量", index = 1)
+    @ApiModelProperty(value = "总订单数量")
+    private Long totalOrderCount;
+
+    @ExcelProperty(value = "总订单金额(元)", index = 2)
+    @ApiModelProperty(value = "总订单金额(单位:元)")
+    private String totalOrderAmountYuan;
+
+    @ExcelProperty(value = "平台信息服务费(元)", index = 3)
+    @ApiModelProperty(value = "平台信息服务费(单位:元)")
+    private String platformServiceFeeYuan;
+}
+

+ 49 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerExportResponseVO.java

@@ -0,0 +1,49 @@
+package shop.alien.entity.store.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 律师对账导出响应对象
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@ApiModel(value = "LawyerExportResponseVO对象", description = "律师对账导出响应对象")
+public class LawyerExportResponseVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "导出状态:true-成功,false-失败")
+    private Boolean success;
+
+    @ApiModelProperty(value = "导出消息")
+    private String message;
+
+    @ApiModelProperty(value = "律所ID")
+    private Integer firmId;
+
+    @ApiModelProperty(value = "律所名称")
+    private String firmName;
+
+    @ApiModelProperty(value = "查询到的总数据条数")
+    private Integer totalCount;
+
+    @ApiModelProperty(value = "实际导出的数据条数")
+    private Integer exportCount;
+
+    @ApiModelProperty(value = "文件名")
+    private String fileName;
+
+    @ApiModelProperty(value = "是否导出全部数据:true-全部,false-本页")
+    private Boolean exportAll;
+}
+

+ 62 - 0
alien-entity/src/main/java/shop/alien/entity/store/vo/LawyerListVO.java

@@ -0,0 +1,62 @@
+package shop.alien.entity.store.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 律师对账列表视图对象
+ * 用于 /getLawyerListWithName 接口
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@ApiModel(value = "LawyerListVO对象", description = "律师对账列表视图对象")
+public class LawyerListVO implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "律师ID")
+    private Integer lawyerId;
+
+    @ExcelProperty(value = "律所名称", index = 0)
+    @ApiModelProperty(value = "律所名称")
+    private String firmName;
+
+    @ExcelProperty(value = "律师名称", index = 1)
+    @ApiModelProperty(value = "律师名称")
+    private String lawyerName;
+
+    @ExcelProperty(value = "律师执业证号", index = 2)
+    @ApiModelProperty(value = "律师执业证号")
+    private String lawyerCertificateNo;
+
+    @ExcelProperty(value = "总订单数量", index = 3)
+    @ApiModelProperty(value = "总订单数量")
+    private Long totalOrderCount;
+
+    @ExcelProperty(value = "总订单金额(元)", index = 4)
+    @ApiModelProperty(value = "总订单金额(单位:元)")
+    private String totalOrderAmountYuan;
+
+    @ExcelProperty(value = "平台信息服务费(元)", index = 5)
+    @ApiModelProperty(value = "平台信息服务费(单位:元)")
+    private String platformServiceFeeYuan;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "律师头像")
+    private String headImg;
+
+    @ExcelIgnore
+    @ApiModelProperty(value = "律所ID")
+    private Integer firmId;
+}
+

+ 5 - 3
alien-entity/src/main/java/shop/alien/mapper/LawFirmMapper.java

@@ -6,7 +6,9 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 import shop.alien.entity.store.LawFirm;
+import shop.alien.entity.store.vo.LawFirmListVO;
 import shop.alien.entity.store.vo.LawFirmReconciliationVO;
+import shop.alien.entity.store.vo.LawyerListVO;
 
 import java.util.Date;
 import java.util.List;
@@ -45,7 +47,7 @@ public interface LawFirmMapper extends BaseMapper<LawFirm> {
      * @param endDate   结束日期
      * @return 律所对账统计列表
      */
-    List<LawFirmReconciliationVO> getAllLawFirmReconciliationStatistics(
+    List<LawFirmListVO> getAllLawFirmReconciliationStatistics(
             @Param("firmName") String firmName,
             @Param("startDate") Date startDate,
             @Param("endDate") Date endDate
@@ -59,7 +61,7 @@ public interface LawFirmMapper extends BaseMapper<LawFirm> {
      * @param endDate   结束日期(可选)
      * @return 律师对账统计列表
      */
-    List<LawFirmReconciliationVO> getLawyerReconciliationStatistics(
+    List<LawyerListVO> getLawyerReconciliationStatistics(
             @Param("firmId") Integer firmId,
             @Param("startDate") Date startDate,
             @Param("endDate") Date endDate
@@ -74,7 +76,7 @@ public interface LawFirmMapper extends BaseMapper<LawFirm> {
      * @param endDate   结束日期(可选)
      * @return 律师对账统计列表
      */
-    List<LawFirmReconciliationVO> getLawyerReconciliationStatisticsWithName(
+    List<LawyerListVO> getLawyerReconciliationStatisticsWithName(
             @Param("firmId") Integer firmId,
             @Param("lawyerName") String lawyerName,
             @Param("startDate") Date startDate,

+ 2 - 2
alien-entity/src/main/resources/mapper/LawFirmMapper.xml

@@ -104,7 +104,7 @@
     </select>
 
     <!-- 查询所有律所的对账统计信息(按律所分组) -->
-    <resultMap id="LawFirmReconciliationStatisticsResultMap" type="shop.alien.entity.store.vo.LawFirmReconciliationVO">
+    <resultMap id="LawFirmReconciliationStatisticsResultMap" type="shop.alien.entity.store.vo.LawFirmListVO">
         <result column="firm_id" property="firmId" />
         <result column="firm_name" property="firmName" />
         <result column="order_count" property="totalOrderCount" />
@@ -113,7 +113,7 @@
     </resultMap>
 
     <!-- 查询律所下所有律师的对账统计信息(按律师分组) -->
-    <resultMap id="LawyerReconciliationStatisticsResultMap" type="shop.alien.entity.store.vo.LawFirmReconciliationVO">
+    <resultMap id="LawyerReconciliationStatisticsResultMap" type="shop.alien.entity.store.vo.LawyerListVO">
         <result column="lawyer_id" property="lawyerId" />
         <result column="lawyer_name" property="lawyerName" />
         <result column="head_img" property="headImg" />

+ 18 - 0
alien-lawyer/pom.xml

@@ -264,6 +264,24 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-webflux</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>4.0.3</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>4.0.3</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+            <version>4.0.3</version>
+            <scope>compile</scope>
+        </dependency>
 
     </dependencies>
 

+ 71 - 3
alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawFirmReconciliationController.java

@@ -7,9 +7,12 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.web.bind.annotation.*;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LawFirmListVO;
 import shop.alien.entity.store.vo.LawFirmReconciliationVO;
+import shop.alien.entity.store.vo.LawyerListVO;
 import shop.alien.lawyer.service.LawFirmReconciliationService;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.Date;
 
 /**
@@ -58,7 +61,7 @@ public class LawFirmReconciliationController {
             @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "Integer", paramType = "query")
     })
     @GetMapping("/getAllLawFirmList")
-    public R<IPage<LawFirmReconciliationVO>> getAllLawFirmReconciliationList(
+    public R<IPage<LawFirmListVO>> getAllLawFirmReconciliationList(
             @RequestParam(value = "firmName", required = false) String firmName,
             @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
             @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
@@ -79,7 +82,7 @@ public class LawFirmReconciliationController {
             @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "Integer", paramType = "query")
     })
     @GetMapping("/getLawyerList")
-    public R<IPage<LawFirmReconciliationVO>> getLawyerReconciliationList(
+    public R<IPage<LawyerListVO>> getLawyerReconciliationList(
             @RequestParam(value = "firmId") Integer firmId,
             @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
             @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
@@ -101,7 +104,7 @@ public class LawFirmReconciliationController {
             @ApiImplicitParam(name = "pageSize", value = "页容(默认10)", dataType = "Integer", paramType = "query")
     })
     @GetMapping("/getLawyerListWithName")
-    public R<IPage<LawFirmReconciliationVO>> getLawyerReconciliationListWithName(
+    public R<IPage<LawyerListVO>> getLawyerReconciliationListWithName(
             @RequestParam(value = "firmId") Integer firmId,
             @RequestParam(value = "lawyerName", required = false) String lawyerName,
             @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
@@ -130,5 +133,70 @@ public class LawFirmReconciliationController {
         return lawFirmReconciliationService.getLawyerOrderList(lawyerId, pageNum, pageSize);
     }
 
+    @ApiOperation("导出所有律所的对账统计列表到Excel(基于/getAllLawFirmList接口数据)")
+    @ApiOperationSupport(order = 6)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "firmName", value = "律所名称(可选,模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startDate", value = "开始日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码(可选,不传或传0则导出全部,传值则导出本页)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(可选,与pageNum配合使用)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping(value = "/exportAllLawFirmList", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
+    public void exportAllLawFirmList(
+            HttpServletResponse response,
+            @RequestParam(value = "firmName", required = false) String firmName,
+            @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
+            @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
+            @RequestParam(value = "pageNum", required = false) Integer pageNum,
+            @RequestParam(value = "pageSize", required = false) Integer pageSize) throws Exception {
+        log.info("LawFirmReconciliationController.exportAllLawFirmList?firmName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmName, startDate, endDate, pageNum, pageSize);
+        try {
+            lawFirmReconciliationService.exportAllLawFirmList(response, firmName, startDate, endDate, pageNum, pageSize);
+        } catch (Exception e) {
+            log.error("导出律所对账列表异常", e);
+            if (!response.isCommitted()) {
+                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                response.setContentType("application/json;charset=UTF-8");
+                response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
+            }
+            throw e;
+        }
+    }
+
+    @ApiOperation("导出律师对账统计列表到Excel(基于/getLawyerListWithName接口数据)")
+    @ApiOperationSupport(order = 7)
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "firmId", value = "律所ID", dataType = "Integer", paramType = "query", required = true),
+            @ApiImplicitParam(name = "lawyerName", value = "律师名称(可选,模糊查询)", dataType = "String", paramType = "query", required = false),
+            @ApiImplicitParam(name = "startDate", value = "开始日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "endDate", value = "结束日期(可选,格式:yyyy-MM-dd)", dataType = "String", paramType = "query"),
+            @ApiImplicitParam(name = "pageNum", value = "页码(可选,不传或传0则导出全部,传值则导出本页)", dataType = "Integer", paramType = "query"),
+            @ApiImplicitParam(name = "pageSize", value = "页容(可选,与pageNum配合使用)", dataType = "Integer", paramType = "query")
+    })
+    @GetMapping(value = "/exportLawyerListWithName", produces = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
+    public void exportLawyerListWithName(
+            HttpServletResponse response,
+            @RequestParam(value = "firmId") Integer firmId,
+            @RequestParam(value = "lawyerName", required = false) String lawyerName,
+            @RequestParam(value = "startDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date startDate,
+            @RequestParam(value = "endDate", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date endDate,
+            @RequestParam(value = "pageNum", required = false) Integer pageNum,
+            @RequestParam(value = "pageSize", required = false) Integer pageSize) throws Exception {
+        log.info("LawFirmReconciliationController.exportLawyerListWithName?firmId={},lawyerName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+        try {
+            lawFirmReconciliationService.exportLawyerListWithName(response, firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+        } catch (Exception e) {
+            log.error("导出律师对账列表异常", e);
+            if (!response.isCommitted()) {
+                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                response.setContentType("application/json;charset=UTF-8");
+                response.getWriter().write("{\"code\":500,\"msg\":\"导出失败:" + e.getMessage() + "\"}");
+            }
+            throw e;
+        }
+    }
 }
 

+ 1 - 1
alien-lawyer/src/main/java/shop/alien/lawyer/controller/LawyerImgController.java

@@ -1,6 +1,6 @@
 package shop.alien.lawyer.controller;
 
-import com.alibaba.excel.util.CollectionUtils;
+import com.alibaba.nacos.common.utils.CollectionUtils;
 import io.swagger.annotations.*;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;

+ 48 - 3
alien-lawyer/src/main/java/shop/alien/lawyer/service/LawFirmReconciliationService.java

@@ -2,8 +2,11 @@ package shop.alien.lawyer.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import shop.alien.entity.result.R;
+import shop.alien.entity.store.vo.LawFirmListVO;
 import shop.alien.entity.store.vo.LawFirmReconciliationVO;
+import shop.alien.entity.store.vo.LawyerListVO;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.Date;
 
 /**
@@ -40,7 +43,7 @@ public interface LawFirmReconciliationService {
      * @param pageSize  页容
      * @return 律所对账统计列表(分页)
      */
-    R<IPage<LawFirmReconciliationVO>> getAllLawFirmReconciliationList(
+    R<IPage<LawFirmListVO>> getAllLawFirmReconciliationList(
             String firmName,
             Date startDate,
             Date endDate,
@@ -58,7 +61,7 @@ public interface LawFirmReconciliationService {
      * @param pageSize  页容
      * @return 律师对账统计列表(分页)
      */
-    R<IPage<LawFirmReconciliationVO>> getLawyerReconciliationList(
+    R<IPage<LawyerListVO>> getLawyerReconciliationList(
             Integer firmId,
             Date startDate,
             Date endDate,
@@ -77,7 +80,7 @@ public interface LawFirmReconciliationService {
      * @param pageSize  页容
      * @return 律师对账统计列表(分页)
      */
-    R<IPage<LawFirmReconciliationVO>> getLawyerReconciliationListWithName(
+    R<IPage<LawyerListVO>> getLawyerReconciliationListWithName(
             Integer firmId,
             String lawyerName,
             Date startDate,
@@ -99,5 +102,47 @@ public interface LawFirmReconciliationService {
             Integer pageNum,
             Integer pageSize
     );
+
+    /**
+     * 导出所有律所的对账统计列表到Excel
+     *
+     * @param response  HTTP响应对象
+     * @param firmName  律所名称(可选,模糊查询)
+     * @param startDate 开始日期(可选)
+     * @param endDate   结束日期(可选)
+     * @param pageNum   页码(可选,不传或传0则导出全部,传值则导出本页)
+     * @param pageSize  页容(可选,与pageNum配合使用)
+     * @throws Exception 导出过程中的异常
+     */
+    void exportAllLawFirmList(
+            HttpServletResponse response,
+            String firmName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize
+    ) throws Exception;
+
+    /**
+     * 导出律师对账统计列表到Excel
+     *
+     * @param response   HTTP响应对象
+     * @param firmId     律所ID
+     * @param lawyerName 律师名称(可选,模糊查询)
+     * @param startDate  开始日期(可选)
+     * @param endDate    结束日期(可选)
+     * @param pageNum    页码(可选,不传或传0则导出全部,传值则导出本页)
+     * @param pageSize   页容(可选,与pageNum配合使用)
+     * @throws Exception 导出过程中的异常
+     */
+    void exportLawyerListWithName(
+            HttpServletResponse response,
+            Integer firmId,
+            String lawyerName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize
+    ) throws Exception;
 }
 

+ 78 - 9
alien-lawyer/src/main/java/shop/alien/lawyer/service/impl/LawFirmReconciliationServiceImpl.java

@@ -9,12 +9,16 @@ import org.springframework.transaction.annotation.Transactional;
 import shop.alien.entity.result.R;
 import shop.alien.entity.store.LawFirm;
 import shop.alien.entity.store.LawyerUser;
+import shop.alien.entity.store.vo.LawFirmListVO;
 import shop.alien.entity.store.vo.LawFirmReconciliationVO;
+import shop.alien.entity.store.vo.LawyerListVO;
 import shop.alien.lawyer.service.LawFirmReconciliationService;
 import shop.alien.lawyer.service.LawFirmService;
 import shop.alien.lawyer.service.LawyerUserService;
 import shop.alien.mapper.LawFirmMapper;
+import shop.alien.util.excel.EasyExcelUtil;
 
+import javax.servlet.http.HttpServletResponse;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.Collections;
@@ -87,7 +91,7 @@ public class LawFirmReconciliationServiceImpl implements LawFirmReconciliationSe
 
 
     @Override
-    public R<IPage<LawFirmReconciliationVO>> getAllLawFirmReconciliationList(
+    public R<IPage<LawFirmListVO>> getAllLawFirmReconciliationList(
             String firmName,
             Date startDate,
             Date endDate,
@@ -97,10 +101,10 @@ public class LawFirmReconciliationServiceImpl implements LawFirmReconciliationSe
                 firmName, startDate, endDate, pageNum, pageSize);
 
         // 查询所有律所的统计信息(SQL中已直接计算为元)
-        List<LawFirmReconciliationVO> firmList = lawFirmMapper.getAllLawFirmReconciliationStatistics(firmName, startDate, endDate);
+        List<LawFirmListVO> firmList = lawFirmMapper.getAllLawFirmReconciliationStatistics(firmName, startDate, endDate);
 
         // 手动分页
-        Page<LawFirmReconciliationVO> page = new Page<>();
+        Page<LawFirmListVO> page = new Page<>();
         if (pageNum != null && pageSize != null && pageNum > 0 && pageSize > 0) {
             page.setCurrent(pageNum);
             page.setSize(pageSize);
@@ -124,7 +128,7 @@ public class LawFirmReconciliationServiceImpl implements LawFirmReconciliationSe
     }
 
     @Override
-    public R<IPage<LawFirmReconciliationVO>> getLawyerReconciliationList(
+    public R<IPage<LawyerListVO>> getLawyerReconciliationList(
             Integer firmId,
             Date startDate,
             Date endDate,
@@ -143,10 +147,10 @@ public class LawFirmReconciliationServiceImpl implements LawFirmReconciliationSe
         }
 
         // 查询律所下所有律师的统计信息(SQL中已直接计算为元)
-        List<LawFirmReconciliationVO> lawyerList = lawFirmMapper.getLawyerReconciliationStatistics(firmId, startDate, endDate);
+        List<LawyerListVO> lawyerList = lawFirmMapper.getLawyerReconciliationStatistics(firmId, startDate, endDate);
 
         // 手动分页
-        Page<LawFirmReconciliationVO> page = new Page<>();
+        Page<LawyerListVO> page = new Page<>();
         if (pageNum != null && pageSize != null && pageNum > 0 && pageSize > 0) {
             page.setCurrent(pageNum);
             page.setSize(pageSize);
@@ -170,7 +174,7 @@ public class LawFirmReconciliationServiceImpl implements LawFirmReconciliationSe
     }
 
     @Override
-    public R<IPage<LawFirmReconciliationVO>> getLawyerReconciliationListWithName(
+    public R<IPage<LawyerListVO>> getLawyerReconciliationListWithName(
             Integer firmId,
             String lawyerName,
             Date startDate,
@@ -190,10 +194,10 @@ public class LawFirmReconciliationServiceImpl implements LawFirmReconciliationSe
         }
 
         // 查询律所下所有律师的统计信息(SQL中已直接计算为元,支持律师名称模糊查询)
-        List<LawFirmReconciliationVO> lawyerList = lawFirmMapper.getLawyerReconciliationStatisticsWithName(firmId, lawyerName, startDate, endDate);
+        List<LawyerListVO> lawyerList = lawFirmMapper.getLawyerReconciliationStatisticsWithName(firmId, lawyerName, startDate, endDate);
 
         // 手动分页
-        Page<LawFirmReconciliationVO> page = new Page<>();
+        Page<LawyerListVO> page = new Page<>();
         if (pageNum != null && pageSize != null && pageNum > 0 && pageSize > 0) {
             page.setCurrent(pageNum);
             page.setSize(pageSize);
@@ -244,6 +248,71 @@ public class LawFirmReconciliationServiceImpl implements LawFirmReconciliationSe
         return R.data(orderPage, "查询成功");
     }
 
+    @Override
+    public void exportAllLawFirmList(
+            HttpServletResponse response,
+            String firmName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize) throws Exception {
+        log.info("LawFirmReconciliationServiceImpl.exportAllLawFirmList?firmName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmName, startDate, endDate, pageNum, pageSize);
+
+        // 调用查询接口获取数据
+        R<IPage<LawFirmListVO>> result = getAllLawFirmReconciliationList(firmName, startDate, endDate, pageNum, pageSize);
+
+        if (result == null || !result.isSuccess()) {
+            log.warn("获取律所对账列表失败:{}", result != null ? result.getMsg() : "返回结果为空");
+            throw new RuntimeException(result != null ? result.getMsg() : "获取数据失败,请稍后重试");
+        }
+
+        IPage<LawFirmListVO> page = result.getData();
+        if (page == null || page.getRecords() == null || page.getRecords().isEmpty()) {
+            log.warn("律所对账列表导出:无数据可导出");
+            throw new IllegalArgumentException("暂无数据可导出");
+        }
+
+        List<LawFirmListVO> exportList = page.getRecords();
+        log.info("律所对账列表导出:查询到{}条数据,导出{}条数据", page.getTotal(), exportList.size());
+
+        // 使用EasyExcelUtil导出
+        EasyExcelUtil.exportExcel(response, exportList, LawFirmListVO.class, "律所对账列表", "律所对账列表");
+    }
+
+    @Override
+    public void exportLawyerListWithName(
+            HttpServletResponse response,
+            Integer firmId,
+            String lawyerName,
+            Date startDate,
+            Date endDate,
+            Integer pageNum,
+            Integer pageSize) throws Exception {
+        log.info("LawFirmReconciliationServiceImpl.exportLawyerListWithName?firmId={},lawyerName={},startDate={},endDate={},pageNum={},pageSize={}",
+                firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+
+        // 调用查询接口获取数据
+        R<IPage<LawyerListVO>> result = getLawyerReconciliationListWithName(firmId, lawyerName, startDate, endDate, pageNum, pageSize);
+
+        if (result == null || !result.isSuccess()) {
+            log.warn("获取律师对账列表失败:{}", result != null ? result.getMsg() : "返回结果为空");
+            throw new RuntimeException(result != null ? result.getMsg() : "获取数据失败,请稍后重试");
+        }
+
+        IPage<LawyerListVO> page = result.getData();
+        if (page == null || page.getRecords() == null || page.getRecords().isEmpty()) {
+            log.warn("律师对账列表导出:无数据可导出,firmId={}", firmId);
+            throw new IllegalArgumentException("暂无数据可导出");
+        }
+
+        List<LawyerListVO> exportList = page.getRecords();
+        log.info("律师对账列表导出:查询到{}条数据,导出{}条数据,firmId={}", page.getTotal(), exportList.size(), firmId);
+
+        // 使用EasyExcelUtil导出
+        EasyExcelUtil.exportExcel(response, exportList, LawyerListVO.class, "律师对账列表", "律师对账列表");
+    }
+
     /**
      * 将分转换为元(字符串格式)
      *

+ 1 - 1
alien-util/pom.xml

@@ -121,7 +121,7 @@
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>easyexcel</artifactId>
-            <version>1.1.2-beta5</version>
+            <version>4.0.3</version>
         </dependency>
         <!-- 阿里云内容安全 -->
         <dependency>

+ 45 - 0
alien-util/src/main/java/shop/alien/util/common/WebTool.java

@@ -0,0 +1,45 @@
+package shop.alien.util.common;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Web工具类
+ * 用于在非Controller层获取HttpServletRequest和HttpServletResponse
+ *
+ * @author system
+ */
+public class WebTool {
+
+    /**
+     * 获取当前请求的HttpServletRequest
+     *
+     * @return HttpServletRequest
+     * @throws IllegalStateException 如果当前不在Web请求上下文中
+     */
+    public static HttpServletRequest getRequest() {
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        if (attributes == null) {
+            throw new IllegalStateException("当前不在Web请求上下文中,无法获取HttpServletRequest");
+        }
+        return attributes.getRequest();
+    }
+
+    /**
+     * 获取当前请求的HttpServletResponse
+     *
+     * @return HttpServletResponse
+     * @throws IllegalStateException 如果当前不在Web请求上下文中
+     */
+    public static HttpServletResponse getResponse() {
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        if (attributes == null) {
+            throw new IllegalStateException("当前不在Web请求上下文中,无法获取HttpServletResponse");
+        }
+        return attributes.getResponse();
+    }
+}
+

+ 261 - 0
alien-util/src/main/java/shop/alien/util/excel/EasyExcelUtil.java

@@ -0,0 +1,261 @@
+package shop.alien.util.excel;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.read.listener.ReadListener;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.multipart.MultipartFile;
+import shop.alien.util.common.WebTool;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * EasyExcel 工具类
+ * 封装常用的 Excel 导入导出功能
+ *
+ * @author system
+ * @since 2025-01-XX
+ */
+@Slf4j
+public class EasyExcelUtil {
+
+    /**
+     * 导出 Excel 文件到 HTTP 响应(自动获取响应对象)
+     *
+     * @param dataList  要导出的数据列表
+     * @param clazz     数据类型的 Class 对象
+     * @param fileName  导出的文件名(不带扩展名)
+     * @param sheetName 工作表名称
+     * @param <T>       数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void exportExcel(List<T> dataList, Class<T> clazz, String fileName, String sheetName) throws IOException {
+        HttpServletResponse response = WebTool.getResponse();
+        exportExcel(response, dataList, clazz, fileName, sheetName);
+    }
+
+    /**
+     * 导出 Excel 文件到 HTTP 响应
+     *
+     * @param response  HttpServletResponse 对象
+     * @param dataList  要导出的数据列表
+     * @param clazz     数据类型的 Class 对象
+     * @param fileName  导出的文件名(不带扩展名)
+     * @param sheetName 工作表名称
+     * @param <T>       数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void exportExcel(HttpServletResponse response, List<T> dataList, Class<T> clazz,
+                                       String fileName, String sheetName) throws IOException {
+        if (dataList == null || dataList.isEmpty()) {
+            log.warn("[EasyExcelUtil] 导出数据为空: {}", fileName);
+            throw new IllegalArgumentException("导出数据为空");
+        }
+
+        // 设置响应头
+        setupResponse(response, fileName);
+
+        // 导出Excel
+        EasyExcel.write(response.getOutputStream(), clazz)
+                .sheet(sheetName != null ? sheetName : "Sheet1")
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自动列宽
+                .doWrite(dataList);
+
+        log.info("[EasyExcelUtil] 导出成功: {}, 共 {} 条数据", fileName, dataList.size());
+    }
+
+    /**
+     * 导出 Excel 文件到 HTTP 响应(使用默认工作表名称)
+     *
+     * @param response HttpServletResponse 对象
+     * @param dataList 要导出的数据列表
+     * @param clazz    数据类型的 Class 对象
+     * @param fileName 导出的文件名(不带扩展名)
+     * @param <T>      数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void exportExcel(HttpServletResponse response, List<T> dataList, Class<T> clazz, String fileName) throws IOException {
+        exportExcel(response, dataList, clazz, fileName, "Sheet1");
+    }
+
+    /**
+     * 导出 Excel 文件到本地文件
+     *
+     * @param filePath  文件路径
+     * @param dataList  要导出的数据列表
+     * @param clazz     数据类型的 Class 对象
+     * @param sheetName 工作表名称
+     * @param <T>       数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void exportExcelToFile(String filePath, List<T> dataList, Class<T> clazz, String sheetName) throws IOException {
+        if (dataList == null || dataList.isEmpty()) {
+            log.warn("[EasyExcelUtil] 导出数据为空: {}", filePath);
+            throw new IllegalArgumentException("导出数据为空");
+        }
+
+        EasyExcel.write(filePath, clazz)
+                .sheet(sheetName != null ? sheetName : "Sheet1")
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                .doWrite(dataList);
+
+        log.info("[EasyExcelUtil] 导出到文件成功: {}, 共 {} 条数据", filePath, dataList.size());
+    }
+
+    /**
+     * 导出 Excel 文件到本地文件(使用默认工作表名称)
+     *
+     * @param filePath 文件路径
+     * @param dataList 要导出的数据列表
+     * @param clazz    数据类型的 Class 对象
+     * @param <T>      数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void exportExcelToFile(String filePath, List<T> dataList, Class<T> clazz) throws IOException {
+        exportExcelToFile(filePath, dataList, clazz, "Sheet1");
+    }
+
+    /**
+     * 导出 Excel 文件到输出流
+     *
+     * @param outputStream 输出流
+     * @param dataList     要导出的数据列表
+     * @param clazz        数据类型的 Class 对象
+     * @param sheetName    工作表名称
+     * @param <T>          数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void exportExcelToStream(OutputStream outputStream, List<T> dataList, Class<T> clazz, String sheetName) throws IOException {
+        if (dataList == null || dataList.isEmpty()) {
+            log.warn("[EasyExcelUtil] 导出数据为空");
+            throw new IllegalArgumentException("导出数据为空");
+        }
+
+        EasyExcel.write(outputStream, clazz)
+                .sheet(sheetName != null ? sheetName : "Sheet1")
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                .doWrite(dataList);
+
+        log.info("[EasyExcelUtil] 导出到流成功, 共 {} 条数据", dataList.size());
+    }
+
+    /**
+     * 导入 Excel 文件
+     *
+     * @param file         上传的 MultipartFile 文件
+     * @param clazz        数据类型的 Class 对象
+     * @param readListener 读取监听器
+     * @param <T>          数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void importExcel(MultipartFile file, Class<T> clazz, ReadListener<T> readListener) throws IOException {
+        if (file == null || file.isEmpty()) {
+            throw new IllegalArgumentException("上传文件为空");
+        }
+
+        log.info("[EasyExcelUtil] 开始导入Excel文件: {}", file.getOriginalFilename());
+
+        EasyExcel.read(file.getInputStream(), clazz, readListener)
+                .sheet()
+                .doRead();
+
+        log.info("[EasyExcelUtil] Excel文件导入完成: {}", file.getOriginalFilename());
+    }
+
+    /**
+     * 导入 Excel 文件(指定Sheet)
+     *
+     * @param file         上传的 MultipartFile 文件
+     * @param clazz        数据类型的 Class 对象
+     * @param readListener 读取监听器
+     * @param sheetNo      Sheet索引(从0开始)
+     * @param <T>          数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void importExcel(MultipartFile file, Class<T> clazz, ReadListener<T> readListener, int sheetNo) throws IOException {
+        if (file == null || file.isEmpty()) {
+            throw new IllegalArgumentException("上传文件为空");
+        }
+
+        log.info("[EasyExcelUtil] 开始导入Excel文件: {}, Sheet: {}", file.getOriginalFilename(), sheetNo);
+
+        EasyExcel.read(file.getInputStream(), clazz, readListener)
+                .sheet(sheetNo)
+                .doRead();
+
+        log.info("[EasyExcelUtil] Excel文件导入完成: {}", file.getOriginalFilename());
+    }
+
+    /**
+     * 导入 Excel 文件(指定Sheet名称)
+     *
+     * @param file         上传的 MultipartFile 文件
+     * @param clazz        数据类型的 Class 对象
+     * @param readListener 读取监听器
+     * @param sheetName    Sheet名称
+     * @param <T>          数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void importExcel(MultipartFile file, Class<T> clazz, ReadListener<T> readListener, String sheetName) throws IOException {
+        if (file == null || file.isEmpty()) {
+            throw new IllegalArgumentException("上传文件为空");
+        }
+
+        log.info("[EasyExcelUtil] 开始导入Excel文件: {}, Sheet: {}", file.getOriginalFilename(), sheetName);
+
+        EasyExcel.read(file.getInputStream(), clazz, readListener)
+                .sheet(sheetName)
+                .doRead();
+
+        log.info("[EasyExcelUtil] Excel文件导入完成: {}", file.getOriginalFilename());
+    }
+
+    /**
+     * 下载导入模板(只有表头,无数据)
+     *
+     * @param response HttpServletResponse 对象
+     * @param clazz    数据类型的 Class 对象
+     * @param fileName 文件名(不带扩展名)
+     * @param <T>      数据类型
+     * @throws IOException 如果发生 IO 异常
+     */
+    public static <T> void downloadTemplate(HttpServletResponse response, Class<T> clazz, String fileName) throws IOException {
+        setupResponse(response, fileName);
+
+        // 导出空列表,只有表头
+        EasyExcel.write(response.getOutputStream(), clazz)
+                .sheet("Sheet1")
+                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                .doWrite(new java.util.ArrayList<>());
+
+        log.info("[EasyExcelUtil] 模板下载成功: {}", fileName);
+    }
+
+    /**
+     * 设置 HTTP 响应头
+     *
+     * @param response HttpServletResponse 对象
+     * @param fileName 文件名(不带扩展名)
+     * @throws IOException 如果发生 IO 异常
+     */
+    private static void setupResponse(HttpServletResponse response, String fileName) throws IOException {
+        response.reset(); // 重置响应,清除之前可能设置的响应头
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+
+        // 使用 RFC 5987 标准格式,更好地支持中文文件名
+        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
+        response.setHeader("Content-Disposition", "attachment;filename=\"" + encodedFileName + ".xlsx\";filename*=utf-8''" + encodedFileName + ".xlsx");
+
+        // 设置缓存控制
+        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+        response.setHeader("Pragma", "no-cache");
+        response.setDateHeader("Expires", 0);
+    }
+}
+

+ 35 - 38
alien-util/src/main/java/shop/alien/util/excel/ExcelWriteTest.java

@@ -1,19 +1,17 @@
 package shop.alien.util.excel;
 
-import com.alibaba.excel.ExcelWriter;
-import com.alibaba.excel.metadata.Sheet;
-import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.EasyExcel;
 import org.junit.jupiter.api.Test;
 
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
+ * EasyExcel 2.2.10 写入测试
+ * 
  * @author ssk
- * @version 1.0
+ * @version 2.0
  * @date 2020/7/21 17:18
  */
 public class ExcelWriteTest {
@@ -25,43 +23,42 @@ public class ExcelWriteTest {
      */
     @Test
     public void writeWithoutHead() throws IOException {
-        try (OutputStream out = new FileOutputStream("d://withoutHead.xlsx");) {
-            ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX, false);
-            Sheet sheet1 = new Sheet(1, 0);
-            sheet1.setSheetName("sheet1");
-            List<List<String>> data = new ArrayList<>();
-            for (int i = 0; i < 100; i++) {
-                List<String> item = new ArrayList<>();
-                item.add("item0" + i);
-                item.add("item1" + i);
-                item.add("item2" + i);
-                data.add(item);
-            }
-            writer.write0(data, sheet1);
-            writer.finish();
+        String fileName = "d://withoutHead.xlsx";
+        List<List<String>> data = new ArrayList<>();
+        for (int i = 0; i < 100; i++) {
+            List<String> item = new ArrayList<>();
+            item.add("item0" + i);
+            item.add("item1" + i);
+            item.add("item2" + i);
+            data.add(item);
         }
+        EasyExcel.write(fileName)
+                .sheet("sheet1")
+                .doWrite(data);
     }
 
+    /**
+     * 带表头写入
+     *
+     * @throws IOException
+     */
     @Test
     public void writeWithHead() throws IOException {
-        try (OutputStream out = new FileOutputStream("D://withHead.xlsx")) {
-            ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);
-            Sheet sheet1 = new Sheet(1, 0, UserExcel.class);
-            sheet1.setSheetName("sheet1");
-            List<UserExcel> data = new ArrayList<>();
-            for (int i = 0; i < 100; i++) {
-                UserExcel item = new UserExcel();
-                item.name = "name" + i;
-                item.age = "age" + i;
-                item.email = "email" + i;
-                item.address = "address" + i;
-                item.sax = "sax" + i;
-                item.height = "height" + i;
-                item.last = "last" + i;
-                data.add(item);
-            }
-            writer.write(data, sheet1);
-            writer.finish();
+        String fileName = "D://withHead.xlsx";
+        List<UserExcel> data = new ArrayList<>();
+        for (int i = 0; i < 100; i++) {
+            UserExcel item = new UserExcel();
+            item.name = "name" + i;
+            item.age = "age" + i;
+            item.email = "email" + i;
+            item.address = "address" + i;
+            item.sax = "sax" + i;
+            item.height = "height" + i;
+            item.last = "last" + i;
+            data.add(item);
         }
+        EasyExcel.write(fileName, UserExcel.class)
+                .sheet("sheet1")
+                .doWrite(data);
     }
 }

+ 1 - 2
alien-util/src/main/java/shop/alien/util/excel/UserExcel.java

@@ -1,7 +1,6 @@
 package shop.alien.util.excel;
 
 import com.alibaba.excel.annotation.ExcelProperty;
-import com.alibaba.excel.metadata.BaseRowModel;
 import lombok.Data;
 
 /**
@@ -12,7 +11,7 @@ import lombok.Data;
  * @date 2020/7/21 17:13
  */
 @Data
-public class UserExcel extends BaseRowModel {
+public class UserExcel {
 
     @ExcelProperty(value = "姓名", index = 0)
     public String name;

+ 1 - 1
alien-util/src/main/java/shop/alien/util/port/StartCommand.java

@@ -1,7 +1,7 @@
 package shop.alien.util.port;
 
-import com.alibaba.excel.util.StringUtils;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
 
 /**
  * 设置可用端口