forked from Gitlink/microservices
Compare commits
17 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
c641ae6ab2 | |
|
|
7e5127b078 | |
|
|
0e13149981 | |
|
|
fd0d726a33 | |
|
|
58387872f0 | |
|
|
b62c7a6164 | |
|
|
62d4418679 | |
|
|
ca4c8bb614 | |
|
|
76f865624e | |
|
|
9d1d5527c8 | |
|
|
7a44a2113d | |
|
|
1bf9522d8f | |
|
|
5aa499f4de | |
|
|
35d5b1318e | |
|
|
4ccc743bd2 | |
|
|
84f624fdb7 | |
|
|
e43fb391fa |
|
|
@ -249,6 +249,18 @@ public class CacheConstants {
|
|||
return ZONE_PROJECT_SORT_BY_SCORE + "*:" + zoneId + ":*";
|
||||
}
|
||||
|
||||
/**
|
||||
* 专区统计数据缓存前缀
|
||||
*/
|
||||
private final static String ZONE_STATISTICS_KEY = "zone:statistics:";
|
||||
|
||||
/**
|
||||
* 获取专区统计数据缓存key
|
||||
*/
|
||||
public static String getZoneStatisticsKey(Long zoneId) {
|
||||
return ZONE_STATISTICS_KEY + zoneId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件打包是否完成(0:代表未完成;1:代表已完成;-1:代表打包失败,需要重新打包)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1504,6 +1504,17 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
cmsAsyncService.updateCmsPublishDataByGitLink(cmsProject, cmsDoc, branchName);
|
||||
}
|
||||
|
||||
private int deleteCmsDir(CmsProject cmsProject, CmsDoc cmsDir) {
|
||||
if (!isNoAuditRequired(cmsDir.getDeptId()) || tokenService.hasDeptPerms(cmsDir.getDeptId(), UserConstants.CMS_PERMS_DOCS_AUDIT)) {
|
||||
boolean deleteResult = cmsAsyncService.deleteCmsDocForGitLink(cmsDir, cmsProject, CmsConstants.BRANCH_MASTER);
|
||||
if (deleteResult) {
|
||||
return cmsDocMapper.deleteCmsDocById(cmsDir.getId());
|
||||
}
|
||||
throw new ServiceException("栏目删除失败");
|
||||
}
|
||||
throw new ServiceException("无权限删除栏目");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteCmsDocById(Long id) {
|
||||
CmsDoc cmsDoc = cmsDocMapper.selectCmsDocById(id);
|
||||
|
|
@ -1513,6 +1524,9 @@ public class CmsDocServiceImpl implements ICmsDocService {
|
|||
checkCmsLockStatus(cmsDoc.getDeptId());
|
||||
//2、获取专区项目
|
||||
CmsProject cmsProject = cmsProjectService.selectCmsProjectByDeptId(cmsDoc.getDeptId());
|
||||
if (cmsDoc.getIsDir()) {
|
||||
return deleteCmsDir(cmsProject, cmsDoc);
|
||||
}
|
||||
|
||||
//删除待审核、审核未通过文章——删除临时分支,拒绝合并请求
|
||||
if (DocAuditStatus.TO_BE_AUDIT.getAuditCode().equals(cmsDoc.getAuditStatus())) {
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@
|
|||
<include refid="selectCmsDocVo"/>
|
||||
<where>
|
||||
and name = #{name}
|
||||
and audit_status != 0
|
||||
and dept_id = #{deptId}
|
||||
<choose>
|
||||
<when test="dirName != null">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.microservices.zone.common.controller;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.microservices.common.core.utils.StringUtils;
|
||||
import com.microservices.common.core.web.controller.BaseController;
|
||||
import com.microservices.common.core.web.domain.AjaxResult;
|
||||
|
|
@ -19,8 +20,11 @@ import com.microservices.zone.detail.service.IZoneDetailService;
|
|||
import com.microservices.zone.member.domain.vo.ZoneMemberDataVo;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberOpenSearchVo;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberOverviewVo;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberSearchVo;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberTypeCountVo;
|
||||
import com.microservices.zone.member.domain.vo.ZonePointsTopDocVo;
|
||||
import com.microservices.zone.member.service.IZoneMemberService;
|
||||
import com.microservices.zone.member.service.IZoneMemberTypeService;
|
||||
import com.microservices.zone.member.service.IZonePointsAccountService;
|
||||
import com.microservices.zone.partners.domain.vo.ZonePartnersOverviewVo;
|
||||
import com.microservices.zone.partners.service.IZonePartnersService;
|
||||
|
|
@ -75,6 +79,8 @@ public class OpenController extends BaseController {
|
|||
@Autowired
|
||||
private IZoneMemberService zoneMemberService;
|
||||
@Autowired
|
||||
private IZoneMemberTypeService zoneMemberTypeService;
|
||||
@Autowired
|
||||
private IZonePartnersService zonePartnersService;
|
||||
@Autowired
|
||||
private IZoneProjectService zoneProjectService;
|
||||
|
|
@ -274,6 +280,41 @@ public class OpenController extends BaseController {
|
|||
return getGenericsDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取特色专区会员分类列表(含各分类下已审核会员数量)
|
||||
*/
|
||||
@GetMapping("/{zoneId}/memberType/list")
|
||||
@ApiOperation("获取特色专区会员分类列表(含已审核会员数量)")
|
||||
public GenericsTableDataInfo<ZoneMemberTypeCountVo> memberTypeList(
|
||||
@ApiParam(name = "zoneId", value = "专区Id", required = true) @PathVariable("zoneId") Long zoneId,
|
||||
@ApiParam(name = "name", value = "会员分类名称") String name) {
|
||||
List<ZoneMemberTypeCountVo> list = zoneMemberTypeService.selectZoneMemberTypeListWithCount(zoneId, name);
|
||||
return getGenericsDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取特色专区会员列表(仅已审核会员,支持名称模糊搜索及分类/等级过滤)
|
||||
*/
|
||||
@GetMapping("/{zoneId}/member/list")
|
||||
@ApiOperation("获取特色专区会员列表")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "pageNum", value = "当前页码", paramType = "query", dataType = "Integer"),
|
||||
@ApiImplicitParam(name = "pageSize", value = "每页显示记录数(未传默认100)", paramType = "query", dataType = "Integer")
|
||||
})
|
||||
public GenericsTableDataInfo<ZoneMemberDataVo> memberList(
|
||||
@ApiParam(name = "zoneId", value = "专区Id", required = true) @PathVariable("zoneId") Long zoneId,
|
||||
ZoneMemberSearchVo zoneMemberSearchVo,
|
||||
@RequestParam(value = "pageNum", required = false) Integer pageNum,
|
||||
@RequestParam(value = "pageSize", required = false) Integer pageSize) {
|
||||
zoneMemberSearchVo.setZoneId(zoneId);
|
||||
if (pageNum == null || pageNum < 1) {
|
||||
pageNum = 1;
|
||||
}
|
||||
int effectivePageSize = (pageSize == null || pageSize < 1) ? 100 : pageSize;
|
||||
PageHelper.startPage(pageNum, effectivePageSize);
|
||||
return zoneMemberService.selectOpenZoneMemberList(zoneMemberSearchVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前用户是否有该专区的权限
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -170,11 +170,11 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
|
|||
* Nthreads =Ncpu*Ucpu*(1+W/C)
|
||||
*/
|
||||
static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
|
||||
2
|
||||
5
|
||||
, 10
|
||||
, 10
|
||||
, TimeUnit.SECONDS
|
||||
, new ArrayBlockingQueue<>(100)
|
||||
, new ArrayBlockingQueue<>(10000)
|
||||
, Executors.defaultThreadFactory()
|
||||
, (r, executor) -> {
|
||||
throw new ServiceException("【%s】目前创建的人太多了,请稍后再试", DateUtils.getNowDate());
|
||||
|
|
@ -485,11 +485,6 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
|
|||
List<ZoneProject> zoneProjectList = zoneProjectMapper.selectZoneProjectList(zoneProjectSearchVo);
|
||||
for (ZoneProject zoneProject : zoneProjectList) {
|
||||
setProjectGitlinkInfoCache(zoneProject);
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("初始化gitlink项目统计的定时任务延时被中断");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -541,23 +536,26 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
|
|||
|
||||
@Override
|
||||
public List<JSONObject> getGitlinkProjectsDetail(List<String> gitLinkProjectIdList) {
|
||||
int queryCount = gitLinkProjectIdList.size() / 50 + 1;
|
||||
int total = gitLinkProjectIdList.size();
|
||||
if (total == 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 向上取整分片:修复原 size/50+1 在整除时多出一个空分片(会发出一次 ids 为空的请求)
|
||||
int queryCount = (total + 49) / 50;
|
||||
CountDownLatch countDownLatch = new CountDownLatch(queryCount);
|
||||
List<JSONObject> gitLinkProjectList = new ArrayList<>();
|
||||
// 使用线程安全的 ConcurrentLinkedQueue 收集结果:修复原 ArrayList 在多线程并发 addAll 下丢数据/越界的隐患
|
||||
ConcurrentLinkedQueue<JSONObject> gitLinkProjectQueue = new ConcurrentLinkedQueue<>();
|
||||
for (int i = 0; i < queryCount; i++) {
|
||||
int finalI = i;
|
||||
threadPoolExecutor.execute(() -> {
|
||||
try {
|
||||
int pageMaxCount = (finalI + 1) * 50;
|
||||
if (pageMaxCount >= gitLinkProjectIdList.size()) {
|
||||
pageMaxCount = gitLinkProjectIdList.size();
|
||||
}
|
||||
int pageMaxCount = Math.min((finalI + 1) * 50, total);
|
||||
List<String> tmpIdList = gitLinkProjectIdList.subList(finalI * 50, pageMaxCount);
|
||||
JSONObject result = gitLinkRequestHelper.doGet(
|
||||
ZoneGitLinkRequestUrl.PROJECTS("?ids=" + String.join(ZoneConstants.ARRAY_SPLIT_SYMBOLS, tmpIdList))
|
||||
);
|
||||
|
||||
gitLinkProjectList.addAll(result.getList(ZoneConstants.GITLINK_RESPONSE_PROJECTS_KEY, JSONObject.class));
|
||||
gitLinkProjectQueue.addAll(result.getList(ZoneConstants.GITLINK_RESPONSE_PROJECTS_KEY, JSONObject.class));
|
||||
} catch (Exception e) {
|
||||
logger.error("异步批量获取项目详细信息失败:{}", e.getMessage());
|
||||
} finally {
|
||||
|
|
@ -571,7 +569,7 @@ public class ZoneAsyncServiceImpl implements IZoneAsyncService {
|
|||
logger.error("根据gitlinkProjectId调用Gitlink接口批量获取项目详细信息失败:{}", e.getMessage());
|
||||
throw new ServiceException("获取平台项目详细信息失败");
|
||||
}
|
||||
return gitLinkProjectList;
|
||||
return new ArrayList<>(gitLinkProjectQueue);
|
||||
}
|
||||
|
||||
private Double calcProjectScore(ZoneProject zoneProject, String startDate, String endDate) {
|
||||
|
|
|
|||
|
|
@ -237,6 +237,14 @@ public interface IZoneDetailService {
|
|||
|
||||
ZoneStatisticsVo getZoneStatistics(Long zoneId);
|
||||
|
||||
/**
|
||||
* 计算专区统计数据(内部方法,用于定时任务调用)
|
||||
*
|
||||
* @param zoneId 专区ID
|
||||
* @return 统计数据
|
||||
*/
|
||||
ZoneStatisticsVo calculateZoneStatistics(Long zoneId);
|
||||
|
||||
/**
|
||||
* 判断专区是否为积分专区
|
||||
*
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import com.microservices.zone.resource.mapper.ZoneResourceTypeMapper;
|
|||
import com.microservices.zone.specialProject.domain.ZoneSpecialProject;
|
||||
import com.microservices.zone.specialProject.service.IZoneSpecialProjectService;
|
||||
import com.microservices.zone.utils.ZoneConstants;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
|
@ -67,6 +68,7 @@ import java.time.LocalDate;
|
|||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -76,6 +78,7 @@ import java.util.stream.Collectors;
|
|||
* @date 2023-05-09
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class ZoneDetailServiceImpl implements IZoneDetailService {
|
||||
@Autowired
|
||||
private ZoneDetailMapper zoneDetailMapper;
|
||||
|
|
@ -917,7 +920,7 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ZoneStatisticsVo getZoneStatistics(Long zoneId) {
|
||||
public ZoneStatisticsVo calculateZoneStatistics(Long zoneId) {
|
||||
ZoneStatisticsVo zoneStatisticsVo = new ZoneStatisticsVo();
|
||||
|
||||
ZoneDetail zoneDetail = zoneDetailMapper.selectZoneDetailById(zoneId);
|
||||
|
|
@ -982,6 +985,35 @@ public class ZoneDetailServiceImpl implements IZoneDetailService {
|
|||
return zoneStatisticsVo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZoneStatisticsVo getZoneStatistics(Long zoneId) {
|
||||
String cacheKey = CacheConstants.getZoneStatisticsKey(zoneId);
|
||||
|
||||
try {
|
||||
// 1. 尝试从Redis缓存获取
|
||||
ZoneStatisticsVo cachedStatistics = redisService.getCacheObject(cacheKey);
|
||||
if (cachedStatistics != null) {
|
||||
log.debug("专区[{}]统计数据来自缓存", zoneId);
|
||||
return cachedStatistics;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("专区[{}]统计数据缓存读取失败,执行降级逻辑", zoneId, e);
|
||||
}
|
||||
|
||||
// 2. 缓存异常或未命中,执行实时计算
|
||||
log.info("专区[{}]统计数据缓存未命中,执行实时计算", zoneId);
|
||||
ZoneStatisticsVo statistics = calculateZoneStatistics(zoneId);
|
||||
|
||||
// 3. 尝试缓存计算结果(失败不影响主流程)
|
||||
try {
|
||||
redisService.setCacheObject(cacheKey, statistics, 7200L, TimeUnit.SECONDS);
|
||||
} catch (Exception e) {
|
||||
log.warn("专区[{}]统计数据缓存写入失败", zoneId, e);
|
||||
}
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPointsZone(Long zoneId) {
|
||||
ZoneDetail zoneDetail = zoneDetailMapper.selectZoneDetailById(zoneId);
|
||||
|
|
|
|||
|
|
@ -21,4 +21,6 @@ public class ZoneMemberWithScore extends ZoneMember implements Serializable {
|
|||
private Double searchScore;
|
||||
|
||||
private String memberTypeName;
|
||||
|
||||
private String memberLevelName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package com.microservices.zone.member.domain.vo;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 专区会员分类(含已审核会员数量)对象
|
||||
*
|
||||
* @author otto
|
||||
*/
|
||||
@Data
|
||||
@ApiModel("专区会员分类(含已审核会员数量)对象")
|
||||
public class ZoneMemberTypeCountVo {
|
||||
@ApiModelProperty(value = "专区会员分类Id")
|
||||
private Long id;
|
||||
@ApiModelProperty(value = "专区会员分类名称")
|
||||
private String name;
|
||||
@ApiModelProperty(value = "专区会员分类简介")
|
||||
private String introduction;
|
||||
@ApiModelProperty(value = "排序")
|
||||
private Integer sort;
|
||||
@ApiModelProperty(value = "已审核会员数量")
|
||||
private Long memberCount;
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.microservices.zone.member.mapper;
|
|||
|
||||
import com.microservices.zone.member.domain.ZoneMember;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberSearchVo;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberTypeCountVo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
|
|
@ -121,6 +122,14 @@ public interface ZoneMemberMapper {
|
|||
*/
|
||||
Integer selectCountByZoneId(ZoneMember zoneMember);
|
||||
|
||||
/**
|
||||
* 按会员分类统计专区下已审核会员数量
|
||||
*
|
||||
* @param zoneId 专区Id
|
||||
* @return 每个分类(id)对应的已审核会员数量
|
||||
*/
|
||||
List<ZoneMemberTypeCountVo> selectAuditedMemberCountByType(@Param("zoneId") Long zoneId);
|
||||
|
||||
/**
|
||||
* 获取指定序号的会员
|
||||
*
|
||||
|
|
@ -190,4 +199,28 @@ public interface ZoneMemberMapper {
|
|||
@Param("pageSize") int pageSize);
|
||||
|
||||
Long globalSearchCount(@Param("keyword") String keyword, @Param("zoneId") Long zoneId);
|
||||
|
||||
/**
|
||||
* 全局搜索 - 扩展模糊搜索(含会员分类和等级)
|
||||
*
|
||||
* @param keyword 搜索关键词
|
||||
* @param zoneId 专区ID
|
||||
* @param offset 偏移量
|
||||
* @param pageSize 每页数量
|
||||
* @return 搜索结果列表(含评分)
|
||||
*/
|
||||
List<com.microservices.zone.member.domain.ZoneMemberWithScore> globalSearchWithScoreExtended(
|
||||
@Param("keyword") String keyword,
|
||||
@Param("zoneId") Long zoneId,
|
||||
@Param("offset") int offset,
|
||||
@Param("pageSize") int pageSize);
|
||||
|
||||
/**
|
||||
* 全局搜索 - 扩展模糊搜索计数(含会员分类和等级)
|
||||
*
|
||||
* @param keyword 搜索关键词
|
||||
* @param zoneId 专区ID
|
||||
* @return 匹配总数
|
||||
*/
|
||||
Long globalSearchCountExtended(@Param("keyword") String keyword, @Param("zoneId") Long zoneId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,14 @@ public interface IZoneMemberService {
|
|||
*/
|
||||
public GenericsTableDataInfo<ZoneMemberDetailVo> selectZoneMemberList(ZoneMemberSearchVo zoneMemberSearchVo, boolean isOpen);
|
||||
|
||||
/**
|
||||
* 查询特色专区已审核会员列表(公开接口,不分页由调用方通过 PageHelper 控制)
|
||||
*
|
||||
* @param zoneMemberSearchVo 特色专区会员查询对象(zoneId、name、memberTypeId、memberLevelId)
|
||||
* @return 特色专区会员集合
|
||||
*/
|
||||
public GenericsTableDataInfo<ZoneMemberDataVo> selectOpenZoneMemberList(ZoneMemberSearchVo zoneMemberSearchVo);
|
||||
|
||||
/**
|
||||
* 新增特色专区会员
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.microservices.zone.member.service;
|
||||
|
||||
import com.microservices.zone.member.domain.ZoneMemberType;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberTypeCountVo;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberTypeUpdateVo;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -28,6 +29,15 @@ public interface IZoneMemberTypeService {
|
|||
*/
|
||||
public List<ZoneMemberType> selectZoneMemberTypeList(ZoneMemberType zoneMemberType);
|
||||
|
||||
/**
|
||||
* 查询特色专区会员分类列表(含各分类下已审核会员数量)
|
||||
*
|
||||
* @param zoneId 专区Id
|
||||
* @param name 会员分类名称(模糊搜索,可为空)
|
||||
* @return 会员分类集合(含已审核会员数量)
|
||||
*/
|
||||
public List<ZoneMemberTypeCountVo> selectZoneMemberTypeListWithCount(Long zoneId, String name);
|
||||
|
||||
/**
|
||||
* 新增特色专区会员分类
|
||||
*
|
||||
|
|
|
|||
|
|
@ -129,6 +129,26 @@ public class ZoneMemberServiceImpl implements IZoneMemberService {
|
|||
return PageUtils.toPage(zoneMemberList, this::toZoneMemberDetailVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询特色专区已审核会员列表(公开接口)
|
||||
* 不做数据范围过滤,分页由调用方通过 PageHelper 设置。
|
||||
*
|
||||
* @param zoneMemberSearchVo 特色专区会员查询对象
|
||||
* @return 特色专区会员集合
|
||||
*/
|
||||
@Override
|
||||
public GenericsTableDataInfo<ZoneMemberDataVo> selectOpenZoneMemberList(ZoneMemberSearchVo zoneMemberSearchVo) {
|
||||
zoneMemberSearchVo.setIsAudit(ZoneConstants.MEMBER_AUDIT_PASS);
|
||||
zoneMemberSearchVo.setIsOpen(true);
|
||||
List<ZoneMember> zoneMemberList = zoneMemberMapper.selectZoneMemberList(zoneMemberSearchVo);
|
||||
return PageUtils.toPage(zoneMemberList, this::toOpenZoneMemberDataVo);
|
||||
}
|
||||
|
||||
private ZoneMemberDataVo toOpenZoneMemberDataVo(ZoneMember zoneMember) {
|
||||
String login = zoneMember.getSysUser() != null ? zoneMember.getSysUser().getUserName() : null;
|
||||
return zoneMember.toZoneMemberDataVo(login);
|
||||
}
|
||||
|
||||
private ZoneMemberDetailVo toZoneMemberDetailVo(ZoneMember zoneMember) {
|
||||
ZoneMemberDetailVo zoneMemberDetailVo = new ZoneMemberDetailVo();
|
||||
BeanUtils.copyProperties(zoneMember, zoneMemberDetailVo);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.microservices.zone.detail.service.IZoneDetailService;
|
|||
import com.microservices.zone.member.domain.ZoneMember;
|
||||
import com.microservices.zone.member.domain.ZoneMemberType;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberSearchVo;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberTypeCountVo;
|
||||
import com.microservices.zone.member.domain.vo.ZoneMemberTypeUpdateVo;
|
||||
import com.microservices.zone.member.mapper.ZoneMemberMapper;
|
||||
import com.microservices.zone.member.mapper.ZoneMemberTypeMapper;
|
||||
|
|
@ -20,7 +21,9 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -67,6 +70,37 @@ public class ZoneMemberTypeServiceImpl implements IZoneMemberTypeService {
|
|||
return zoneMemberTypeMapper.selectZoneMemberTypeList(zoneMemberType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询特色专区会员分类列表(含各分类下已审核会员数量)
|
||||
*
|
||||
* @param zoneId 专区Id
|
||||
* @param name 会员分类名称(模糊搜索,可为空)
|
||||
* @return 会员分类集合(含已审核会员数量)
|
||||
*/
|
||||
@Override
|
||||
public List<ZoneMemberTypeCountVo> selectZoneMemberTypeListWithCount(Long zoneId, String name) {
|
||||
ZoneMemberType query = new ZoneMemberType();
|
||||
query.setZoneId(zoneId);
|
||||
query.setName(name);
|
||||
List<ZoneMemberType> zoneMemberTypeList = zoneMemberTypeMapper.selectZoneMemberTypeList(query);
|
||||
if (zoneMemberTypeList == null || zoneMemberTypeList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Map<Long, Long> countMap = zoneMemberMapper.selectAuditedMemberCountByType(zoneId).stream()
|
||||
.collect(Collectors.toMap(ZoneMemberTypeCountVo::getId, ZoneMemberTypeCountVo::getMemberCount));
|
||||
List<ZoneMemberTypeCountVo> result = new ArrayList<>(zoneMemberTypeList.size());
|
||||
for (ZoneMemberType zoneMemberType : zoneMemberTypeList) {
|
||||
ZoneMemberTypeCountVo vo = new ZoneMemberTypeCountVo();
|
||||
vo.setId(zoneMemberType.getId());
|
||||
vo.setName(zoneMemberType.getName());
|
||||
vo.setIntroduction(zoneMemberType.getIntroduction());
|
||||
vo.setSort(zoneMemberType.getSort());
|
||||
vo.setMemberCount(countMap.getOrDefault(zoneMemberType.getId(), 0L));
|
||||
result.add(vo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增特色专区会员类别
|
||||
*
|
||||
|
|
|
|||
|
|
@ -128,44 +128,51 @@ public class ZoneProjectServiceImpl implements IZoneProjectService {
|
|||
|
||||
@Override
|
||||
public List<ZoneProjectDataVo> toZoneProjectDataVoList(List<ZoneProject> zoneProjectList) {
|
||||
List<ZoneProjectDataVo> zoneProjectDataVoList = new ArrayList<>();
|
||||
if (zoneProjectList.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 原方案为遍历项目聚合再逐一从GitLink获取对应项目详情,此方案延时过高,调整为根据项目Id批量获取GitLink项目信息
|
||||
List<String> gitLinkProjectIdList = zoneProjectList
|
||||
.stream()
|
||||
.map(x -> String.valueOf(x.getGitlinkProjectId()))
|
||||
.collect(Collectors.toList());
|
||||
List<JSONObject> gitLinkProjectList = zoneAsyncService.getGitlinkProjectsDetail(gitLinkProjectIdList);
|
||||
List<String> resultGitLinkProjectIdList = gitLinkProjectList
|
||||
.stream()
|
||||
.map(x -> x.getString("id"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//检查查询的GitLink项目Id列表和需查询的项目Id列表是否一致,不一致需要考虑是否该项目在GitLink中设置为私有或者删除了,此时需要清理该项目
|
||||
gitLinkProjectIdList.removeAll(resultGitLinkProjectIdList);
|
||||
if (!gitLinkProjectIdList.isEmpty()) {
|
||||
for (String gitLinkProjectId : gitLinkProjectIdList) {
|
||||
Long projectId = zoneProjectList
|
||||
.stream()
|
||||
.filter(x -> gitLinkProjectId.equals(String.valueOf(x.getGitlinkProjectId())))
|
||||
.map(ZoneProject::getId)
|
||||
.findFirst().orElse(null);
|
||||
if (projectId == null) {
|
||||
throw new ServiceException("该GitLink项目对应的项目聚合不存在(GitLink项目Id[" + gitLinkProjectId + "])");
|
||||
}
|
||||
logger.info("GitLink中未查找到对应项目(GitLink项目Id为:{}),对该项目进行删除(项目Id为:{})"
|
||||
, gitLinkProjectId
|
||||
, projectId);
|
||||
deleteZoneProjectById(projectId);
|
||||
zoneProjectList = zoneProjectList.stream().filter(x -> !x.getId().equals(projectId)).collect(Collectors.toList());
|
||||
}
|
||||
// 【性能优化】整页 zoneId 恒定:getGitlinkProjectKeys(每次2次DB) 与字典缓存(每次1次Redis) 提到循环外,整页只查一次
|
||||
String[] projectKeys = getGitlinkProjectKeys(zoneProjectList.get(0).getZoneId());
|
||||
List<SysDictData> sysDictDataList = DictUtils.getDictCache(CacheConstants.SYS_DICT_GITLINK_PROJECT_PROPERTIES);
|
||||
|
||||
// 【性能优化】构建索引Map,将原本逐项 O(N²) 的 stream 过滤改为 O(1) 查表
|
||||
Map<Long, JSONObject> gitLinkByProjectId = new HashMap<>(gitLinkProjectList.size() * 2);
|
||||
Set<String> returnedGitLinkIds = new HashSet<>(gitLinkProjectList.size() * 2);
|
||||
for (JSONObject jp : gitLinkProjectList) {
|
||||
gitLinkByProjectId.put(jp.getLong("id"), jp);
|
||||
returnedGitLinkIds.add(jp.getString("id"));
|
||||
}
|
||||
|
||||
//检查查询的GitLink项目Id列表和需查询的项目Id列表是否一致,不一致需要考虑是否该项目在GitLink中设置为私有或者删除了,此时需要清理该项目
|
||||
//(直接遍历zoneProject按gitlinkId查Set,替代原本逐项的stream过滤;删除时仅记录id并在富化循环中跳过,不原地修改zoneProjectList,以免影响外层PageHelper的分页total统计)
|
||||
Set<Long> removedProjectIds = new HashSet<>();
|
||||
for (ZoneProject zp : zoneProjectList) {
|
||||
if (returnedGitLinkIds.contains(String.valueOf(zp.getGitlinkProjectId()))) {
|
||||
continue;
|
||||
}
|
||||
if (removedProjectIds.contains(zp.getId())) {
|
||||
continue;
|
||||
}
|
||||
logger.info("GitLink中未查找到对应项目(GitLink项目Id为:{}),对该项目进行删除(项目Id为:{})"
|
||||
, zp.getGitlinkProjectId()
|
||||
, zp.getId());
|
||||
deleteZoneProjectById(zp.getId());
|
||||
removedProjectIds.add(zp.getId());
|
||||
}
|
||||
|
||||
List<ZoneProjectDataVo> zoneProjectDataVoList = new ArrayList<>(zoneProjectList.size());
|
||||
for (ZoneProject zoneProject : zoneProjectList) {
|
||||
JSONObject gitLinkProject = gitLinkProjectList
|
||||
.stream()
|
||||
.filter(x -> zoneProject.getGitlinkProjectId().equals(x.getLong("id")))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (removedProjectIds.contains(zoneProject.getId())) {
|
||||
continue;
|
||||
}
|
||||
JSONObject gitLinkProject = gitLinkByProjectId.get(zoneProject.getGitlinkProjectId());
|
||||
if (gitLinkProject == null) {
|
||||
throw new ServiceException("该项目聚合对应的GitLink项目不存在(项目Id[" + zoneProject.getId() + "])");
|
||||
}
|
||||
|
|
@ -179,7 +186,7 @@ public class ZoneProjectServiceImpl implements IZoneProjectService {
|
|||
zoneProjectMapper.updateZoneProject(zoneProject);
|
||||
}
|
||||
}
|
||||
zoneProjectDataVoList.add(zoneProjectToZoneProjectDataVo(zoneProject, gitLinkProject));
|
||||
zoneProjectDataVoList.add(zoneProjectToZoneProjectDataVo(zoneProject, gitLinkProject, projectKeys, sysDictDataList));
|
||||
}
|
||||
return zoneProjectDataVoList;
|
||||
}
|
||||
|
|
@ -234,6 +241,16 @@ public class ZoneProjectServiceImpl implements IZoneProjectService {
|
|||
}
|
||||
|
||||
private ZoneProjectDataVo zoneProjectToZoneProjectDataVo(ZoneProject zoneProject, JSONObject gitLinkProject) {
|
||||
// 单项查询路径(如 selectZoneProjectDataById):在此计算 keys 与字典,再委托给 4 参重载
|
||||
return zoneProjectToZoneProjectDataVo(
|
||||
zoneProject,
|
||||
gitLinkProject,
|
||||
getGitlinkProjectKeys(zoneProject.getZoneId()),
|
||||
DictUtils.getDictCache(CacheConstants.SYS_DICT_GITLINK_PROJECT_PROPERTIES));
|
||||
}
|
||||
|
||||
private ZoneProjectDataVo zoneProjectToZoneProjectDataVo(ZoneProject zoneProject, JSONObject gitLinkProject,
|
||||
String[] projectKeys, List<SysDictData> sysDictDataList) {
|
||||
//每次从GitLink查询项目时自动更新项目名称和项目标识
|
||||
updateProjectNameAndFullName(gitLinkProject, zoneProject);
|
||||
ZoneProjectDataVo zoneProjectDataVo = zoneProject.toZoneProjectDataVo();
|
||||
|
|
@ -262,9 +279,7 @@ public class ZoneProjectServiceImpl implements IZoneProjectService {
|
|||
projectPropertiesVo.put("description", description);
|
||||
projectKeyName.put("description", "项目简介");
|
||||
|
||||
String[] projectKeys = getGitlinkProjectKeys(zoneProject.getZoneId());
|
||||
//当专区设置的项目需展示的Key时通过Key获取值,否则默认获取相关值
|
||||
List<SysDictData> sysDictDataList = DictUtils.getDictCache(CacheConstants.SYS_DICT_GITLINK_PROJECT_PROPERTIES);
|
||||
//当专区设置的项目需展示的Key时通过Key获取值,否则默认获取相关值(projectKeys 与 sysDictDataList 已由调用方按页预算并传入,避免逐项查询)
|
||||
if (sysDictDataList != null) {
|
||||
for (String projectKey : projectKeys) {
|
||||
SysDictData sysDictData = sysDictDataList.stream().filter(x -> projectKey.equals(x.getDictValue())).findFirst().orElse(null);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,6 @@ public class GlobalSearchRequest implements Serializable {
|
|||
private Integer pageSize = 20;
|
||||
|
||||
|
||||
@ApiModelProperty(value = "额外搜索参数(项目类型:projectTypeId,topic)")
|
||||
@ApiModelProperty(value = "额外搜索参数(1. 项目搜索:可传入projectTypeId搜索项目分裂,传入topic搜索项目标签;2. 会员搜索:传入supportMemberCategoryLevelSearch值为true可支持通过关键词模糊搜索会员分类和会员等级)")
|
||||
private JSONObject extraSearchParam;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,8 +117,23 @@ public class GlobalSearchServiceImpl implements IGlobalSearchService {
|
|||
if (searchTypes.contains("member")) {
|
||||
List<GlobalSearchResult> memberResults = searchZoneMembers(request);
|
||||
allResults.addAll(memberResults);
|
||||
Long searchMemberCount = zoneMemberMapper.globalSearchCount(request.getKeyword(), request.getZoneId());
|
||||
|
||||
// 检查是否启用扩展搜索(支持会员分类和等级模糊搜索)
|
||||
boolean supportMemberCategoryLevelSearch = false;
|
||||
if (request.getExtraSearchParam() != null
|
||||
&& request.getExtraSearchParam().containsKey("supportMemberCategoryLevelSearch")) {
|
||||
supportMemberCategoryLevelSearch = request.getExtraSearchParam()
|
||||
.getBoolean("supportMemberCategoryLevelSearch");
|
||||
}
|
||||
|
||||
Long searchMemberCount;
|
||||
if (supportMemberCategoryLevelSearch) {
|
||||
searchMemberCount = zoneMemberMapper.globalSearchCountExtended(request.getKeyword(), request.getZoneId());
|
||||
} else {
|
||||
searchMemberCount = zoneMemberMapper.globalSearchCount(request.getKeyword(), request.getZoneId());
|
||||
}
|
||||
typeCounts.put("member", searchMemberCount);
|
||||
|
||||
searchTotal += searchMemberCount;
|
||||
}
|
||||
|
||||
|
|
@ -347,12 +362,31 @@ public class GlobalSearchServiceImpl implements IGlobalSearchService {
|
|||
private List<GlobalSearchResult> searchZoneMembers(GlobalSearchRequest request) {
|
||||
List<GlobalSearchResult> results = new ArrayList<>();
|
||||
try {
|
||||
List<ZoneMemberWithScore> members = zoneMemberMapper.globalSearchWithScore(
|
||||
request.getKeyword(),
|
||||
request.getZoneId(),
|
||||
(request.getPageNum() - 1) * request.getPageSize(),
|
||||
request.getPageSize()
|
||||
);
|
||||
// 检查是否启用扩展搜索(支持会员分类和等级模糊搜索)
|
||||
boolean supportMemberCategoryLevelSearch = false;
|
||||
if (request.getExtraSearchParam() != null
|
||||
&& request.getExtraSearchParam().containsKey("supportMemberCategoryLevelSearch")) {
|
||||
supportMemberCategoryLevelSearch = request.getExtraSearchParam()
|
||||
.getBoolean("supportMemberCategoryLevelSearch");
|
||||
}
|
||||
|
||||
int offset = (request.getPageNum() - 1) * request.getPageSize();
|
||||
List<ZoneMemberWithScore> members;
|
||||
if (supportMemberCategoryLevelSearch) {
|
||||
members = zoneMemberMapper.globalSearchWithScoreExtended(
|
||||
request.getKeyword(),
|
||||
request.getZoneId(),
|
||||
offset,
|
||||
request.getPageSize()
|
||||
);
|
||||
} else {
|
||||
members = zoneMemberMapper.globalSearchWithScore(
|
||||
request.getKeyword(),
|
||||
request.getZoneId(),
|
||||
offset,
|
||||
request.getPageSize()
|
||||
);
|
||||
}
|
||||
|
||||
for (ZoneMemberWithScore member : members) {
|
||||
GlobalSearchResult result = new GlobalSearchResult();
|
||||
|
|
@ -394,6 +428,9 @@ public class GlobalSearchServiceImpl implements IGlobalSearchService {
|
|||
if (StringUtils.isNotEmpty(member.getMemberTypeName())) {
|
||||
extendData.put("memberTypeName", member.getMemberTypeName());
|
||||
}
|
||||
if (StringUtils.isNotEmpty(member.getMemberLevelName())) {
|
||||
extendData.put("memberLevelName", member.getMemberLevelName());
|
||||
}
|
||||
if (member.getSysUser() != null) {
|
||||
extendData.put("username", member.getSysUser().getUserName());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
package com.microservices.zone.task;
|
||||
|
||||
import com.microservices.common.core.constant.CacheConstants;
|
||||
import com.microservices.common.redis.service.RedisService;
|
||||
import com.microservices.zone.detail.domain.vo.ZoneBaseDataVo;
|
||||
import com.microservices.zone.detail.service.IZoneDetailService;
|
||||
import com.microservices.zone.project.domain.vo.ZoneStatisticsVo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 专区统计数据预计算定时任务
|
||||
*/
|
||||
@Component
|
||||
public class ZoneStatisticsJob {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ZoneStatisticsJob.class);
|
||||
|
||||
@Autowired
|
||||
private IZoneDetailService zoneDetailService;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
/**
|
||||
* 应用启动时执行专区统计数据预计算
|
||||
* 确保服务启动后接口立即可用
|
||||
*/
|
||||
@PostConstruct
|
||||
public void initZoneStatistics() {
|
||||
logger.info("应用启动,执行专区统计数据初始化");
|
||||
calculateZoneStatistics();
|
||||
}
|
||||
|
||||
/**
|
||||
* 每小时执行一次专区统计数据预计算
|
||||
* cron表达式: 每小时的第5分钟执行
|
||||
*/
|
||||
@Scheduled(cron = "0 5 * * * ?")
|
||||
public void calculateZoneStatistics() {
|
||||
logger.info("开始执行专区统计数据预计算任务");
|
||||
|
||||
try {
|
||||
// 获取所有已发布的专区列表
|
||||
List<ZoneBaseDataVo> zoneList = zoneDetailService.selectPublishedZoneBaseList();
|
||||
logger.info("发现已发布专区数量: {}", zoneList.size());
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
for (ZoneBaseDataVo zone : zoneList) {
|
||||
try {
|
||||
// 计算单个专区的统计数据
|
||||
ZoneStatisticsVo statistics = zoneDetailService.calculateZoneStatistics(zone.getId());
|
||||
|
||||
// 存储到Redis缓存
|
||||
String cacheKey = CacheConstants.getZoneStatisticsKey(zone.getId());
|
||||
redisService.setCacheObject(cacheKey, statistics, 7200L, TimeUnit.SECONDS);
|
||||
|
||||
successCount++;
|
||||
logger.debug("专区[{}]统计数据计算完成", zone.getId());
|
||||
|
||||
} catch (Exception e) {
|
||||
failCount++;
|
||||
logger.error("专区[{}]统计数据计算失败", zone.getId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("专区统计数据预计算任务完成 - 成功: {}, 失败: {}", successCount, failCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("专区统计数据预计算任务执行失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -105,6 +105,7 @@
|
|||
id="ZoneMemberWithScoreResult" extends="ZoneMemberResult">
|
||||
<result property="searchScore" column="search_score"/>
|
||||
<result property="memberTypeName" column="typeName"/>
|
||||
<result property="memberLevelName" column="levelName"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectZoneMemberAliasVo">
|
||||
|
|
@ -368,6 +369,14 @@
|
|||
and zm.member_type_id = #{memberTypeId}
|
||||
and zm.member_level_id = #{memberLevelId}
|
||||
</select>
|
||||
<select id="selectAuditedMemberCountByType"
|
||||
resultType="com.microservices.zone.member.domain.vo.ZoneMemberTypeCountVo">
|
||||
select member_type_id as id, count(0) as memberCount
|
||||
from zone_member
|
||||
where zone_id = #{zoneId}
|
||||
and is_audit = '1'
|
||||
group by member_type_id
|
||||
</select>
|
||||
<select id="selectZoneMemberByOrderAndZoneId" resultMap="ZoneMemberResult">
|
||||
<include refid="selectZoneMemberVo"/>
|
||||
where zone_id=#{zoneMember.zoneId}
|
||||
|
|
@ -534,4 +543,39 @@
|
|||
OR u.user_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
)
|
||||
</select>
|
||||
|
||||
<!-- 全局搜索 - 扩展模糊搜索(含会员分类和等级) -->
|
||||
<select id="globalSearchWithScoreExtended" resultMap="ZoneMemberWithScoreResult">
|
||||
<include refid="selectZoneMemberWithSearchVo"/>
|
||||
WHERE zm.zone_id = #{zoneId}
|
||||
AND zm.is_audit = '1'
|
||||
AND zm.del_flag = '0'
|
||||
AND (
|
||||
MATCH(zm.name) AGAINST(#{keyword} IN BOOLEAN MODE)
|
||||
OR zm.name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR u.user_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR zmt.name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR zml.name LIKE CONCAT('%', #{keyword}, '%')
|
||||
)
|
||||
ORDER BY search_score DESC, zm.create_time DESC
|
||||
LIMIT #{offset}, #{pageSize}
|
||||
</select>
|
||||
|
||||
<select id="globalSearchCountExtended" resultType="java.lang.Long">
|
||||
SELECT COUNT(*)
|
||||
FROM zone_member zm
|
||||
left join sys_user u on u.user_id = zm.user_id
|
||||
left join zone_member_level zml on zml.id = zm.member_level_id
|
||||
left join zone_member_type zmt on zmt.id = zm.member_type_id
|
||||
WHERE zm.zone_id = #{zoneId}
|
||||
AND zm.is_audit = '1'
|
||||
AND zm.del_flag = '0'
|
||||
AND (
|
||||
MATCH(zm.name) AGAINST(#{keyword} IN BOOLEAN MODE)
|
||||
OR zm.name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR u.user_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR zmt.name LIKE CONCAT('%', #{keyword}, '%')
|
||||
OR zml.name LIKE CONCAT('%', #{keyword}, '%')
|
||||
)
|
||||
</select>
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue