|
@@ -0,0 +1,343 @@
|
|
|
+package com.idiot.operationbackend.controller;
|
|
|
+
|
|
|
+import com.idiot.operationbackend.entity.Account;
|
|
|
+import com.idiot.operationbackend.entity.AccountFans;
|
|
|
+import com.idiot.operationbackend.entity.AccountFansStat;
|
|
|
+import com.idiot.operationbackend.entity.FansActionStat;
|
|
|
+import com.idiot.operationbackend.service.facade.AccountFansService;
|
|
|
+import com.idiot.operationbackend.service.facade.AccountFansStatService;
|
|
|
+import com.idiot.operationbackend.service.facade.AccountService;
|
|
|
+import com.idiot.operationbackend.service.facade.FansActionStatService;
|
|
|
+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.GeneralStatData;
|
|
|
+
|
|
|
+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.time.LocalTime;
|
|
|
+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 AccountFansStatService fansStatService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AccountFansService fansService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FansActionStatService actionStatService;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @GetMapping("/summary")
|
|
|
+ @ApiOperation(value = "查询公众号昨日统计数据概览")
|
|
|
+ public ResponseEntity<JsonResult<AccountFansStat>> getFansStat (@RequestHeader String token) {
|
|
|
+ String userId = JwtTokenUtil.getUserId(token);
|
|
|
+ logger.info("用户:{}查询首页数据统计概览---start",userId);
|
|
|
+ List<Account> accounts = accountService.queryAccountByUserId(userId);
|
|
|
+ List<String> accountIds = accounts.stream().map(Account::getId).collect(Collectors.toList());
|
|
|
+ // 查询数据 不知道怎么优化
|
|
|
+ String yesterdayStr = Constants.DATE_FORMATTER.format(LocalDate.now().plusDays(-1));
|
|
|
+ List<AccountFansStat> ydStatData = fansStatService.queryByDateAndAccIds(accountIds,yesterdayStr);
|
|
|
+ String beforeYesterdayStr = Constants.DATE_FORMATTER.format(LocalDate.now().plusDays(-2));
|
|
|
+ List<AccountFansStat> beforeYdStat = fansStatService.queryByDateAndAccIds(accountIds,beforeYesterdayStr);
|
|
|
+
|
|
|
+ logger.info("用户:{}首页概览数据计算中---ing",userId);
|
|
|
+ // 所有公众号昨日数据
|
|
|
+ long totalNum = ydStatData.stream().mapToLong(AccountFansStat::getTotalFansNum).sum();
|
|
|
+ long addNum = ydStatData.stream().mapToLong(AccountFansStat::getAddNum).sum();
|
|
|
+ long cancelNum = ydStatData.stream().mapToLong(AccountFansStat::getCancelNum).sum();
|
|
|
+ long newNum = ydStatData.stream().mapToLong(AccountFansStat::getNewNum).sum();
|
|
|
+ long inactiveNum = ydStatData.stream().mapToLong(AccountFansStat::getInactiveNum).sum();
|
|
|
+ // 所有公众号前日数据
|
|
|
+ long bfCancelNum = beforeYdStat.stream().mapToLong(AccountFansStat::getCancelNum).sum();
|
|
|
+ long bfAddNum = beforeYdStat.stream().mapToLong(AccountFansStat::getAddNum).sum();
|
|
|
+ long bfNewNum = beforeYdStat.stream().mapToLong(AccountFansStat::getNewNum).sum();
|
|
|
+ long bfInactiveNum = beforeYdStat.stream().mapToLong(AccountFansStat::getInactiveNum).sum();
|
|
|
+ long bfTotalNum = beforeYdStat.stream().mapToLong(AccountFansStat::getTotalFansNum).sum();
|
|
|
+
|
|
|
+ AccountFansStat result = new AccountFansStat();
|
|
|
+ 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<JsonResult<AccountFansStat>> 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));
|
|
|
+ AccountFansStat fansStat = fansStatService.queryByDateAndAccountId(accountId,yesterdayStr);
|
|
|
+ logger.info("用户:{}查询首页单个公众号:{}数据统计概览---end",userId,accountId);
|
|
|
+ return ResponseEntity.ok(JsonResult.success(fansStat));
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @GetMapping("/fansGrowth/{accountId}")
|
|
|
+ @ApiOperation(value = "查询单个公众号--粉丝增长")
|
|
|
+ public ResponseEntity<JsonResult<Map<String,Object>>> 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<String,Object> 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<GeneralStatData> statDataList = queryFansGrowthStatData(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("/fansProperty/{accountId}")
|
|
|
+ @ApiOperation(value = "查询单个公众号--粉丝属性")
|
|
|
+ public ResponseEntity<JsonResult<Map<String,Object>>> 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);
|
|
|
+ List<AccountFans> accountFans;
|
|
|
+ Map<String,Object> result = new HashMap<>(4);
|
|
|
+ if (!StringUtils.isEmpty(startDate) && !StringUtils.isEmpty(endDate) ){
|
|
|
+ accountFans = fansService.queryFansByDateAndDate(accountId,startDate,endDate);
|
|
|
+ }else {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.info("用户:{}查询首页单个公众号:{}属性,startDate:{},endDate:{} --->end",userId,accountId,startDate,endDate);
|
|
|
+ return null;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 统计粉丝数据 好难受写不出完美的代码
|
|
|
+ * @author wangxiao
|
|
|
+ * @date 16:00 2020/9/16
|
|
|
+ * @param accountId
|
|
|
+ * @param type
|
|
|
+ * @param startDate
|
|
|
+ * @param endDate
|
|
|
+ * @return java.util.List<com.idiot.operationbackend.vo.StatData>
|
|
|
+ */
|
|
|
+ private List<GeneralStatData> queryFansGrowthStatData(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(LocalTime.of(0,0,0));
|
|
|
+ LocalDateTime endLocalDate = endDate.plusDays(1).atTime(LocalTime.of(0,0,0));
|
|
|
+ List<FansActionStat> 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<GeneralStatData> statDataList = new ArrayList<>();
|
|
|
+ GeneralStatData statData = null;
|
|
|
+ List<FansActionStat> 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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<FansActionStat> 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 fans
|
|
|
+ * @param map
|
|
|
+ * @return void
|
|
|
+ */
|
|
|
+ private void statFansProperty(List<AccountFans> fans,Map<String,Object> map) {
|
|
|
+ long totalFansNum = 0;
|
|
|
+ long manNum = 0;
|
|
|
+ BigDecimal manRate = BigDecimal.ZERO;
|
|
|
+ long womanNum = 0;
|
|
|
+ BigDecimal womanRate = BigDecimal.ZERO;
|
|
|
+ Map<String,Long> subscribeScene = null;
|
|
|
+ if (!CollectionUtils.isEmpty(fans)) {
|
|
|
+ totalFansNum = fans.size();
|
|
|
+ manNum = fans.parallelStream().filter(e->1==e.getSex()).count();
|
|
|
+ womanNum = fans.parallelStream().filter(e->2==e.getSex()).count();
|
|
|
+ manRate = BigDecimal.valueOf((manNum*100)/totalFansNum).setScale(2);
|
|
|
+ womanRate = BigDecimal.valueOf((womanNum*100)/totalFansNum).setScale(2);
|
|
|
+ subscribeScene = fans.stream().collect(Collectors.groupingBy(AccountFans::getSubscribeScene,Collectors.reducing(0L,accountFans -> 1L,Long::sum)));
|
|
|
+ }
|
|
|
+ Map<String,Object> base = new HashMap<>(6);
|
|
|
+ base.put("totalFansNum",totalFansNum);
|
|
|
+ base.put("manNum",manNum);
|
|
|
+ base.put("womanNum",womanNum);
|
|
|
+ base.put("manRate",manRate);
|
|
|
+ base.put("womanRate",womanRate);
|
|
|
+ map.put("base",base);
|
|
|
+ Map<String,Object> sex = new HashMap<>(4);
|
|
|
+ sex.put("manNum",manNum);
|
|
|
+ sex.put("womanNum",womanNum);
|
|
|
+ sex.put("manRate",manRate);
|
|
|
+ sex.put("womanRate",womanRate);
|
|
|
+ map.put("sex",sex);
|
|
|
+ map.put("subscribeScene",subscribeScene);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}
|