package com.peanut.modules.master.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.peanut.common.utils.DateUtils; import com.peanut.common.utils.R; import com.peanut.modules.common.entity.UserVip; import com.peanut.modules.master.service.UserVipService; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.io.OutputStream; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.URLEncoder; import java.text.DecimalFormat; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @Slf4j @RestController("masterStatisticsBusinessVip") @RequestMapping("master/statisticsBusinessVip") public class StatisticsBusinessVipController { @Autowired private UserVipService userVipService; @RequestMapping("/getUserVipByMonth") public R getUserVipByMonth(@RequestBody Map params) { CountResult result = countInfo(userVipService.getUserVipByMonth(params.get("date").toString()), params.get("date").toString()); List> allResultList = result.resultList; Map banCounts = new HashMap<>(); Map yanCounts = new HashMap<>(); BigDecimal banTotalPrice = BigDecimal.ZERO; BigDecimal yanTotalPrice = BigDecimal.ZERO; String date = params.get("date").toString(); Map> reBuyRules = buildReBuyRules(date); for (Map map : allResultList) { String vipType = map.get("vipType").toString(); boolean isBan = map.get("startTime").equals(map.get("uvlStartTime")); // 选择对应的计数 Map Map targetCounts = isBan ? banCounts : yanCounts; // 初始化该 vipType 的 Map targetCounts.put(vipType, targetCounts.getOrDefault(vipType, 0) + 1); // 累加价格 BigDecimal price = new BigDecimal(map.get("price").toString()); if (isBan) { banTotalPrice = banTotalPrice.add(price); } else { yanTotalPrice = yanTotalPrice.add(price); } map.put("isReBuy","否"); List state1UserVips = userVipService.list(new LambdaQueryWrapper() .eq(UserVip::getUserId, map.get("userId").toString()) .eq(UserVip::getState,1).lt(UserVip::getStartTime,map.get("startTime").toString())) .stream().map(UserVip::getType).collect(Collectors.toList()); // 判断是否复购 List required = reBuyRules.get(vipType); if (required != null && state1UserVips.containsAll(required)) { map.put("isReBuy", "是"); } } // 计算办理总人数 int banTotalCount = banCounts.values().stream() .mapToInt(yearMap -> yearMap.intValue()) .sum(); int yanTotalCount = yanCounts.values().stream() .mapToInt(yearMap -> yearMap.intValue()) .sum(); return R.ok().put("allResultList",allResultList) .put("banTotalCount",banTotalCount).put("yanTotalCount",yanTotalCount) .put("banTotalPrice",banTotalPrice).put("yanTotalPrice",yanTotalPrice) .put("banCounts",banCounts).put("yanCounts",yanCounts); } //导出月度汇总 @RequestMapping("/exportUserVipByMonth") public void exportUserVipByMonth(HttpServletResponse response,@RequestBody Map params) { R r = getUserVipByMonth(params); String date = (String) params.get("date"); Date nowMonth = DateUtils.stringToDate(date,"yyyy-MM"); Date lastMonth = DateUtils.addDateMonths(nowMonth,-1); String lastMothStr = DateUtils.format(lastMonth,"yyyy-MM"); params.put("date", lastMothStr); R r2 = getUserVipByMonth(params); if (r.get("code").toString().equals("0")) { String fileName = "月度VIP汇总"; XSSFWorkbook wb = new XSSFWorkbook(); Sheet sheet = wb.createSheet(fileName); int rowNum = 0; Row row0 = sheet.createRow(rowNum++); row0.createCell(0).setCellValue("办理总人数");row0.createCell(1).setCellValue(r.get("banTotalCount").toString()); row0.createCell(2).setCellValue("延期总人数");row0.createCell(3).setCellValue(r.get("yanTotalCount").toString()); row0.createCell(4).setCellValue("总办理金额");row0.createCell(5).setCellValue(r.get("banTotalPrice").toString()); BigDecimal a = calcGrowthRate(r.get("banTotalCount"), r2.get("banTotalCount")); DecimalFormat df = new DecimalFormat("0.00%"); row0.createCell(6).setCellValue("办理总人数环比增长率:");row0.createCell(7).setCellValue(df.format(a)); Row row1 = sheet.createRow(rowNum++); Map state1Counts = (Map) r.get("banCounts"); Map state0Counts = (Map) r.get("yanCounts"); row1.createCell(0).setCellValue("医学超级");row1.createCell(1).setCellValue(state1Counts.getOrDefault("医学超级",0).toString()); row1.createCell(2).setCellValue("医学超级");row1.createCell(3).setCellValue(state0Counts.getOrDefault("医学超级",0).toString()); row1.createCell(4).setCellValue("总延期金额");row1.createCell(5).setCellValue(r.get("yanTotalPrice").toString()); BigDecimal b = calcGrowthRate(r.get("yanTotalCount"), r2.get("yanTotalCount")); row1.createCell(6).setCellValue("延期总人数环比增长率:");row1.createCell(7).setCellValue(df.format(b)); Row row2 = sheet.createRow(rowNum++); row2.createCell(0).setCellValue("国学心理学超级");row2.createCell(1).setCellValue(state1Counts.getOrDefault("国学心理学超级",0).toString()); row2.createCell(2).setCellValue("国学心理学超级");row2.createCell(3).setCellValue(state0Counts.getOrDefault("国学心理学超级",0).toString()); row2.createCell(4).setCellValue("总金额");row2.createCell(5).setCellValue( new BigDecimal(r.get("banTotalPrice").toString()).add(new BigDecimal(r.get("yanTotalPrice").toString())).toString()); BigDecimal c = calcGrowthRate(r.get("banTotalPrice"), r2.get("banTotalPrice")); row2.createCell(6).setCellValue("办理金额环比增长率:");row2.createCell(7).setCellValue(df.format(c)); Row row3 = sheet.createRow(rowNum++); row3.createCell(0).setCellValue("中医学");row3.createCell(1).setCellValue(state1Counts.getOrDefault("中医学", 0).toString()); row3.createCell(2).setCellValue("中医学");row3.createCell(3).setCellValue(state0Counts.getOrDefault("中医学", 0).toString()); BigDecimal d = calcGrowthRate(r.get("yanTotalPrice"), r2.get("yanTotalPrice")); row3.createCell(6).setCellValue("延期金额环比增长率:");row3.createCell(7).setCellValue(df.format(d)); Row row4 = sheet.createRow(rowNum++); row4.createCell(0).setCellValue("针灸学");row4.createCell(1).setCellValue(state1Counts.getOrDefault("针灸学", 0).toString()); row4.createCell(2).setCellValue("针灸学");row4.createCell(3).setCellValue(state0Counts.getOrDefault("针灸学", 0).toString()); Row row5 = sheet.createRow(rowNum++); row5.createCell(0).setCellValue("肿瘤学");row5.createCell(1).setCellValue(state1Counts.getOrDefault("肿瘤学", 0).toString()); row5.createCell(2).setCellValue("肿瘤学");row5.createCell(3).setCellValue(state0Counts.getOrDefault("肿瘤学", 0).toString()); Row row6 = sheet.createRow(rowNum++); row6.createCell(0).setCellValue("妇幼生殖学");row6.createCell(1).setCellValue(state1Counts.getOrDefault("妇幼生殖学",0).toString()); row6.createCell(2).setCellValue("妇幼生殖学");row6.createCell(3).setCellValue(state0Counts.getOrDefault("妇幼生殖学", 0).toString()); Row row7 = sheet.createRow(rowNum++); row7.createCell(0).setCellValue("国学");row7.createCell(1).setCellValue(state1Counts.getOrDefault("国学",0).toString()); row7.createCell(2).setCellValue("国学");row7.createCell(3).setCellValue(state0Counts.getOrDefault("国学", 0).toString()); Row row8 = sheet.createRow(rowNum++); row8.createCell(0).setCellValue("心理学");row8.createCell(1).setCellValue(state1Counts.getOrDefault("心理学", 0).toString()); row8.createCell(2).setCellValue("心理学");row8.createCell(3).setCellValue(state0Counts.getOrDefault("心理学", 0).toString()); Row row9 = sheet.createRow(rowNum++); row9.createCell(0).setCellValue("中西汇通学");row9.createCell(1).setCellValue(state1Counts.getOrDefault("中西汇通学", 0).toString()); row9.createCell(2).setCellValue("中西汇通学");row9.createCell(3).setCellValue(state0Counts.getOrDefault("中西汇通学", 0).toString()); sheet.createRow(rowNum++); String[] header = {"时间","姓名","电话","VIP类型","是否延期", "本次年限","本次金额","本次开始时间","本次结束时间","到期时间","是否复购"}; String[] cellValues = {"payTime","name","tel","vipType", "isYan","year","price","uvlStartTime","uvlEndTime","endTime","isReBuy"}; export(wb,sheet,fileName,++rowNum,response,header,cellValues, (List>) r.get("allResultList")); } } @RequestMapping("/getUserVipByYear") public R getUserVipByYear(@RequestBody Map params) { CountResult result = countInfo(userVipService.getUserVipByYear(Integer.parseInt(params.get("year").toString()))); List> allResultList = result.resultList; Map> banCounts = new HashMap<>(); Map> yanCounts = new HashMap<>(); BigDecimal banTotalPrice = BigDecimal.ZERO; BigDecimal yanTotalPrice = BigDecimal.ZERO; for (Map map : allResultList) { String vipType = map.get("vipType").toString(); String year = map.get("year").toString(); boolean isBan = map.get("startTime").equals(map.get("uvlStartTime")); // 选择对应的计数 Map Map> targetCounts = isBan ? banCounts : yanCounts; // 初始化该 vipType 的 Map targetCounts.putIfAbsent(vipType, new HashMap<>()); Map yearMap = targetCounts.get(vipType); // 更新计数 if (isBan){ if ("3".equals(year)||"4".equals(year)){ yearMap.put(year, yearMap.getOrDefault(year, 0) + 1); }else { yearMap.put("其他", yearMap.getOrDefault("其他", 0) + 1); } }else { if ("1".equals(year)||"3".equals(year)||"4".equals(year)){ yearMap.put(year, yearMap.getOrDefault(year, 0) + 1); }else { yearMap.put("其他", yearMap.getOrDefault("其他", 0) + 1); } } // 累加价格 BigDecimal price = new BigDecimal(map.get("price").toString()); if (isBan) { banTotalPrice = banTotalPrice.add(price); } else { yanTotalPrice = yanTotalPrice.add(price); } } // 统计各个类型的办理总人数 Map banTypeTotalCounts = new HashMap<>(); for (Map.Entry> entry : banCounts.entrySet()) { String vipType = entry.getKey(); Map yearMap = entry.getValue(); // 合计该类型所有年份的人数 int total = yearMap.values().stream().mapToInt(Integer::intValue).sum(); banTypeTotalCounts.put(vipType, total); } // 统计各个类型的延期总人数 Map yanTypeTotalCounts = new HashMap<>(); for (Map.Entry> entry : yanCounts.entrySet()) { String vipType = entry.getKey(); Map yearMap = entry.getValue(); // 合计该类型所有年份的人数 int total = yearMap.values().stream().mapToInt(Integer::intValue).sum(); yanTypeTotalCounts.put(vipType, total); } // 计算办理总人数 int banTotalCount = banCounts.values().stream() .mapToInt(yearMap -> yearMap.values().stream().mapToInt(Integer::intValue).sum()) .sum(); int yanTotalCount = yanCounts.values().stream() .mapToInt(yearMap -> yearMap.values().stream().mapToInt(Integer::intValue).sum()) .sum(); // 计算各类型办理人数占比 Map banTypeRatios = new HashMap<>(); for (Map.Entry> entry : banCounts.entrySet()) { String vipType = entry.getKey(); int typeTotal = entry.getValue().values().stream().mapToInt(Integer::intValue).sum(); double ratio = banTotalCount == 0 ? 0.0 : (typeTotal * 1.0 / banTotalCount); DecimalFormat df = new DecimalFormat("0.00%"); String ratioStr = df.format(ratio); banTypeRatios.put(vipType, ratioStr); } // 初始化 12 个月的统计 Map banMonthCounts = new HashMap<>(); Map yanMonthCounts = new HashMap<>(); Map monthAmounts = new HashMap<>(); for (int m = 1; m <= 12; m++) { banMonthCounts.put(m, 0); yanMonthCounts.put(m, 0); monthAmounts.put(m, BigDecimal.ZERO); } for (Map map : allResultList) { String payTimeStr = map.get("payTime").toString(); LocalDate payDate = LocalDate.parse(payTimeStr, DateTimeFormatter.ofPattern("yyyy-MM-dd")); int month = payDate.getMonthValue(); boolean isBan = map.get("startTime").equals(map.get("uvlStartTime")); BigDecimal price = new BigDecimal(map.get("price").toString()); if (isBan) { banMonthCounts.put(month, banMonthCounts.get(month) + 1); } else { yanMonthCounts.put(month, yanMonthCounts.get(month) + 1); } // 总金额(办理 + 延期) monthAmounts.put(month, monthAmounts.get(month).add(price)); } return R.ok() .put("banTotalCount",banTotalCount).put("yanTotalCount",yanTotalCount) .put("banTypeTotalCounts",banTypeTotalCounts).put("yanTypeTotalCounts",yanTypeTotalCounts) .put("banTotalPrice",banTotalPrice).put("yanTotalPrice",yanTotalPrice) .put("banCounts",banCounts).put("yanCounts",yanCounts) .put("banTypeRatios",banTypeRatios) .put("banMonthCounts",banMonthCounts).put("yanMonthCounts",yanMonthCounts).put("monthAmounts",monthAmounts); } //导出全部汇总 @RequestMapping("/exportUserVipByYear") public void exportUserVipByYear(HttpServletResponse response,@RequestBody Map params) { R r = getUserVipByYear(params); int year = Integer.parseInt(params.get("year").toString()); params.put("year", year-1); R r2 = getUserVipByYear(params); if (r.get("code").toString().equals("0")) { String fileName = "年度VIP汇总"; XSSFWorkbook wb = new XSSFWorkbook(); Sheet sheet = wb.createSheet(fileName); int rowNum = 0; Row row0 = sheet.createRow(rowNum++); row0.createCell(0).setCellValue("办理总人数");row0.createCell(1).setCellValue(r.get("banTotalCount").toString()); row0.createCell(2).setCellValue("延期总人数");row0.createCell(3).setCellValue(r.get("yanTotalCount").toString()); row0.createCell(4).setCellValue("总办理金额");row0.createCell(5).setCellValue(r.get("banTotalPrice").toString()); BigDecimal a = calcGrowthRate(r.get("banTotalCount"), r2.get("banTotalCount")); DecimalFormat df = new DecimalFormat("0.00%"); row0.createCell(6).setCellValue("办理总人数同比增长率:");row0.createCell(7).setCellValue(df.format(a)); Row row1 = sheet.createRow(rowNum++); Map state1Counts = (Map) r.get("banTypeTotalCounts"); Map state0Counts = (Map) r.get("yanTypeTotalCounts"); row1.createCell(0).setCellValue("医学超级");row1.createCell(1).setCellValue(state1Counts.containsKey("医学超级")?state1Counts.get("医学超级").toString():""); row1.createCell(2).setCellValue("医学超级");row1.createCell(3).setCellValue(state0Counts.containsKey("医学超级")?state0Counts.get("医学超级").toString():""); row1.createCell(4).setCellValue("总延期金额");row1.createCell(5).setCellValue(r.get("yanTotalPrice").toString()); BigDecimal b = calcGrowthRate(r.get("yanTotalCount"), r2.get("yanTotalCount")); row1.createCell(6).setCellValue("延期总人数同比增长率:");row1.createCell(7).setCellValue(df.format(b)); Row row2 = sheet.createRow(rowNum++); row2.createCell(0).setCellValue("国学心理学超级");row2.createCell(1).setCellValue(state1Counts.get("国学心理学超级").toString()); row2.createCell(2).setCellValue("国学心理学超级");row2.createCell(3).setCellValue(state0Counts.get("国学心理学超级").toString()); row2.createCell(4).setCellValue("总金额");row2.createCell(5).setCellValue( new BigDecimal(r.get("banTotalPrice").toString()).add(new BigDecimal(r.get("yanTotalPrice").toString())).toString()); BigDecimal c = calcGrowthRate(r.get("banTotalPrice"), r2.get("banTotalPrice")); row2.createCell(6).setCellValue("办理金额同比增长率:");row2.createCell(7).setCellValue(df.format(c)); Row row3 = sheet.createRow(rowNum++); row3.createCell(0).setCellValue("中医学");row3.createCell(1).setCellValue(state1Counts.getOrDefault("中医学", 0).toString()); row3.createCell(2).setCellValue("中医学");row3.createCell(3).setCellValue(state0Counts.getOrDefault("中医学", 0).toString()); BigDecimal d = calcGrowthRate(r.get("yanTotalPrice"), r2.get("yanTotalPrice")); row3.createCell(6).setCellValue("延期金额同比增长率:");row3.createCell(7).setCellValue(df.format(d)); Row row4 = sheet.createRow(rowNum++); row4.createCell(0).setCellValue("针灸学");row4.createCell(1).setCellValue(state1Counts.getOrDefault("针灸学", 0).toString()); row4.createCell(2).setCellValue("针灸学");row4.createCell(3).setCellValue(state0Counts.getOrDefault("针灸学", 0).toString()); Row row5 = sheet.createRow(rowNum++); row5.createCell(0).setCellValue("肿瘤学");row5.createCell(1).setCellValue(state1Counts.getOrDefault("肿瘤学", 0).toString()); row5.createCell(2).setCellValue("肿瘤学");row5.createCell(3).setCellValue(state0Counts.getOrDefault("肿瘤学", 0).toString()); Row row6 = sheet.createRow(rowNum++); row6.createCell(0).setCellValue("妇幼生殖学");row6.createCell(1).setCellValue(state1Counts.getOrDefault("妇幼生殖学",0).toString()); row6.createCell(2).setCellValue("妇幼生殖学");row6.createCell(3).setCellValue(state0Counts.getOrDefault("妇幼生殖学", 0).toString()); Row row7 = sheet.createRow(rowNum++); row7.createCell(0).setCellValue("国学");row7.createCell(1).setCellValue(state1Counts.getOrDefault("国学",0).toString()); row7.createCell(2).setCellValue("国学");row7.createCell(3).setCellValue(state0Counts.getOrDefault("国学", 0).toString()); Row row8 = sheet.createRow(rowNum++); row8.createCell(0).setCellValue("心理学");row8.createCell(1).setCellValue(state1Counts.getOrDefault("心理学", 0).toString()); row8.createCell(2).setCellValue("心理学");row8.createCell(3).setCellValue(state0Counts.getOrDefault("心理学", 0).toString()); Row row9 = sheet.createRow(rowNum++); row9.createCell(0).setCellValue("中西汇通学");row9.createCell(1).setCellValue(state1Counts.getOrDefault("中西汇通学", 0).toString()); row9.createCell(2).setCellValue("中西汇通学");row9.createCell(3).setCellValue(state0Counts.getOrDefault("中西汇通学", 0).toString()); sheet.createRow(rowNum++); String[] header2 = {"","首次办理三年","首次办理四年","其他","延期一年", "延期三年","延期四年","其他"}; Row row22 = sheet.createRow(rowNum++); for (int i = 0; i < header2.length; i++) { row22.createCell(i).setCellValue(header2[i]); } Map> banCounts = (Map>) r.get("banCounts"); Map> yanCounts = (Map>) r.get("yanCounts"); fillRow(sheet.createRow(rowNum++), 0, "医学超级", banCounts, yanCounts); fillRow(sheet.createRow(rowNum++), 0, "国学心理学超级", banCounts, yanCounts); fillRow(sheet.createRow(rowNum++), 0, "中医学", banCounts, yanCounts); fillRow(sheet.createRow(rowNum++), 0, "针灸学", banCounts, yanCounts); fillRow(sheet.createRow(rowNum++), 0, "肿瘤学", banCounts, yanCounts); fillRow(sheet.createRow(rowNum++), 0, "妇幼生殖学", banCounts, yanCounts); fillRow(sheet.createRow(rowNum++), 0, "国学", banCounts, yanCounts); fillRow(sheet.createRow(rowNum++), 0, "心理学", banCounts, yanCounts); fillRow(sheet.createRow(rowNum++), 0, "中西汇通学", banCounts, yanCounts); sheet.createRow(rowNum++); Row rateRow = sheet.createRow(rowNum++); rateRow.createCell(0).setCellValue("分类");rateRow.createCell(1).setCellValue("年度办理占比"); Map banTypeRatios = (Map) r.get("banTypeRatios"); sheet.createRow(rowNum++).createCell(0).setCellValue("医学超级");sheet.getRow(rowNum-1).createCell(1).setCellValue(banTypeRatios.getOrDefault("医学超级",0).toString()); sheet.createRow(rowNum++).createCell(0).setCellValue("国学心理学超级");sheet.getRow(rowNum-1).createCell(1).setCellValue(banTypeRatios.getOrDefault("国学心理学超级",0).toString()); sheet.createRow(rowNum++).createCell(0).setCellValue("中医学");sheet.getRow(rowNum-1).createCell(1).setCellValue(banTypeRatios.getOrDefault("中医学",0).toString()); sheet.createRow(rowNum++).createCell(0).setCellValue("针灸学");sheet.getRow(rowNum-1).createCell(1).setCellValue(banTypeRatios.getOrDefault("针灸学",0).toString()); sheet.createRow(rowNum++).createCell(0).setCellValue("肿瘤学");sheet.getRow(rowNum-1).createCell(1).setCellValue(banTypeRatios.getOrDefault("肿瘤学",0).toString()); sheet.createRow(rowNum++).createCell(0).setCellValue("妇幼生殖学");sheet.getRow(rowNum-1).createCell(1).setCellValue(banTypeRatios.getOrDefault("妇幼生殖学",0).toString()); sheet.createRow(rowNum++).createCell(0).setCellValue("国学");sheet.getRow(rowNum-1).createCell(1).setCellValue(banTypeRatios.getOrDefault("国学",0).toString()); sheet.createRow(rowNum++).createCell(0).setCellValue("心理学");sheet.getRow(rowNum-1).createCell(1).setCellValue(banTypeRatios.getOrDefault("心理学",0).toString()); sheet.createRow(rowNum++).createCell(0).setCellValue("中西汇通学");sheet.getRow(rowNum-1).createCell(1).setCellValue(banTypeRatios.getOrDefault("中西汇通学",0).toString()); sheet.createRow(rowNum++); String[] header3 = {"办理时间","办理人数","延期人数","缴费金额"}; Row row33 = sheet.createRow(rowNum++); for (int i = 0; i < header3.length; i++) { row33.createCell(i).setCellValue(header3[i]); } for (int i = 1; i < 13; i++) { Row row = sheet.createRow(rowNum++); row.createCell(0).setCellValue(i); row.createCell(1).setCellValue(((Map)r.get("banMonthCounts")).get(i).toString()); row.createCell(2).setCellValue(((Map)r.get("yanMonthCounts")).get(i).toString()); row.createCell(3).setCellValue(((Map)r.get("monthAmounts")).get(i).toString()); } export(wb,null,fileName,++rowNum,response,null,null, null); } } private void fillRow(Row row, int startCol, String vipType, Map> banCounts, Map> yanCounts) { row.createCell(startCol).setCellValue(vipType); Map banMap = banCounts.getOrDefault(vipType, new HashMap<>()); Map yanMap = yanCounts.getOrDefault(vipType, new HashMap<>()); row.createCell(startCol + 1).setCellValue(banMap.getOrDefault("3", "0").toString()); row.createCell(startCol + 2).setCellValue(banMap.getOrDefault("4", "0").toString()); row.createCell(startCol + 3).setCellValue(banMap.getOrDefault("其他", "0").toString()); row.createCell(startCol + 4).setCellValue(yanMap.getOrDefault("1", "0").toString()); row.createCell(startCol + 5).setCellValue(yanMap.getOrDefault("3", "0").toString()); row.createCell(startCol + 6).setCellValue(yanMap.getOrDefault("4", "0").toString()); row.createCell(startCol + 7).setCellValue(yanMap.getOrDefault("其他", "0").toString()); } @RequestMapping("/getUserVipByState") public R getUserVipByState() { CountResult state0Result = countInfo(userVipService.getUserVipByState(0)); CountResult state1Result = countInfo(userVipService.getUserVipByState(1)); // 合并结果 List> allResultList = new ArrayList<>(); allResultList.addAll(state1Result.resultList); allResultList.addAll(state0Result.resultList); return R.ok() .put("resultList", allResultList) .put("state0Total", state0Result.total.size()) .put("state1Total", state1Result.total.size()) .put("state0Counts", state0Result.counts) .put("state1Counts", state1Result.counts); } //导出全部汇总 @RequestMapping("/exportUserVipByState") public void exportUserVipByState(HttpServletResponse response) { R r = getUserVipByState(); if (r.get("code").toString().equals("0")) { String fileName = "全部VIP汇总"; String[] headers = {"办理时间","姓名","注册电话","VIP类型", "最近一次年限","最近一次缴费金额","到期时间"}; String[] cellValues = {"time","name","tel","vipType", "year","price","endTime"}; XSSFWorkbook wb = new XSSFWorkbook(); Sheet sheet = wb.createSheet(fileName); int rowNum = 0; Row row0 = sheet.createRow(rowNum++); row0.createCell(1).setCellValue("到期总人数");row0.createCell(2).setCellValue(r.get("state1Total").toString()); row0.createCell(4).setCellValue("在期总人数");row0.createCell(5).setCellValue(r.get("state0Total").toString()); Row row1 = sheet.createRow(rowNum++); Map state1Counts = (Map) r.get("state1Counts"); Map state0Counts = (Map) r.get("state0Counts"); row1.createCell(1).setCellValue("医学超级");row1.createCell(2).setCellValue(state1Counts.get("yxSuperCount").toString()); row1.createCell(4).setCellValue("医学超级");row1.createCell(5).setCellValue(state0Counts.get("yxSuperCount").toString()); Row row2 = sheet.createRow(rowNum++); row2.createCell(1).setCellValue("国学心理超级");row2.createCell(2).setCellValue(state1Counts.get("gxSuperCount").toString()); row2.createCell(4).setCellValue("国学心理超级");row2.createCell(5).setCellValue(state0Counts.get("gxSuperCount").toString()); Row row3 = sheet.createRow(rowNum++); row3.createCell(1).setCellValue("中医学");row3.createCell(2).setCellValue(state1Counts.get("zyCount").toString()); row3.createCell(4).setCellValue("中医学");row3.createCell(5).setCellValue(state0Counts.get("zyCount").toString()); Row row4 = sheet.createRow(rowNum++); row4.createCell(1).setCellValue("针灸学");row4.createCell(2).setCellValue(state1Counts.get("zjCount").toString()); row4.createCell(4).setCellValue("针灸学");row4.createCell(5).setCellValue(state0Counts.get("zjCount").toString()); Row row5 = sheet.createRow(rowNum++); row5.createCell(1).setCellValue("肿瘤学");row5.createCell(2).setCellValue(state1Counts.get("zlCount").toString()); row5.createCell(4).setCellValue("肿瘤学");row5.createCell(5).setCellValue(state0Counts.get("zlCount").toString()); Row row6 = sheet.createRow(rowNum++); row6.createCell(1).setCellValue("妇幼生殖学");row6.createCell(2).setCellValue(state1Counts.get("fyszCount").toString()); row6.createCell(4).setCellValue("妇幼生殖学");row6.createCell(5).setCellValue(state0Counts.get("fyszCount").toString()); Row row7 = sheet.createRow(rowNum++); row7.createCell(1).setCellValue("国学");row7.createCell(2).setCellValue(state1Counts.get("gxCount").toString()); row7.createCell(4).setCellValue("国学");row7.createCell(5).setCellValue(state0Counts.get("gxCount").toString()); Row row8 = sheet.createRow(rowNum++); row8.createCell(1).setCellValue("心理学");row8.createCell(2).setCellValue(state1Counts.get("xlCount").toString()); row8.createCell(4).setCellValue("心理学");row8.createCell(5).setCellValue(state0Counts.get("xlCount").toString()); Row row9 = sheet.createRow(rowNum++); row9.createCell(1).setCellValue("中西汇通学");row9.createCell(2).setCellValue(state1Counts.get("zxhtCount").toString()); row9.createCell(4).setCellValue("中西汇通学");row9.createCell(5).setCellValue(state0Counts.get("zxhtCount").toString()); export(wb,sheet,fileName,++rowNum,response,headers,cellValues, (List>) r.get("resultList")); } } //导出近3个月临到期 @RequestMapping("/getExpiresByMonth") public R getExpiresByMonth(@RequestBody Map params) { int month = Integer.parseInt(params.get("month").toString()); CountResult result = countInfo(userVipService.getUserVipMerge(month)); return R.ok() .put("total", result.total.size()) .put("resultList", result.resultList) .put("count", result.counts); } //导出近3个月临到期 @RequestMapping("/exportExpiresByMonth") public void exportCourseSaleInfoByCourseLabel(HttpServletResponse response,@RequestBody Map params) { R r = getExpiresByMonth(params); if (r.get("code").toString().equals("0")) { String fileName = "近"+params.get("month").toString()+"个月临到期("+r.get("total")+")"; String[] headers = {"办理时间","姓名","注册电话","VIP类型", "最近一次年限","最近一次缴费金额","到期时间"}; String[] cellValues = {"time","name","tel","vipType", "year","price","endTime"}; XSSFWorkbook wb = new XSSFWorkbook(); Sheet sheet = wb.createSheet(fileName); int rowNum = 0; export(wb,sheet,fileName,rowNum,response,headers,cellValues, (List>) r.get("resultList")); } } // 类型映射表 private static final Map TYPE_NAME_MAP = Map.of( "4","中医学", "5","针灸学", "6","肿瘤学", "7","国学", "8","心理学", "9","中西汇通学", "10","妇幼生殖学" ); // 封装统计结果 private static class CountResult { List> resultList = new ArrayList<>(); List total = new ArrayList<>(); Map counts = new HashMap<>(); } private static final String MEDICAL_SUPER_CUTOFF = "2026-05"; private CountResult countInfo(List> userVips) { return countInfo(userVips, null); } private CountResult countInfo(List> userVips, String date) { CountResult result = new CountResult(); // 初始化计数器 result.counts.put("yxSuperCount",0); result.counts.put("gxSuperCount",0); result.counts.put("zyCount",0); result.counts.put("zjCount",0); result.counts.put("zlCount",0); result.counts.put("gxCount",0); result.counts.put("xlCount",0); result.counts.put("zxhtCount",0); result.counts.put("fyszCount",0); for (Map map : userVips) { String recordMonth = resolveRecordMonth(map, date); List medicalSuperTypes = getMedicalSuperTypes(recordMonth); String[] types = map.get("type").toString().split(","); Set typeSet = new HashSet<>(Arrays.asList(types)); int yxFlag = 0; int gxFlag = 0; // 超级类型 if (typeSet.containsAll(medicalSuperTypes)) { yxFlag = 1; Map copy = new HashMap<>(map); copy.put("vipType","医学超级"); copy.put("price",Math.round(Double.parseDouble(map.get("price").toString())*medicalSuperTypes.size())); result.resultList.add(copy); result.counts.computeIfPresent("yxSuperCount",(k,v)->v+1); result.total.add(map.get("tel").toString()); } if (typeSet.containsAll(Arrays.asList("7","8"))) { gxFlag = 1; Map copy = new HashMap<>(map); copy.put("vipType","国学心理学超级"); copy.put("price",Math.round(Double.parseDouble(map.get("price").toString())*2)); result.resultList.add(copy); result.counts.computeIfPresent("gxSuperCount",(k,v)->v+1); result.total.add(map.get("tel").toString()); } // 普通类型 for (String type : types) { Map copy = new HashMap<>(map); if ("4".equals(type) && yxFlag==0) { copy.put("vipType","中医学"); result.counts.computeIfPresent("zyCount",(k,v)->v+1); } else if ("5".equals(type) && yxFlag==0) { copy.put("vipType","针灸学"); result.counts.computeIfPresent("zjCount",(k,v)->v+1); } else if ("6".equals(type) && yxFlag==0) { copy.put("vipType","肿瘤学"); result.counts.computeIfPresent("zlCount",(k,v)->v+1); } else if ("7".equals(type) && gxFlag==0) { copy.put("vipType","国学"); result.counts.computeIfPresent("gxCount",(k,v)->v+1); } else if ("8".equals(type) && gxFlag==0) { copy.put("vipType","心理学"); result.counts.computeIfPresent("xlCount",(k,v)->v+1); } else if ("9".equals(type) && yxFlag==0) { copy.put("vipType","中西汇通学"); result.counts.computeIfPresent("zxhtCount",(k,v)->v+1); } else if ("10".equals(type) && yxFlag==0 && isFyszEnabled(recordMonth)) { copy.put("vipType","妇幼生殖学"); result.counts.computeIfPresent("fyszCount",(k,v)->v+1); } if (copy.get("vipType") != null) { result.resultList.add(copy); result.total.add(map.get("tel").toString()); } } } return result; } private String resolveRecordMonth(Map map, String date) { if (date != null) { return date; } Object payTime = map.get("payTime"); if (payTime != null) { String payTimeStr = payTime.toString(); return payTimeStr.length() >= 7 ? payTimeStr.substring(0, 7) : payTimeStr; } return MEDICAL_SUPER_CUTOFF; } private List getMedicalSuperTypes(String recordMonth) { if (recordMonth.compareTo(MEDICAL_SUPER_CUTOFF) < 0) { return Arrays.asList("4", "5", "6", "9"); } return Arrays.asList("4", "5", "6", "9", "10"); } private boolean isFyszEnabled(String recordMonth) { return recordMonth.compareTo(MEDICAL_SUPER_CUTOFF) >= 0; } private Map> buildReBuyRules(String date) { Map> reBuyRules = new HashMap<>(); if (date.compareTo(MEDICAL_SUPER_CUTOFF) < 0) { reBuyRules.put("医学超级", Arrays.asList(4, 5, 6, 9)); } else { reBuyRules.put("医学超级", Arrays.asList(4, 5, 6, 9, 10)); reBuyRules.put("妇幼生殖学", Arrays.asList(10)); } reBuyRules.put("国学心理学超级", Arrays.asList(7, 8)); reBuyRules.put("中医学", Arrays.asList(4)); reBuyRules.put("针灸学", Arrays.asList(5)); reBuyRules.put("肿瘤学", Arrays.asList(6)); reBuyRules.put("国学", Arrays.asList(7)); reBuyRules.put("心理学", Arrays.asList(8)); reBuyRules.put("中西汇通学", Arrays.asList(9)); return reBuyRules; } private BigDecimal calcGrowthRate(Object current, Object previous) { BigDecimal currentVal = new BigDecimal(current.toString()); if (currentVal.compareTo(BigDecimal.ZERO) == 0) { return BigDecimal.ZERO; } BigDecimal previousVal = new BigDecimal(previous.toString()); return currentVal.subtract(previousVal).divide(currentVal, 4, RoundingMode.HALF_UP); } private void export(XSSFWorkbook wb,Sheet sheet,String fileName,int rowNum,HttpServletResponse response, String[] headers, String[] cellValues, List> list){ if (sheet!=null){ // 表头 Row titleRow = sheet.createRow(rowNum); for (int i = 0; i < headers.length; i++) { titleRow.createCell(i).setCellValue(headers[i]); } for (Map map : list) { Row row = sheet.createRow(++rowNum); for (int i = 0; i < cellValues.length; i++) { row.createCell(i).setCellValue(map.get(cellValues[i]).toString()); } } } // 输出 OutputStream outputStream =null; try { //文件名编码格式 fileName = URLEncoder.encode(fileName,"UTF-8"); //设置ContentType请求信息格式 response.setContentType("application/vnd.ms-excel"); //设置标头 response.setHeader("Content-disposition", "attachment;filename=" + fileName+".xlsx"); outputStream = response.getOutputStream(); wb.write(outputStream); } catch (IOException e) { e.printStackTrace(); }finally { try { outputStream.flush(); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }