From 9264ea26d34b953505388c2346c1883f594cd8a5 Mon Sep 17 00:00:00 2001
From: Bunny <1319900154@qq.com>
Date: Tue, 26 Nov 2024 23:10:38 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=BC=E5=87=BA=E8=B4=A6=E5=8D=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
dao/pom.xml | 5 +
.../bunny/dao/dto/financial/bill/BillDto.java | 3 +-
.../dao/dto/financial/bill/BillExportDto.java | 33 ++++++
.../financial/bill/user/BillAddUserDto.java | 6 ++
.../bunny/dao/excel/BillUserExportExcel.java | 47 +++++++++
.../dao/vo/financial/user/BillUserVo.java | 6 ++
pom.xml | 7 ++
service/pom.xml | 6 --
.../controller/financial/BillController.java | 16 ++-
.../bunny/services/excel/BillAddUserDAO.java | 11 ++
.../services/excel/BillAddUserListener.java | 60 +++++++++++
.../bunny/services/factory/BillFactory.java | 97 ++++++++++++++++++
.../service/financial/BillService.java | 18 ++++
.../financial/impl/BillServiceImpl.java | 29 ++++++
.../resources/mapper/financial/BillMapper.xml | 2 +-
.../main/resources/static/bill-template.xlsx | Bin 0 -> 9776 bytes
.../financial/impl/BillServiceImplTest.java | 84 +++++++++++++++
.../src/test/java/excel/fill/FillTest.java | 30 ++++++
18 files changed, 451 insertions(+), 9 deletions(-)
create mode 100644 dao/src/main/java/cn/bunny/dao/dto/financial/bill/BillExportDto.java
create mode 100644 dao/src/main/java/cn/bunny/dao/excel/BillUserExportExcel.java
create mode 100644 service/src/main/java/cn/bunny/services/excel/BillAddUserDAO.java
create mode 100644 service/src/main/java/cn/bunny/services/excel/BillAddUserListener.java
create mode 100644 service/src/main/java/cn/bunny/services/factory/BillFactory.java
create mode 100644 service/src/main/resources/static/bill-template.xlsx
create mode 100644 service/src/test/java/cn/bunny/services/service/financial/impl/BillServiceImplTest.java
create mode 100644 service/src/test/java/excel/fill/FillTest.java
diff --git a/dao/pom.xml b/dao/pom.xml
index a142267..4b9e9aa 100644
--- a/dao/pom.xml
+++ b/dao/pom.xml
@@ -44,5 +44,10 @@
swagger-annotations
1.6.14
+
+
+ com.alibaba
+ easyexcel
+
diff --git a/dao/src/main/java/cn/bunny/dao/dto/financial/bill/BillDto.java b/dao/src/main/java/cn/bunny/dao/dto/financial/bill/BillDto.java
index 6234230..43ca3ee 100644
--- a/dao/src/main/java/cn/bunny/dao/dto/financial/bill/BillDto.java
+++ b/dao/src/main/java/cn/bunny/dao/dto/financial/bill/BillDto.java
@@ -37,4 +37,5 @@ public class BillDto {
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd")
private LocalDate endDate;
-}
\ No newline at end of file
+}
+
diff --git a/dao/src/main/java/cn/bunny/dao/dto/financial/bill/BillExportDto.java b/dao/src/main/java/cn/bunny/dao/dto/financial/bill/BillExportDto.java
new file mode 100644
index 0000000..27fafe7
--- /dev/null
+++ b/dao/src/main/java/cn/bunny/dao/dto/financial/bill/BillExportDto.java
@@ -0,0 +1,33 @@
+package cn.bunny.dao.dto.financial.bill;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDate;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Schema(name = "BillExportDto对象", title = "导出账单信息", description = "导出账单信息")
+public class BillExportDto {
+
+ @Schema(name = "userId", title = "绑定的用户id")
+ private Long userId;
+
+ @Schema(name = "startDate", title = "开始交易日期")
+ @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd")
+ @NotNull(message = "开始日期不能为空")
+ private LocalDate startDate;
+
+ @Schema(name = "endDate", title = "结束交易日期")
+ @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd")
+ @NotNull(message = "结束日期不能为空")
+ private LocalDate endDate;
+
+}
\ No newline at end of file
diff --git a/dao/src/main/java/cn/bunny/dao/dto/financial/bill/user/BillAddUserDto.java b/dao/src/main/java/cn/bunny/dao/dto/financial/bill/user/BillAddUserDto.java
index f289df7..fd191bd 100644
--- a/dao/src/main/java/cn/bunny/dao/dto/financial/bill/user/BillAddUserDto.java
+++ b/dao/src/main/java/cn/bunny/dao/dto/financial/bill/user/BillAddUserDto.java
@@ -1,5 +1,6 @@
package cn.bunny.dao.dto.financial.bill.user;
+import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
@@ -24,23 +25,28 @@ public class BillAddUserDto {
@NotNull(message = "类型不能为空")
@Min(value = -1, message = "类型格式不正确")
@Max(value = 1, message = "类型格式不正确")
+ @ExcelProperty(index = 0)
private Byte type;
@Schema(name = "amount", title = "金额")
@NotNull(message = "金额不能为空")
@Min(value = 0, message = "金额格式不正确")
+ @ExcelProperty("金额")
private BigDecimal amount;
@Schema(name = "description", title = "描述")
+ @ExcelProperty("描述")
private String description;
@Schema(name = "transactionDate", title = "交易日期")
@NotNull(message = "交易日期不能为空")
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+ @ExcelProperty("交易日期")
private LocalDateTime transactionDate;
@Schema(name = "categoryId", title = "类别id")
@NotNull(message = "类别id不能为空")
+ @ExcelProperty("类别id")
private Long categoryId;
}
\ No newline at end of file
diff --git a/dao/src/main/java/cn/bunny/dao/excel/BillUserExportExcel.java b/dao/src/main/java/cn/bunny/dao/excel/BillUserExportExcel.java
new file mode 100644
index 0000000..1c9d87e
--- /dev/null
+++ b/dao/src/main/java/cn/bunny/dao/excel/BillUserExportExcel.java
@@ -0,0 +1,47 @@
+package cn.bunny.dao.excel;
+
+import cn.bunny.dao.common.vo.BaseVo;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Schema(name = "BillUserVo对象", title = "用户账单信息返回内容", description = "用户账单信息返回内容")
+public class BillUserExportExcel extends BaseVo {
+
+ @Schema(name = "username", title = "类型:1 - 收入,-1 - 支出")
+ @ExcelProperty("类型:1 - 收入,-1 - 支出")
+ private String type;
+
+ @Schema(name = "amount", title = "金额")
+ @ExcelProperty("金额")
+ private BigDecimal amount;
+
+ @Schema(name = "description", title = "描述")
+ @ExcelProperty("描述")
+ private String description;
+
+ @Schema(name = "transactionDate", title = "交易日期")
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ @JsonSerialize(using = LocalDateTimeSerializer.class)
+ @JsonDeserialize(using = LocalDateTimeDeserializer.class)
+ @ExcelProperty("交易日期")
+ private LocalDateTime transactionDate;
+
+ @Schema(name = "categoryName", title = "类别分类")
+ @ExcelProperty("类别分类")
+ private String categoryName;
+
+}
\ No newline at end of file
diff --git a/dao/src/main/java/cn/bunny/dao/vo/financial/user/BillUserVo.java b/dao/src/main/java/cn/bunny/dao/vo/financial/user/BillUserVo.java
index 086c0f4..b95c516 100644
--- a/dao/src/main/java/cn/bunny/dao/vo/financial/user/BillUserVo.java
+++ b/dao/src/main/java/cn/bunny/dao/vo/financial/user/BillUserVo.java
@@ -1,6 +1,7 @@
package cn.bunny.dao.vo.financial.user;
import cn.bunny.dao.common.vo.BaseVo;
+import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@@ -23,18 +24,22 @@ import java.time.LocalDateTime;
public class BillUserVo extends BaseVo {
@Schema(name = "username", title = "类型:1 - 收入,-1 - 支出")
+ @ExcelProperty(index = 0)
private Byte type;
@Schema(name = "amount", title = "金额")
+ @ExcelProperty("金额")
private BigDecimal amount;
@Schema(name = "description", title = "描述")
+ @ExcelProperty("描述")
private String description;
@Schema(name = "transactionDate", title = "交易日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
+ @ExcelProperty("交易日期")
private LocalDateTime transactionDate;
@Schema(name = "categoryId", title = "分类Id")
@@ -43,6 +48,7 @@ public class BillUserVo extends BaseVo {
private String categoryId;
@Schema(name = "categoryName", title = "类别分类")
+ @ExcelProperty("类别分类")
private String categoryName;
}
diff --git a/pom.xml b/pom.xml
index 5917148..2122b39 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
2.2
3.1
5.1.0
+ 4.0.2
4.3.1
2.12.3
2.3.2
@@ -151,6 +152,12 @@
quartz
${quartz-scheduler.version}
+
+
+ com.alibaba
+ easyexcel
+ ${easyexcel.version}
+
diff --git a/service/pom.xml b/service/pom.xml
index c32eb8d..2e3bc49 100644
--- a/service/pom.xml
+++ b/service/pom.xml
@@ -84,12 +84,6 @@
spring-context-support
6.1.6
-
-
- com.alibaba
- easyexcel
- 4.0.2
-
diff --git a/service/src/main/java/cn/bunny/services/controller/financial/BillController.java b/service/src/main/java/cn/bunny/services/controller/financial/BillController.java
index bda3e6c..f83e2b5 100644
--- a/service/src/main/java/cn/bunny/services/controller/financial/BillController.java
+++ b/service/src/main/java/cn/bunny/services/controller/financial/BillController.java
@@ -1,6 +1,7 @@
package cn.bunny.services.controller.financial;
import cn.bunny.dao.dto.financial.bill.BillDto;
+import cn.bunny.dao.dto.financial.bill.BillExportDto;
import cn.bunny.dao.dto.financial.bill.ExpendWithIncomeDto;
import cn.bunny.dao.dto.financial.bill.admin.BillAddDto;
import cn.bunny.dao.dto.financial.bill.admin.BillUpdateDto;
@@ -18,6 +19,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -67,13 +69,25 @@ public class BillController {
return Mono.just(Result.success(pageResult));
}
- @Operation(summary = "账单收入和支出", description = "账单收入和支出")
+ @Operation(summary = "账单收入和支出图表展示", description = "账单收入和支出图表展示")
@GetMapping("noManage/getExpendOrIncome")
public Mono> getExpendOrIncome(ExpendWithIncomeDto dto) {
ExpendWithIncomeListVo vo = billService.getExpendOrIncome(dto);
return Mono.just(Result.success(vo));
}
+ @Operation(summary = "导出用户账单信息", description = "导出用户账单信息")
+ @PostMapping("noManage/exportBill")
+ public void exportBill(@Valid @RequestBody BillExportDto dto, HttpServletResponse response) {
+ billService.exportBill(dto, response);
+ }
+
+ @Operation(summary = "管理员导出账单信息", description = "管理员导出账单信息")
+ @PostMapping("exportBillByAdmin")
+ public void exportBillByAdmin(@Valid @RequestBody BillExportDto dto, HttpServletResponse response) {
+ billService.exportBillByAdmin(dto, response);
+ }
+
@Operation(summary = "添加账单信息", description = "添加账单信息")
@PostMapping("addBill")
public Mono> addBill(@Valid @RequestBody BillAddDto dto) {
diff --git a/service/src/main/java/cn/bunny/services/excel/BillAddUserDAO.java b/service/src/main/java/cn/bunny/services/excel/BillAddUserDAO.java
new file mode 100644
index 0000000..fe99a69
--- /dev/null
+++ b/service/src/main/java/cn/bunny/services/excel/BillAddUserDAO.java
@@ -0,0 +1,11 @@
+package cn.bunny.services.excel;
+
+import cn.bunny.dao.dto.financial.bill.user.BillAddUserDto;
+
+import java.util.List;
+
+public class BillAddUserDAO {
+ public void save(List list) {
+ // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
+ }
+}
\ No newline at end of file
diff --git a/service/src/main/java/cn/bunny/services/excel/BillAddUserListener.java b/service/src/main/java/cn/bunny/services/excel/BillAddUserListener.java
new file mode 100644
index 0000000..5a6e64d
--- /dev/null
+++ b/service/src/main/java/cn/bunny/services/excel/BillAddUserListener.java
@@ -0,0 +1,60 @@
+package cn.bunny.services.excel;
+
+import cn.bunny.dao.dto.financial.bill.user.BillAddUserDto;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.read.listener.ReadListener;
+import com.alibaba.excel.util.ListUtils;
+import com.alibaba.fastjson2.JSON;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+@Slf4j
+public class BillAddUserListener implements ReadListener {
+
+ private static final int BATCH_COUNT = 100;
+ private final BillAddUserDAO billAddUserDAO;
+ private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
+
+ public BillAddUserListener() {
+ billAddUserDAO = new BillAddUserDAO();
+ }
+
+ public BillAddUserListener(BillAddUserDAO billAddUserDAO) {
+ this.billAddUserDAO = billAddUserDAO;
+ }
+
+ /**
+ * 这个每一条数据解析都会来调用
+ *
+ * @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
+ */
+ @Override
+ public void invoke(BillAddUserDto data, AnalysisContext context) {
+ log.info("解析到一条数据:{}", JSON.toJSONString(data));
+ cachedDataList.add(data);
+ // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
+ if (cachedDataList.size() >= BATCH_COUNT) {
+ saveData();
+ // 存储完成清理 list
+ cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
+ }
+ }
+
+ /**
+ * 所有数据解析完成了 都会来调用
+ */
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext context) {
+ saveData();
+ }
+
+ /**
+ * 加上存储数据库
+ */
+ private void saveData() {
+ log.info("{}条数据,开始存储数据库!", cachedDataList.size());
+ billAddUserDAO.save(cachedDataList);
+ log.info("存储数据库成功!");
+ }
+}
diff --git a/service/src/main/java/cn/bunny/services/factory/BillFactory.java b/service/src/main/java/cn/bunny/services/factory/BillFactory.java
new file mode 100644
index 0000000..7f9a0b4
--- /dev/null
+++ b/service/src/main/java/cn/bunny/services/factory/BillFactory.java
@@ -0,0 +1,97 @@
+package cn.bunny.services.factory;
+
+import cn.bunny.common.service.exception.BunnyException;
+import cn.bunny.dao.dto.financial.bill.BillExportDto;
+import cn.bunny.dao.dto.financial.bill.ExpendWithIncomeDto;
+import cn.bunny.dao.excel.BillUserExportExcel;
+import cn.bunny.dao.pojo.result.ResultCodeEnum;
+import cn.bunny.dao.vo.financial.user.expendAndIncome.ExpendWithIncome;
+import cn.bunny.services.mapper.financial.BillMapper;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+@Component
+public class BillFactory {
+
+ @Autowired
+ private BillMapper billMapper;
+
+ public void exportBill(BillExportDto dto, HttpServletResponse response) {
+ LocalDate startDate = dto.getStartDate();
+ LocalDate endDate = dto.getEndDate();
+
+ // 设置日期范围
+ String dateRange = startDate + "~" + endDate;
+
+ // 导出后的文件名
+ String filename = URLEncoder.encode(dateRange, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
+
+ // 初始化查询条件,将日期向后移一天查询包含当前的数据
+ ExpendWithIncomeDto expendWithIncomeDto = new ExpendWithIncomeDto();
+ BeanUtils.copyProperties(dto, expendWithIncomeDto);
+ expendWithIncomeDto.setEndDate(endDate.plusDays(1));
+
+ // 设置收入和支出的值
+ AtomicReference income = new AtomicReference<>(new BigDecimal(0));
+ AtomicReference expend = new AtomicReference<>(new BigDecimal(0));
+
+ // 查询数据
+ List expendWithIncomeList = billMapper.selectListByExpendWithIncomeDto(expendWithIncomeDto);
+ List excelList = expendWithIncomeList.stream().map(expendWithIncome -> {
+ BillUserExportExcel billUserExportExcel = new BillUserExportExcel();
+ BeanUtils.copyProperties(expendWithIncome, billUserExportExcel);
+
+ // 设置收支类型
+ String type;
+ if (expendWithIncome.getType().equals(Byte.parseByte("1"))) {
+ type = "收入";
+ income.updateAndGet(amount -> amount.add(expendWithIncome.getAmount()));
+ } else {
+ type = "支出";
+ expend.updateAndGet(bigDecimal -> bigDecimal.add(expendWithIncome.getAmount()));
+ }
+ billUserExportExcel.setType(type);
+
+ return billUserExportExcel;
+ }).toList();
+
+ // 查找模板文件
+ String filenameTemplate = Objects.requireNonNull(getClass().getResource("/static/bill-template.xlsx")).getFile();
+ if (filenameTemplate == null) throw new BunnyException(ResultCodeEnum.MISSING_TEMPLATE_FILES);
+
+ try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(filenameTemplate).build()) {
+ // 填充数据
+ WriteSheet writeSheet = EasyExcel.writerSheet().build();
+ excelWriter.fill(excelList, writeSheet);
+
+ // 写入模板数据
+ Map map = new HashMap<>();
+ map.put("dateRange", dateRange);
+ map.put("income", income.get());
+ map.put("expend", expend.get());
+ map.put("profit", income.get().subtract(expend.get()));
+ excelWriter.fill(map, writeSheet);
+
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+ response.setCharacterEncoding("utf-8");
+ response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + filename + ".xlsx");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/service/src/main/java/cn/bunny/services/service/financial/BillService.java b/service/src/main/java/cn/bunny/services/service/financial/BillService.java
index 3af1a81..2758c89 100644
--- a/service/src/main/java/cn/bunny/services/service/financial/BillService.java
+++ b/service/src/main/java/cn/bunny/services/service/financial/BillService.java
@@ -1,6 +1,7 @@
package cn.bunny.services.service.financial;
import cn.bunny.dao.dto.financial.bill.BillDto;
+import cn.bunny.dao.dto.financial.bill.BillExportDto;
import cn.bunny.dao.dto.financial.bill.ExpendWithIncomeDto;
import cn.bunny.dao.dto.financial.bill.admin.BillAddDto;
import cn.bunny.dao.dto.financial.bill.admin.BillUpdateDto;
@@ -13,6 +14,7 @@ import cn.bunny.dao.vo.financial.user.BillUserVo;
import cn.bunny.dao.vo.financial.user.expendAndIncome.ExpendWithIncomeListVo;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
+import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
@@ -89,4 +91,20 @@ public interface BillService extends IService {
* @return 账单收入和支出
*/
ExpendWithIncomeListVo getExpendOrIncome(ExpendWithIncomeDto dto);
+
+ /**
+ * 导出用户账单
+ *
+ * @param dto 日期选择
+ * @param response 响应体
+ */
+ void exportBill(BillExportDto dto, HttpServletResponse response);
+
+ /**
+ * 管理员导出账单信息
+ *
+ * @param dto 日期选择
+ * @param response 响应体
+ */
+ void exportBillByAdmin(BillExportDto dto, HttpServletResponse response);
}
diff --git a/service/src/main/java/cn/bunny/services/service/financial/impl/BillServiceImpl.java b/service/src/main/java/cn/bunny/services/service/financial/impl/BillServiceImpl.java
index a38b93d..47782fd 100644
--- a/service/src/main/java/cn/bunny/services/service/financial/impl/BillServiceImpl.java
+++ b/service/src/main/java/cn/bunny/services/service/financial/impl/BillServiceImpl.java
@@ -3,6 +3,7 @@ package cn.bunny.services.service.financial.impl;
import cn.bunny.common.service.context.BaseContext;
import cn.bunny.common.service.exception.BunnyException;
import cn.bunny.dao.dto.financial.bill.BillDto;
+import cn.bunny.dao.dto.financial.bill.BillExportDto;
import cn.bunny.dao.dto.financial.bill.ExpendWithIncomeDto;
import cn.bunny.dao.dto.financial.bill.admin.BillAddDto;
import cn.bunny.dao.dto.financial.bill.admin.BillUpdateDto;
@@ -16,6 +17,7 @@ import cn.bunny.dao.vo.financial.user.BillUserVo;
import cn.bunny.dao.vo.financial.user.expendAndIncome.CategoryAmount;
import cn.bunny.dao.vo.financial.user.expendAndIncome.ExpendWithIncome;
import cn.bunny.dao.vo.financial.user.expendAndIncome.ExpendWithIncomeListVo;
+import cn.bunny.services.factory.BillFactory;
import cn.bunny.services.factory.HomeFactory;
import cn.bunny.services.mapper.financial.BillMapper;
import cn.bunny.services.service.financial.BillService;
@@ -23,6 +25,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -44,6 +47,8 @@ public class BillServiceImpl extends ServiceImpl implements Bi
@Autowired
private HomeFactory homeFactory;
+ @Autowired
+ private BillFactory billFactory;
/**
* * 账单信息 服务实现类
@@ -208,6 +213,30 @@ public class BillServiceImpl extends ServiceImpl implements Bi
.build();
}
+ /**
+ * 导出用户账单
+ *
+ * @param dto 日期选择
+ * @param response 返回响应
+ */
+ @Override
+ public void exportBill(BillExportDto dto, HttpServletResponse response) {
+ dto.setUserId(BaseContext.getUserId());
+ billFactory.exportBill(dto, response);
+ }
+
+
+ /**
+ * 管理员导出账单信息
+ *
+ * @param dto 日期选择
+ * @param response 响应体
+ */
+ @Override
+ public void exportBillByAdmin(BillExportDto dto, HttpServletResponse response) {
+ billFactory.exportBill(dto, response);
+ }
+
/**
* 删除|批量删除账单信息
*
diff --git a/service/src/main/resources/mapper/financial/BillMapper.xml b/service/src/main/resources/mapper/financial/BillMapper.xml
index b46f03f..05d1fea 100644
--- a/service/src/main/resources/mapper/financial/BillMapper.xml
+++ b/service/src/main/resources/mapper/financial/BillMapper.xml
@@ -62,7 +62,7 @@
select *
from t_bill b left join t_category tc on b.category_id = tc.id
-
+
and b.user_id = #{dto.userId}
diff --git a/service/src/main/resources/static/bill-template.xlsx b/service/src/main/resources/static/bill-template.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..d35b08c039e1fc6f454cf2f832bdae97cae3a8ba
GIT binary patch
literal 9776
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
s{1mY8`+L~^9sYNp_*eK4)nDL$ct}-6B$##q0Cd income = new AtomicReference<>(new BigDecimal(0));
+ AtomicReference expend = new AtomicReference<>(new BigDecimal(0));
+
+ // 查询数据
+ List expendWithIncomeList = billMapper.selectListByExpendWithIncomeDto(expendWithIncomeDto);
+ List excelList = expendWithIncomeList.stream().map(expendWithIncome -> {
+ BillUserExportExcel billUserExportExcel = new BillUserExportExcel();
+ BeanUtils.copyProperties(expendWithIncome, billUserExportExcel);
+
+ // 设置收支类型
+ String type;
+ if (expendWithIncome.getType().equals(Byte.parseByte("1"))) {
+ type = "收入";
+ income.updateAndGet(amount -> amount.add(expendWithIncome.getAmount()));
+ } else {
+ type = "支出";
+ expend.updateAndGet(bigDecimal -> bigDecimal.add(expendWithIncome.getAmount()));
+ }
+ billUserExportExcel.setType(type);
+
+ return billUserExportExcel;
+ }).toList();
+
+ String filenameTemplate = Objects.requireNonNull(getClass().getResource("/static/bill-template.xlsx")).getFile();
+ if (filenameTemplate == null) throw new BunnyException(ResultCodeEnum.MISSING_TEMPLATE_FILES);
+
+ try (ExcelWriter excelWriter = EasyExcel.write("F:\\数据库备份\\" + dateRange + ".xlsx").withTemplate(filenameTemplate).build()) {
+ // 填充数据
+ WriteSheet writeSheet = EasyExcel.writerSheet().build();
+ excelWriter.fill(excelList, writeSheet);
+
+ // 写入模板数据
+ Map map = new HashMap<>();
+ map.put("dateRange", dateRange);
+ map.put("income", income.get());
+ map.put("expend", expend.get());
+ map.put("profit", income.get().subtract(expend.get()));
+ excelWriter.fill(map, writeSheet);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/service/src/test/java/excel/fill/FillTest.java b/service/src/test/java/excel/fill/FillTest.java
new file mode 100644
index 0000000..d3aab4b
--- /dev/null
+++ b/service/src/test/java/excel/fill/FillTest.java
@@ -0,0 +1,30 @@
+package excel.fill;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+public class FillTest {
+
+ @Test
+ void fillTest1() {
+ // // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
+ // String filenameTemplate = "F:\\数据库备份\\bill-template.xlsx";
+ //
+ // // 设置数据库查询时间
+ // BillExportDto billExportDto = new BillExportDto();
+ // billExportDto.setUserId(1849444494908125181L);
+ // billExportDto.setStartDate(LocalDate.of(2024, 11, 1));
+ // billExportDto.setEndDate(LocalDate.of(2024, 11, 30));
+ //
+ // EasyExcel.write("F:\\数据库备份\\" + billExportDto.getStartDate() + "~" + billExportDto.getEndDate() + ".xlsx").withTemplate(filenameTemplate).sheet().doFill(fillData);
+
+ // // 方案2 根据Map填充
+ // fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx";
+ // // 这里 会填充到第一个sheet, 然后文件流会自动关闭
+ // Map map = MapUtils.newHashMap();
+ // map.put("name", "张三");
+ // map.put("number", 5.2);
+ // EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map);
+ }
+}