package com.example.demo.serviceImpl; import com.example.demo.domain.entity.Statistics; import com.example.demo.domain.vo.WorkbenchCard; import com.example.demo.domain.vo.WorkbenchFullStatistics; import com.example.demo.domain.vo.WorkbenchMarketCard; import com.example.demo.domain.vo.WorkbenchMarketGraph; import com.example.demo.mapper.StatisticsMapper; import com.example.demo.mapper.WorkBenchMapper; import com.example.demo.service.GeneralService; import com.example.demo.service.StatisticsService; import com.example.demo.service.WorkbenchService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.time.LocalDate; import java.time.DayOfWeek; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; /** * @program: gold-java * @ClassName WorkbenchServiceImpl * @description: * @author: Ethan * @create: 2025−06-18 10:47 * @Version 1.0 **/ @Service public class WorkbenchServiceImpl implements WorkbenchService { private static final Logger log = LoggerFactory.getLogger(GeneralServiceImpl.class); private final RedisTemplate redisTemplate; @Autowired private WorkBenchMapper workBenchMapper; @Autowired private StatisticsMapper statisticsMapper; @Autowired public WorkbenchServiceImpl(RedisTemplate redisTemplate, StatisticsMapper statisticsMapper) { this.redisTemplate = redisTemplate; this.statisticsMapper = statisticsMapper; } private static final String CACHE_KEY = "workbench_card_cache"; @Override public List getAdminMarket(String account) { try { String market = workBenchMapper.getAdminMarket(account); if (market == null) { throw new Exception("没有地区权限"); } List list = Arrays.asList(market.split(",")); //判断是否是总部 if (list != null && list.contains("总部")) { List allMarkets = workBenchMapper.getMarket(); // 获取所有地区 allMarkets.remove("总部"); // 先移除可能存在的总部 allMarkets.add(0, "总部"); // 将总部添加到列表第一位 list = allMarkets; // 更新list } return list; } catch (Exception e) { // 记录日志 log.error("获取地区权限失败", e); // 重新抛出异常,或者根据需要返回一个默认值或空列表 throw new RuntimeException("获取地区权限失败", e); } } @Override public WorkbenchCard getCard( List markets) { Date date=new Date(); // 获取开始时间和结束时间(当天) LocalDateTime startOfDay = LocalDateTime.now().with(LocalTime.MIN); LocalDateTime endOfDay = startOfDay.plusDays(1).minusSeconds(1); // 获取开始时间和结束时间(昨天) LocalDateTime startOfYday = startOfDay.minusDays(1); LocalDateTime endOfYday = endOfDay.minusDays(1); // 批量获取统计数据 //当天的统计数据 List currentStatsList = statisticsMapper.selectByMarketsAndDate(markets, Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant()), Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant())); //昨天的统计数据 List ydayStatsList = statisticsMapper.selectByMarketsAndDate(markets, Date.from(startOfYday.atZone(ZoneId.systemDefault()).toInstant()), Date.from(endOfYday.atZone(ZoneId.systemDefault()).toInstant())); // 将 List 转换为 Map,以market为键,保留第一个出现的数据 Map currentStatsMap = currentStatsList.stream() .collect(Collectors.toMap(Statistics::getMarket, Function.identity(), (existing, replacement) -> existing)); //转换昨日统计数据为Map<> Map ydayStatsMap = ydayStatsList.stream() .collect(Collectors.toMap(Statistics::getMarket, Function.identity(), (existing, replacement) -> existing)); // 并行处理市场列表,创建市场卡片列表 List marketCards = markets.parallelStream() .filter(Objects::nonNull) .map(market -> createWorkbenchMarketCard( market, currentStatsMap.getOrDefault(market, null), ydayStatsMap.getOrDefault(market, null), new Date())) .collect(Collectors.toList()); Integer sumWow= calculateAllWeekOverWeek(date,markets); Integer sumDaily=calculateAllDayOverDay(date, markets); Date updateTime = findLatestUpdateTime(currentStatsList); return new WorkbenchCard(marketCards, new ArrayList<>(), markets, new Date(), new Date(),sumWow,sumDaily,updateTime); } @Override public WorkbenchCard getCardCache(List markets) { //从缓存中获取工作台数据 WorkbenchCard cached = redisTemplate.opsForValue().get(CACHE_KEY); //有缓存直接返回缓存数据 if (cached != null) { System.out.println("读取缓存数据: " + new Date()); return cached; } //没缓存获取新数据并存入缓存 try { //获取新的工作台数据 WorkbenchCard freshData = getCard(markets); //存入缓存,一小时后过期(与统计表更新时间对应) redisTemplate.opsForValue().set(CACHE_KEY, freshData, 1, java.util.concurrent.TimeUnit.HOURS); System.out.println("刷新缓存并存储新数据: " + new Date()); return freshData; } catch (Exception e) { System.err.println("查询数据库失败,尝试使用旧缓存数据或抛出异常:" + e.getMessage()); throw e; // 或者你可以选择返回上次的缓存数据(如果有) } } /* 获取卡片数据 */ @Override public WorkbenchMarketCard createWorkbenchMarketCard(String market,Statistics currentStatistics, Statistics ydayStatistics, Date currentDate) { WorkbenchMarketCard card = new WorkbenchMarketCard(); card.setMarket(market); if (currentStatistics != null&& ydayStatistics != null) { // 卡片一:当前金币相关 card.setCurrentPermanent(currentStatistics.getCurrentPermanent());//余量-永久金币 card.setCurrentFreeJune(currentStatistics.getCurrentFreeJune()); //余量-免费六月金币 card.setCurrentFreeDecember(currentStatistics.getCurrentFreeDecember()); //余量-免费十二月金币 card.setCurrentTask(currentStatistics.getCurrentTask()); //余量-任务金币 card.setCurrentFree(card.getCurrentFreeJune() + card.getCurrentFreeDecember()); //余量-免费金币 card.setCurrentGold(card.getCurrentPermanent() + card.getCurrentFree() + card.getCurrentTask()); //余量-总金币 card.setDailyChange(currentStatistics.getDailyChange()); //较前一日变化 // 卡片二:充值相关 card.setRecharge(ydayStatistics.getRecharge()); //充值-昨日充值 card.setMoney(ydayStatistics.getMoney()); //充值-昨日金额(永久) card.setYearlyRecharge(currentStatistics.getYearlyRecharge()); // 充值-全年累计充值 card.setYearlyMoney(currentStatistics.getYearlyMoney()); // 充值-全年累计金额(永久)//充值-全年累计金额(永久) // 卡片三:消费与退款 card.setConsumePermanent(ydayStatistics.getConsumePermanent());//昨日消费-永久金币 card.setConsumeFreeJune(ydayStatistics.getConsumeFreeJune());//昨日消费-免费六月金币 card.setConsumeFreeDecember(ydayStatistics.getConsumeFreeDecember());//昨日消费-免费十二月金币 card.setConsumeTask(ydayStatistics.getConsumeTask());//昨日消费-任务金币 card.setRefundPermanent(ydayStatistics.getRefundPermanent());//昨日退款-永久金币 card.setRefundFreeJune(ydayStatistics.getRefundFreeJune());//昨日退款-免费六月金币 card.setRefundFreeDecember(ydayStatistics.getRefundFreeDecember());//昨日退款-免费十二月金币 card.setRefundTask(ydayStatistics.getRefundTask());//昨日退款-任务金币 //昨日总消费 int totalConsume = card.getConsumePermanent() + card.getConsumeFreeJune() + card.getConsumeFreeDecember() + card.getConsumeTask(); //昨日总退款 int totalRefund = card.getRefundPermanent() + card.getRefundFreeJune() + card.getRefundFreeDecember() + card.getRefundTask(); card.setDailyReduce(totalConsume - totalRefund);//昨日总消耗 card.setYearlyConsume(currentStatistics.getYearlyConsume()); // 年累计消费 card.setYearlyRefund(currentStatistics.getYearlyRefund()); // 年累计退款 card.setYearlyReduce(card.getYearlyConsume() - card.getYearlyRefund());//年累计消耗 // 卡片四:人头数相关 card.setRechargeNum(currentStatistics.getRechargeNum());//当天充值人数 card.setYdayRechargeNum(ydayStatistics.getRechargeNum()); //昨日充值人数 card.setFirstRecharge(ydayStatistics.getFirstRecharge()); //昨日充值人数中首充的数量 card.setYearlyRechargeNum(ydayStatistics.getYearlyRechargeNum()); //年累计充值人数-至昨天 // 周环比、日同比 card.setWow(calculateWeekOverWeek(market, currentDate)); card.setDaily(calculateDayOverDay(market, currentDate)); //更新时间 card.setUpdateTime(currentStatistics.getUpdateTime()); } return card; } @Override public WorkbenchCard getGraph( Date startDate, Date endDate, List markets) { if (markets == null || markets.isEmpty()) { return new WorkbenchCard(new ArrayList<>(), new ArrayList<>(), markets, startDate, endDate,0,0,new Date()); } // 单次批量查询 List statsList = workBenchMapper.getFullStatisticsByMarketAndDate1(markets, startDate, endDate); // 构建 map: market -> statistics Map statMap = statsList.stream() .collect(Collectors.toMap(WorkbenchFullStatistics::getMarket, Function.identity())); // 构建最终结果 List marketGraphs = new ArrayList<>(); for (String market : markets) { WorkbenchFullStatistics stats = statMap.getOrDefault(market, new WorkbenchFullStatistics()); Map sums = new HashMap<>(); sums.put("recharge", stats.getTotalRecharge() != null ? stats.getTotalRecharge() : 0); sums.put("money", stats.getTotalMoney() != null ? stats.getTotalMoney() : 0); sums.put("rFree", sums.get("recharge") - sums.get("money")); sums.put("cPermanent", stats.getTotalConsumePermanent() != null ? stats.getTotalConsumePermanent() : 0); sums.put("cFree", stats.getTotalConsumeFree() != null ? stats.getTotalConsumeFree() : 0); sums.put("cTask", stats.getTotalConsumeTask() != null ? stats.getTotalConsumeTask() : 0); sums.put("consume", sums.get("cPermanent") + sums.get("cFree") + sums.get("cTask")); WorkbenchMarketGraph graph = new WorkbenchMarketGraph(); graph.setMarket(market); graph.setSumRechargePermanent(sums.get("money")); graph.setSumRechargeFree(sums.get("rFree")); graph.setSumConsumePermanent(sums.get("cPermanent")); graph.setSumConsumeFree(sums.get("cFree")); graph.setSumConsumeTask(sums.get("cTask")); graph.setSumConsume(sums.get("consume")); marketGraphs.add(graph); } return new WorkbenchCard(new ArrayList<>(), marketGraphs, markets, startDate, endDate,0,0,new Date()); } /* 获取该日期该市场的日环比 */ @Override public Integer calculateDayOverDay(String market, Date date) { //传入日期的数据 LocalDateTime startTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MIN); LocalDateTime endTime= startTime.plusDays(1).minusSeconds(1); Statistics currentStatistics = statisticsMapper.selectByMarketAndDate(market, Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()), Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant())); //空数据或数量为零,为避免0除数错误,返回增长率为0 if (currentStatistics == null || currentStatistics.getRechargeNum() == null) { return 0; } Date yesterday = addDays(date, -1); //传入日期减一,昨天 Statistics yesterdayStatistics = statisticsMapper.selectByMarketAndDate(market, Date.from(yesterday.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant()), Date.from(yesterday.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant())); //空数据或数量为零,为避免0除数错误,返回增长率为0 if (yesterdayStatistics == null || yesterdayStatistics.getRechargeNum() == null || yesterdayStatistics.getRechargeNum() == 0) { return 0; } //计算增长率 double rate = ((double) (currentStatistics.getRechargeNum() - yesterdayStatistics.getRechargeNum()) / yesterdayStatistics.getRechargeNum()) * 100; return (int) Math.round(rate); } // 计算所有市场总体日环比 @Override public Integer calculateAllDayOverDay(Date date,List markets) { //获取今天的开始时间和结束时间 LocalDateTime startTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MIN); LocalDateTime endTime= startTime.plusDays(1).minusSeconds(1); //获取地区列表 // List markets = generalService.getMarket(); int currentTotal = 0; //今日所有地区总的充值人数 int yesterdayTotal = 0; //昨日所有地区总的充值人数 Date yesterday = addDays(date, -1); //过去昨天的开始时间和结束时间 LocalDateTime ydayStartTime = yesterday.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MIN); LocalDateTime ydayEndTime= ydayStartTime.plusDays(1).minusSeconds(1); for (String market : markets) { currentTotal += statisticsMapper.countRechargeNum(market, Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()), Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant())); yesterdayTotal += statisticsMapper.countRechargeNum(market, Date.from(ydayStartTime.atZone(ZoneId.systemDefault()).toInstant()), Date.from(ydayEndTime.atZone(ZoneId.systemDefault()).toInstant())); } if (yesterdayTotal == 0) return 0; // 避免除以零的情况 double rate = ((double)(currentTotal - yesterdayTotal) / yesterdayTotal) * 100; return (int)Math.round(rate); } /* 获取该日期该市场的周环比 */ @Override public Integer calculateWeekOverWeek(String market, Date date) { //获取传入日期所在周的周一 Date thisWeekStart = getStartOfWeek(date); //获取传入日期上一周的周一 Date lastWeekStart = addDays(thisWeekStart, -7); // 获取本周周一至今天的充值人数总和 int thisWeekTotal = statisticsMapper.countRechargeNum(market, thisWeekStart, date); // 获取上周同一时间段的充值人数总和 int lastWeekTotal = statisticsMapper.countRechargeNum(market, lastWeekStart, addDays(date, -7)); if (lastWeekTotal == 0) { return 0; // 避免除以零的情况 } double rate = ((double) (thisWeekTotal - lastWeekTotal) / lastWeekTotal) * 100; return (int) Math.round(rate); } /* 获取该天总体的的周环比 */ @Override public Integer calculateAllWeekOverWeek( Date date,List markets) { int thisWeekTotal = 0; //本周至当天充值人数 int lastWeekTotal = 0; //上周至当天充值人数 //获取本周周一 Date thisWeekStart = getStartOfWeek(date); //获取传入日期上一周的周一 Date lastWeekStart = addDays(thisWeekStart, -7); for (String market : markets) { // 获取本周周一至今天的充值人数总和 thisWeekTotal += statisticsMapper.countRechargeNum(market, thisWeekStart, date); // 获取上周同一时间段的充值人数总和 lastWeekTotal += statisticsMapper.countRechargeNum(market, lastWeekStart, addDays(date, -7)); } if (lastWeekTotal == 0) { return 0; // 避免除以零的情况 } double rate = ((double) (thisWeekTotal - lastWeekTotal) / lastWeekTotal) * 100; return (int) Math.round(rate); } @Override public Date addDays(Date date, int days) { Calendar cal = Calendar.getInstance(); cal.setTime(date); //设置日期为传入的日期 cal.add(Calendar.DATE, days); //传入日期加上多少天 return cal.getTime(); //返回处理后的日期 } @Override public Date getStartOfWeek(Date date) { // 将 Date 转换为 LocalDate LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); // 获取周一作为一周的第一天 DayOfWeek firstDayOfWeek = DayOfWeek.MONDAY; // 返回所在周的第一天,转换回Date格式 return Date.from(localDate.with(firstDayOfWeek) .atStartOfDay(ZoneId.systemDefault()) .toInstant()); } //获取最近的更新时间 private Date findLatestUpdateTime(List statsList) { // 使用流式处理来找到最新的 updateTime Optional latestUpdateTime = statsList.stream() .map(Statistics::getUpdateTime) .filter(Objects::nonNull) .max(Date::compareTo); return latestUpdateTime.orElse(new Date(0)); // 如果没有找到,则返回一个较早的时间点 } }