feat(REQ-3714) 工人退场流程优化 - 工人自动打离场标签以及取消标签的XXL-JOB
This commit is contained in:
parent
59a0daf3bf
commit
2ad2378ab1
@ -125,5 +125,10 @@
|
||||
<artifactId>msg-center-api-v2</artifactId>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!--xxl-job-->
|
||||
<dependency>
|
||||
<groupId>com.xuxueli</groupId>
|
||||
<artifactId>xxl-job-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -4,6 +4,7 @@ import cn.axzo.foundation.exception.BusinessException;
|
||||
import cn.axzo.foundation.result.ApiResult;
|
||||
import cn.axzo.foundation.util.TraceUtils;
|
||||
import cn.axzo.orgmanax.common.config.BizResultCode;
|
||||
import cn.azxo.framework.common.model.CommonResponse;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.http.HttpStatus;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
@ -39,6 +40,23 @@ public class RpcWrapper {
|
||||
return wrapApiResult(supplier, desc, params, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* common res 返回
|
||||
* @param t
|
||||
* @return
|
||||
* @param <T>
|
||||
*/
|
||||
public static <T> T commonRes(Supplier<CommonResponse<T>> t) {
|
||||
CommonResponse<T> result = t.get();
|
||||
if (result == null) {
|
||||
throw new BusinessException(BizResultCode.RPC_ERROR);
|
||||
}
|
||||
if (!Objects.equals(result.getCode(), HttpStatus.HTTP_OK)) {
|
||||
throw new BusinessException(Convert.toStr(result.getCode()), result.getMsg(), null);
|
||||
}
|
||||
return result.getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Api result 返回
|
||||
*
|
||||
|
||||
@ -10,12 +10,26 @@ import cn.axzo.apollo.workspace.api.v2.workspace.req.WorkspaceUpsertReq;
|
||||
import cn.axzo.apollo.workspace.api.v2.workspace.resp.WorkspaceDTO;
|
||||
import cn.axzo.apollo.workspace.api.v2.workspace.resp.WorkspaceDetailResp;
|
||||
import cn.axzo.apollo.workspace.api.v2.workspace.resp.WorkspaceUpsertResp;
|
||||
import cn.axzo.apollo.workspace.api.workspace.WorkspaceSceneConfigApi;
|
||||
import cn.axzo.apollo.workspace.api.workspace.req.scene.WorkspaceSceneConfigListReq;
|
||||
import cn.axzo.apollo.workspace.api.workspace.res.scene.SceneConfigRes;
|
||||
import cn.axzo.apollo.workspace.common.enums.SceneConfigSwitch;
|
||||
import cn.axzo.apollo.workspace.common.enums.WorkspaceSceneConfigType;
|
||||
import cn.axzo.orgmanax.infra.client.RpcWrapper;
|
||||
import cn.azxo.framework.common.logger.MethodAroundLog;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -25,9 +39,15 @@ import java.util.List;
|
||||
@Slf4j
|
||||
public class WorkspaceGateway {
|
||||
|
||||
private static final ImmutableList<WorkspaceSceneConfigType> AUTO_LEAVE_CONFIG_TYPES = ImmutableList.of(
|
||||
WorkspaceSceneConfigType.WORKER_AUTO_LEAVE,
|
||||
WorkspaceSceneConfigType.WORKER_AUTO_LEAVE_DAYS
|
||||
);
|
||||
|
||||
private final WorkspaceV2Api workspaceV2Api;
|
||||
|
||||
private final ParticipatingUnitV2Api participatingUnitV2Api;
|
||||
private final WorkspaceSceneConfigApi workspaceSceneConfigApi;
|
||||
|
||||
/**
|
||||
* 更新和新增
|
||||
@ -62,4 +82,55 @@ public class WorkspaceGateway {
|
||||
public List<ParticipatingUnitResp> listParticipatingUnits(ParticipatingUnitListReq unitListReq) {
|
||||
return RpcWrapper.wrapApiResult(() -> participatingUnitV2Api.list(unitListReq));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询项目相关配置项
|
||||
*
|
||||
* @param request 入参
|
||||
* @return 配置项列表
|
||||
*/
|
||||
@MethodAroundLog(source = "orgmanax", target = "workspace", value = "查询项目相关配置项")
|
||||
public List<SceneConfigRes> listConfigs(WorkspaceSceneConfigListReq request) {
|
||||
return RpcWrapper.commonRes(() -> workspaceSceneConfigApi.listConfigs(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取人员自动离场的天数配置(连续多少天没有打卡记录)
|
||||
*
|
||||
* @param workspaceIds 项目id列表
|
||||
* @return 配置项的map
|
||||
*/
|
||||
@MethodAroundLog(source = "orgmanax", target = "workspace", value = "获取人员自动离场的天数配置")
|
||||
public Map<Long, Integer> listAutoLeaveDaysConfig(Collection<Long> workspaceIds) {
|
||||
if (CollUtil.isEmpty(workspaceIds)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
WorkspaceSceneConfigListReq request = WorkspaceSceneConfigListReq.builder()
|
||||
.workspaceIds(CollUtil.distinct(workspaceIds))
|
||||
.types(AUTO_LEAVE_CONFIG_TYPES)
|
||||
.build();
|
||||
// list
|
||||
List<SceneConfigRes> sceneConfigs = listConfigs(request);
|
||||
if (CollUtil.isEmpty(sceneConfigs)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
// grouping
|
||||
Map<Long, List<SceneConfigRes>> groupingByWorkspaceId = sceneConfigs.stream()
|
||||
.collect(Collectors.groupingBy(SceneConfigRes::getWorkspaceId));
|
||||
// mapper function
|
||||
Function<List<SceneConfigRes>, SceneConfigRes> mapper = list -> {
|
||||
if (list.stream().anyMatch(e -> WorkspaceSceneConfigType.WORKER_AUTO_LEAVE.name().equals(e.getType())
|
||||
&& Objects.equals(e.getValue(), SceneConfigSwitch.OPEN.getValue()))) {
|
||||
return list.stream()
|
||||
.filter(e -> WorkspaceSceneConfigType.WORKER_AUTO_LEAVE_DAYS.name().equals(e.getType()))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
// switch not exist or closed
|
||||
return null;
|
||||
};
|
||||
return groupingByWorkspaceId.values().stream()
|
||||
.map(mapper)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(SceneConfigRes::getWorkspaceId, e -> Integer.valueOf(e.getValue()), (oldVal, newVal) -> oldVal));
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,12 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public interface NodeUserQueryRepository {
|
||||
|
||||
@ -52,6 +57,8 @@ public interface NodeUserQueryRepository {
|
||||
private Long id;
|
||||
@CriteriaField(field = "id", operator = Operator.GT)
|
||||
private Long idGt;
|
||||
@CriteriaField(field = "id", operator = Operator.LT)
|
||||
private Long idLt;
|
||||
@CriteriaField(field = "id", operator = Operator.IN)
|
||||
private Collection<Long> ids;
|
||||
|
||||
@ -156,6 +163,8 @@ public interface NodeUserQueryRepository {
|
||||
*/
|
||||
@CriteriaField
|
||||
private Long workspaceId;
|
||||
@CriteriaField(field = "workspaceId", operator = Operator.GT)
|
||||
private Long workspaceIdGt;
|
||||
@CriteriaField(field = "workspaceId", operator = Operator.IN)
|
||||
private Collection<Long> workspaceIds;
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
package cn.axzo.orgmanax.server.cooperateship.foundation;
|
||||
|
||||
import cn.axzo.orgmanax.infra.dao.cooperateship.entity.SaasCooperateShip;
|
||||
import cn.axzo.orgmanax.infra.dao.cooperateship.repository.CooperateShipQueryRepository.ListReq;
|
||||
import cn.axzo.orgmanax.server.cooperateship.foundation.dto.CooperateShipCreator;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
public interface CooperateShipFoundationService {
|
||||
@ -76,4 +76,12 @@ public interface CooperateShipFoundationService {
|
||||
* 创建协同关系通用方法
|
||||
*/
|
||||
SaasCooperateShip create(CooperateShipCreator creator);
|
||||
|
||||
/**
|
||||
* 协同关系通用查询
|
||||
*
|
||||
* @param req 入参
|
||||
* @return 协同关系节点列表
|
||||
*/
|
||||
List<SaasCooperateShip> list(ListReq req);
|
||||
}
|
||||
|
||||
@ -217,4 +217,9 @@ public class CooperateShipFoundationServiceImpl implements CooperateShipFoundati
|
||||
return savedCooperateShip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SaasCooperateShip> list(CooperateShipQueryRepository.ListReq req) {
|
||||
return cooperateShipQueryRepository.list(req);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
package cn.axzo.orgmanax.server.nodeuser.foundation;
|
||||
|
||||
import cn.axzo.foundation.page.PageResp;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserExtraQueryRepository.ListReq;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserExtraQueryRepository.NodeUserExtraResp;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author luofu
|
||||
* @version 1.0
|
||||
* @date 2025/3/11
|
||||
*/
|
||||
public interface NodeUserExtraFoundationService {
|
||||
|
||||
/***
|
||||
* 分页查询
|
||||
*
|
||||
* @param req 入参
|
||||
* @return node_user_extra 列表
|
||||
*/
|
||||
PageResp<NodeUserExtraResp> page(ListReq req);
|
||||
|
||||
/**
|
||||
* 批量设置用户标签
|
||||
*
|
||||
* @param idTagsMap key:id, value:标签集合
|
||||
*/
|
||||
void updateTagsMap(Map<Long, Set<String>> idTagsMap);
|
||||
}
|
||||
@ -1,8 +1,11 @@
|
||||
package cn.axzo.orgmanax.server.nodeuser.foundation;
|
||||
|
||||
import cn.axzo.foundation.page.PageResp;
|
||||
import cn.axzo.orgmanax.dto.nodeuser.req.SearchEntNodeUserReq;
|
||||
import cn.axzo.orgmanax.dto.nodeuser.resp.SearchEntNodeUserResp;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.entity.OrganizationalNodeUser;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserQueryRepository.ListReq;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserQueryRepository.NodeUserResp;
|
||||
import cn.axzo.orgmanax.server.nodeuser.foundation.req.NodeUserCreate;
|
||||
import cn.axzo.orgmanax.server.nodeuser.foundation.req.NodeUserDelete;
|
||||
import cn.axzo.orgmanax.server.nodeuser.foundation.req.NodeUserUpdate;
|
||||
@ -55,4 +58,12 @@ public interface NodeUserFoundationService {
|
||||
* @return
|
||||
*/
|
||||
List<SearchEntNodeUserResp> searchEntUser(SearchEntNodeUserReq req);
|
||||
|
||||
/**
|
||||
* 通用分页查询
|
||||
*
|
||||
* @param req 入参
|
||||
* @return node_user列表
|
||||
*/
|
||||
PageResp<NodeUserResp> page(ListReq req);
|
||||
}
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
package cn.axzo.orgmanax.server.nodeuser.foundation.impl;
|
||||
|
||||
import cn.axzo.foundation.page.PageResp;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserExtraQueryRepository;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserExtraQueryRepository.ListReq;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserExtraQueryRepository.NodeUserExtraResp;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserExtraUpsertRepository;
|
||||
import cn.axzo.orgmanax.server.nodeuser.foundation.NodeUserExtraFoundationService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author luofu
|
||||
* @version 1.0
|
||||
* @date 2025/3/11
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class NodeUserExtraFoundationServiceImpl implements NodeUserExtraFoundationService {
|
||||
|
||||
private final NodeUserExtraQueryRepository queryRepository;
|
||||
private final NodeUserExtraUpsertRepository upsertRepository;
|
||||
|
||||
@Override
|
||||
public PageResp<NodeUserExtraResp> page(ListReq req) {
|
||||
return queryRepository.page(req);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTagsMap(Map<Long, Set<String>> idTagsMap) {
|
||||
upsertRepository.updateTagsMap(idTagsMap);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ import cn.axzo.apollo.workspace.api.v2.workspace.resp.WorkspaceDetailResp;
|
||||
import cn.axzo.foundation.event.support.Event;
|
||||
import cn.axzo.foundation.event.support.producer.EventProducer;
|
||||
import cn.axzo.foundation.exception.Axssert;
|
||||
import cn.axzo.foundation.page.PageResp;
|
||||
import cn.axzo.orgmanax.common.config.BizResultCode;
|
||||
import cn.axzo.orgmanax.dto.common.util.NumberUtil;
|
||||
import cn.axzo.orgmanax.dto.nodeuser.enums.NodeUserTypeEnum;
|
||||
@ -21,6 +22,8 @@ import cn.axzo.orgmanax.infra.dao.node.repository.NodeQueryRepository;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.entity.OrganizationalNodeUser;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.mapper.OrganizationalNodeUserMapper;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserQueryRepository;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserQueryRepository.ListReq;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserQueryRepository.NodeUserResp;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserUpsertRepository;
|
||||
import cn.axzo.orgmanax.infra.dao.orgjob.entity.OrgJob;
|
||||
import cn.axzo.orgmanax.infra.dao.orgjob.repository.OrgJobQueryRepository;
|
||||
@ -46,7 +49,11 @@ import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -258,6 +265,11 @@ public class NodeUserFoundationServiceImpl implements NodeUserFoundationService
|
||||
return organizationalNodeUserMapper.searchEntUser(req);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResp<NodeUserResp> page(ListReq req) {
|
||||
return nodeUserQueryRepository.page(req);
|
||||
}
|
||||
|
||||
private Long resolveWorkspaceId(OrganizationalNode node) {
|
||||
if (Objects.equals(node.getNodeType(), NodeUserTypeEnum.TEAM.getValue())) {
|
||||
return 0L;
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
package cn.axzo.orgmanax.server.orguser.config;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author luofu
|
||||
* @version 1.0
|
||||
* @description 人员标签配置
|
||||
* @date 2025/3/6
|
||||
*/
|
||||
@Data
|
||||
@RefreshScope
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "person-tag")
|
||||
public class PersonTagConfig {
|
||||
|
||||
/**
|
||||
* 离场标签
|
||||
*/
|
||||
private TagNode leaveTagNode = TagNode.of("station", "leave proj");
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class TagNode implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8933389377639107477L;
|
||||
|
||||
private String nodeCode;
|
||||
|
||||
private String valueCode;
|
||||
|
||||
public String format(String spliter) {
|
||||
return this.nodeCode + spliter + this.valueCode;
|
||||
}
|
||||
|
||||
private static TagNode of(String nodeCode, String valueCode) {
|
||||
return TagNode.builder()
|
||||
.nodeCode(nodeCode)
|
||||
.valueCode(valueCode)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,212 @@
|
||||
package cn.axzo.orgmanax.server.orguser.xxl;
|
||||
|
||||
import cn.axzo.foundation.page.PageResp;
|
||||
import cn.axzo.orgmanax.dto.common.IdentityType;
|
||||
import cn.axzo.orgmanax.dto.cooperateship.enums.CooperateShipStatusEnum;
|
||||
import cn.axzo.orgmanax.dto.cooperateship.enums.CooperateShipTypeEnum;
|
||||
import cn.axzo.orgmanax.dto.nodeuser.enums.TagOperateEnum;
|
||||
import cn.axzo.orgmanax.dto.nodeuser.req.TagOperateReq;
|
||||
import cn.axzo.orgmanax.infra.client.workspace.dto.Workspace;
|
||||
import cn.axzo.orgmanax.infra.dao.cooperateship.entity.SaasCooperateShip;
|
||||
import cn.axzo.orgmanax.infra.dao.cooperateship.repository.CooperateShipQueryRepository;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserQueryRepository.ListReq;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserQueryRepository.NodeUserResp;
|
||||
import cn.axzo.orgmanax.server.cooperateship.foundation.CooperateShipFoundationService;
|
||||
import cn.axzo.orgmanax.server.nodeuser.foundation.NodeUserFoundationService;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author luofu
|
||||
* @version 1.0
|
||||
* @description 自动退场OR标记为离场人员的JOB
|
||||
* @date 2025/3/6
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AutoAddWorkerLeavedTagJob extends BaseAutoOperatePersonLeavedTagJob<NodeUserResp> {
|
||||
|
||||
private static final String LOG_PREFIX = "[ADD_WORKER_LEAVED_TAG ] ";
|
||||
|
||||
private static final ImmutableList<Integer> INCLUDE_WORKSPACE_TYPES = ImmutableList.of(Workspace.WorkspaceTypeEnum.GENERAL_PROJECT.getValue());
|
||||
private static final ImmutableList<Integer> INCLUDE_COOPERATE_TYPES = ImmutableList.of(CooperateShipTypeEnum.PROJ_TEAM.getCode(),
|
||||
CooperateShipTypeEnum.PROJ_GROUP.getCode());
|
||||
private static final ImmutableList<Integer> INCLUDE_STATUS = ImmutableList.of(CooperateShipStatusEnum.PRESENT.getCode());
|
||||
|
||||
@Resource
|
||||
private NodeUserFoundationService nodeUserFoundationService;
|
||||
@Resource
|
||||
private CooperateShipFoundationService cooperateShipFoundationService;
|
||||
|
||||
private Context context;
|
||||
|
||||
@Override
|
||||
@XxlJob("autoAddWorkerLeavedTagJob")
|
||||
public ReturnT<String> execute(String param) {
|
||||
// init context of job
|
||||
initContext();
|
||||
if (context.invalid()) {
|
||||
// failed to init context.
|
||||
error("failed to init context.");
|
||||
return ReturnT.FAIL;
|
||||
}
|
||||
// start to execute
|
||||
doExecute(param);
|
||||
return ReturnT.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
String logPrefix() {
|
||||
return LOG_PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
LocalDateTime startDateTimeOfJobExecution() {
|
||||
return context.getStartDateTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
List<NodeUserResp> scrollList(long scrollIndex) {
|
||||
ListReq page = new ListReq();
|
||||
page.setIdLt(scrollIndex);
|
||||
page.setIdentityType(IdentityType.WORKER.getCode());
|
||||
page.setWorkspaceIdGt(0L);
|
||||
PageResp<NodeUserResp> pageResult = nodeUserFoundationService.page(page);
|
||||
return Optional.ofNullable(pageResult.getData()).orElseGet(Collections::emptyList);
|
||||
}
|
||||
|
||||
@Override
|
||||
Long extractId(NodeUserResp record) {
|
||||
return record.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
Long extractPersonId(NodeUserResp record) {
|
||||
return record.getPersonId();
|
||||
}
|
||||
|
||||
@Override
|
||||
Long extractWorkspaceId(NodeUserResp record) {
|
||||
return record.getWorkspaceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
void handle(Long workspaceId, List<NodeUserResp> records, List<Long> clockedPersonIds) {
|
||||
Date joinedAtLe = context.getJoinedAtLe(workspaceId);
|
||||
if (Objects.isNull(joinedAtLe)) {
|
||||
return;
|
||||
}
|
||||
List<Long> withoutClockedPersonIds = records.stream()
|
||||
// filter join_at condition
|
||||
.filter(e -> Objects.nonNull(e.getJoinAt()) && joinedAtLe.after(e.getJoinAt()))
|
||||
.map(NodeUserResp::getPersonId)
|
||||
.filter(personId -> !clockedPersonIds.contains(personId))
|
||||
.distinct().collect(Collectors.toList());
|
||||
if (CollUtil.isEmpty(withoutClockedPersonIds)) {
|
||||
// all person contains clocked record.
|
||||
return;
|
||||
}
|
||||
// auto add leaved tag
|
||||
addLeavedTag(workspaceId, withoutClockedPersonIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
Integer listAutoLeaveDaysConfig(Long workspaceId) {
|
||||
return context.getWorkspaceDaysConfig().get(workspaceId);
|
||||
}
|
||||
|
||||
private void initContext() {
|
||||
context = new Context();
|
||||
assembleDataScope(context);
|
||||
assembleWorkspaceConfig(context);
|
||||
}
|
||||
|
||||
private void assembleDataScope(Context context) {
|
||||
CooperateShipQueryRepository.ListReq listReq = new CooperateShipQueryRepository.ListReq();
|
||||
listReq.setWorkspaceTypes(INCLUDE_WORKSPACE_TYPES);
|
||||
listReq.setIncludeCooperateTypes(INCLUDE_COOPERATE_TYPES);
|
||||
listReq.setStatuses(INCLUDE_STATUS);
|
||||
Map<Long, List<SaasCooperateShip>> dataScope = cooperateShipFoundationService.list(listReq).stream()
|
||||
.collect(Collectors.groupingBy(SaasCooperateShip::getWorkspaceId));
|
||||
context.setActiveProjectTeamAndGroups(dataScope);
|
||||
}
|
||||
|
||||
private void assembleWorkspaceConfig(Context context) {
|
||||
if (context.invalid()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, Integer> workspaceDaysConfig = Maps.newHashMap();
|
||||
context.setWorkspaceDaysConfig(workspaceDaysConfig);
|
||||
Set<Long> workspaceIds = context.getWorkspaceDaysConfig().keySet();
|
||||
List<List<Long>> partition = Lists.partition(Lists.newArrayList(workspaceIds), 20);
|
||||
for (List<Long> sub : partition) {
|
||||
Map<Long, Integer> autoLeaveDaysConfigMap = workspaceGateway.listAutoLeaveDaysConfig(sub);
|
||||
if (CollUtil.isEmpty(autoLeaveDaysConfigMap)) {
|
||||
continue;
|
||||
}
|
||||
workspaceDaysConfig.putAll(autoLeaveDaysConfigMap);
|
||||
}
|
||||
}
|
||||
|
||||
private void addLeavedTag(Long workspaceId, List<Long> personIds) {
|
||||
TagOperateReq param = new TagOperateReq();
|
||||
param.setPersonIds(personIds);
|
||||
param.setLoginWorkspaceId(workspaceId);
|
||||
param.setTagNodeCode(personTagConfig.getLeaveTagNode().getNodeCode());
|
||||
param.setTagValueCode(personTagConfig.getLeaveTagNode().getValueCode());
|
||||
param.setOperateType(TagOperateEnum.ADD);
|
||||
orgUserService.operateTag(param);
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class Context {
|
||||
|
||||
private LocalDateTime startDateTime = LocalDateTime.now();
|
||||
|
||||
/**
|
||||
* 在场的项目内班组和小组节点
|
||||
* key: workspaceId
|
||||
* value: 在场的项目内班组和小组节点列表
|
||||
*/
|
||||
private Map<Long, List<SaasCooperateShip>> activeProjectTeamAndGroups;
|
||||
|
||||
/**
|
||||
* 开关开启的项目id集合
|
||||
* key: workspaceId
|
||||
* value: days
|
||||
*/
|
||||
private Map<Long, Integer> workspaceDaysConfig;
|
||||
|
||||
boolean invalid() {
|
||||
return CollUtil.isEmpty(activeProjectTeamAndGroups);
|
||||
}
|
||||
|
||||
Date getJoinedAtLe(Long workspaceId) {
|
||||
Integer days = workspaceDaysConfig.get(workspaceId);
|
||||
if (Objects.isNull(days)) {
|
||||
return null;
|
||||
}
|
||||
LocalDateTime le = startDateTime.minusDays(days);
|
||||
return Date.from(le.atZone(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
package cn.axzo.orgmanax.server.orguser.xxl;
|
||||
|
||||
import cn.axzo.foundation.page.PageResp;
|
||||
import cn.axzo.orgmanax.dto.nodeuser.enums.TagOperateEnum;
|
||||
import cn.axzo.orgmanax.dto.nodeuser.req.TagOperateReq;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserExtraQueryRepository;
|
||||
import cn.axzo.orgmanax.infra.dao.nodeuser.repository.NodeUserExtraQueryRepository.NodeUserExtraResp;
|
||||
import cn.axzo.orgmanax.server.nodeuser.foundation.NodeUserExtraFoundationService;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.xxl.job.core.biz.model.ReturnT;
|
||||
import com.xxl.job.core.handler.annotation.XxlJob;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author luofu
|
||||
* @version 1.0
|
||||
* @description 自动清除人员离场标签的JOB
|
||||
* @date 2025/3/6
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class AutoClearPersonLeavedTagJob extends BaseAutoOperatePersonLeavedTagJob<NodeUserExtraResp> {
|
||||
|
||||
private static final String LOG_PREFIX = "[CLEAR_PERSON_LEAVED_TAG] ";
|
||||
|
||||
@Resource
|
||||
private NodeUserExtraFoundationService nodeUserExtraFoundationService;
|
||||
|
||||
private Context context;
|
||||
|
||||
@Override
|
||||
@XxlJob("autoClearPersonLeavedTagJob")
|
||||
public ReturnT<String> execute(String param) {
|
||||
// init context
|
||||
context = new Context();
|
||||
doExecute(param);
|
||||
return ReturnT.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
String logPrefix() {
|
||||
return LOG_PREFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
LocalDateTime startDateTimeOfJobExecution() {
|
||||
return context.getStartDateTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
List<NodeUserExtraResp> scrollList(long scrollIndex) {
|
||||
String tag = personTagConfig.getLeaveTagNode().format(":");
|
||||
NodeUserExtraQueryRepository.ListReq page = new NodeUserExtraQueryRepository.ListReq();
|
||||
page.setIdLt(scrollIndex);
|
||||
page.setTag(tag);
|
||||
page.setPageSize(MAX_CNT_ONCE);
|
||||
page.setSort(Collections.singletonList(sortDesc("id")));
|
||||
PageResp<NodeUserExtraQueryRepository.NodeUserExtraResp> pageResult = nodeUserExtraFoundationService.page(page);
|
||||
return Optional.ofNullable(pageResult.getData()).orElseGet(Collections::emptyList);
|
||||
}
|
||||
|
||||
@Override
|
||||
Long extractId(NodeUserExtraResp record) {
|
||||
return record.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
Long extractPersonId(NodeUserExtraResp record) {
|
||||
return record.getPersonId();
|
||||
}
|
||||
|
||||
@Override
|
||||
Long extractWorkspaceId(NodeUserExtraResp record) {
|
||||
return record.getWorkspaceId();
|
||||
}
|
||||
|
||||
@Override
|
||||
void handle(Long workspaceId, List<NodeUserExtraResp> records, List<Long> clockedPersonIds) {
|
||||
if (CollUtil.isEmpty(clockedPersonIds)) {
|
||||
// none clocked person.
|
||||
return;
|
||||
}
|
||||
TagOperateReq param = new TagOperateReq();
|
||||
param.setPersonIds(clockedPersonIds);
|
||||
param.setLoginWorkspaceId(workspaceId);
|
||||
param.setTagNodeCode(personTagConfig.getLeaveTagNode().getNodeCode());
|
||||
param.setTagValueCode(personTagConfig.getLeaveTagNode().getValueCode());
|
||||
param.setOperateType(TagOperateEnum.REMOVE);
|
||||
orgUserService.operateTag(param);
|
||||
}
|
||||
|
||||
@Override
|
||||
Integer listAutoLeaveDaysConfig(Long workspaceId) {
|
||||
// fetch from local cache first.
|
||||
Integer days = context.getWorkspaceDaysConfig().get(workspaceId);
|
||||
if (Objects.nonNull(days)) {
|
||||
return days;
|
||||
}
|
||||
// fetch from attendance service.
|
||||
Map<Long, Integer> map = workspaceGateway.listAutoLeaveDaysConfig(Collections.singletonList(workspaceId));
|
||||
if (CollUtil.isEmpty(map)) {
|
||||
return null;
|
||||
}
|
||||
// cache the config of this workspace
|
||||
context.getWorkspaceDaysConfig().putAll(map);
|
||||
// return days config
|
||||
return context.getWorkspaceDaysConfig().get(workspaceId);
|
||||
}
|
||||
|
||||
@Data
|
||||
private static class Context {
|
||||
|
||||
/**
|
||||
* start time of job execute
|
||||
*/
|
||||
private LocalDateTime startDateTime = LocalDateTime.now();
|
||||
|
||||
/**
|
||||
* days configuration for add person tag of workspace.
|
||||
* key: workspaceId
|
||||
* value: days
|
||||
*/
|
||||
private Map<Long, Integer> workspaceDaysConfig = Maps.newHashMap();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,172 @@
|
||||
package cn.axzo.orgmanax.server.orguser.xxl;
|
||||
|
||||
import cn.axzo.foundation.page.IPageReq;
|
||||
import cn.axzo.orgmanax.infra.client.attendance.AttendanceRecordClient;
|
||||
import cn.axzo.orgmanax.infra.client.attendance.dto.AttendanceClockRecordListReq;
|
||||
import cn.axzo.orgmanax.infra.client.attendance.dto.AttendanceClockRecordResp;
|
||||
import cn.axzo.orgmanax.infra.client.workspace.WorkspaceGateway;
|
||||
import cn.axzo.orgmanax.server.orguser.config.PersonTagConfig;
|
||||
import cn.axzo.orgmanax.server.orguser.service.OrgUserService;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.xxl.job.core.handler.IJobHandler;
|
||||
import com.xxl.job.core.log.XxlJobLogger;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author luofu
|
||||
* @version 1.0
|
||||
* @date 2025/3/6
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseAutoOperatePersonLeavedTagJob<T> extends IJobHandler {
|
||||
|
||||
static final int MAX_CNT_ONCE = 4;
|
||||
|
||||
@Resource
|
||||
PersonTagConfig personTagConfig;
|
||||
@Resource
|
||||
OrgUserService orgUserService;
|
||||
|
||||
@Resource
|
||||
AttendanceRecordClient attendanceRecordApiClient;
|
||||
@Resource
|
||||
WorkspaceGateway workspaceGateway;
|
||||
|
||||
void doExecute(String param) {
|
||||
long scrollIndex = Long.MAX_VALUE;
|
||||
info("start to scan org_user.");
|
||||
// key: workspaceId value: records
|
||||
Map<Long, List<T>> localCache = Maps.newHashMap();
|
||||
List<T> records = scrollList(scrollIndex);
|
||||
while (CollUtil.isNotEmpty(records)) {
|
||||
// the min id of records.
|
||||
scrollIndex = records.stream().mapToLong(this::extractId).min().orElse(0);
|
||||
// cache first, then trigger if enough.
|
||||
cacheAndTrigger(localCache, records);
|
||||
// the next
|
||||
records = scrollList(scrollIndex);
|
||||
}
|
||||
// scan cached records
|
||||
localCache.forEach(this::trigger);
|
||||
info("completed to scan org_user.");
|
||||
}
|
||||
|
||||
abstract String logPrefix();
|
||||
|
||||
abstract LocalDateTime startDateTimeOfJobExecution();
|
||||
|
||||
abstract List<T> scrollList(long scrollIndex);
|
||||
|
||||
abstract Long extractId(T record);
|
||||
|
||||
abstract Long extractPersonId(T record);
|
||||
|
||||
abstract Long extractWorkspaceId(T record);
|
||||
|
||||
abstract void handle(Long workspaceId, List<T> records, List<Long> clockedPersonIds);
|
||||
|
||||
abstract Integer listAutoLeaveDaysConfig(Long workspaceId);
|
||||
|
||||
private void cacheAndTrigger(Map<Long, List<T>> localCache, List<T> records) {
|
||||
// grouping by workspaceId
|
||||
Map<Long, List<T>> groupingByWorkspaceId = records.stream().collect(Collectors.groupingBy(this::extractWorkspaceId));
|
||||
// scan for cache and trigger
|
||||
groupingByWorkspaceId.forEach((k, v) -> cacheAndTrigger(localCache, k, v));
|
||||
}
|
||||
|
||||
private void cacheAndTrigger(Map<Long, List<T>> localCache, Long workspaceId, List<T> sub) {
|
||||
List<T> cacheRecords = localCache.computeIfAbsent(workspaceId, k -> Lists.newArrayList());
|
||||
// merge cur records and cached records
|
||||
cacheRecords.addAll(sub);
|
||||
if (cacheRecords.size() < MAX_CNT_ONCE) {
|
||||
// return if not matched
|
||||
return;
|
||||
}
|
||||
// remove from local cache
|
||||
localCache.remove(workspaceId);
|
||||
// deal matched partitions and cache not matched
|
||||
List<List<T>> partitions = Lists.partition(cacheRecords, MAX_CNT_ONCE);
|
||||
for (List<T> partition : partitions) {
|
||||
if (partition.size() < MAX_CNT_ONCE) {
|
||||
// cache
|
||||
localCache.put(workspaceId, partition);
|
||||
continue;
|
||||
}
|
||||
// trigger
|
||||
trigger(workspaceId, partition);
|
||||
}
|
||||
}
|
||||
|
||||
public static String sortDesc(String field) {
|
||||
return field + IPageReq.SORT_DELIMITER + IPageReq.SORT_DESC;
|
||||
}
|
||||
|
||||
private void trigger(Long workspaceId, List<T> records) {
|
||||
try {
|
||||
// list days config of cur workspace.
|
||||
Integer days = listAutoLeaveDaysConfig(workspaceId);
|
||||
if (Objects.isNull(days)) {
|
||||
info("lack of days config for this workspace. workspaceId:[{}]", workspaceId);
|
||||
return;
|
||||
}
|
||||
// in try ... catch block for continue the next ...
|
||||
// find persons which contains clocked record during specified time.
|
||||
List<Long> personIds = findClockedPersons(workspaceId, days, records);
|
||||
info("start to remove person tag of workspace. [{}]", workspaceId);
|
||||
// begin to handle the personIds.
|
||||
handle(workspaceId, records, personIds);
|
||||
info("completed to remove person tag of workspace. [{}]", workspaceId);
|
||||
} catch (Exception e) {
|
||||
error("broke out some exception. workspaceId:[{}]", workspaceId, e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Long> findClockedPersons(Long workspaceId, Integer days, List<T> records) {
|
||||
List<Long> personIds = CollUtil.map(records, this::extractPersonId, true);
|
||||
if (CollUtil.isEmpty(personIds)) {
|
||||
List<Long> ids = CollUtil.map(records, this::extractId, true);
|
||||
error("invalid records, lack of personId. workspaceId:[{}], recordIds:{}", workspaceId, JSON.toJSONString(ids));
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// list persons which clocked during the range of specified time.
|
||||
List<AttendanceClockRecordResp> clockRecords = listClockRecords(workspaceId, days, personIds);
|
||||
// extract personId
|
||||
return CollUtil.map(clockRecords, AttendanceClockRecordResp::getPersonId, true);
|
||||
}
|
||||
|
||||
private List<AttendanceClockRecordResp> listClockRecords(Long workspaceId, Integer days, List<Long> personIds) {
|
||||
LocalDateTime to = startDateTimeOfJobExecution();
|
||||
LocalDateTime from = to.minusDays(days);
|
||||
AttendanceClockRecordListReq request = new AttendanceClockRecordListReq();
|
||||
request.setPersonIds(personIds);
|
||||
request.setWorkspaceId(workspaceId);
|
||||
request.setClockAtFrom(Date.from(from.atZone(ZoneOffset.systemDefault()).toInstant()));
|
||||
request.setClockAtTo(Date.from(to.atZone(ZoneOffset.systemDefault()).toInstant()));
|
||||
return attendanceRecordApiClient.listClockRecords(request);
|
||||
}
|
||||
|
||||
void info(String msgFormat, Object... args) {
|
||||
msgFormat = logPrefix() + msgFormat;
|
||||
log.info(msgFormat, args);
|
||||
XxlJobLogger.log(msgFormat, args);
|
||||
}
|
||||
|
||||
void error(String msgFormat, Object... args) {
|
||||
msgFormat = logPrefix() + msgFormat;
|
||||
log.error(msgFormat, args);
|
||||
XxlJobLogger.log(msgFormat, args);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user