feat(REQ-2106): 调整

This commit is contained in:
chenwenjian 2024-04-20 16:55:35 +08:00
parent 58accea015
commit 244bae35fc
4 changed files with 166 additions and 38 deletions

View File

@ -16,6 +16,7 @@ import java.util.Map;
@NoArgsConstructor
@AllArgsConstructor
public class JoinedWorkspaceOuJob {
/**
* 加入的项目部及在该项目部下担任的所有岗位
*/
@ -26,4 +27,14 @@ public class JoinedWorkspaceOuJob {
*/
private Map<Long, List<String>> ouJobMap;
/**
* 加入的单位及在该单位加入的项目部
*/
private Map<Long, List<Long>> ouWorkspaceMap;
/**
* 加入的项目部及在该项目部加入的单位
*/
private Map<Long, List<Long>> workspaceOuMap;
}

View File

@ -8,9 +8,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -27,30 +25,130 @@ public enum MaterialTargetUserTypeEnum {
ALL("ALL", "全部用户") {
@Override
public boolean isDeliverRequired(List<Long> workspaceIds, List<Long> ouIds, List<String> jobCodes, JoinedWorkspaceOuJob workspaceOuJob) {
public boolean isDeliverRequired(List<Long> workspaceIds, List<Long> ouIds, List<String> jobCodes,
Long loginWorkspaceId, Long loginOuid,
JoinedWorkspaceOuJob workspaceOuJob) {
return true;
}
},
PROJECT("PROJECT", "按照项目部") {
@Override
public boolean isDeliverRequired(List<Long> workspaceIds, List<Long> ouIds, List<String> jobCodes, JoinedWorkspaceOuJob workspaceOuJob) {
// 若workspaceIds和jobCodes都为空则表示不限制否则需要满足配置的workspaceIds和jobCodes与用户加入的workspaceJob的keyvalue均存在交集
public boolean isDeliverRequired(List<Long> workspaceIds, List<Long> ouIds, List<String> jobCodes,
Long loginWorkspaceId, Long loginOuid,
JoinedWorkspaceOuJob workspaceOuJob) {
Map<Long, List<String>> workspaceJobMap = workspaceOuJob.getWorkspaceJobMap();
log.info("投放项目部:{},投放岗位岗位:{},用户加入项目部及担任岗位:{}", JSONUtil.toJsonStr(workspaceIds), JSONUtil.toJsonStr(jobCodes), JSONUtil.toJsonStr(workspaceJobMap));
return CollectionUtils.isEmpty(workspaceIds) ? CollectionUtils.isEmpty(jobCodes) || jobCodes.stream().anyMatch(jobCode -> workspaceJobMap.values().stream().flatMap(List::stream).distinct().collect(Collectors.toList()).contains(jobCode))
: workspaceIds.stream().anyMatch(workspaceId -> CollectionUtils.isEmpty(jobCodes) || workspaceJobMap.get(workspaceId).stream().anyMatch(jobCodes::contains));
log.info("投放项目部:{},投放岗位岗位:{},当前登录单位:{},用户加入项目部及担任岗位:{},单位加入的项目部:{},项目部下的单位:{}",
JSONUtil.toJsonStr(ouIds), JSONUtil.toJsonStr(jobCodes),
JSONUtil.toJsonStr(loginOuid),
JSONUtil.toJsonStr(workspaceJobMap),
JSONUtil.toJsonStr(workspaceOuJob.getOuWorkspaceMap()),
JSONUtil.toJsonStr(workspaceOuJob.getWorkspaceOuMap()));
// 配置全部项目部
if (CollectionUtils.isEmpty(workspaceIds)) {
// 未加入任何项目部
if (Objects.isNull(workspaceJobMap) || workspaceJobMap.isEmpty()){
return false;
}
if (Objects.isNull(loginOuid) || loginOuid == 0L){
if (CollectionUtils.isEmpty(jobCodes)){
return true;
}
return jobCodes.stream().anyMatch(jobCode -> workspaceJobMap.values().stream().anyMatch(jobCodeList -> jobCodeList.contains(jobCode)));
}else {
// 登录了具体的单位且以纯单位身份登录
return !CollectionUtils.isEmpty(workspaceOuJob.getOuWorkspaceMap().get(loginOuid));
}
}
// 若配置了具体项目部
if (Objects.isNull(loginOuid) || loginOuid == 0L) {
// 加入的全部项目部与配置的项目部有交集
if (workspaceIds.stream().anyMatch(workspaceJobMap::containsKey)){
if (CollectionUtils.isEmpty(jobCodes)){
return true;
}
return workspaceIds.stream().anyMatch(workspaceId -> workspaceJobMap.get(workspaceId).stream().anyMatch(jobCodes::contains));
}
return false;
} else {
// 给定了具体登录单位
// 登录了具体的单位且以纯单位身份登录
if (CollectionUtils.isEmpty(workspaceOuJob.getOuWorkspaceMap().get(loginOuid))){
return false;
}
// 以项目部下单位登录
// 加入的全部项目部与配置的项目部有交集
if (workspaceIds.stream().anyMatch(workspaceJobMap::containsKey)){
if (CollectionUtils.isEmpty(jobCodes)){
return true;
}
return workspaceIds.stream().anyMatch(workspaceId -> workspaceJobMap.get(workspaceId).stream().anyMatch(jobCodes::contains));
}
return false;
}
}
},
UNIT("UNIT", "按照企业") {
UNIT("UNIT", "按照单位") {
@Override
public boolean isDeliverRequired(List<Long> workspaceIds, List<Long> ouIds, List<String> jobCodes, JoinedWorkspaceOuJob workspaceOuJob) {
// 若workspaceIds和jobCodes都为空则表示不限制否则需要满足配置的workspaceIds和jobCodes与用户加入的workspaceJob的keyvalue均存在交集
public boolean isDeliverRequired(List<Long> workspaceIds, List<Long> ouIds, List<String> jobCodes,
Long loginWorkspaceId, Long loginOuid,
JoinedWorkspaceOuJob workspaceOuJob) {
Map<Long, List<String>> ouJobMap = workspaceOuJob.getOuJobMap();
log.info("投放单位:{},投放岗位岗位:{},用户加入单位及担任岗位:{}", JSONUtil.toJsonStr(workspaceIds), JSONUtil.toJsonStr(jobCodes), JSONUtil.toJsonStr(ouJobMap));
return CollectionUtils.isEmpty(ouIds) ? CollectionUtils.isEmpty(jobCodes) || jobCodes.stream().anyMatch(jobCode -> ouJobMap.values().stream().flatMap(List::stream).distinct().collect(Collectors.toList()).contains(jobCode))
: ouIds.stream().anyMatch(ouId -> CollectionUtils.isEmpty(jobCodes) || ouJobMap.get(ouId).stream().anyMatch(jobCodes::contains));
log.info("投放单位:{},投放岗位岗位:{},当前登录单位:{},用户加入单位及担任岗位:{}",
JSONUtil.toJsonStr(ouIds), JSONUtil.toJsonStr(jobCodes),
JSONUtil.toJsonStr(loginOuid),
JSONUtil.toJsonStr(ouJobMap));
// 判断配置的单位和岗位是否为空
if (CollectionUtils.isEmpty(ouIds)) {
// 若配置的单位为空即配置了全部单位
if (CollectionUtils.isEmpty(jobCodes)) {
// 若配置的岗位也为空即配置了所有岗位则不限制投放
return true;
} else {
// 若配置了具体岗位检查用户加入的所有单位下的岗位是否包含任一配置岗位
if (Objects.isNull(loginOuid) || loginOuid == 0L){
return jobCodes.stream().anyMatch(jobCode -> ouJobMap.values().stream()
.flatMap(List::stream)
.distinct()
.collect(Collectors.toList())
.contains(jobCode));
}else {
return jobCodes.stream().anyMatch(jobCode -> ouJobMap.get(loginOuid).stream().anyMatch(jobCodes::contains));
}
}
}
// 若配置了具体单位
if (Objects.isNull(loginOuid) || loginOuid == 0L) {
// 若登录单位为空工人APP登录检查用户加入的单位是否与配置单位有交集
if (ouIds.stream().anyMatch(ouJobMap::containsKey)){
if (CollectionUtils.isEmpty(jobCodes)){
return true;
}
return ouIds.stream().anyMatch(o -> ouJobMap.get(o).stream().anyMatch(jobCodes::contains));
}
return false;
} else {
// 若登录单位不为空管理APP或CMS登录检查登录单位是否与配置单位有交集
if (!ouIds.contains(loginOuid)) {
return false;
}
if (CollectionUtils.isEmpty(jobCodes)) {
// 若配置了所有岗位由于登录单位已满足条件此时无需再检查岗位
return true;
} else {
// 若配置了具体岗位检查登录单位下担任的岗位是否包含任一配置岗位
return !CollectionUtils.isEmpty(ouJobMap.get(loginOuid)) && jobCodes.stream().anyMatch(jobCode -> ouJobMap.get(loginOuid).stream().anyMatch(jobCodes::contains));
}
}
}
};
@ -64,10 +162,14 @@ public enum MaterialTargetUserTypeEnum {
* @param workspaceIds 配置的投放项目部
* @param ouIds 配置的投放单位
* @param jobCodes 配置的投放岗位
* @param loginWorkspaceId 当前登录的项目部
* @param loginOuid 当前登录的单位
* @param workspaceOuJob 用户参与的项目部和单位及其岗位
* @return 是否需要投放
*/
public abstract boolean isDeliverRequired(List<Long> workspaceIds, List<Long> ouIds, List<String> jobCodes, JoinedWorkspaceOuJob workspaceOuJob);
public abstract boolean isDeliverRequired(List<Long> workspaceIds, List<Long> ouIds, List<String> jobCodes,
Long loginWorkspaceId, Long loginOuid,
JoinedWorkspaceOuJob workspaceOuJob);
public static MaterialTargetUserTypeEnum getByCode(String code) {
if (StringUtils.isEmpty(code)) {

View File

@ -7,7 +7,6 @@ import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import java.util.List;
/**
* @author chenwenjian
@ -33,20 +32,20 @@ public class ListMaterialByBannerCodeReq {
private Long personId;
/**
* 项目部id用于筛选相关运营素材是否配置给了该项目部人员
* 当前登录项目部id用于筛选相关运营素材是否配置给了该项目部人员
*/
private List<Long> workspaceIds;
private Long workspaceId;
/**
* 单位id用于筛选相关运营素材是否配置给了该单位人员
* 当前登录单位id用于筛选相关运营素材是否配置给了该单位人员
*/
private List<Long> ouIds;
private Long ouId;
/**
* 岗位编码搭配{@code workspaceIds}{@code unitIds}使用
* 用于筛选相关运营素材是否配置给了指定项目部下指定岗位人员
* 或指定单位下指定岗位人员
*/
private List<String> jobCodes;
private String jobCode;
}

View File

@ -104,7 +104,7 @@ public class MaterialServiceImpl extends ServiceImpl<MaterialDao, Material> impl
@Transactional(rollbackFor = Exception.class)
@Override
public List<MaterialResp> listMaterialByBannerCode(ListMaterialByBannerCodeReq req, String traceId) {
// 获取广告位并校验
// 获取广告位并校验(是否存在是否启用是否在有效期)
if (Objects.isNull(validateBanner(req.getBannerCode()))) {
return Collections.emptyList();
}
@ -118,17 +118,17 @@ public class MaterialServiceImpl extends ServiceImpl<MaterialDao, Material> impl
// 获取当前登录用户加入的所有项目部单位及其在项目部或在单位下担任的岗位
JoinedWorkspaceOuJob personJoinedWorkspaceOuJob = getPersonJoinedWorkspaceOuJob(req.getPersonId());
if (Objects.isNull(personJoinedWorkspaceOuJob)) {
// 理论上不会走到这里因为登录用户必然加入了一个项目部或单位
throw new ServiceException("数据异常");
}
// 根据素材投放规则进行过滤
List<Material> list = materialList.stream()
.filter(m -> {
// 投放人群过滤
return m.getTargetUserType().isDeliverRequired(m.getWorkspaceIds(), m.getOuIds(), m.getJobCodes(), personJoinedWorkspaceOuJob)
return m.getTargetUserType()
.isDeliverRequired(m.getWorkspaceIds(), m.getOuIds(), m.getJobCodes(),
req.getWorkspaceId(), req.getOuId(),
personJoinedWorkspaceOuJob)
&&
// 投放频次过滤
filterByDisplayFrequency(req, m);
})
.collect(Collectors.toList());
@ -316,15 +316,15 @@ public class MaterialServiceImpl extends ServiceImpl<MaterialDao, Material> impl
*
* @param req {@link ListMaterialByBannerCodeReq}
* @param m {@link Material}
* @return true符合投放规则false不符合投放规则
* @return true符合投放规则即为达到最大投放次数限制false不符合投放规则已到达最大投放次数限制
*/
private boolean filterByDisplayFrequency(ListMaterialByBannerCodeReq req, Material m) {
boolean displayFrequencyFilter;
String key = buildMaterialDisplayFrequencyKey(m, req.getPersonId());
if (RedisClient.KeyOps.hasKey(key)) {
int frequency = Integer.parseInt(RedisClient.StringOps.get(key));
log.info("{}已投放次数:{},素材最大投放次数:{}", req.getPersonId(), frequency, m.getMaxDisplayFrequency());
displayFrequencyFilter = frequency < m.getMaxDisplayFrequency();
log.info("{素材投放key{}value{}", key, frequency);
displayFrequencyFilter = MaterialDisplayFrequencyTypeEnum.NO_LIMIT.getCode().equals(m.getDisplayFrequencyType().getCode()) || frequency < m.getMaxDisplayFrequency();
if (displayFrequencyFilter) {
// 本次需要投放
RedisClient.StringOps.incrBy(key, 1L);
@ -332,7 +332,9 @@ public class MaterialServiceImpl extends ServiceImpl<MaterialDao, Material> impl
} else {
displayFrequencyFilter = true;
RedisClient.StringOps.set(key, String.valueOf(1));
RedisClient.KeyOps.expire(key, materialDisplayFrequencyKeyTtl, TimeUnit.DAYS);
// key失效时间有效期天数加上配置的有效期天数
long intervalDays = (m.getEndTime().getTime() - m.getStartTime().getTime()) / (1000 * 60 * 60 * 24);
RedisClient.KeyOps.expire(key, intervalDays + materialDisplayFrequencyKeyTtl, TimeUnit.DAYS);
}
return displayFrequencyFilter;
}
@ -430,10 +432,22 @@ public class MaterialServiceImpl extends ServiceImpl<MaterialDao, Material> impl
// Map<ouId, List<topNodeId>>
Map<Long, List<Long>> ouIdToTopNodeIdMap = genericQuery.stream()
.filter(c -> c.getWorkspaceId() == 1)
.filter(c -> c.getWorkspaceType() == 1)
.collect(Collectors.groupingBy(CooperateShipResp::getOrganizationalUnitId,
Collectors.mapping(CooperateShipResp::getOrganizationalNodeId, Collectors.toList())));
// 单位加入的项目部Map<ouId,List<workspace>>
Map<Long, List<Long>> ouIdToWorkspaceMap = genericQuery.stream()
.filter(c -> c.getWorkspaceType() == 2)
.collect(Collectors.groupingBy(CooperateShipResp::getOrganizationalUnitId,
Collectors.mapping(CooperateShipResp::getWorkspaceId, Collectors.toList())));
// 项目部下的单位 Map<workspaceId, List<ouId>
Map<Long, List<Long>> workspaceToOuIdMap = genericQuery.stream()
.filter(c -> c.getWorkspaceType() == 2)
.collect(Collectors.groupingBy(CooperateShipResp::getWorkspaceId,
Collectors.mapping(CooperateShipResp::getOrganizationalUnitId, Collectors.toList())));
// Map<workspaceId, List<jobCode>
Map<Long, List<String>> workspaceToJobCodeMap = workspaceToTopNodeIdMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
@ -454,7 +468,7 @@ public class MaterialServiceImpl extends ServiceImpl<MaterialDao, Material> impl
.distinct()
.collect(Collectors.toList())));
return new JoinedWorkspaceOuJob(workspaceToJobCodeMap, ouIdToJobCodeMap);
return new JoinedWorkspaceOuJob(workspaceToJobCodeMap, ouIdToJobCodeMap,ouIdToWorkspaceMap,workspaceToOuIdMap);
}
/**
@ -481,6 +495,8 @@ public class MaterialServiceImpl extends ServiceImpl<MaterialDao, Material> impl
LocalDate now = LocalDate.now();
String suffix = "";
switch (material.getDisplayFrequencyType()) {
case NO_LIMIT:
suffix = String.join("_", MaterialDisplayFrequencyTypeEnum.NO_LIMIT.getCode());
case VALIDITY_PERIOD:
suffix = String.join("_", "P", DateUtil.format(material.getStartTime(), datePattern), DateUtil.format(material.getEndTime(), datePattern));
break;