You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
19 KiB
378 lines
19 KiB
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<String, WorkbenchCard> redisTemplate;
|
|
|
|
@Autowired
|
|
private WorkBenchMapper workBenchMapper;
|
|
@Autowired
|
|
private StatisticsMapper statisticsMapper;
|
|
@Autowired
|
|
public WorkbenchServiceImpl(RedisTemplate<String, WorkbenchCard> redisTemplate, StatisticsMapper statisticsMapper) {
|
|
this.redisTemplate = redisTemplate;
|
|
this.statisticsMapper = statisticsMapper;
|
|
}
|
|
private static final String CACHE_KEY = "workbench_card_cache";
|
|
|
|
@Override
|
|
public List<String> getAdminMarket(String account) {
|
|
try {
|
|
String market = workBenchMapper.getAdminMarket(account);
|
|
if (market == null) {
|
|
throw new Exception("没有地区权限");
|
|
}
|
|
List<String> list = Arrays.asList(market.split(","));
|
|
//判断是否是总部
|
|
if (list != null && list.contains("总部")) {
|
|
List<String> 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<String> 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<Statistics> currentStatsList = statisticsMapper.selectByMarketsAndDate(markets,
|
|
Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant()),
|
|
Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant()));
|
|
//昨天的统计数据
|
|
List<Statistics> ydayStatsList = statisticsMapper.selectByMarketsAndDate(markets,
|
|
Date.from(startOfYday.atZone(ZoneId.systemDefault()).toInstant()),
|
|
Date.from(endOfYday.atZone(ZoneId.systemDefault()).toInstant()));
|
|
// 将 List<Statistics> 转换为 Map<String, Statistics>,以market为键,保留第一个出现的数据
|
|
Map<String, Statistics> currentStatsMap = currentStatsList.stream()
|
|
.collect(Collectors.toMap(Statistics::getMarket, Function.identity(), (existing, replacement) -> existing));
|
|
//转换昨日统计数据为Map<>
|
|
Map<String, Statistics> ydayStatsMap = ydayStatsList.stream()
|
|
.collect(Collectors.toMap(Statistics::getMarket, Function.identity(), (existing, replacement) -> existing));
|
|
|
|
// 并行处理市场列表,创建市场卡片列表
|
|
List<WorkbenchMarketCard> 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<String> 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<String> markets) {
|
|
if (markets == null || markets.isEmpty()) {
|
|
return new WorkbenchCard(new ArrayList<>(), new ArrayList<>(), markets, startDate, endDate,0,0,new Date());
|
|
}
|
|
|
|
// 单次批量查询
|
|
List<WorkbenchFullStatistics> statsList = workBenchMapper.getFullStatisticsByMarketAndDate1(markets, startDate, endDate);
|
|
|
|
// 构建 map: market -> statistics
|
|
Map<String, WorkbenchFullStatistics> statMap = statsList.stream()
|
|
.collect(Collectors.toMap(WorkbenchFullStatistics::getMarket, Function.identity()));
|
|
|
|
// 构建最终结果
|
|
List<WorkbenchMarketGraph> marketGraphs = new ArrayList<>();
|
|
for (String market : markets) {
|
|
WorkbenchFullStatistics stats = statMap.getOrDefault(market, new WorkbenchFullStatistics());
|
|
|
|
Map<String, Integer> 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<String> markets) {
|
|
//获取今天的开始时间和结束时间
|
|
LocalDateTime startTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime().with(LocalTime.MIN);
|
|
LocalDateTime endTime= startTime.plusDays(1).minusSeconds(1);
|
|
//获取地区列表
|
|
// List<String> 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<String> 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<Statistics> statsList) {
|
|
// 使用流式处理来找到最新的 updateTime
|
|
Optional<Date> latestUpdateTime = statsList.stream()
|
|
.map(Statistics::getUpdateTime)
|
|
.filter(Objects::nonNull)
|
|
.max(Date::compareTo);
|
|
|
|
return latestUpdateTime.orElse(new Date(0)); // 如果没有找到,则返回一个较早的时间点
|
|
}
|
|
|
|
|
|
}
|