package com.idiot.operationbackend.controller; import com.idiot.operationbackend.entity.Account; import com.idiot.operationbackend.entity.AccountStat; import com.idiot.operationbackend.entity.ArticleStat; import com.idiot.operationbackend.entity.FansActionStat; import com.idiot.operationbackend.service.facade.*; import com.idiot.operationbackend.support.Constants; import com.idiot.operationbackend.support.CustomException; import com.idiot.operationbackend.support.JsonResult; import com.idiot.operationbackend.util.JwtTokenUtil; import com.idiot.operationbackend.vo.ArticleStatData; import com.idiot.operationbackend.vo.GeneralStatData; import com.idiot.operationbackend.vo.NumberStatData; import com.idiot.operationbackend.vo.RemainStatData; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import java.math.BigDecimal; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; import static com.idiot.operationbackend.support.Constants.calcRate; /** * 运营星 首页 * @author wang xiao * @date Created in 10:55 2020/9/15 */ @RestController @RequestMapping("/index") @Api(value = "IndexController", tags ="首页") public class IndexController { private final Logger logger = LoggerFactory.getLogger(IndexController.class); private final String HOUR = "H"; private final String WEEK = "W"; private final String DAY = "D"; private final String MONTH = "M"; @Autowired private AccountService accountService; @Autowired private AccountStatService accountStatService; @Autowired private AccountFansService fansService; @Autowired private FansActionStatService actionStatService; @Autowired private ArticleStatService articleStatService; @GetMapping("/summary") @ApiOperation(value = "查询公众号昨日统计数据概览") public ResponseEntity> getFansStat (@RequestHeader String token) { String userId = JwtTokenUtil.getUserId(token); logger.info("用户:{}查询首页数据统计概览---start",userId); List accounts = accountService.queryAccountByUserId(userId); List accountIds = accounts.stream().map(Account::getId).collect(Collectors.toList()); // 查询数据 不知道怎么优化 String yesterdayStr = Constants.DATE_FORMATTER.format(LocalDate.now().plusDays(-1)); List ydStatData = accountStatService.queryByDateAndAccIds(accountIds,yesterdayStr); String beforeYesterdayStr = Constants.DATE_FORMATTER.format(LocalDate.now().plusDays(-2)); List beforeYdStat = accountStatService.queryByDateAndAccIds(accountIds,beforeYesterdayStr); logger.info("用户:{}首页概览数据计算中---ing",userId); // 所有公众号昨日数据 long totalNum = ydStatData.stream().mapToLong(AccountStat::getTotalFansNum).sum(); long addNum = ydStatData.stream().mapToLong(AccountStat::getAddNum).sum(); long cancelNum = ydStatData.stream().mapToLong(AccountStat::getCancelNum).sum(); long newNum = ydStatData.stream().mapToLong(AccountStat::getNewNum).sum(); long inactiveNum = ydStatData.stream().mapToLong(AccountStat::getInactiveNum).sum(); // 所有公众号前日数据 long bfCancelNum = beforeYdStat.stream().mapToLong(AccountStat::getCancelNum).sum(); long bfAddNum = beforeYdStat.stream().mapToLong(AccountStat::getAddNum).sum(); long bfNewNum = beforeYdStat.stream().mapToLong(AccountStat::getNewNum).sum(); long bfInactiveNum = beforeYdStat.stream().mapToLong(AccountStat::getInactiveNum).sum(); long bfTotalNum = beforeYdStat.stream().mapToLong(AccountStat::getTotalFansNum).sum(); AccountStat result = new AccountStat(); result.setAddNum(addNum); result.setNewNum(newNum); result.setCancelNum(cancelNum); result.setInactiveNum(inactiveNum); result.setTotalFansNum(totalNum); result.setAddRate(calcRate(addNum,bfAddNum)); result.setCancelRate(calcRate(cancelNum,bfCancelNum)); result.setNewRate(calcRate(newNum,bfNewNum)); result.setInactiveRate(calcRate(inactiveNum,bfInactiveNum)); result.setTotalFansRate(calcRate(totalNum,bfTotalNum)); logger.info("用户:{}查询首页数据统计概览---end",userId); return ResponseEntity.ok(JsonResult.success(result)); } @GetMapping("/single/{accountId}") @ApiOperation(value = "查询单个公众号昨日统计数据概览") public ResponseEntity> getSingleFansStat (@RequestHeader String token, @PathVariable String accountId) { String userId = JwtTokenUtil.getUserId(token); logger.info("用户:{}查询首页单个公众号:{}数据统计概览---start",userId,accountId); String yesterdayStr = Constants.DATE_FORMATTER.format(LocalDate.now().plusDays(-1)); AccountStat fansStat = accountStatService.queryByDateAndAccountId(accountId,yesterdayStr); logger.info("用户:{}查询首页单个公众号:{}数据统计概览---end",userId,accountId); return ResponseEntity.ok(JsonResult.success(fansStat)); } @GetMapping("/growth/{accountId}") @ApiOperation(value = "查询单个公众号--粉丝增长") public ResponseEntity>> getFansGrowthStat (@PathVariable String accountId, @RequestHeader String token, @RequestParam String type, @RequestParam String startDate, @RequestParam String endDate) { String userId = JwtTokenUtil.getUserId(token); if (!checkDate(startDate,endDate)){ throw new CustomException(500,"请选择开始和结束时间!"); } Map result = new HashMap<>(8); logger.info("用户:{}查询首页单个公众号:{}粉丝增长,type:{},startDate:{},endDate:{} --->start", userId,accountId,type,startDate,endDate); LocalDate start = LocalDate.parse(startDate); LocalDate end= LocalDate.parse(endDate); long days = end.toEpochDay() - start.toEpochDay(); List statDataList = statFansGrowthData(accountId, type, start, end); long newNum =0; long addNum =0; long inactiveNum =0; long cancelNum =0; BigDecimal ave =BigDecimal.ZERO; if (CollectionUtils.isEmpty(statDataList)) { newNum = statDataList.parallelStream().mapToLong(GeneralStatData::getNewNum).sum(); addNum = statDataList.parallelStream().mapToLong(GeneralStatData::getAddNum).sum(); inactiveNum = statDataList.parallelStream().mapToLong(GeneralStatData::getInactiveNum).sum(); cancelNum = statDataList.parallelStream().mapToLong(GeneralStatData::getCancelNum).sum(); if (days == 0){ days = 1; } ave = new BigDecimal(newNum/days).setScale(0); } result.put("newNum",newNum); result.put("addNum",addNum); result.put("inactiveNum",inactiveNum); result.put("cancelNum",cancelNum); result.put("aveNum",ave); result.put("tableData",statDataList); logger.info("用户:{}查询首页单个公众号:{}粉丝增长,type:{},startDate:{},endDate:{} --->end", userId,accountId,type,startDate,endDate); return ResponseEntity.ok(JsonResult.success(result)); } @GetMapping("/property/{accountId}") @ApiOperation(value = "查询单个公众号--粉丝属性") public ResponseEntity>> getFansPropertyStat( @RequestHeader String token, @PathVariable String accountId, @RequestParam(required = false) String startDate, @RequestParam(required = false) String endDate) { String userId = JwtTokenUtil.getUserId(token); logger.info("用户:{}查询首页单个公众号:{}粉丝属性,startDate:{},endDate:{} --->start",userId,accountId,startDate,endDate); Map result = new HashMap<>(4); List numberStatData = fansService.statByFansProperty(accountId,startDate,endDate); statFansProperty(numberStatData,result); logger.info("用户:{}查询首页单个公众号:{}粉丝属性,startDate:{},endDate:{} --->end",userId,accountId,startDate,endDate); return ResponseEntity.ok(JsonResult.success(result)); } @GetMapping("/inactive/{accountId}") @ApiOperation(value = "查询单个公众号--粉丝活跃度") public ResponseEntity>> getFansInactiveStat(@RequestHeader String token, @PathVariable String accountId, @RequestParam String startDate, @RequestParam String endDate) { String userId = JwtTokenUtil.getUserId(token); if (!checkDate(startDate,endDate)){ throw new CustomException(500,"请选择查询时间范围!"); } logger.info("用户:{}查询首页单个公众号:{}粉丝活跃度,startDate:{},endDate:{} --->start",userId,accountId,startDate,endDate); List accountStats = accountStatService.queryAccountStatByDate(accountId,startDate,endDate); logger.info("用户:{}查询首页单个公众号:{}粉丝活跃度,startDate:{},endDate:{} --->end",userId,accountId,startDate,endDate); return ResponseEntity.ok(JsonResult.success(accountStats)); } @GetMapping("/remain/{accountId}") @ApiOperation(value = "查询单个公众号--粉丝忠诚度") public ResponseEntity>> getFansRemainStat(@RequestHeader String token, @PathVariable String accountId, @RequestParam String date) { String userId = JwtTokenUtil.getUserId(token); if (StringUtils.isEmpty(date)){ throw new CustomException(500,"请选择查询时间!"); } LocalDateTime endDate = Constants.toLocalDateTime(date); LocalDateTime startDate = Constants.toLocalDateTime(date).plusDays(-7); logger.info("用户:{}查询首页单个公众号:{}粉丝忠诚度,date:{} --->start",userId,accountId,date); List accountStats = accountStatService.queryAccountStatByDate(accountId,startDate,endDate); List remainStatData = statFansRemainData(accountStats); if (Objects.isNull(remainStatData)) { remainStatData = new ArrayList<>(); } logger.info("用户:{}查询首页单个公众号:{}粉丝忠诚度,date:{} --->end",userId,accountId,date); return ResponseEntity.ok(JsonResult.success(remainStatData)); } @GetMapping("/articles/{accountId}") @ApiOperation(value = "查询单个公众号--图文影响力") public ResponseEntity>> getFansPageStat(@RequestHeader String token, @PathVariable String accountId, @RequestParam String startDate, @RequestParam String endDate) { String userId = JwtTokenUtil.getUserId(token); if (!checkDate(startDate,endDate)){ throw new CustomException(500,"请选择查询时间!"); } logger.info("用户:{}查询首页单个公众号:{}图文影响力,startDate:{},endDate:{} --->start",userId,accountId,startDate,endDate); List articleStats = articleStatService.queryArticleStatByDate(accountId,startDate,endDate); List articleStatData = statArticleData(articleStats); if (CollectionUtils.isEmpty(articleStatData)){ articleStatData = new ArrayList<>(); } logger.info("用户:{}查询首页单个公众号:{}图文影响力,startDate:{},endDate:{} --->start",userId,accountId,startDate,endDate); return ResponseEntity.ok(JsonResult.success(articleStatData)); } /** * 统计粉丝数据 好难受写不出完美的代码 * @author wangxiao * @date 16:00 2020/9/16 * @param accountId * @param type * @param startDate * @param endDate * @return java.util.List */ private List statFansGrowthData(String accountId, String type, LocalDate startDate, LocalDate endDate) { // 粉丝增长数据 因为能选择今天 需要在 用户动作分析里面取数据。或者一小时分析一次数据 long disValue = 0; if (HOUR.equals(type)) { disValue = 7200; }else if (DAY.equals(type)){ disValue = 86400; }else if (WEEK.equals(type)){ disValue = 604800; }else if (MONTH.equals(type)){ // 默认 30 天 disValue = 2592000; }else { throw new CustomException(500,"请你选择正确的时间统计类型!"); } LocalDateTime startLocalDate = startDate.atTime(Constants.DEFAULT_TIME); LocalDateTime endLocalDate = endDate.plusDays(1).atTime(Constants.DEFAULT_TIME); List fansActionStats = actionStatService.queryFansActionStat(accountId,startLocalDate,endLocalDate); if (CollectionUtils.isEmpty(fansActionStats)) { throw new CustomException(500,"暂无数据!"); } long start = startLocalDate.toEpochSecond(Constants.DEFAULT_ZONE); long end = endLocalDate.toEpochSecond(Constants.DEFAULT_ZONE); List statDataList = new ArrayList<>(); GeneralStatData statData = null; List tempStatList = null; while (start<=end){ long finalStart = start; long finalDisValue = disValue; tempStatList = fansActionStats.stream() .filter(e->e.getCreateTime() >= finalStart && e.getCreateTime()<= finalStart + finalDisValue) .sorted(Comparator.comparingLong(FansActionStat::getCreateTime)) .collect(Collectors.toList()); statData = countStatData(tempStatList,finalStart,finalStart+finalDisValue,type); statDataList.add(statData); start += finalDisValue; } return statDataList; } /** * 校验日期 * @author wangxiao * @date 11:58 2020/9/17 * @param start * @param end * @return boolean */ private boolean checkDate (String start, String end) { return !(StringUtils.isEmpty(start) | StringUtils.isEmpty(end)); } /** * 统计 粉丝增长 时间段 * @author wangxiao * @date 16:45 2020/9/16 * @param fansActionStats * @param start * @param end * @param type * @return com.idiot.operationbackend.vo.StatData */ private GeneralStatData countStatData (List fansActionStats, long start, long end, String type) { LocalDateTime startDateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(start),Constants.DEFAULT_ZONE); LocalDateTime endDateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(end),Constants.DEFAULT_ZONE); GeneralStatData statData = new GeneralStatData(); long newNum = 0; long cancelNum = 0; long inactiveNum = 0; if (!CollectionUtils.isEmpty(fansActionStats)) { newNum = fansActionStats.parallelStream().filter(e->Constants.NEW == e.getAction()).count(); cancelNum = fansActionStats.parallelStream().filter(e->Constants.CANCEL == e.getAction()).count(); inactiveNum = fansActionStats.parallelStream().filter(e->Constants.NEW != e.getAction() && Constants.CANCEL != e.getAction()).count(); } String label = ""; if (HOUR.equals(type)) { label = String.format("%s %s:%s-%s:%s",startDateTime.toLocalDate(),startDateTime.getHour(),"00",endDateTime.getHour(),"00"); }else if (DAY.equals(type)){ label = String.format("%s",startDateTime.toLocalDate()); }else if (WEEK.equals(type)){ label = String.format("%s/%s-%s/%s",startDateTime.getMonthValue(),startDateTime.getDayOfMonth() ,endDateTime.getMonthValue(),endDateTime.getDayOfMonth()); }else if (MONTH.equals(type)){ label = String.format("%s-%s",startDateTime.getYear(),startDateTime.getMonthValue()); } statData.setDateLabel(label); statData.setAddNum(newNum-cancelNum); statData.setCancelNum(cancelNum); statData.setNewNum(newNum); statData.setInactiveNum(inactiveNum); return statData; } /** * 统计粉丝属性 * @author wangxiao * @date 18:57 2020/9/16 * @param fansData * @param map * @return void */ private void statFansProperty(List fansData,Map map) { if (!CollectionUtils.isEmpty(fansData)) { Map> numData = fansData.stream().collect(Collectors.groupingBy(NumberStatData::getType)); map.putAll(numData); } } /** * 统计留存粉丝 * @author wangxiao * @date 14:22 2020/9/17 * @param accountStats * @return java.util.List */ private List statFansRemainData(List accountStats) { if (CollectionUtils.isEmpty(accountStats)) { return null; } int size = accountStats.size(); List remainStatDataList = new ArrayList<>(size); String statDate = null; Long newNum = null; Long totalNum = null; RemainStatData remain = null; for (AccountStat temp : accountStats) { statDate = temp.getStatDate(); newNum = temp.getNewNum(); totalNum = temp.getTotalFansNum(); remain = new RemainStatData(statDate,newNum); remain.setDay1Num(calcRemain(accountStats,totalNum,statDate,1)); remain.setDay2Num(calcRemain(accountStats,totalNum,statDate,2)); remain.setDay3Num(calcRemain(accountStats,totalNum,statDate,3)); remain.setDay4Num(calcRemain(accountStats,totalNum,statDate,4)); remain.setDay5Num(calcRemain(accountStats,totalNum,statDate,5)); remain.setDay6Num(calcRemain(accountStats,totalNum,statDate,6)); remain.setDay7Num(calcRemain(accountStats,totalNum,statDate,7)); remainStatDataList.add(remain); } return remainStatDataList; } /** * 计算留存数 * @author wangxiao * @date 14:35 2020/9/17 * @param accountStats 数组 * @param nowTotal 当前总数 * @param targetIndex 几天前下表 * @param nowStatDate 当前统计日期 * @return long */ private long calcRemain (List accountStats,long nowTotal,String nowStatDate,int targetIndex) { long remainNum; LocalDate nowDate = LocalDate.parse(nowStatDate,Constants.DATE_FORMATTER); String targetStatDate = nowDate.plusDays(-targetIndex).format(Constants.DATE_FORMATTER); remainNum = accountStats.stream() .filter(e->targetStatDate.equals(e.getStatDate())) .findAny().map(e->e.getTotalFansNum()-nowTotal) .orElse(0L); return remainNum; } /** * 统计图文影响力 * @author wangxiao * @date 16:06 2020/9/17 * @param articleStats * @return java.util.List */ private List statArticleData (List articleStats) { if (CollectionUtils.isEmpty(articleStats)){ return null; } Map> temp = articleStats.stream().collect(Collectors.groupingBy(ArticleStat::getStatDate)); List articleStatData = new ArrayList<>(temp.size()); List var = null; ArticleStatData result = null; for (Map.Entry> entry : temp.entrySet()) { var = entry.getValue(); result = new ArticleStatData(entry.getKey(),var); result.setShareUser(var.stream().mapToLong(ArticleStat::getShareUser).sum()); result.setShareCount(var.stream().mapToLong(ArticleStat::getShareCount).sum()); result.setAddToFavCount(var.stream().mapToLong(ArticleStat::getAddToFavCount).sum()); result.setAddToFavUser(var.stream().mapToLong(ArticleStat::getAddToFavUser).sum()); result.setIntPageReadCount(var.stream().mapToLong(ArticleStat::getIntPageReadCount).sum()); result.setIntPageReadUser(var.stream().mapToLong(ArticleStat::getIntPageReadUser).sum()); result.setOriPageReadCount(var.stream().mapToLong(ArticleStat::getOriPageReadCount).sum()); result.setOriPageReadUser(var.stream().mapToLong(ArticleStat::getOriPageReadUser).sum()); articleStatData.add(result); } return articleStatData; } }