ProfitAnalysisServiceImpl.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. package com.ydtech.modules.admin.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  3. import com.ydtech.core.page.HttpResult;
  4. import com.ydtech.modules.admin.model.dto.PayAmountExcel;
  5. import com.ydtech.modules.admin.model.dto.ReceivableExcel;
  6. import com.ydtech.modules.admin.model.report.profitAnalysis.ProfitAnalysisDto;
  7. import com.ydtech.modules.admin.model.report.profitAnalysis.ProfitAnalysisQueryDto;
  8. import com.ydtech.modules.admin.service.ProfitAnalysisService;
  9. import com.ydtech.modules.admin.utils.SliceUpDateUtil;
  10. import com.ydtech.modules.console.dao.ProfitAnalysisMapper;
  11. import com.ydtech.modules.fnc.dao.InsPlyIncomeMapper;
  12. import com.ydtech.modules.inv.dao.InvAccountExpensesMapper;
  13. import com.ydtech.modules.inv.dao.InvAccountMapper;
  14. import com.ydtech.modules.inv.model.InvAccount;
  15. import com.ydtech.modules.inv.model.InvAccountCardVo;
  16. import com.ydtech.modules.inv.model.InvAccountExpenses;
  17. import com.ydtech.modules.inv.utils.EasyExcelUtils;
  18. import com.ydtech.utils.BigDecimalUtils;
  19. import org.apache.commons.lang3.ObjectUtils;
  20. import org.apache.commons.lang3.StringUtils;
  21. import org.springframework.beans.factory.annotation.Autowired;
  22. import org.springframework.beans.factory.annotation.Value;
  23. import org.springframework.stereotype.Service;
  24. import java.io.IOException;
  25. import java.math.BigDecimal;
  26. import java.math.RoundingMode;
  27. import java.nio.file.Files;
  28. import java.nio.file.Path;
  29. import java.nio.file.Paths;
  30. import java.util.*;
  31. import java.util.stream.Collectors;
  32. import java.util.stream.Stream;
  33. /**
  34. * @version
  35. * @author: hxl
  36. * @Date: 2024/7/1 11:36
  37. * @Description: 控制台-运营数据
  38. */
  39. @Service
  40. public class ProfitAnalysisServiceImpl implements ProfitAnalysisService {
  41. @Autowired
  42. private InvAccountMapper invAccountMapper;
  43. @Autowired
  44. private InsPlyIncomeMapper insPlyIncomeMapper;
  45. @Autowired
  46. private ProfitAnalysisMapper profitAnalysisMapper;
  47. @Autowired
  48. private InvAccountExpensesMapper invAccountExpensesMapper;
  49. @Value("${upload.file.path}")
  50. private String uploadPath;
  51. @Value("${upload.file.url}")
  52. private String uploadUrl;
  53. /**
  54. * @version
  55. * @author: hxl
  56. * @Date: 2024/7/31 17:42
  57. * @Description: 查询利润数据
  58. */
  59. @Override
  60. public List<ProfitAnalysisDto> queryPage(ProfitAnalysisQueryDto profitAnalysisQueryDto) {
  61. HashMap<String, String> dateMapByType = getDateMapByType(profitAnalysisQueryDto.getDateType(), profitAnalysisQueryDto.getYear());
  62. profitAnalysisQueryDto.setBeginDate(dateMapByType.get("startDate"));
  63. profitAnalysisQueryDto.setEndDate(dateMapByType.get("endDate"));
  64. List<ProfitAnalysisDto> list = insPlyIncomeMapper.queryPage(profitAnalysisQueryDto);
  65. return list;
  66. }
  67. /**
  68. * @version
  69. * @author: whp
  70. * @Date: 2024/8/16 15:35
  71. * @Description: 查询利润分析统计
  72. */
  73. @Override
  74. public List<ProfitAnalysisDto> getProfitAnalysisQueryList(ProfitAnalysisQueryDto profitAnalysisQueryDto) {
  75. HashMap<String, String> dateMapByType = getDateMapByType(profitAnalysisQueryDto.getDateType(), profitAnalysisQueryDto.getYear());
  76. profitAnalysisQueryDto.setBeginDate(dateMapByType.get("startDate"));
  77. profitAnalysisQueryDto.setEndDate(dateMapByType.get("endDate"));
  78. List<ProfitAnalysisDto> profitAnalysisQueryList = insPlyIncomeMapper.getProfitAnalysisQueryList(profitAnalysisQueryDto);
  79. if(!profitAnalysisQueryList.isEmpty()){
  80. for (ProfitAnalysisDto profitAnalysisDto : profitAnalysisQueryList) {
  81. BigDecimal variableCost = BigDecimal.ZERO;
  82. String yearAndMonth = profitAnalysisDto.getYearAndMonth();
  83. profitAnalysisQueryDto.setYearAndMonth(yearAndMonth);
  84. List<ProfitAnalysisDto> profitAnalysisDto1 = insPlyIncomeMapper.changeAmount(profitAnalysisQueryDto);
  85. for (ProfitAnalysisDto analysisDto : profitAnalysisDto1) {
  86. String outCardNum = analysisDto.getOutCardNum();
  87. String enterCardNum = analysisDto.getEnterCardNum();
  88. InvAccountCardVo outAccount = invAccountMapper.getByCarNumber(outCardNum);
  89. InvAccountCardVo enterAccount = invAccountMapper.getByCarNumber(enterCardNum);
  90. if(ObjectUtils.isNotEmpty(outAccount) && ObjectUtils.isNotEmpty(enterAccount) ){
  91. if( (StringUtils.isNotEmpty(outAccount.getOuterFlag()) && StringUtils.isNotEmpty(enterAccount.getOuterFlag()))){
  92. if(!enterAccount.getOuterFlag().equals(outAccount.getOuterFlag())){
  93. variableCost=variableCost.add(analysisDto.getApplyAmount());
  94. }
  95. }
  96. }
  97. }
  98. profitAnalysisDto.setVariableCost(variableCost);
  99. }
  100. }
  101. return profitAnalysisQueryList;
  102. }
  103. @Override
  104. public List<ProfitAnalysisDto> getProfitAnalysisQueryDetailsList(ProfitAnalysisQueryDto profitAnalysisQueryDto) {
  105. List<ProfitAnalysisDto> resultList =new ArrayList<>();
  106. // 总支出=人员成本+固定成本+变动成本+税费
  107. BigDecimal allExpenditure =this.getAllExpenditure(profitAnalysisQueryDto);
  108. if (StringUtils.isNotEmpty(profitAnalysisQueryDto.getExpensesType()) && "3".equals(profitAnalysisQueryDto.getExpensesType())) {
  109. List<ProfitAnalysisDto> profitAnalysisDtos = profitAnalysisMapper.selectAccountExpenses(profitAnalysisQueryDto);
  110. Map<String, ProfitAnalysisDto> map = new HashMap<>();
  111. for (ProfitAnalysisDto dto : profitAnalysisDtos) {
  112. map.put(dto.getExpensesId(), dto);
  113. }
  114. ArrayList<ProfitAnalysisDto> parentExpenIdList = new ArrayList<>();
  115. // 如果没有parentId为0 的子级 则构建
  116. for (ProfitAnalysisDto profitAnalysisDto : profitAnalysisDtos) {
  117. if(ObjectUtils.isEmpty(map.get(profitAnalysisDto.getParentId())) && !profitAnalysisDto.getParentId() .equals("0")){
  118. QueryWrapper<InvAccountExpenses> queryWrapper = new QueryWrapper<>();
  119. queryWrapper.eq("expenses_id", profitAnalysisDto.getParentId());
  120. InvAccountExpenses invAccountExpenses = invAccountExpensesMapper.selectOne(queryWrapper);
  121. ProfitAnalysisDto profitAnalysisDto1 = new ProfitAnalysisDto();
  122. profitAnalysisDto1.setExpensesId(invAccountExpenses.getExpensesId());
  123. profitAnalysisDto1.setParentId(invAccountExpenses.getParentId());
  124. profitAnalysisDto1.setCategory(invAccountExpenses.getCategory());
  125. parentExpenIdList.add(profitAnalysisDto1);
  126. map.put(profitAnalysisDto1.getExpensesId(),profitAnalysisDto1);
  127. }
  128. }
  129. profitAnalysisDtos.addAll(parentExpenIdList);
  130. List<ProfitAnalysisDto> addVariableCost = new ArrayList<>(); //变动成本金额
  131. List<ProfitAnalysisDto> profitAnalysisDto1 = insPlyIncomeMapper.changeAmount(profitAnalysisQueryDto);
  132. for (ProfitAnalysisDto analysisDto : profitAnalysisDto1) {
  133. String outCardNum = analysisDto.getOutCardNum();
  134. String enterCardNum = analysisDto.getEnterCardNum();
  135. InvAccountCardVo outAccount = invAccountMapper.getByCarNumber(outCardNum);
  136. InvAccountCardVo enterAccount = invAccountMapper.getByCarNumber(enterCardNum);
  137. if (StringUtils.isNotEmpty(outAccount.getOuterFlag()) && StringUtils.isNotEmpty(enterAccount.getOuterFlag())) {
  138. if (!enterAccount.getOuterFlag().equals(outAccount.getOuterFlag())) {
  139. addVariableCost.add(analysisDto);
  140. }
  141. }
  142. }
  143. Map<String, BigDecimal> profitAnalysisDtoMap = addVariableCost.stream()
  144. .collect(Collectors.toMap(
  145. ProfitAnalysisDto::getExpensesId, // keyMapper,用于提取键(expensesId)
  146. ProfitAnalysisDto::getApplyAmount, // valueMapper,用于提取初始值(applyAmount)
  147. BigDecimal::add // mergeFunction,用于处理键冲突(相加 applyAmount)
  148. ));
  149. for (ProfitAnalysisDto profitAnalysisDto : profitAnalysisDtos) {
  150. String expensesId = profitAnalysisDto.getExpensesId();
  151. if (profitAnalysisDtoMap.containsKey(expensesId)) {
  152. BigDecimal bigDecimal = profitAnalysisDtoMap.get(expensesId);
  153. profitAnalysisDto.setApplyAmount(bigDecimal);
  154. }
  155. }
  156. for (ProfitAnalysisDto dto : profitAnalysisDtos) {
  157. if (dto.getParentId().equals("0")) {
  158. BigDecimal totalAmount = aggregateChildAmounts(dto.getExpensesId(), map);
  159. dto.setApplyAmount(totalAmount);
  160. }
  161. }
  162. for (ProfitAnalysisDto allAccountExpens : profitAnalysisDtos) {
  163. BigDecimal applyAmount = allAccountExpens.getApplyAmount() != null ? allAccountExpens.getApplyAmount() : BigDecimal.ZERO;
  164. if(applyAmount.compareTo(BigDecimal.ZERO)>0){
  165. allAccountExpens.setRadio(applyAmount.divide(allExpenditure,4,RoundingMode.HALF_UP).multiply(new BigDecimal("100")).setScale(2,RoundingMode.HALF_UP).toString());
  166. }
  167. }
  168. resultList.addAll(profitAnalysisDtos) ;
  169. } else {
  170. /**
  171. * 子类目金额
  172. */
  173. List<ProfitAnalysisDto> allAccountExpenses = profitAnalysisMapper.getAllAccountExpenses(profitAnalysisQueryDto);
  174. //二级类目
  175. List<String> collect = allAccountExpenses.stream().filter(item -> !item.getParentId().equals("0")).map(ProfitAnalysisDto::getExpensesId).collect(Collectors.toList());
  176. profitAnalysisQueryDto.setIds(collect);
  177. List<ProfitAnalysisDto> pidAmountList = profitAnalysisMapper.getSumCategory(profitAnalysisQueryDto);
  178. Map<String, BigDecimal> amountMap = pidAmountList.stream().collect(Collectors.toMap(ProfitAnalysisDto::getParentId, ProfitAnalysisDto::getApplyAmount));
  179. Map<String, List<String>> stringListMap = buildTreeMap(allAccountExpenses);
  180. stringListMap.forEach((id ,children) ->{
  181. profitAnalysisQueryDto.setExpensesIds(children);
  182. ProfitAnalysisDto sumCategory = profitAnalysisMapper.getSumByparentAmount(profitAnalysisQueryDto);
  183. if(!amountMap.containsKey(id) && ObjectUtils.isNotEmpty(sumCategory)){
  184. amountMap.put(id,sumCategory.getApplyAmount());
  185. }
  186. });
  187. for (ProfitAnalysisDto allAccountExpens : allAccountExpenses) {
  188. if (amountMap.containsKey(allAccountExpens.getExpensesId())) {
  189. allAccountExpens.setApplyAmount(amountMap.get(allAccountExpens.getExpensesId()));
  190. }
  191. }
  192. for (ProfitAnalysisDto allAccountExpens : allAccountExpenses) {
  193. BigDecimal applyAmount = allAccountExpens.getApplyAmount() != null ? allAccountExpens.getApplyAmount() : BigDecimal.ZERO;
  194. if(applyAmount.compareTo(BigDecimal.ZERO)>0){
  195. allAccountExpens.setRadio(applyAmount.divide(allExpenditure,4,RoundingMode.HALF_UP).multiply(new BigDecimal("100")).setScale(2,RoundingMode.HALF_UP).toString());
  196. }
  197. }
  198. resultList.addAll(allAccountExpenses);
  199. }
  200. List<ProfitAnalysisDto> tree = getTree(resultList);
  201. String orderBy = profitAnalysisQueryDto.getOrderBy();
  202. return sortListByField(tree, orderBy);
  203. }
  204. /**
  205. *
  206. * @param tree
  207. * @param orderBy
  208. * @return 排序
  209. */
  210. private List<ProfitAnalysisDto> sortListByField(List<ProfitAnalysisDto> tree, String orderBy) {
  211. if (tree == null || tree.isEmpty()) {
  212. return tree;
  213. }
  214. if(StringUtils.isEmpty(orderBy)){
  215. return tree;
  216. }
  217. // 初始化比较器
  218. Comparator<ProfitAnalysisDto> comparator = (dto1, dto2) -> {
  219. BigDecimal applyAmount = dto1.getApplyAmount();// 假设字段名为applyAmount,根据实际情况调整
  220. BigDecimal applyAmount1 = dto2.getApplyAmount();
  221. return applyAmount.compareTo(applyAmount1);
  222. };
  223. // 根据排序方向调整比较器
  224. if ("desc".equalsIgnoreCase(orderBy)) {
  225. comparator = comparator.reversed();
  226. }
  227. // 对列表进行排序
  228. List<ProfitAnalysisDto> sortedList = new ArrayList<>(tree);
  229. Collections.sort(sortedList, comparator);
  230. return sortedList;
  231. }
  232. private BigDecimal aggregateChildAmounts(String expensesId, Map<String, ProfitAnalysisDto> map) {
  233. BigDecimal totalAmount = BigDecimal.ZERO;
  234. for (ProfitAnalysisDto dto : map.values()) {
  235. if (dto.getParentId() .equals(expensesId) ) {
  236. totalAmount = totalAmount.add(dto.getApplyAmount() != null ? dto.getApplyAmount() : BigDecimal.ZERO);
  237. totalAmount = totalAmount.add(aggregateChildAmounts(dto.getExpensesId(), map)); // 递归查找子对象
  238. }
  239. }
  240. return totalAmount;
  241. }
  242. private BigDecimal getAllExpenditure(ProfitAnalysisQueryDto profitAnalysisQueryDto) {
  243. HashMap<String, BigDecimal> sumExpenditure = profitAnalysisMapper.getTotalExpenditure(profitAnalysisQueryDto);
  244. // 查询变动成本
  245. BigDecimal variableCost = BigDecimal.ZERO;
  246. BigDecimal allExpenditure = BigDecimal.ZERO;
  247. String yearAndMonth = profitAnalysisQueryDto.getYearAndMonth();
  248. profitAnalysisQueryDto.setYearAndMonth(yearAndMonth);
  249. List<ProfitAnalysisDto> profitAnalysisDto1 = insPlyIncomeMapper.changeAmount(profitAnalysisQueryDto);
  250. for (ProfitAnalysisDto analysisDto : profitAnalysisDto1) {
  251. String outCardNum = analysisDto.getOutCardNum();
  252. String enterCardNum = analysisDto.getEnterCardNum();
  253. InvAccountCardVo outAccount = invAccountMapper.getByCarNumber(outCardNum);
  254. InvAccountCardVo enterAccount = invAccountMapper.getByCarNumber(enterCardNum);
  255. if(StringUtils.isNotEmpty(outAccount.getOuterFlag()) && StringUtils.isNotEmpty(enterAccount.getOuterFlag())){
  256. if(!enterAccount.getOuterFlag().equals(outAccount.getOuterFlag())){
  257. variableCost=variableCost.add(analysisDto.getApplyAmount());
  258. }
  259. }
  260. }
  261. if(!sumExpenditure.isEmpty()){
  262. BigDecimal fixedCost = sumExpenditure.get("fixedCost") != null ? sumExpenditure.get("fixedCost") : BigDecimal.ZERO;
  263. BigDecimal laborCost = sumExpenditure.get("laborCost") != null ? sumExpenditure.get("laborCost") : BigDecimal.ZERO;
  264. BigDecimal taxation = sumExpenditure.get("taxation") != null ? sumExpenditure.get("taxation") : BigDecimal.ZERO;
  265. allExpenditure =allExpenditure.add(fixedCost).add(laborCost).add(taxation).add(variableCost);
  266. }
  267. return allExpenditure;
  268. }
  269. public static Map<String, List<String>> buildTreeMap(List<ProfitAnalysisDto> nodes) {
  270. Map<String, ProfitAnalysisDto> nodeMap = new HashMap<>();
  271. for (ProfitAnalysisDto node : nodes) {
  272. nodeMap.put(node.getExpensesId(), node);
  273. }
  274. Map<String, List<String>> parentChildrenMap = new HashMap<>();
  275. for (ProfitAnalysisDto node : nodes) {
  276. if (node.getParentId().equals("0")) {
  277. // Root node, start recursion here
  278. List<String> allIds = getAllIds(node, nodeMap, new ArrayList<>());
  279. parentChildrenMap.put(String.valueOf(node.getExpensesId()), allIds);
  280. }
  281. }
  282. return parentChildrenMap;
  283. }
  284. private static List<String> getAllIds(ProfitAnalysisDto node, Map<String, ProfitAnalysisDto> nodeMap, List<String> ids) {
  285. ids.add(String.valueOf(node.getExpensesId()));
  286. for (ProfitAnalysisDto potentialChild : nodeMap.values()) {
  287. if (potentialChild.getParentId() .equals(node.getExpensesId())) {
  288. getAllIds(potentialChild, nodeMap, ids);
  289. }
  290. }
  291. return ids;
  292. }
  293. /**
  294. * 组装树形结构结果集方法
  295. */
  296. public List<ProfitAnalysisDto> getTree(List<ProfitAnalysisDto> list){
  297. return list.stream().filter(accountExpenses -> accountExpenses.getParentId().equals("0"))
  298. .peek(accountExpenses -> accountExpenses.setChildren(getChildren(accountExpenses,list))).collect(Collectors.toList());
  299. }
  300. public List<ProfitAnalysisDto> getChildren(ProfitAnalysisDto user,List<ProfitAnalysisDto> userList){
  301. return userList.stream().filter(u -> Objects.equals(u.getParentId(),user.getExpensesId())).map(
  302. u -> {
  303. u.setChildren(getChildren(u,userList));
  304. return u;
  305. }
  306. ).collect(Collectors.toList());
  307. }
  308. /**
  309. * @version
  310. * @author: hxl
  311. * @Date: 2024/6/19 11:57
  312. * @Description: 通过时间类型判断
  313. */
  314. private static HashMap<String,String> getDateMapByType(String type, String year) {
  315. HashMap<String,String> map =new HashMap<>();
  316. String startDateStr="";
  317. String endDateStr="";
  318. if(StringUtils.isBlank(year) && StringUtils.isBlank(type)){
  319. Calendar cale = Calendar.getInstance();
  320. int yearNow = cale.get(Calendar.YEAR);
  321. startDateStr= SliceUpDateUtil.getYearBeginDateStr(yearNow)+" 00:00:00";
  322. endDateStr= SliceUpDateUtil.getYearEndDateStr(yearNow)+" 23:59:59";
  323. }else{
  324. if(StringUtils.isNotBlank(year)){
  325. startDateStr= SliceUpDateUtil.getYearBeginDateStr(Integer.parseInt(year))+" 00:00:00";
  326. endDateStr= SliceUpDateUtil.getYearEndDateStr(Integer.parseInt(year))+" 23:59:59";
  327. }else{
  328. if(StringUtils.isNotBlank(type)){
  329. if("1".equals(type)){
  330. // 一月内
  331. startDateStr=SliceUpDateUtil.getBeforeDay(new Date(),2)+" 00:00:00";
  332. endDateStr=SliceUpDateUtil.getBeforeDay(new Date(),0)+" 23:59:59";
  333. }else if("2".equals(type)){
  334. // 三月内
  335. startDateStr=SliceUpDateUtil.getBeforeDay(new Date(),3)+" 00:00:00";
  336. endDateStr=SliceUpDateUtil.getBeforeDay(new Date(),0)+" 23:59:59";
  337. }else if("3".equals(type)){
  338. // 近半年
  339. startDateStr=SliceUpDateUtil.getBeforeDay(new Date(),4)+" 00:00:00";
  340. endDateStr=SliceUpDateUtil.getBeforeDay(new Date(),0)+" 23:59:59";
  341. }
  342. }
  343. }
  344. }
  345. map.put("startDate",startDateStr);
  346. map.put("endDate",endDateStr);
  347. return map;
  348. }
  349. /**
  350. * @description: 控制台-利润分析应收金额导出明细
  351. * @author: whp
  352. **/
  353. @Override
  354. public HttpResult receivableExcel(ProfitAnalysisQueryDto profitAnalysisQueryDto) {
  355. List<ReceivableExcel> list = profitAnalysisMapper.receivableExcel(profitAnalysisQueryDto);
  356. String uploadPath = this.uploadPath + "/excel/";
  357. Path path = Paths.get(uploadPath);
  358. if (!Files.exists(path)) {
  359. try {
  360. Files.createDirectories(path);
  361. } catch (IOException e) {
  362. e.printStackTrace();
  363. }
  364. }
  365. String fileName = EasyExcelUtils.downExcel(uploadPath, "应收明细", ReceivableExcel.class, list);
  366. return HttpResult.ok("", uploadUrl + "excel/" + fileName);
  367. }
  368. /**
  369. * @description: 控制台-利润分析实付金额导出明细
  370. * @author: whp
  371. **/
  372. @Override
  373. public HttpResult payAmountExcel(ProfitAnalysisQueryDto profitAnalysisQueryDto) {
  374. List<PayAmountExcel> list = profitAnalysisMapper.payAmountExcel(profitAnalysisQueryDto);
  375. String uploadPath = this.uploadPath + "/excel/";
  376. Path path = Paths.get(uploadPath);
  377. if (!Files.exists(path)) {
  378. try {
  379. Files.createDirectories(path);
  380. } catch (IOException e) {
  381. e.printStackTrace();
  382. }
  383. }
  384. String fileName = EasyExcelUtils.downExcel(uploadPath, "实付明细", PayAmountExcel.class, list);
  385. return HttpResult.ok("", uploadUrl + "excel/" + fileName);
  386. }
  387. }