package com.ruoyi.account.service.impl; 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 com.ruoyi.account.dto.AccountDto; import com.ruoyi.account.dto.AccountDto2; import com.ruoyi.account.dto.AccountDto3; import com.ruoyi.account.mapper.AccountExpenseMapper; import com.ruoyi.account.mapper.AccountIncomeMapper; import com.ruoyi.account.pojo.AccountExpense; import com.ruoyi.account.pojo.AccountIncome; import com.ruoyi.account.service.AccountExpenseService; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.dto.DateQueryDto; import com.ruoyi.project.system.domain.SysDictData; import com.ruoyi.project.system.mapper.SysDictDataMapper; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @AllArgsConstructor @Service public class AccountExpenseServiceImpl extends ServiceImpl implements AccountExpenseService { private AccountExpenseMapper accountExpenseMapper; private AccountIncomeMapper accountIncomeMapper; private SysDictDataMapper sysDictDataMapper; //分页查询 @Override public IPage accountExpenseListPage(Page page, AccountExpense accountExpense) { return accountExpenseMapper.accountExpenseListPage(page,accountExpense); } //导出 @Override public void accountExpenseExport(HttpServletResponse response, AccountExpense accountExpense) { List accountExpenses =accountExpenseMapper.accountExpenseExport(accountExpense); ExcelUtil util = new ExcelUtil(AccountExpense.class); util.exportExcel(response, accountExpenses, "支出管理导出"); } //财务报表图表 @Override public AccountDto report(DateQueryDto dateQueryDto) { AccountDto accountDto = new AccountDto(); //获取该段时间内的所有收入 List accountIncomes =accountIncomeMapper.report(dateQueryDto); Long incomeNumber = accountIncomeMapper.selectCount(Wrappers.lambdaQuery() .between(AccountIncome::getIncomeDate, dateQueryDto.getEntryDateStart(), dateQueryDto.getEntryDateEnd())); accountDto.setIncomeNumber(incomeNumber); BigDecimal totalIncome = accountIncomes.stream().map(AccountDto2::getAccount).reduce(BigDecimal.ZERO, BigDecimal::add); accountDto.setTotalIncome(totalIncome); accountIncomes.stream().forEach(accountDto2 -> { accountDto2.setProportion(accountDto2.getAccount().divide(totalIncome,2,BigDecimal.ROUND_HALF_UP)); }); accountDto.setIncomeType(accountIncomes); //获取该段时间内的所有支出 List accountExpenses =accountExpenseMapper.report(dateQueryDto); accountDto.setExpenseType(accountExpenses); Long expenseNumber = accountExpenseMapper.selectCount(Wrappers.lambdaQuery() .between(AccountExpense::getExpenseDate, dateQueryDto.getEntryDateStart(), dateQueryDto.getEntryDateEnd())); accountDto.setExpenseNumber(expenseNumber); BigDecimal totalExpense = accountExpenses.stream().map(AccountDto2::getAccount).reduce(BigDecimal.ZERO, BigDecimal::add); accountDto.setTotalExpense(totalExpense); accountExpenses.stream().forEach(accountDto2 -> { accountDto2.setProportion(accountDto2.getAccount().divide(totalExpense,2,BigDecimal.ROUND_HALF_UP)); }); accountDto.setExpenseType(accountExpenses); //净收入 BigDecimal netRevenue = totalIncome.subtract(totalExpense); accountDto.setNetRevenue(netRevenue); return accountDto; } //财务报表年查询 @Override public List reportExpense() { List accountDto3s = new ArrayList<>(); //先查询收入类型有哪些 List incomeTypes = sysDictDataMapper.selectDictDataByType("expense_types"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); int currentYear = LocalDate.now().getYear(); // 获取当前年份(如2025) for (SysDictData incomeType : incomeTypes) { AccountDto3 accountDto3 = new AccountDto3(); accountDto3.setTypeName(incomeType.getDictLabel());//类型 List account=new ArrayList<>(); for (int i = 1; i <= 12; i++) { // 当月第一天:年份为当前年,月份为i,日期为1 LocalDate firstDay = LocalDate.of(currentYear, i, 1); DateQueryDto dateQueryDto = new DateQueryDto(); dateQueryDto.setEntryDateStart(firstDay.format(formatter)); // 当月最后一天:第一天的月份的最后一天 dateQueryDto.setEntryDateEnd(firstDay.plusMonths(1).minusDays(1).format(formatter)); account.add(accountExpenseMapper.report1(dateQueryDto,incomeType.getDictValue())); } accountDto3.setAccount(account);//类型 accountDto3s.add(accountDto3); } return accountDto3s; } @Override public Map> analysis() { // 获取最近四个月(当前月 + 前3个月)的时间范围 LocalDate today = LocalDate.now(); DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy-MM"); // 年月格式化器 Map> result = new HashMap<>(); List months = new ArrayList<>(); // 存储年月(如 2025-12、2025-11) List totalIncomeList = new ArrayList<>(); // 每月总收入 List totalExpenseList = new ArrayList<>(); // 每月总支出 List netIncomeList = new ArrayList<>(); // 每月净收入(收入-支出) // 步骤1:计算近4个月的年月列表(当前月、前1月、前2月、前3月) List targetMonths = new ArrayList<>(); for (int i = 0; i < 4; i++) { LocalDate currentMonth = today.minusMonths(i); String monthStr = currentMonth.format(monthFormatter); targetMonths.add(monthStr); } // 反转列表,确保顺序为「前3月 → 当前月」(可选,按需求调整顺序) Collections.reverse(targetMonths); // 步骤2:一次性查询近4个月所有收入数据,按“年月”分组汇总 LocalDate fourMonthsAgo = today.minusMonths(3).withDayOfMonth(1); // 近4个月起始日(前3月1号) LocalDate currentMonthEnd = today.withDayOfMonth(today.lengthOfMonth()); // 当前月结束日 ZoneId zoneId = ZoneId.of("Asia/Shanghai"); // 查询近4个月所有收入 List allIncomes = accountIncomeMapper.selectList( Wrappers.lambdaQuery() .ge(AccountIncome::getIncomeDate, fourMonthsAgo.toString()) // 大于等于起始日 .le(AccountIncome::getIncomeDate, currentMonthEnd.toString()) // 小于等于结束日 ); // 收入按“年月”分组汇总(key:年月字符串,value:当月总收入) Map monthlyIncomeMap = allIncomes.stream() .filter(income -> income.getIncomeMoney() != null) // 过滤空金额 .collect(Collectors.groupingBy( income -> { // 将输入时间(字符串)转换为LocalDate,再格式化为年月 LocalDate inputDate = income.getIncomeDate().toInstant().atZone(zoneId).toLocalDate(); return inputDate.format(monthFormatter); }, Collectors.reducing(BigDecimal.ZERO, AccountIncome::getIncomeMoney, BigDecimal::add) )); // 步骤3:一次性查询近4个月所有支出数据,按“年月”分组汇总 List allExpenses = accountExpenseMapper.selectList( Wrappers.lambdaQuery() .ge(AccountExpense::getExpenseDate, fourMonthsAgo.toString()) .le(AccountExpense::getExpenseDate, currentMonthEnd.toString()) ); // 支出按“年月”分组汇总 Map monthlyExpenseMap = allExpenses.stream() .filter(expense -> expense.getExpenseMoney() != null) // 过滤空金额 .collect(Collectors.groupingBy( expense -> { LocalDate inputDate = expense.getExpenseDate().toInstant().atZone(zoneId).toLocalDate(); return inputDate.format(monthFormatter); }, Collectors.reducing(BigDecimal.ZERO, AccountExpense::getExpenseMoney, BigDecimal::add) )); // 步骤4:循环4个目标月份,填充统计数据(无数据时默认为0) for (String month : targetMonths) { // 当月总收入(无数据则为0) BigDecimal totalIncome = monthlyIncomeMap.getOrDefault(month, BigDecimal.ZERO); // 当月总支出(无数据则为0) BigDecimal totalExpense = monthlyExpenseMap.getOrDefault(month, BigDecimal.ZERO); // 当月净收入(收入 - 支出) BigDecimal netIncome = totalIncome.subtract(totalExpense); // 填充列表 months.add(month); totalIncomeList.add(totalIncome.toString()); totalExpenseList.add(totalExpense.toString()); netIncomeList.add(netIncome.toString()); } // 组装结果 result.put("days", months); // 年月(如 ["2025-09", "2025-10", "2025-11", "2025-12"]) result.put("totalIncome", totalIncomeList); // 对应月份总收入 result.put("totalExpense", totalExpenseList); // 对应月份总支出 result.put("netIncome", netIncomeList); // 对应月份净收入 return result; } @Override public AccountExpense getByInvoiceNumber(String purchaseContractNumber) { return accountExpenseMapper.selectOne(Wrappers.lambdaQuery() .eq(AccountExpense::getInvoiceNumber, purchaseContractNumber)); } @Override public List getByInvoiceNumberList(String purchaseContractNumber) { return accountExpenseMapper.selectList(Wrappers.lambdaQuery() .eq(AccountExpense::getInvoiceNumber, purchaseContractNumber)); } }