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 zcmeHt1y@|z(stucx^Z_WxLfcLAUKUn(BKZi-AV93aDoL55FCQL6C@BCm*6xS9Ntdu z+;3)b=lccs?z7g}XRULdTHU+qsj6MIRTU8s@c_sGQ~&@#4KO{-vND1L0OAn=09*hn zyuP%fgPVneo3W;slZC4xyQjS!RURTdQ!W4=_Wl3HfAI>GCl4!ka{^^Ap501suqiBj zl0fA<2=2#WQG3$Slhjvgrk`zX{g54X1C)A?ZzE8RGrr)%eLQOQ$-%xpII^!r9Wy3q zpaX0~D!|d(zfa#zNSx@Rrwh%+BbDGHHa31^nE`m`+|aGgE3+!8P)=fnPw*sgJ!`&S z3$)zVgjuRrEHnV=Tv66q0tpV4Oaa%w;7;3EGy52wuCvJ_#?bgI5O^3Bs#5D{Fq^73 zxm>3zwz)Dj^31$2gJ0qdk8j0Z99u?iX%nDO;wAZ15`9*x*B5Wg+U{%M>)p;28*`?5 z-OP_~Wz43ZEfGW=JUItYwT-DIsNi-O_v;bI&woCA)J3&;L%)hk*0W4%-kcp5Z>~&4 zWB+)ius>TyMM!`p*#E=8V54A%%7Gb~uS3xnlnq4L%Llw4pq=K^4k_SZ2Q{5v#KFqI zj)>3x#HN18;c?1N0QWsI0PygD08srKE$hIXv}Z6|dk$kA28@=*E*5sK9PB^u|D)r7 zF$e$h*UJ-?RJu7a!=cY^BL*&J7UO|ZimxP}wNPvN2g)xzs(X`9N4nU~NCwm-2|HxadvGIG~7b6a00QkAiX{%-6hk@k8m?m8V42ORSg2UyGi32 z0kbJZJ6Ph8LeKXn)9?peO<#Wd=sRFXeSSp(*02(?t}@Mb5vK7nHn;0ImC0zwz4K*L z&Kyvu;l;V+9+&H<&pi*&tK~f&&hqLJKJ1?D-R826ho<#vA|UHQ69`6f4j#5Og{>k{1YsoEF#U*tYn!i&0=u$aCmCCL7W zPus*!!~@^Nl z&H%X9=>_80m|R%W4cJ39&9$WFY!_U=qWg2*RzH%S@Ix~|$te{Cs}DB`!CN^>UsL(- z`w+D&bY63!K&7Kadnp<#R%72rix03Yne_T_?gx-yh1BX*(8Yg#ly~m20Pp6_y+z-$ z_-vNi{VqwfUY*`WA#ap!D|}Ph4O64Z^acg^)@E0&Tk#0lj|?;ZRddp*W+$RefDuU% zBA4CcFt`%?k^n6&wjYi0RHj?ech-%j6Cza9s3=dRaJi>6Owm86Z2}AB`N1aw`3(Bi zy8PrXUD+dZ?QDgd<&B(IZHjWEcx(Z}Q_+_74cvjLZ}OhMoahOk+7GiCG>N$laX_oH zKBWR@IPqUNP9dw66ij`eW-* z3!^O-L*o|I-27Lu`%LB^-AD>IZ;MHDvM3kRyj)pV}6!afNTD}K(g zc$lm~cHbz!pg?0&XNvuG2pskSdOiPgoi!V35`q}n)mHyKmCK#{T*x&hd;?`zkVx$0 zi%F5}f+RJ4N*_VG2eTNv8`>RZ^5IWZLkmn&sZLsk)mM>Lz-b1m6%6lv9iK)04*~zk zm7pidsi!cGG{g8p0zid>aphkz<*$tScYJ|^rF$?b|J|cpT}iQr6WEGzAHn%5!vi1m z&5eU*AH4Snb+DEh!a&XAf3{4@(rl>nRgnY1G29C}+V63}hrNb?d)~=f9F2qMjcW%L zL2?{D9YsJjJ*c1vmP5kD+27qOJHWtBcg1abk~r`<8&B~2B@;byM1B#C%vsZ9IIqnZ zr#rWe>je1_%)buTZUkP3YV218*ETdzr88S@J~T-~ejw)rdr^KlB}S7M#qt7t*`B6m zz3i0zfqL@!)>6r9%(gTC8grAZOp&6LPs}&mjVfY5jxvBb(RDK~tpyBSb(6XoVs3|= zJRUxST5b&X&n&|<{h#i2HoY)!2(~Ry#R33`U|;;`~orD8hFxUbp+7_vtw zUMV~!<<#1)r?bwCN7eht`h{KbWc_A%J6q;!b-k&2gKDCf&WYf|rAhsar3EQ~kotWc zy>;dux&oV{1Q+QZw%*9}Jk$51N?mmb@e44JS-ysDu*5Wv9!1K;!DPFcfGnHOnAK_# z&QyXs;vDkS%_EoDA48d|M(-8vM{q=BSbWz3Lb6OWVdbzEX?^_M1$!1J#Q~;2&+QwO z45zy;c{GRGnYx&S=a2Y|>mMX2nbzedEgl3+Ceiq+K=%NZ;(b5*!YNu-km=GL(2 zoKUtC5(nb8r1svnALDb-KdW6uRxw|Q!V(QiruRC*y%+IZA;8-Hvg#||*cuviZyX7p;d)I7dgRx)T8>K9 zmS8$8%XQ_6d4KJZt=vWe`3Yo{xyjCUm8T)8|Pv3IP_3eXxz0P)3 zNA-jV^|RejChMR?%Xsb_H+)7vLHX|a)_06i!VAKtMHQ%7K`Z|N6k3mAk)uyNZh`hb<=>q6qy!VXK$c>2XHd!(3WmR4$5jdJIUJujv!D*w!YP2-R>$p!v?v z+A9{^=%Y-KkF4;N@o8wWHK$Q8y6||%TN0n*fZ##g>9P*cuCtU~@fmJ<6pex#-3jIR zPT2D9HCD@c^`g+qNj~ShqNB`cX?c2K$}VDgt|u7UVe{(d43bF`SMwd1uW5KcE_g6v zyKLUsRex?=?V+B{E*cUMrud5d;f_iW+vbCx__kk(edd$PH{Pv>I;7hD-g#G~rCMpb z645NBS0^X+i8cpk;+mt};1B6G&f*#3w{7}H#$c6lC%=q|`KDWRC9?XK2uHNw8Q}rr zf+}N&I3Kof3Sw16OCnao-2!V!OZ|;Xa!y9&bTd{zirRMa8ebVK!`{WKw_~(Ts<&Fe7MtX^uYNBhU64c~8)W#3m@}8ugSZNU-5m%Iu}-}c zV5>ijo1>-IH-9d0mjJ&wL4-=T>k~)0>~no7J?ki{T^oB52rGB>T^&bMzP6jbweoK=>kdd@b*Nkyx)1x>;Z>{Okn*CeQY~C+0C>|np1HC&kLS)Fo`=Af1)V6yK>cf$ zi-|o)1I%Jv1>(gKDkYi%0x-nMo&sfI&q7bCdJ1&oe1APIbP2RNEIX2TB1JRHt29g) z0r~UJtpwtU^CPr8MUEmyy;ncWNb=d0Dtd64mCS){(Y$*;Q3L4WX(ZL<$_YHf8B}{g zdFGj*t;i8Grq4(U#1WR2hghx1x2fXBh9!}?Vy}5^2Ql6qRWJ#JX>Kq+Ln^Udvnl0bwStMxIjC+viCC{oT zo$@1)?X#!mc6jR6ZqINAhKHU4)$Mz}Uj|K?P4n+QdBT%$q(@~#E#5Vax(=s@o;+s| zt15LKNJ$NXJj*Ws6ctPA1eca=Hk8?k=ju??l+@{2SFj9kU;W|k-A5eUG&C2RQ?F2> zsRTa{l3pfV9ib%Nuhqs!8cb>%3l?3-;+Ee@X`s}Pm=@CxQ4mRY+6+(+L&|rPhu{-b zQqt&CQQoCN*Q3XPS}X{qj*(tclZtQef{Rcn^`;JXeUWS&Ufn(sUK))&w-^4LyVT$`*^X)8@qw7QH1yCniH0|I>n1cCARPtM#7ZFDqGqQ1Zrob%P4XP$PQXNq9-nN(D! z&oB@fA60l@4sX0uIu*02V7SO{@^qpb^5B@O?Y@`V*sjt&Y~eqgQTF)wqu&;O@`wF1 ztE@CS5(1o2cUtpRce?3;kD;=Vt~~n1N{za)H3mEh`y_lFZlS7)-Nv9C=$#2^7*qC; z=w?4)Z+~dRz`E!aQqnFyU@yWQDLKtS@uNrtqg-n0JgNCS)%&*@@=~nbL`u$TS=T0m z;P1_bMbo*A($)T?3M8XnBL?d55-{c^Gz^)ob~9fsRDn9Y<`CJ04>5mSJEc9&^=LHM zr4f!msEHalA-p$Cwn%XPA}2bA@KJ_QJ0uh}Qz)5^TpK$;1B(|-lGix1ONC0w*2F(B z)c+Ja(Sd<@+|rZgtk#x|d-95n$@e4v*AGLti=C5+Y}AX4$7c(~Qx+bNB%_c<(=k_) zodi!xHos)z2zD=^>gom%G89R9oJ-4qrM*P*VuoefrGHh6PMu4zP9-aNwJeVO z`T~{TA93wVMA9-nzCj^Q8^1X!f#20PXLiz~XiJbexE$NpYGRQrMyy1d zx8YE*o@9gC4zgrEKdOyUyLJFNZcJp7UVMNRaTf^B5I;v>^X4mKs=42ghkaKfi$;!S zX>{ZDhI$$Feq#*?%wjb0n{%l3F)3W!#%A?zpDnG;uhQO$P^%H&my;|L^W7DGiN61e zG=UOZ?`;)|-JMp;eoRDTPJEg@Lgq|2M-wkl(Zu)@LI;f zlJ*T)K?Ih!|4iCl-Ms89Tz_WjpS4G0A^gBL!fg>$gWL}`=Vrr3A}Jwtt?=*#l7Wlx zVrNo{$x5L(hS5uN%-(y2Dif~QvMQrO+l3O#g>#}$BMQt1Jz9S_By|MR#|rW+-#S?e zdNvQeb61p+^rE1FF31uLnq^P;6685L(%iu7hV^&_6OYSBel3tti7@u(V%kVg*b}0j zga>(n@|u!k+Sc)oGtC%%D;cd{r&PI#F_iKt2y0S%_SZ+ zF+pVthRFAC@QT8PuZQYV!pa{Xif3#ZtS}=KvsS(_v)yzLb*zvnl+n02Gv_7vyiy{H z(uA#|L6YRxDE47%r7;CDk&q@8=lRI+9Lx1b0@s*{xn3IR;HV>US}DZxn0hSS9S<+&1e6#uk1^?! zmR6z)Hx1bnhpF0%KJBbe&%SnfZmB4pW~L@VLh4rW!`izDUCBDJvRZ7RAg-WoM~y2Z zoGChQ_^`)a#W^VGUV>0i_y|ZjG{*qZi?@&FBsyQk8z*bJj-N z@)EvMOk6PQ{mN?w^j^F2sWO*v%1)ISYpM`Y{_FF6BJ3gqI#txN?)(w1g4MtTO{vdF zS)(%=CrGy{P06Z{!$U&Xh{s_md;Xf0ixga#9x;tjgr0Vxe^do&Nlsr;aY*L1o_`&*pb%Tk`g<6qb*;(;u1bN*mKR@4yWt^%#;Lf`cQG z``n0J;}Vft?+VrHknFog7N|VsKpKKj!@9x@1$h02ELIE=eH-jSL>INzT0Sg1iAUmgWOa& zMfIX&e^!~5uX|(2;jTtEh!5c{h17_ZX8mWeMjx||?0Y7XSCn}V{(cuNTdv(Vnyac> zQaJMN#FO@}NyDm8+{88dB!P@L>eA(QYBWPjRq`Q2jvcmCJ(9pGn+(lF9}eI2OKyEjlE093tgMk=OR;57O;6wTax9IMd%y5_0>6goYp1 z-5`b7W-z^B>olIN1@aLfw<%i(F7_;)iX2+3GawKUy`I<9bySkKSxIj0`2!eo;#h zgKm!7a50yVi$w8OnK3PWF}Gg?4oovAQraC0au;p;KF)KDC1cPX7| z*Qk=xv+z2hAz+CP4q0&|aCFz|y@8PorUrHp2Z=$(t5hm0r)CJ%%yQrs)2`6`FiO7f zk)Xf-F#9920x`ze=k)2<89393vwJ~<5fTli8We=!&Vc!#0aw+|%kR%c?si7Qu&ANu z43sKbp-HfEfrT{F_xL3UFcHVHHy+{TDr@Z?=lPqd|xjS!Zh)pr6Ix)7FI);uFPT8L=dc+XzpmH z>f-3+%3` z?e!9^8gf<_TV8f%JoMjTO?i}&NB8?eGZ>*a30C#YE?dOOV+zEyYgYqsgNg}*;MWdQ zO(L?f{Lj%-QgKXck=|Gi?#TDB4k;qdmX=GTVb+?-t7{nbTG>CB<@)N2g*27z{*0Af z5zJ{8XA{QSNBIriY2^mjAkZY3IGi+B|6Hb44t$2uFJC9yC`h=Ba))z&Z zgR^@#hX)oHSD@9-+HWt$YIWx2B9`kzU{Z1Ingnb_$7`Pnx_y2cJae3R+G6fU8qR>0~# zR|Syw*0vBPU{MV2AsNiJNF_(!$MTqmxOGtE*S;xIhG;m_1+2}=sjvA$=E}p~y?%w1 zq3n_pG}XrIpiyz;O&%LXntqb2omK7(~`GgLd3%g-w=bv348d89jq}d zOcV2Zbumc%MiQ@-ZAC1;RVYlX;+2EHFhzvU)M3d}LgG1x0s{QSZtspCwj4X6A((7{g_AZoQF4R}OoYS0nsPWNPuG!ImFFC-N7*+F zObxUqlzv=?>=oTZ#w?*9>?sI(KWTv!ggg8pH^GfXvQHstBet9Ts!z^86Es7XF)0m{ z#~H37GRK5V(e7{GZqmXu^DiF@2hRqJegAwm=imDK_xukhbyOAqF5vH{2>uQHIj6!% z@|QCOzXN~oto{l84CCH!eb(Q>e{VJY2?YSQ(SCvdKaHo~<^0}%_)`)o=KuQ<|Iv;3 zUCQrOyuU~R{gU!~P49OBzwfC26fgk`j(^)<{SN&-rTG&YP4+M7?+MQD68;`&|HK0T 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); + } +}