liding
2025-12-11 c999acf2f8fe57bdf6b97681bfdfe47ce5c102a6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
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<AccountExpenseMapper, AccountExpense> implements AccountExpenseService {
 
    private AccountExpenseMapper accountExpenseMapper;
 
    private AccountIncomeMapper accountIncomeMapper;
 
    private SysDictDataMapper sysDictDataMapper;
 
 
    //分页查询
    @Override
    public IPage<AccountExpense> accountExpenseListPage(Page page, AccountExpense accountExpense) {
        return accountExpenseMapper.accountExpenseListPage(page,accountExpense);
    }
 
    //导出
    @Override
    public void accountExpenseExport(HttpServletResponse response, AccountExpense accountExpense) {
        List<AccountExpense> accountExpenses =accountExpenseMapper.accountExpenseExport(accountExpense);
        ExcelUtil<AccountExpense> util = new ExcelUtil<AccountExpense>(AccountExpense.class);
        util.exportExcel(response, accountExpenses, "支出管理导出");
    }
 
    //财务报表图表
    @Override
    public AccountDto report(DateQueryDto dateQueryDto) {
        AccountDto accountDto = new AccountDto();
        //获取该段时间内的所有收入
        List<AccountDto2> accountIncomes =accountIncomeMapper.report(dateQueryDto);
 
        Long incomeNumber = accountIncomeMapper.selectCount(Wrappers.<AccountIncome>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<AccountDto2> accountExpenses =accountExpenseMapper.report(dateQueryDto);
        accountDto.setExpenseType(accountExpenses);
        Long expenseNumber = accountExpenseMapper.selectCount(Wrappers.<AccountExpense>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<AccountDto3> reportExpense() {
        List<AccountDto3> accountDto3s = new ArrayList<>();
        //先查询收入类型有哪些
        List<SysDictData> 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<BigDecimal> 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<String, List<String>> analysis() {
        // 获取最近四个月(当前月 + 前3个月)的时间范围
        LocalDate today = LocalDate.now();
        DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("yyyy-MM"); // 年月格式化器
        Map<String, List<String>> result = new HashMap<>();
        List<String> months = new ArrayList<>(); // 存储年月(如 2025-12、2025-11)
        List<String> totalIncomeList = new ArrayList<>(); // 每月总收入
        List<String> totalExpenseList = new ArrayList<>(); // 每月总支出
        List<String> netIncomeList = new ArrayList<>(); // 每月净收入(收入-支出)
 
        // 步骤1:计算近4个月的年月列表(当前月、前1月、前2月、前3月)
        List<String> 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<AccountIncome> allIncomes = accountIncomeMapper.selectList(
                Wrappers.<AccountIncome>lambdaQuery()
                        .ge(AccountIncome::getIncomeDate, fourMonthsAgo.toString()) // 大于等于起始日
                        .le(AccountIncome::getIncomeDate, currentMonthEnd.toString()) // 小于等于结束日
        );
 
        // 收入按“年月”分组汇总(key:年月字符串,value:当月总收入)
        Map<String, BigDecimal> 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<AccountExpense> allExpenses = accountExpenseMapper.selectList(
                Wrappers.<AccountExpense>lambdaQuery()
                        .ge(AccountExpense::getExpenseDate, fourMonthsAgo.toString())
                        .le(AccountExpense::getExpenseDate, currentMonthEnd.toString())
        );
 
        // 支出按“年月”分组汇总
        Map<String, BigDecimal> 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.<AccountExpense>lambdaQuery()
                .eq(AccountExpense::getInvoiceNumber, purchaseContractNumber));
    }
 
    @Override
    public List<AccountExpense> getByInvoiceNumberList(String purchaseContractNumber) {
        return accountExpenseMapper.selectList(Wrappers.<AccountExpense>lambdaQuery()
                .eq(AccountExpense::getInvoiceNumber, purchaseContractNumber));
    }
 
 
}