IndexController.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. package com.idiot.operationbackend.controller;
  2. import com.idiot.operationbackend.entity.Account;
  3. import com.idiot.operationbackend.entity.AccountStat;
  4. import com.idiot.operationbackend.entity.ArticleStat;
  5. import com.idiot.operationbackend.entity.FansActionStat;
  6. import com.idiot.operationbackend.service.facade.*;
  7. import com.idiot.operationbackend.support.Constants;
  8. import com.idiot.operationbackend.support.CustomException;
  9. import com.idiot.operationbackend.support.JsonResult;
  10. import com.idiot.operationbackend.util.JwtTokenUtil;
  11. import com.idiot.operationbackend.vo.ArticleStatData;
  12. import com.idiot.operationbackend.vo.GeneralStatData;
  13. import com.idiot.operationbackend.vo.NumberStatData;
  14. import com.idiot.operationbackend.vo.RemainStatData;
  15. import io.swagger.annotations.Api;
  16. import io.swagger.annotations.ApiOperation;
  17. import org.slf4j.Logger;
  18. import org.slf4j.LoggerFactory;
  19. import org.springframework.beans.factory.annotation.Autowired;
  20. import org.springframework.http.ResponseEntity;
  21. import org.springframework.util.CollectionUtils;
  22. import org.springframework.util.StringUtils;
  23. import org.springframework.web.bind.annotation.*;
  24. import java.math.BigDecimal;
  25. import java.time.Instant;
  26. import java.time.LocalDate;
  27. import java.time.LocalDateTime;
  28. import java.util.*;
  29. import java.util.stream.Collectors;
  30. import static com.idiot.operationbackend.support.Constants.calcRate;
  31. /**
  32. * 运营星 首页
  33. * @author wang xiao
  34. * @date Created in 10:55 2020/9/15
  35. */
  36. @RestController
  37. @RequestMapping("/index")
  38. @Api(value = "IndexController", tags ="首页")
  39. public class IndexController {
  40. private final Logger logger = LoggerFactory.getLogger(IndexController.class);
  41. private final String HOUR = "H";
  42. private final String WEEK = "W";
  43. private final String DAY = "D";
  44. private final String MONTH = "M";
  45. @Autowired
  46. private AccountService accountService;
  47. @Autowired
  48. private AccountStatService accountStatService;
  49. @Autowired
  50. private AccountFansService fansService;
  51. @Autowired
  52. private FansActionStatService actionStatService;
  53. @Autowired
  54. private ArticleStatService articleStatService;
  55. @GetMapping("/summary")
  56. @ApiOperation(value = "查询公众号昨日统计数据概览")
  57. public ResponseEntity<JsonResult<AccountStat>> getFansStat (@RequestHeader String token) {
  58. String userId = JwtTokenUtil.getUserId(token);
  59. logger.info("用户:{}查询首页数据统计概览---start",userId);
  60. List<Account> accounts = accountService.queryAccountByUserId(userId);
  61. List<String> accountIds = accounts.stream().map(Account::getId).collect(Collectors.toList());
  62. // 查询数据 不知道怎么优化
  63. String yesterdayStr = Constants.DATE_FORMATTER.format(LocalDate.now().plusDays(-1));
  64. List<AccountStat> ydStatData = accountStatService.queryByDateAndAccIds(accountIds,yesterdayStr);
  65. String beforeYesterdayStr = Constants.DATE_FORMATTER.format(LocalDate.now().plusDays(-2));
  66. List<AccountStat> beforeYdStat = accountStatService.queryByDateAndAccIds(accountIds,beforeYesterdayStr);
  67. logger.info("用户:{}首页概览数据计算中---ing",userId);
  68. // 所有公众号昨日数据
  69. long totalNum = ydStatData.stream().mapToLong(AccountStat::getTotalFansNum).sum();
  70. long addNum = ydStatData.stream().mapToLong(AccountStat::getAddNum).sum();
  71. long cancelNum = ydStatData.stream().mapToLong(AccountStat::getCancelNum).sum();
  72. long newNum = ydStatData.stream().mapToLong(AccountStat::getNewNum).sum();
  73. long inactiveNum = ydStatData.stream().mapToLong(AccountStat::getInactiveNum).sum();
  74. // 所有公众号前日数据
  75. long bfCancelNum = beforeYdStat.stream().mapToLong(AccountStat::getCancelNum).sum();
  76. long bfAddNum = beforeYdStat.stream().mapToLong(AccountStat::getAddNum).sum();
  77. long bfNewNum = beforeYdStat.stream().mapToLong(AccountStat::getNewNum).sum();
  78. long bfInactiveNum = beforeYdStat.stream().mapToLong(AccountStat::getInactiveNum).sum();
  79. long bfTotalNum = beforeYdStat.stream().mapToLong(AccountStat::getTotalFansNum).sum();
  80. AccountStat result = new AccountStat();
  81. result.setAddNum(addNum);
  82. result.setNewNum(newNum);
  83. result.setCancelNum(cancelNum);
  84. result.setInactiveNum(inactiveNum);
  85. result.setTotalFansNum(totalNum);
  86. result.setAddRate(calcRate(addNum,bfAddNum));
  87. result.setCancelRate(calcRate(cancelNum,bfCancelNum));
  88. result.setNewRate(calcRate(newNum,bfNewNum));
  89. result.setInactiveRate(calcRate(inactiveNum,bfInactiveNum));
  90. result.setTotalFansRate(calcRate(totalNum,bfTotalNum));
  91. logger.info("用户:{}查询首页数据统计概览---end",userId);
  92. return ResponseEntity.ok(JsonResult.success(result));
  93. }
  94. @GetMapping("/single/{accountId}")
  95. @ApiOperation(value = "查询单个公众号昨日统计数据概览")
  96. public ResponseEntity<JsonResult<AccountStat>> getSingleFansStat (@RequestHeader String token,
  97. @PathVariable String accountId) {
  98. String userId = JwtTokenUtil.getUserId(token);
  99. logger.info("用户:{}查询首页单个公众号:{}数据统计概览---start",userId,accountId);
  100. String yesterdayStr = Constants.DATE_FORMATTER.format(LocalDate.now().plusDays(-1));
  101. AccountStat fansStat = accountStatService.queryByDateAndAccountId(accountId,yesterdayStr);
  102. logger.info("用户:{}查询首页单个公众号:{}数据统计概览---end",userId,accountId);
  103. return ResponseEntity.ok(JsonResult.success(fansStat));
  104. }
  105. @GetMapping("/growth/{accountId}")
  106. @ApiOperation(value = "查询单个公众号--粉丝增长")
  107. public ResponseEntity<JsonResult<Map<String,Object>>> getFansGrowthStat (@PathVariable String accountId,
  108. @RequestHeader String token,
  109. @RequestParam String type,
  110. @RequestParam String startDate,
  111. @RequestParam String endDate) {
  112. String userId = JwtTokenUtil.getUserId(token);
  113. if (!checkDate(startDate,endDate)){
  114. throw new CustomException(500,"请选择开始和结束时间!");
  115. }
  116. Map<String,Object> result = new HashMap<>(8);
  117. logger.info("用户:{}查询首页单个公众号:{}粉丝增长,type:{},startDate:{},endDate:{} --->start",
  118. userId,accountId,type,startDate,endDate);
  119. LocalDate start = LocalDate.parse(startDate);
  120. LocalDate end= LocalDate.parse(endDate);
  121. long days = end.toEpochDay() - start.toEpochDay();
  122. List<GeneralStatData> statDataList = statFansGrowthData(accountId, type, start, end);
  123. long newNum =0;
  124. long addNum =0;
  125. long inactiveNum =0;
  126. long cancelNum =0;
  127. BigDecimal ave =BigDecimal.ZERO;
  128. if (CollectionUtils.isEmpty(statDataList)) {
  129. newNum = statDataList.parallelStream().mapToLong(GeneralStatData::getNewNum).sum();
  130. addNum = statDataList.parallelStream().mapToLong(GeneralStatData::getAddNum).sum();
  131. inactiveNum = statDataList.parallelStream().mapToLong(GeneralStatData::getInactiveNum).sum();
  132. cancelNum = statDataList.parallelStream().mapToLong(GeneralStatData::getCancelNum).sum();
  133. if (days == 0){
  134. days = 1;
  135. }
  136. ave = new BigDecimal(newNum/days).setScale(0);
  137. }
  138. result.put("newNum",newNum);
  139. result.put("addNum",addNum);
  140. result.put("inactiveNum",inactiveNum);
  141. result.put("cancelNum",cancelNum);
  142. result.put("aveNum",ave);
  143. result.put("tableData",statDataList);
  144. logger.info("用户:{}查询首页单个公众号:{}粉丝增长,type:{},startDate:{},endDate:{} --->end",
  145. userId,accountId,type,startDate,endDate);
  146. return ResponseEntity.ok(JsonResult.success(result));
  147. }
  148. @GetMapping("/property/{accountId}")
  149. @ApiOperation(value = "查询单个公众号--粉丝属性")
  150. public ResponseEntity<JsonResult<Map<String,Object>>> getFansPropertyStat(
  151. @RequestHeader String token,
  152. @PathVariable String accountId,
  153. @RequestParam(required = false) String startDate,
  154. @RequestParam(required = false) String endDate) {
  155. String userId = JwtTokenUtil.getUserId(token);
  156. logger.info("用户:{}查询首页单个公众号:{}粉丝属性,startDate:{},endDate:{} --->start",userId,accountId,startDate,endDate);
  157. Map<String,Object> result = new HashMap<>(4);
  158. List<NumberStatData> numberStatData = fansService.statByFansProperty(accountId,startDate,endDate);
  159. statFansProperty(numberStatData,result);
  160. logger.info("用户:{}查询首页单个公众号:{}粉丝属性,startDate:{},endDate:{} --->end",userId,accountId,startDate,endDate);
  161. return ResponseEntity.ok(JsonResult.success(result));
  162. }
  163. @GetMapping("/inactive/{accountId}")
  164. @ApiOperation(value = "查询单个公众号--粉丝活跃度")
  165. public ResponseEntity<JsonResult<List<AccountStat>>> getFansInactiveStat(@RequestHeader String token,
  166. @PathVariable String accountId,
  167. @RequestParam String startDate,
  168. @RequestParam String endDate) {
  169. String userId = JwtTokenUtil.getUserId(token);
  170. if (!checkDate(startDate,endDate)){
  171. throw new CustomException(500,"请选择查询时间范围!");
  172. }
  173. logger.info("用户:{}查询首页单个公众号:{}粉丝活跃度,startDate:{},endDate:{} --->start",userId,accountId,startDate,endDate);
  174. List<AccountStat> accountStats = accountStatService.queryAccountStatByDate(accountId,startDate,endDate);
  175. logger.info("用户:{}查询首页单个公众号:{}粉丝活跃度,startDate:{},endDate:{} --->end",userId,accountId,startDate,endDate);
  176. return ResponseEntity.ok(JsonResult.success(accountStats));
  177. }
  178. @GetMapping("/remain/{accountId}")
  179. @ApiOperation(value = "查询单个公众号--粉丝忠诚度")
  180. public ResponseEntity<JsonResult<List<RemainStatData>>> getFansRemainStat(@RequestHeader String token,
  181. @PathVariable String accountId,
  182. @RequestParam String date) {
  183. String userId = JwtTokenUtil.getUserId(token);
  184. if (StringUtils.isEmpty(date)){
  185. throw new CustomException(500,"请选择查询时间!");
  186. }
  187. LocalDateTime endDate = Constants.toLocalDateTime(date);
  188. LocalDateTime startDate = Constants.toLocalDateTime(date).plusDays(-7);
  189. logger.info("用户:{}查询首页单个公众号:{}粉丝忠诚度,date:{} --->start",userId,accountId,date);
  190. List<AccountStat> accountStats = accountStatService.queryAccountStatByDate(accountId,startDate,endDate);
  191. List<RemainStatData> remainStatData = statFansRemainData(accountStats);
  192. if (Objects.isNull(remainStatData)) {
  193. remainStatData = new ArrayList<>();
  194. }
  195. logger.info("用户:{}查询首页单个公众号:{}粉丝忠诚度,date:{} --->end",userId,accountId,date);
  196. return ResponseEntity.ok(JsonResult.success(remainStatData));
  197. }
  198. @GetMapping("/articles/{accountId}")
  199. @ApiOperation(value = "查询单个公众号--图文影响力")
  200. public ResponseEntity<JsonResult<List<ArticleStatData>>> getFansPageStat(@RequestHeader String token,
  201. @PathVariable String accountId,
  202. @RequestParam String startDate,
  203. @RequestParam String endDate) {
  204. String userId = JwtTokenUtil.getUserId(token);
  205. if (!checkDate(startDate,endDate)){
  206. throw new CustomException(500,"请选择查询时间!");
  207. }
  208. logger.info("用户:{}查询首页单个公众号:{}图文影响力,startDate:{},endDate:{} --->start",userId,accountId,startDate,endDate);
  209. List<ArticleStat> articleStats = articleStatService.queryArticleStatByDate(accountId,startDate,endDate);
  210. List<ArticleStatData> articleStatData = statArticleData(articleStats);
  211. if (CollectionUtils.isEmpty(articleStatData)){
  212. articleStatData = new ArrayList<>();
  213. }
  214. logger.info("用户:{}查询首页单个公众号:{}图文影响力,startDate:{},endDate:{} --->start",userId,accountId,startDate,endDate);
  215. return ResponseEntity.ok(JsonResult.success(articleStatData));
  216. }
  217. /**
  218. * 统计粉丝数据 好难受写不出完美的代码
  219. * @author wangxiao
  220. * @date 16:00 2020/9/16
  221. * @param accountId
  222. * @param type
  223. * @param startDate
  224. * @param endDate
  225. * @return java.util.List<com.idiot.operationbackend.vo.StatData>
  226. */
  227. private List<GeneralStatData> statFansGrowthData(String accountId, String type, LocalDate startDate, LocalDate endDate) {
  228. // 粉丝增长数据 因为能选择今天 需要在 用户动作分析里面取数据。或者一小时分析一次数据
  229. long disValue = 0;
  230. if (HOUR.equals(type)) {
  231. disValue = 7200;
  232. }else if (DAY.equals(type)){
  233. disValue = 86400;
  234. }else if (WEEK.equals(type)){
  235. disValue = 604800;
  236. }else if (MONTH.equals(type)){
  237. // 默认 30 天
  238. disValue = 2592000;
  239. }else {
  240. throw new CustomException(500,"请你选择正确的时间统计类型!");
  241. }
  242. LocalDateTime startLocalDate = startDate.atTime(Constants.DEFAULT_TIME);
  243. LocalDateTime endLocalDate = endDate.plusDays(1).atTime(Constants.DEFAULT_TIME);
  244. List<FansActionStat> fansActionStats = actionStatService.queryFansActionStat(accountId,startLocalDate,endLocalDate);
  245. if (CollectionUtils.isEmpty(fansActionStats)) {
  246. throw new CustomException(500,"暂无数据!");
  247. }
  248. long start = startLocalDate.toEpochSecond(Constants.DEFAULT_ZONE);
  249. long end = endLocalDate.toEpochSecond(Constants.DEFAULT_ZONE);
  250. List<GeneralStatData> statDataList = new ArrayList<>();
  251. GeneralStatData statData = null;
  252. List<FansActionStat> tempStatList = null;
  253. while (start<=end){
  254. long finalStart = start;
  255. long finalDisValue = disValue;
  256. tempStatList = fansActionStats.stream()
  257. .filter(e->e.getCreateTime() >= finalStart && e.getCreateTime()<= finalStart + finalDisValue)
  258. .sorted(Comparator.comparingLong(FansActionStat::getCreateTime))
  259. .collect(Collectors.toList());
  260. statData = countStatData(tempStatList,finalStart,finalStart+finalDisValue,type);
  261. statDataList.add(statData);
  262. start += finalDisValue;
  263. }
  264. return statDataList;
  265. }
  266. /**
  267. * 校验日期
  268. * @author wangxiao
  269. * @date 11:58 2020/9/17
  270. * @param start
  271. * @param end
  272. * @return boolean
  273. */
  274. private boolean checkDate (String start, String end) {
  275. return !(StringUtils.isEmpty(start) | StringUtils.isEmpty(end));
  276. }
  277. /**
  278. * 统计 粉丝增长 时间段
  279. * @author wangxiao
  280. * @date 16:45 2020/9/16
  281. * @param fansActionStats
  282. * @param start
  283. * @param end
  284. * @param type
  285. * @return com.idiot.operationbackend.vo.StatData
  286. */
  287. private GeneralStatData countStatData (List<FansActionStat> fansActionStats, long start, long end, String type) {
  288. LocalDateTime startDateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(start),Constants.DEFAULT_ZONE);
  289. LocalDateTime endDateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(end),Constants.DEFAULT_ZONE);
  290. GeneralStatData statData = new GeneralStatData();
  291. long newNum = 0;
  292. long cancelNum = 0;
  293. long inactiveNum = 0;
  294. if (!CollectionUtils.isEmpty(fansActionStats)) {
  295. newNum = fansActionStats.parallelStream().filter(e->Constants.NEW == e.getAction()).count();
  296. cancelNum = fansActionStats.parallelStream().filter(e->Constants.CANCEL == e.getAction()).count();
  297. inactiveNum = fansActionStats.parallelStream().filter(e->Constants.NEW != e.getAction() && Constants.CANCEL != e.getAction()).count();
  298. }
  299. String label = "";
  300. if (HOUR.equals(type)) {
  301. label = String.format("%s %s:%s-%s:%s",startDateTime.toLocalDate(),startDateTime.getHour(),"00",endDateTime.getHour(),"00");
  302. }else if (DAY.equals(type)){
  303. label = String.format("%s",startDateTime.toLocalDate());
  304. }else if (WEEK.equals(type)){
  305. label = String.format("%s/%s-%s/%s",startDateTime.getMonthValue(),startDateTime.getDayOfMonth()
  306. ,endDateTime.getMonthValue(),endDateTime.getDayOfMonth());
  307. }else if (MONTH.equals(type)){
  308. label = String.format("%s-%s",startDateTime.getYear(),startDateTime.getMonthValue());
  309. }
  310. statData.setDateLabel(label);
  311. statData.setAddNum(newNum-cancelNum);
  312. statData.setCancelNum(cancelNum);
  313. statData.setNewNum(newNum);
  314. statData.setInactiveNum(inactiveNum);
  315. return statData;
  316. }
  317. /**
  318. * 统计粉丝属性
  319. * @author wangxiao
  320. * @date 18:57 2020/9/16
  321. * @param fansData
  322. * @param map
  323. * @return void
  324. */
  325. private void statFansProperty(List<NumberStatData> fansData,Map<String,Object> map) {
  326. if (!CollectionUtils.isEmpty(fansData)) {
  327. Map<String,List<NumberStatData>> numData = fansData.stream().collect(Collectors.groupingBy(NumberStatData::getType));
  328. map.putAll(numData);
  329. }
  330. }
  331. /**
  332. * 统计留存粉丝
  333. * @author wangxiao
  334. * @date 14:22 2020/9/17
  335. * @param accountStats
  336. * @return java.util.List<com.idiot.operationbackend.vo.RemainStatData>
  337. */
  338. private List<RemainStatData> statFansRemainData(List<AccountStat> accountStats) {
  339. if (CollectionUtils.isEmpty(accountStats)) {
  340. return null;
  341. }
  342. int size = accountStats.size();
  343. List<RemainStatData> remainStatDataList = new ArrayList<>(size);
  344. String statDate = null;
  345. Long newNum = null;
  346. Long totalNum = null;
  347. RemainStatData remain = null;
  348. for (AccountStat temp : accountStats) {
  349. statDate = temp.getStatDate();
  350. newNum = temp.getNewNum();
  351. totalNum = temp.getTotalFansNum();
  352. remain = new RemainStatData(statDate,newNum);
  353. remain.setDay1Num(calcRemain(accountStats,totalNum,statDate,1));
  354. remain.setDay2Num(calcRemain(accountStats,totalNum,statDate,2));
  355. remain.setDay3Num(calcRemain(accountStats,totalNum,statDate,3));
  356. remain.setDay4Num(calcRemain(accountStats,totalNum,statDate,4));
  357. remain.setDay5Num(calcRemain(accountStats,totalNum,statDate,5));
  358. remain.setDay6Num(calcRemain(accountStats,totalNum,statDate,6));
  359. remain.setDay7Num(calcRemain(accountStats,totalNum,statDate,7));
  360. remainStatDataList.add(remain);
  361. }
  362. return remainStatDataList;
  363. }
  364. /**
  365. * 计算留存数
  366. * @author wangxiao
  367. * @date 14:35 2020/9/17
  368. * @param accountStats 数组
  369. * @param nowTotal 当前总数
  370. * @param targetIndex 几天前下表
  371. * @param nowStatDate 当前统计日期
  372. * @return long
  373. */
  374. private long calcRemain (List<AccountStat> accountStats,long nowTotal,String nowStatDate,int targetIndex) {
  375. long remainNum;
  376. LocalDate nowDate = LocalDate.parse(nowStatDate,Constants.DATE_FORMATTER);
  377. String targetStatDate = nowDate.plusDays(-targetIndex).format(Constants.DATE_FORMATTER);
  378. remainNum = accountStats.stream()
  379. .filter(e->targetStatDate.equals(e.getStatDate()))
  380. .findAny().map(e->e.getTotalFansNum()-nowTotal)
  381. .orElse(0L);
  382. return remainNum;
  383. }
  384. /**
  385. * 统计图文影响力
  386. * @author wangxiao
  387. * @date 16:06 2020/9/17
  388. * @param articleStats
  389. * @return java.util.List<com.idiot.operationbackend.vo.ArticleStatData>
  390. */
  391. private List<ArticleStatData> statArticleData (List<ArticleStat> articleStats) {
  392. if (CollectionUtils.isEmpty(articleStats)){
  393. return null;
  394. }
  395. Map<String,List<ArticleStat>> temp = articleStats.stream().collect(Collectors.groupingBy(ArticleStat::getStatDate));
  396. List<ArticleStatData> articleStatData = new ArrayList<>(temp.size());
  397. List<ArticleStat> var = null;
  398. ArticleStatData result = null;
  399. for (Map.Entry<String,List<ArticleStat>> entry : temp.entrySet()) {
  400. var = entry.getValue();
  401. result = new ArticleStatData(entry.getKey(),var);
  402. result.setShareUser(var.stream().mapToLong(ArticleStat::getShareUser).sum());
  403. result.setShareCount(var.stream().mapToLong(ArticleStat::getShareCount).sum());
  404. result.setAddToFavCount(var.stream().mapToLong(ArticleStat::getAddToFavCount).sum());
  405. result.setAddToFavUser(var.stream().mapToLong(ArticleStat::getAddToFavUser).sum());
  406. result.setIntPageReadCount(var.stream().mapToLong(ArticleStat::getIntPageReadCount).sum());
  407. result.setIntPageReadUser(var.stream().mapToLong(ArticleStat::getIntPageReadUser).sum());
  408. result.setOriPageReadCount(var.stream().mapToLong(ArticleStat::getOriPageReadCount).sum());
  409. result.setOriPageReadUser(var.stream().mapToLong(ArticleStat::getOriPageReadUser).sum());
  410. articleStatData.add(result);
  411. }
  412. return articleStatData;
  413. }
  414. }