From 3f0a32de14f713cf4b37c9851c2d542c2da23f88 Mon Sep 17 00:00:00 2001 From: yang <1175@qq.com> Date: Fri, 6 Sep 2024 11:04:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A1=88=E4=BE=8B=E8=90=83=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/xboe/constants/CacheName.java | 20 ++ .../java/com/xboe/enums/CasesRankEnum.java | 24 ++ .../com/xboe/module/boecase/api/CasesApi.java | 147 +++++--- .../com/xboe/module/boecase/dao/CasesDao.java | 120 ++++++- .../xboe/module/boecase/dao/CasesRankDao.java | 87 +++++ .../com/xboe/module/boecase/entity/Cases.java | 61 +++- .../xboe/module/boecase/entity/CasesRank.java | 63 ++++ .../module/boecase/service/ICasesService.java | 27 +- .../service/impl/CasesServiceImpl.java | 335 +++++++++++++++++- .../xboe/module/boecase/vo/CaseRankingVo.java | 12 + .../boecase/vo/CasesQueryRecommendRankVo.java | 34 ++ 11 files changed, 856 insertions(+), 74 deletions(-) create mode 100644 servers/boe-server-all/src/main/java/com/xboe/enums/CasesRankEnum.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/boecase/dao/CasesRankDao.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/boecase/entity/CasesRank.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/boecase/vo/CaseRankingVo.java create mode 100644 servers/boe-server-all/src/main/java/com/xboe/module/boecase/vo/CasesQueryRecommendRankVo.java diff --git a/servers/boe-server-all/src/main/java/com/xboe/constants/CacheName.java b/servers/boe-server-all/src/main/java/com/xboe/constants/CacheName.java index b92aff08..3a1e70de 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/constants/CacheName.java +++ b/servers/boe-server-all/src/main/java/com/xboe/constants/CacheName.java @@ -133,4 +133,24 @@ public interface CacheName { * 字典缓存key * */ String KEY_DICT="dict"; + + /** + * 季度观看量排行key + * */ + String CASE_RANK_VIEWS_QUARTER="case:rank:views:quarter"; + + /** + * 总观看排行key + * */ + String CASE_RANK_VIEWS_ALL="case:rank:views:all"; + + /** + * 季度点赞排行key + * */ + String CASE_RANK_PRAISE_QUARTER ="case:rank:praise:quarter"; + + /** + * 总点赞排行key + * */ + String CASE_RANK_PRAISE_ALL="case:rank:praise:all"; } diff --git a/servers/boe-server-all/src/main/java/com/xboe/enums/CasesRankEnum.java b/servers/boe-server-all/src/main/java/com/xboe/enums/CasesRankEnum.java new file mode 100644 index 00000000..302f692d --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/enums/CasesRankEnum.java @@ -0,0 +1,24 @@ +package com.xboe.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import static com.xboe.constants.CacheName.*; + + +@AllArgsConstructor +@Getter +public enum CasesRankEnum { + + QUARTER_VIEWS(1,"季度观看量排行",CASE_RANK_VIEWS_QUARTER), + ALL_VIEWS(2,"总观看排行",CASE_RANK_VIEWS_ALL), + QUARTER_PRAISES(3,"季度点赞排行",CASE_RANK_PRAISE_QUARTER), + ALL_PRAISES(4,"总点赞排行",CASE_RANK_PRAISE_ALL), + ; + + private Integer type; + private String name; + private String cacheKey; + + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/api/CasesApi.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/api/CasesApi.java index 60bf649b..47a42413 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/api/CasesApi.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/api/CasesApi.java @@ -12,17 +12,15 @@ import com.xboe.module.boecase.dto.*; import com.xboe.module.boecase.entity.CasesMajorType; import com.xboe.module.boecase.service.ICasesRecommendPushRecordService; import com.xboe.module.boecase.vo.CaseExportVo; +import com.xboe.module.boecase.vo.CaseRankingVo; +import com.xboe.module.boecase.vo.CasesQueryRecommendRankVo; import com.xboe.module.dict.entity.DictItem; import com.xboe.module.excel.ExportsExcelSenderUtil; import com.xboe.system.user.dao.UserDao; import com.xboe.system.user.entity.User; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; @@ -127,25 +125,12 @@ public class CasesApi extends ApiBaseController { @PostMapping("/queryListV2") public JsonResponse> queryCaseBreV2(@Validated @RequestBody CasePageVo req) { String type = req.getType(); - PageList subViews1; - PageList subViews2; - PageList views = null; req.setUserId(getCurrent().getAccountId()); - if (StringUtils.isNotEmpty(type)) { - if ("recommend".equals(type)) { - views = casesService.queryRecommendPageCasesV2(req); - } else { - views = casesService.queryPageCasesV2(req); - } + PageList views = null; + if (type.equals("recommend")) { + views = casesService.queryRecommendPageCasesV2(req); } else { - PageDto pageDto = new PageDto(); - pageDto.setPageIndex(req.getPageIndex()); - pageDto.setPageSize(req.getPageSize()); - req.setPageSize(1000000); - req.setPageIndex(1); - subViews1 = casesService.queryRecommendPageCasesV2(req); - subViews2 = casesService.queryPageCasesV2(req); - views = merge(subViews1, subViews2, pageDto); + views = casesService.queryPageCasesV2(req); } if (views != null) { @@ -175,24 +160,6 @@ public class CasesApi extends ApiBaseController { return success(views); } - public static PageList merge(PageList pageList1, PageList pageList2, PageDto pageDto) { - List mergedCaseList = new ArrayList<>(); - mergedCaseList.addAll(pageList1.getList()); - mergedCaseList.addAll(pageList2.getList()); - List pageCaseList = mergedCaseList.stream() - .skip((long) (pageDto.getPageIndex() - 1) * pageDto.getPageSize()) - .limit(pageDto.getPageSize()) - .collect(Collectors.toList()); - int totalCount = pageList1.getCount() + pageList2.getCount(); - int pageSize = pageDto.getPageSize(); - - PageList mergePageList = new PageList<>(); - mergePageList.setList(pageCaseList); - mergePageList.setCount(totalCount); - mergePageList.setPageSize(pageSize); - return mergePageList; - } - @PostMapping("/caseYears") public JsonResponse> caseYears() { List result = casesService.getCaseYears(); @@ -654,7 +621,7 @@ public class CasesApi extends ApiBaseController { * 设置/取消优秀案例 */ @PostMapping("/excellent") - public JsonResponse excellent(String id, Boolean excellent) { + public JsonResponse excellent(String id, Boolean excellent,String excellentReason) { if (StringUtil.isBlank(id)) { return badRequest("参数异常"); } @@ -662,12 +629,108 @@ public class CasesApi extends ApiBaseController { excellent = false;//默认设置取消 } try { - casesService.excellent(id, excellent); + casesService.excellent(id, excellent,excellentReason); return success(true); } catch (Exception e) { return error("设置或者取消失败", e.getMessage()); } } + /** + * 不同专业分类下排行榜刷新 + */ + @PostMapping("/refreshViewsRankOfMajor") + public JsonResponse refreshViewsRankOfMajor() { + casesService.refreshViewsRankOfMajor(); + return success(true); + } + + @PostMapping("/refreshLastQuarterStatistics") + public JsonResponse refreshLastQuarterStatistics() { + casesService.refreshLastQuarterStatistics(); + return success(true); + } + + + /** + * 热度榜(当前季度、总榜) 按观看量排行 + * @param pageSize + * @param rankType 1:当前季度,2:总榜 + * @return + */ + @SuppressWarnings("unchecked") + @GetMapping("/queryPopularity") + public JsonResponse> queryPopularity(@RequestParam(required = false) Integer pageSize,@RequestParam Integer rankType) { + List caseRankingVoList =casesService.queryRank(pageSize,rankType); + return success(caseRankingVoList); + } + + + /** + * 好评榜 (当前季度、总榜) 按点赞量排行 + * @param pageSize + * @param rankType + * @return + */ + @SuppressWarnings("unchecked") + @GetMapping("/queryHighOpinion") + public JsonResponse> queryHighOpinion(@RequestParam(required = false) Integer pageSize,@RequestParam Integer rankType) { + List caseRankingVoList =casesService.queryRank(pageSize,rankType); + return success(caseRankingVoList); + } + + /** + * 不同专业月热度榜 按观看量排行 + */ + @SuppressWarnings("unchecked") + @GetMapping("/queryPopularityOfMajor") + public JsonResponse> queryPopularityOfMajor(@RequestParam(required = false) Integer pageSize, + @RequestParam Long majorId, + @RequestParam LocalDateTime rankMonth) { + List caseRankingVoList =casesService.queryPopularityOfMajor(pageSize,majorId,rankMonth); + return success(caseRankingVoList); + } + + + /** + * 查询推荐案例榜单 + * @return + */ + @SuppressWarnings("unchecked") + @GetMapping("/queryRecommendRank") + public JsonResponse> queryRecommendRank() { + List list = casesService.queryRecommendRank(); + return success(list); + } + + /** + * 案例上榜 + */ + @SuppressWarnings("unchecked") + @PostMapping("/riseIntoRank") + public JsonResponse riseIntoRank(@RequestParam Long caseId) { + casesService.riseIntoRank(caseId,getCurrent()); + return success(true); + } + + /** + * 案例下榜 + */ + @SuppressWarnings("unchecked") + @PostMapping("/cancelRiseIntoRank") + public JsonResponse cancelRiseIntoRank(@RequestParam Long caseId) { + casesService.cancelRiseIntoRank(caseId); + return success(true); + } + + /** + * 推荐案例调整排名 + */ + @SuppressWarnings("unchecked") + @PostMapping("/adjustRank") + public JsonResponse adjustRank(@RequestParam List caseIds) { + casesService.adjustRank(caseIds); + return success(true); + } } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/dao/CasesDao.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/dao/CasesDao.java index 1ffa20db..8db4365c 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/dao/CasesDao.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/dao/CasesDao.java @@ -1,18 +1,24 @@ package com.xboe.module.boecase.dao; -import java.util.List; - -import org.springframework.stereotype.Repository; - import com.xboe.common.OrderCondition; import com.xboe.common.PageList; import com.xboe.core.orm.BaseDao; +import com.xboe.core.orm.FieldFilters; import com.xboe.core.orm.IFieldFilter; import com.xboe.module.boecase.entity.Cases; -import com.xboe.core.orm.FieldFilters; -import com.xboe.module.boecase.dto.CaseVo; +import com.xboe.module.boecase.vo.CaseRankingVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import static com.xboe.enums.CasesRankEnum.*; @Repository +@Slf4j public class CasesDao extends BaseDao { /** @@ -35,4 +41,106 @@ public class CasesDao extends BaseDao { public Cases getByTitle(String title) { return this.getGenericDao().findOne(Cases.class, FieldFilters.eq("title", title)); } + + public HashMap findExcellentCount(List authorIdList) { + String sql = + "SELECT author_id,COUNT(1) AS excellentCount\n" + + "FROM boe_cases \n" + + "WHERE excellent = 1 AND author_id IN (?1) AND deleted=0 \n" + + "GROUP BY author_id"; + List list = this.sqlFindList(sql, authorIdList); + + HashMap map = new HashMap<>(); + + list.forEach(objects -> { + String authorId = objects[0].toString(); + int excellentCount = Integer.parseInt(objects[1].toString()); + map.put(authorId, excellentCount); + }); + + return map; + } + + private List handleCaseRank(List listFields) { + List list = new ArrayList<>(); + for (Object[] o : listFields) { + CaseRankingVo dto = new CaseRankingVo(); + dto.setCaseId(String.valueOf(o[0])); + dto.setCaseTitle((String) o[1]); + list.add(dto); + } + return list; + } + + public List findPopularityOfAll(Integer pageSize) { + + String sql = + "SELECT id,title\n" + + "FROM boe_cases \n" + + "WHERE deleted=0\n" + + "ORDER BY views DESC,sys_create_time DESC\n" + + "LIMIT ?1"; + + List listFields = this.sqlFindList(sql, pageSize); + List caseRankingVoList = handleCaseRank(listFields); + return caseRankingVoList; + } + + public List findPopularityOfQuarter(Integer pageSize) { + String sql = + "SELECT id,title\n" + + "FROM boe_cases \n" + + "WHERE deleted=0\n" + + "ORDER BY (views-last_month_views) DESC,sys_create_time DESC \n" + + "LIMIT ?1"; + + List listFields = this.sqlFindList(sql, pageSize); + + List caseRankingVoList = handleCaseRank(listFields); + return caseRankingVoList; + } + + public List findHighOpinionOfAll(Integer pageSize) { + String sql = + "SELECT id,title\n" + + "FROM boe_cases \n" + + "WHERE deleted=0\n" + + "ORDER BY praises DESC,sys_create_time DESC \n" + + "LIMIT ?1"; + List listFields = this.sqlFindList(sql, pageSize); + List caseRankingVoList = handleCaseRank(listFields); + return caseRankingVoList; + } + + + public List findHighOpinionOfQuarter(Integer pageSize) { + String sql = + "SELECT id,title\n" + + "FROM boe_cases \n" + + "WHERE deleted=0\n" + + "ORDER BY (praises-last_month_praises) DESC,sys_create_time DESC \n" + + "LIMIT ?1"; + + List listFields = this.sqlFindList(sql, pageSize); + List caseRankingVoList = handleCaseRank(listFields); + return caseRankingVoList; + } + + public List findRank(Integer pageSize, Integer rankType) { + List popularityOfQuarter = Collections.emptyList(); + if (rankType == QUARTER_VIEWS.getType()){ + popularityOfQuarter = findPopularityOfQuarter(pageSize); + } + if (rankType == ALL_VIEWS.getType()){ + popularityOfQuarter = findPopularityOfAll(pageSize); + } + if (rankType == QUARTER_PRAISES.getType()){ + popularityOfQuarter = findHighOpinionOfQuarter(pageSize); + } + if (rankType == ALL_PRAISES.getType()){ + popularityOfQuarter = findHighOpinionOfAll(pageSize); + } + return popularityOfQuarter; + } + } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/dao/CasesRankDao.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/dao/CasesRankDao.java new file mode 100644 index 00000000..9225e274 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/dao/CasesRankDao.java @@ -0,0 +1,87 @@ +package com.xboe.module.boecase.dao; + +import com.xboe.core.orm.BaseDao; +import com.xboe.module.boecase.entity.CasesRank; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +@Repository +@Slf4j +public class CasesRankDao extends BaseDao { + + public List findViewsRankRecordByCaseId(String caseId) { + String sql = + "SELECT bdmt.name,bcvr.rise_rank_time,bcvr.rank \n" + + "FROM\n" + + " boe_cases_rank bcvr\n" + + "JOIN boe_dict_major_type bdmt \n" + + " ON CAST(bdmt.code AS SIGNED) = bcvr.major_id \n" + + "WHERE bcvr.case_id = ?1 AND bcvr.deleted=0 AND bdmt.deleted=0\n" + + "ORDER BY bcvr.sys_update_time DESC \n" + + "LIMIT 2;"; + + List list = this.sqlFindList(sql, caseId); + + List resultList = new ArrayList<>(); + for (Object[] o : list) { + CasesRank casesRank = new CasesRank(); + casesRank.setMajorName(o[0].toString()); + + casesRank.setSysCreateTime(((Timestamp) o[1]).toLocalDateTime()); + casesRank.setRank(Integer.valueOf(o[2].toString())); + resultList.add(casesRank); + } + return resultList; + } + + public HashMap findViewTopCount(List authorIdList) { + String sql= + "SELECT author_id,COUNT(1) as viewTopCount\n" + + "FROM boe_cases bc JOIN boe_cases_rank bcvr ON bcvr.case_id = bc.id\n" + + "WHERE bc.author_id IN (?1) \n" + + " AND bcvr.deleted = 0 \n" + + " AND bc.deleted = 0\n" + + "GROUP BY bc.author_id;"; + List list = this.sqlFindList(sql, authorIdList); + + HashMap map = new HashMap<>(); + + list.forEach(objects -> { + String authorId = objects[0].toString(); + Integer viewTopCount = Integer.parseInt(objects[1].toString()); + map.put(authorId, viewTopCount); + }); + + return map; + } + + + public List findPopularityOfMajor(Integer pageSize, LocalDateTime startTime, LocalDateTime endTime, Long majorId) { + + String sql = + "SELECT bcr.case_id\n" + + "FROM boe_cases_rank bcr\n" + + "JOIN boe_cases bc on bcr.case_id = bc.id\n" + + "WHERE bcr.deleted=0 AND bc.deleted=0 AND bcr.rise_rank_time BETWEEN ?1 and ?2 AND major_id=?3\n" + + "ORDER BY rank\n" + + "LIMIT ?4"; + + List listFields = this.sqlFindList(sql, startTime, endTime,majorId,pageSize); + + System.out.println(listFields.get(0).getClass()); + + + List collect = listFields.stream().map(item -> { + return item.toString(); + }).collect(Collectors.toList()); + + return collect; + } +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/entity/Cases.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/entity/Cases.java index 7e933ba8..56429c33 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/entity/Cases.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/entity/Cases.java @@ -248,16 +248,7 @@ public class Cases extends BaseEntity { @Column(name = "case_value") private String caseValue; - // 种类字段1 - // 暂不上线 -// @Column(name = "sys_type1") -// private String sysType1; - //种类字段2 -// @Column(name = "sys_type2") -// private String sysType2; - //种类字段3 -// @Column(name = "sys_type3") -// private String sysType3; + /** * 最佳案例标识 @@ -272,6 +263,42 @@ public class Cases extends BaseEntity { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime excellentTime; + /** + * 上月浏览量 + */ + @Column(name = "last_month_views") + private Integer lastMonthViews; + + /** + * 上季度浏览量 + */ + @Column(name = "last_quarter_views") + private Integer lastQuarterViews; + + /** + * 上季度点赞量 + */ + @Column(name = "last_quarter_praises") + private Integer lastQuarterPraises; + + /** + * 推荐榜排序 + * */ + @Column(name = "recommend_rank") + private Integer recommendRank; + + /** + * 推送用户名称 + */ + @Column(name = "recommend_rank_push_user_name") + private String recommendRankPushUserName; + + /** + * 推送时间 + */ + @Column(name = "recommend_rank_push_time") + private LocalDateTime recommendRankPushTime; + /** * type 种类 * 区分 案例/推荐案例 @@ -279,11 +306,6 @@ public class Cases extends BaseEntity { @Transient private String type; - /** - * id - */ - - @Transient private List majorIds; @@ -296,6 +318,15 @@ public class Cases extends BaseEntity { @Transient private String refId; + @Transient + private String excellentTag; + + @Transient + private List viewRankTags; + + @Transient + private List authorTags; + public String getRefId() { return refId; } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/entity/CasesRank.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/entity/CasesRank.java new file mode 100644 index 00000000..0b254d72 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/entity/CasesRank.java @@ -0,0 +1,63 @@ +package com.xboe.module.boecase.entity; + +import com.xboe.core.SysConstant; +import com.xboe.core.orm.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import javax.persistence.Transient; +import java.time.LocalDateTime; + +/** +* 案例推荐表 +* @TableName boe_cases_rank +*/ +@Data +@Entity +@EqualsAndHashCode(callSuper = false) +@Table(name = SysConstant.TABLE_PRE+"cases_rank") +public class CasesRank extends BaseEntity { + + /** + * 案例ID + */ + @Column(name = "case_id") + private Long caseId; + + /** + * 专业ID + */ + @Column(name = "major_id") + private Long majorId; + + /** + * 排名 + */ + @Column(name = "rank") + private Integer rank; + + /** + * 浏览量每月增量 + */ + @Column(name = "monthly_increment") + private Integer monthlyIncrement; + + + /** + * 排行类型 + */ + @Column(name = "rank_type") + private Integer rankType; + + @Column(name = "rise_rank_time") + private LocalDateTime riseRankTime; + + @Transient + private String majorName; + + + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/ICasesService.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/ICasesService.java index 151af48b..962d77bf 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/ICasesService.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/ICasesService.java @@ -1,14 +1,18 @@ package com.xboe.module.boecase.service; -import java.util.List; - import com.xboe.common.PageList; import com.xboe.core.CurrentUser; import com.xboe.module.boecase.dto.*; import com.xboe.module.boecase.entity.Cases; +import com.xboe.module.boecase.vo.CaseRankingVo; +import com.xboe.module.boecase.vo.CasesQueryRecommendRankVo; import com.xboe.module.dict.entity.DictItem; import com.xboe.school.vo.CasesVo; +import javax.transaction.Transactional; +import java.time.LocalDateTime; +import java.util.List; + public interface ICasesService{ /** * 案例分页搜索,是否被推荐 @@ -101,7 +105,7 @@ public interface ICasesService{ /** * 设置或者取消优秀案例 * */ - void excellent(String id,Boolean excellent); + void excellent(String id, Boolean excellent, String excellentReason); PageList queryRecommendPageCasesV2(CasePageVo req); @@ -110,5 +114,22 @@ public interface ICasesService{ List getCaseYears(); + @Transactional + void refreshViewsRankOfMajor(); + + void refreshLastQuarterStatistics(); + + List queryRecommendRank(); + + void riseIntoRank(Long caseId, CurrentUser currentUser); + + void cancelRiseIntoRank(Long caseId); + + void adjustRank(List caseIdList); + + List queryRank(Integer pageSize, Integer rankType); + + List queryPopularityOfMajor(Integer pageSize, Long majorId, LocalDateTime month); + } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CasesServiceImpl.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CasesServiceImpl.java index c546e399..597d8181 100644 --- a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CasesServiceImpl.java +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/service/impl/CasesServiceImpl.java @@ -2,8 +2,10 @@ package com.xboe.module.boecase.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONUtil; import com.xboe.common.OrderCondition; import com.xboe.common.OrderDirection; import com.xboe.common.PageList; @@ -11,29 +13,36 @@ import com.xboe.common.utils.IDGenerator; import com.xboe.common.utils.StringUtil; import com.xboe.core.CurrentUser; import com.xboe.core.orm.*; +import com.xboe.enums.CasesRankEnum; import com.xboe.module.boecase.dao.*; import com.xboe.module.boecase.dto.*; -import com.xboe.module.boecase.entity.Cases; -import com.xboe.module.boecase.entity.CasesMajorType; -import com.xboe.module.boecase.entity.CasesRecommend; -import com.xboe.module.boecase.entity.CasesRecommendPushRecord; +import com.xboe.module.boecase.entity.*; import com.xboe.module.boecase.service.ICasesService; +import com.xboe.module.boecase.vo.CaseRankingVo; +import com.xboe.module.boecase.vo.CasesQueryRecommendRankVo; import com.xboe.module.dict.dao.SysDictionaryDao; import com.xboe.module.dict.entity.DictItem; import com.xboe.orm.CustomFieldInFilter; import com.xboe.school.vo.CasesVo; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; -import javax.transaction.Transactional; +import java.lang.reflect.Array; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import java.util.stream.IntStream; + @Slf4j @Service @@ -61,6 +70,12 @@ public class CasesServiceImpl implements ICasesService { @Autowired private CasesRecommendDao casesRecommendDao; + @Resource + private CasesRankDao casesRankDao; + + @Resource + private StringRedisTemplate stringRedisTemplate; + /** * 案例分页查询,用于门户的查询 */ @@ -357,11 +372,63 @@ public class CasesServiceImpl implements ICasesService { } } + addAuthorTagAndCaseNewTag(list); } return page; } + private void addAuthorTagAndCaseNewTag(List caseList) { + if (CollUtil.isEmpty(caseList)) { + return; + } + + List authorIdList = caseList.stream().map(Cases::getAuthorId).distinct().collect(Collectors.toList()); + HashMap excellentCountMap = casesDao.findExcellentCount(authorIdList); + HashMap viewTopCountMap = casesRankDao.findViewTopCount(authorIdList); + + + caseList.forEach(e -> { + // 获取最新的两个浏览量上榜记录 + List viewsRankRecords = casesRankDao.findViewsRankRecordByCaseId(e.getId()); + if (CollUtil.isNotEmpty(viewsRankRecords)) { + // 拼接生成浏览量排行榜的标签 + List viewRankTags = viewsRankRecords.stream().map(casesRank -> { + String pattern = casesRank.getSysCreateTime().getMonthValue() < 10 ? "yyyy年M月" : "yyyy年MM月"; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); + String time = casesRank.getSysCreateTime().format(formatter); + + // 2023年6月 经营管理类 浏览量 TOP1 + String viewRankTag = time + casesRank.getMajorName() + "类浏览量TOP" + (casesRank.getRank() + 1); + return viewRankTag; + }).collect(Collectors.toList()); + e.setViewRankTags(viewRankTags); + + } + + // 拼接生成年度最佳标签 + if (e.getExcellent()!=null && e.getExcellent()) { + e.setExcellentTag(e.getExcellentTime().format(DateTimeFormatter.ofPattern("yyyy年"))+"度最佳"); + } + + List authorTags = new ArrayList<>(); + + if (viewTopCountMap.get(e.getAuthorId())!=null){ + String authorTagOfView = "上榜浏览量TOP榜单" + viewTopCountMap.get(e.getAuthorId()) + "次"; + authorTags.add(authorTagOfView); + } + + if (excellentCountMap.get(e.getAuthorId())!=null){ + String authorTagOfExcellent = "最佳案例" + excellentCountMap.get(e.getAuthorId()) + "篇"; + authorTags.add(authorTagOfExcellent); + } + + e.setAuthorTags(authorTags); + + }); + } + + @Override public PageList queryRecommendPageCasesV2(CasePageVo caseVo) { @@ -451,6 +518,7 @@ public class CasesServiceImpl implements ICasesService { } } } + addAuthorTagAndCaseNewTag(content); pageList.setList(content); return pageList; } @@ -1078,15 +1146,266 @@ public class CasesServiceImpl implements ICasesService { } @Override - public void excellent(String id, Boolean excellent) { + public void excellent(String id, Boolean excellent, String excellentReason) { //取消时,把时间清空 if (excellent) { - casesDao.updateMultiFieldById(id, UpdateBuilder.create("excellent", excellent), UpdateBuilder.create("excellentTime", LocalDateTime.now())); + casesDao.updateMultiFieldById(id, UpdateBuilder.create("excellent", excellent), UpdateBuilder.create("excellentTime", LocalDateTime.now()), UpdateBuilder.create("excellentReason", excellentReason)); } else { - casesDao.updateMultiFieldById(id, UpdateBuilder.create("excellent", excellent), UpdateBuilder.create("excellentTime", null)); + casesDao.updateMultiFieldById(id, UpdateBuilder.create("excellent", excellent), UpdateBuilder.create("excellentTime", null), UpdateBuilder.create("excellentReason", null)); } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void refreshViewsRankOfMajor(){ + LocalDateTime riseRankTime = LocalDateTime.now().plusMonths(1).withDayOfMonth(1).minusDays(1).toLocalDate().atTime(23, 59, 59); // 上月最后一天的结束时间 + int count = casesRankDao.sqlCount("SELECT count(1) FROM boe_cases_rank WHERE deleted=0 AND rise_rank_time = ?1", riseRankTime); + if (count > 0){ + return; + } + + //获取案例当月排名 + String sql = + "SELECT bc.id,bcmt.major_id,bc.views - COALESCE(bc.last_month_views, 0) AS increment,bc.case_owner\n" + + "FROM boe_cases bc\n" + + "JOIN boe_cases_major_type bcmt ON bcmt.case_id = bc.id and bc.deleted=0"; + List caseListOfObject = casesDao.sqlFindList(sql); + + // 转为casesRank + List casesRankList = caseListOfObject.stream() + .filter(o -> Array.get(o, 3)!=null && StringUtils.isNotBlank(Array.get(o, 3).toString())) + .map(o -> { + CasesRank casesRank = new CasesRank(); + casesRank.setCaseId(Long.valueOf(Array.get(o, 0).toString())); + casesRank.setMajorId(Long.valueOf(Array.get(o, 1).toString())); + casesRank.setMonthlyIncrement(Integer.parseInt(Array.get(o, 2).toString())); + casesRank.setRiseRankTime(riseRankTime); + return casesRank; + }).collect(Collectors.toList()); + // 根据专业分类进行分组 + Map> casesRankMap = casesRankList.stream().collect(Collectors.groupingBy(CasesRank::getMajorId)); + + // 生成当前月不同分类的最新的浏览量排名 + ArrayList lastMonthRank = new ArrayList<>(); + casesRankMap.forEach((majorId, caseList) -> { + List sortedCaseList = caseList.stream() + .sorted(Comparator.comparingInt(CasesRank::getMonthlyIncrement).reversed()).limit(10) + .collect(Collectors.toList()); + + IntStream.range(0, sortedCaseList.size()) + .forEach(i -> sortedCaseList.get(i).setRank(i)); + lastMonthRank.addAll(sortedCaseList); + }); + + casesRankDao.saveList(lastMonthRank); + + casesDao.sqlUpdate("update boe_cases set last_month_views=views where deleted=0"); } + @Override + public void refreshLastQuarterStatistics() { + casesDao.sqlUpdate("update boe_cases set last_quarter_views=views where deleted=0"); + casesDao.sqlUpdate("update boe_cases set last_quarter_praise=praise where deleted=0"); + } + + @Override + public List queryRank(Integer pageSize, Integer rankType) { + if (pageSize == null) { + pageSize = 3; + } + String cacheKey = ""; + // 获取缓存key + for (CasesRankEnum enumValue : CasesRankEnum.values()) { + if (rankType == enumValue.getType()) { + cacheKey = enumValue.getCacheKey(); + } + } + + List caseListOfJson = stringRedisTemplate.opsForList().range(cacheKey, 0, -1); + + if (CollectionUtil.isNotEmpty(caseListOfJson)) { + // 缓存中存在数据 + List casesList = caseListOfJson.stream().map(item -> JSONUtil.toBean(item, CaseRankingVo.class)).collect(Collectors.toList()); + return casesList; + } + + List casesList = casesDao.findRank(pageSize,rankType); + + if (CollectionUtil.isNotEmpty(casesList)) { + // 缓存不存在数据,数据库中不存在数据,重建缓存 + List serializedCases = casesList.stream().map(item -> JSONUtil.toJsonStr(item)).collect(Collectors.toList()); + stringRedisTemplate.opsForList().rightPushAll(cacheKey, serializedCases); + stringRedisTemplate.expire(cacheKey, 10000, TimeUnit.SECONDS); + return casesList; + } + return Collections.emptyList(); + + } + + + @Override + public List queryPopularityOfMajor(Integer pageSize, Long majorId, LocalDateTime month) { + if (pageSize == null) { + pageSize = 10; + } + + LocalDateTime startTime = month.withDayOfMonth(1); + LocalDateTime endTime = month.plusMonths(1).minusDays(1); + + List caseIdList = casesRankDao.findPopularityOfMajor(pageSize, startTime, endTime,majorId); + QueryBuilder query = QueryBuilder.from(Cases.class); + query.addFilter(FieldFilters.in("id",caseIdList)); + query.addFilter(FieldFilters.eq("deleted",false)); + + List casesList = casesDao.findList(query.builder()); + + if (CollUtil.isNotEmpty(casesList)) { + List caseIds = casesList.stream().map(Cases::getId).collect(Collectors.toList()); + QueryBuilder recommendCasesQuery = QueryBuilder.from(CasesRecommendPushRecord.class); + recommendCasesQuery.addField("new CasesRecommendPushRecord(id,recommendId,caseId)"); + List subFilters = new ArrayList<>(); + subFilters.add(FieldFilters.in("caseId", caseIds)); + subFilters.add(FieldFilters.eq("pushStatus", 3)); + subFilters.add(FieldFilters.eq("deleted", Boolean.FALSE)); + + QueryBuilder queryBuilder = recommendCasesQuery.addFilters(subFilters) + .addOrder("sysCreateTime", OrderDirection.DESC) + .addGroupBy("caseId"); + List pushRecords = casesRecommendPushRecordDao.findList(queryBuilder.builder()); + if (CollUtil.isNotEmpty(pushRecords)) { + List rIds = pushRecords.stream().map(CasesRecommendPushRecord::getRecommendId).distinct().collect(Collectors.toList()); + QueryBuilder builder = QueryBuilder.from(CasesRecommend.class); + builder.addField("new CasesRecommend(id,recommendOrgName)"); + List recommendFilters = new ArrayList<>(); + recommendFilters.add(FieldFilters.eq("deleted", Boolean.FALSE)); + recommendFilters.add(FieldFilters.in("id", rIds)); + builder.addFilters(recommendFilters); + List recommands = casesRecommendDao.findList(builder.builder()); + if (CollUtil.isNotEmpty(recommands)) { + Map collect = recommands.stream().collect(Collectors.toMap(k -> k.getId(), v -> v.getRecommendOrgName())); + casesList.forEach(it -> { + CasesRecommendPushRecord one = CollUtil.findOne(pushRecords, (a) -> StrUtil.equals(a.getCaseId(), it.getId())); + if (Objects.nonNull(one)) { + String recommendOrgName = collect.get(one.getRecommendId()); + it.setBreCommend(1); + it.setRecommendOrgName(recommendOrgName); + } else { + it.setBreCommend(0); + } + }); + } + } + + addAuthorTagAndCaseNewTag(casesList); + } + + return casesList; + } + + @Override + public List queryRecommendRank() { + QueryBuilder queryBuilder = QueryBuilder.from(Cases.class); + queryBuilder.setPageSize(10); + queryBuilder.addFilter(FieldFilters.eq("deleted",false)); + queryBuilder.addFilter(FieldFilters.isNotNull("recommend_rank")); + queryBuilder.addOrder(OrderCondition.asc("recommend_rank")); + + List recommendRank = casesDao.findList(queryBuilder.builder()); + + List collect = recommendRank.stream().map(e -> { + CasesQueryRecommendRankVo casesQueryRecommendRankVo = new CasesQueryRecommendRankVo(); + casesQueryRecommendRankVo.setCaseId(e.getId()); + casesQueryRecommendRankVo.setCaseTitle(e.getTitle()); + casesQueryRecommendRankVo.setCaseAuthor(e.getAuthorName()); + casesQueryRecommendRankVo.setRecommendRankPushUserName(e.getRecommendRankPushUserName()); + casesQueryRecommendRankVo.setRecommendRankPusTime(e.getRecommendRankPushTime()); + return casesQueryRecommendRankVo; + }).collect(Collectors.toList()); + return collect; + } + + @Override + public void riseIntoRank(Long caseId, CurrentUser currentUser) { + Cases caseOfNewRise = casesDao.findOne(FieldFilters.eq("id", String.valueOf(caseId)),FieldFilters.isNull("recommend_rank"),FieldFilters.eq("deleted",false)); + if (caseOfNewRise == null) { + log.error("案例不存在或已经在榜,不可上榜", caseId); + return; + } + + int count = casesDao.sqlCount("select count(1) from boe_cases where deleted=0 and recommend_rank is not null order by recommend_rank asc limit 10"); + + if (count == 10){ + log.error("已满10条,无法添加"); + throw new RuntimeException("已满10条,无法添加"); + } + + caseOfNewRise.setRecommendRank(count); + caseOfNewRise.setRecommendRankPushUserName(currentUser.getLoginName()); + caseOfNewRise.setRecommendRankPushTime(LocalDateTime.now()); + + casesDao.update(caseOfNewRise); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void cancelRiseIntoRank(Long caseId) { + Cases caseOfCancelRise = casesDao.findOne(FieldFilters.eq("id", String.valueOf(caseId)),FieldFilters.isNotNull("recommend_rank"),FieldFilters.eq("deleted",false)); + if (caseOfCancelRise == null) { + log.error("案例不存在或已经在榜,不可下榜", caseId); + return; + } + QueryBuilder queryBuilder = QueryBuilder.from(Cases.class); + queryBuilder.setPageSize(10); + queryBuilder.addFilter(FieldFilters.eq("deleted",false)); + queryBuilder.addOrder(OrderCondition.asc("recommend_rank")); + queryBuilder.addFilter(FieldFilters.gt("recommend_rank",caseOfCancelRise.getRecommendRank())); + List recommendRank = casesDao.findList(queryBuilder.builder()); + + if (CollectionUtils.isEmpty(recommendRank)){ + return; + } + // 更新排序 + for (int i = caseOfCancelRise.getRecommendRank(); i < recommendRank.size(); i++){ + Integer currentSort = recommendRank.get(i).getRecommendRank(); + recommendRank.get(i).setRecommendRank(currentSort - 1); + casesDao.update(recommendRank.get(i)); + } + + // 取消排序 + caseOfCancelRise.setRecommendRank(null); + caseOfCancelRise.setRecommendRankPushTime(null); + caseOfCancelRise.setRecommendRankPushUserName(null); + casesDao.update(caseOfCancelRise); + } + + @Override + public void adjustRank(List caseIdList) { + QueryBuilder queryBuilder = QueryBuilder.from(Cases.class); + queryBuilder.setPageSize(10); + queryBuilder.addFilter(FieldFilters.eq("deleted",false)); + queryBuilder.addFilter(FieldFilters.isNotNull("recommend_rank")); + queryBuilder.addOrder(OrderCondition.asc("recommend_rank")); + List recommendRank = casesDao.findList(queryBuilder.builder()); + + // 遍历排序后的案例列表,并根据 caseIdList 设置或重置 recommendRank + recommendRank.forEach(caseItem -> { + int index = caseIdList.indexOf(Long.valueOf(caseItem.getId())); + // 如果 ID 存在于 caseIdList 中,则设置推荐等级为该 ID 的索引位置,否则设置为 null + if (index != -1) { + caseItem.setRecommendRank(index); + } else { + caseItem.setRecommendRank(null); + caseItem.setRecommendRankPushTime(null); + caseItem.setRecommendRankPushUserName(null); + } + }); + + // 保存对案例排序的更改 + recommendRank.forEach(casesDao::update); + } + + + } diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/vo/CaseRankingVo.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/vo/CaseRankingVo.java new file mode 100644 index 00000000..d5a3c5a6 --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/vo/CaseRankingVo.java @@ -0,0 +1,12 @@ +package com.xboe.module.boecase.vo; + +import lombok.Data; + +@Data +public class CaseRankingVo { + + private String caseId; + + private String caseTitle; + +} diff --git a/servers/boe-server-all/src/main/java/com/xboe/module/boecase/vo/CasesQueryRecommendRankVo.java b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/vo/CasesQueryRecommendRankVo.java new file mode 100644 index 00000000..5c59358a --- /dev/null +++ b/servers/boe-server-all/src/main/java/com/xboe/module/boecase/vo/CasesQueryRecommendRankVo.java @@ -0,0 +1,34 @@ +package com.xboe.module.boecase.vo; + +import lombok.Data; + +import java.time.LocalDateTime; + + +@Data +public class CasesQueryRecommendRankVo { + + /** + * 案例id + */ + private String caseId; + + /** + * 案例标题 + */ + private String caseTitle; + + private String caseAuthor; + + /** + * 推送用户名称 + */ + private String recommendRankPushUserName; + + /** + * 推送时间 + */ + private LocalDateTime recommendRankPusTime; + + +}