Merge pull request 'AD用户状态同步ehr员工状态' (#4) from nflg-bom-transition-lhj0801 into feature/DM/nflg-bom-transition

Reviewed-on: http://192.168.0.40:3000/root/nflg_project/pulls/4
This commit is contained in:
10002327 2024-08-01 17:42:22 +08:00
commit 0ff9cfe794
6 changed files with 345 additions and 1 deletions

View File

@ -17,6 +17,7 @@ import com.nflg.product.material.pojo.vo.AuthorityMenuVO;
import com.nflg.product.material.pojo.vo.AuthorityUserVO;
import com.nflg.product.material.service.AuthorityPowerService;
import com.nflg.product.material.service.AuthorityUserService;
import com.nflg.product.material.service.EhrService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import nflg.product.common.constant.STATE;
@ -174,6 +175,10 @@ public class AuthorityUserApi extends BaseApi {
return ResultVO.success(authorityUserService.updateById(user));
}
@GetMapping("syncEhrEmployee")
@ApiOperation("同步ehr员工的状态")
public ResultVO<String> syncEhrEmployee(){
return ResultVO.success(authorityUserService.syncEhrEmployee());
}
}

View File

@ -38,6 +38,9 @@ public class SaticScheduleTask {
@Resource
private MaterialUpdateBillService materialUpdateBillService;
@Resource
private AuthorityUserService authorityUserService;
/**
* 更新AD(每天6点)
*/
@ -149,4 +152,12 @@ public class SaticScheduleTask {
public void doQueryCurrentTwoYearsUsage() throws Exception {
materialMainService.doQueryCurrentTwoYearsUsage();
}
/**
* 每周六 同步ehr用户人员状态离职的更新为未启用
*/
@Scheduled(cron = "${ehr.sync.employee.state.time:0 0 9 ? * SAT}")
public void syncEhrEmployee() {
authorityUserService.syncEhrEmployee();
}
}

View File

@ -0,0 +1,88 @@
package com.nflg.product.material.pojo.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* packageName com.nflg.product.material.pojo.dto
*
* @author luohj
* @className EhrEmployeeDTO
* @date 2024/8/1 0001
* @description ehr 员工信息
*/
@Data
public class EhrEmployeeDTO implements Serializable {
@ApiModelProperty("员工ID")
private String EmpID;
@ApiModelProperty("员工编号")
private String EmpNo;
@ApiModelProperty("员工姓名")
private String EmpName;
@ApiModelProperty("所属部门ID")
private String DeptId;
@ApiModelProperty("所属部门编号")
private String DEPTCODE;
@ApiModelProperty("员工状态 0启用1停用")
private String DISABLE;
@ApiModelProperty("人员类别 1在职5离职")
private String A0191;
@ApiModelProperty("手机号码")
private String CELLPHONE;
@ApiModelProperty("岗位ID")
private String POSITIONID;
@ApiModelProperty("身份证号")
private String IDCARDNO;
@ApiModelProperty("性别")
private String SEX;
@ApiModelProperty("最后修改日期")
private Date LASTMODIFYTIME;
@ApiModelProperty("开户银行")
private String A0136;
@ApiModelProperty("银行代码")
private String YHDM;
@ApiModelProperty("银行账号")
private String A0137;
@ApiModelProperty("银行国家代码")
private String YHGJDM;
@ApiModelProperty("职级")
private String GWZJ;
@ApiModelProperty("序列")
private String RYXL;
@ApiModelProperty("是否结算加班费")
private String A01190;
@ApiModelProperty("入职日期")
private String A0141;
@ApiModelProperty("成本中心编码")
private String COSTNO;
@ApiModelProperty("成本中心")
private String COSTNAME;
@ApiModelProperty("第一次合同签订时间")
private Date HT_BEGIN_DATE;
}

View File

@ -3,10 +3,13 @@ package com.nflg.product.material.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.nflg.product.base.core.conmon.util.DESUtil;
import com.nflg.product.base.core.vo.PageVO;
import com.nflg.product.material.mapper.master.AuthorityPowerMapper;
@ -14,23 +17,30 @@ import com.nflg.product.material.mapper.master.AuthorityPowerPermissionMapper;
import com.nflg.product.material.mapper.master.AuthorityRoleMapper;
import com.nflg.product.material.mapper.master.AuthorityUserMapper;
import com.nflg.product.material.pojo.dto.AuthorityUserDTO;
import com.nflg.product.material.pojo.dto.EhrEmployeeDTO;
import com.nflg.product.material.pojo.dto.SaveUserMemuPermissionDTO;
import com.nflg.product.material.pojo.dto.UserRoleDTO;
import com.nflg.product.material.pojo.entity.AuthorityPowerPermissionEntity;
import com.nflg.product.material.pojo.entity.AuthorityUserEntity;
import com.nflg.product.material.pojo.query.AuthorityUserQuery;
import com.nflg.product.material.pojo.vo.AuthorityUserVO;
import com.nflg.product.material.util.HttpUtils;
import com.nflg.product.material.util.ListCommonUtil;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -58,6 +68,8 @@ public class AuthorityUserService extends ServiceImpl<AuthorityUserMapper, Autho
@Resource
AuthorityRoleMapper authorityRoleMapper;
@Resource
EhrService ehrService;
/**
* 分页查询所有数据
*
@ -199,5 +211,37 @@ public class AuthorityUserService extends ServiceImpl<AuthorityUserMapper, Autho
}
/**
* 同步ehr用户人员状态离职的更新为未启用
* 将来源为AD域同步的用户与ehr数据更新状态
*/
public String syncEhrEmployee(){
String res = "";
//获取AD域同步的用户状态是启用中的集合判断在ehr中是否已经离职了
List<AuthorityUserEntity> userList = this.list(Wrappers.<AuthorityUserEntity>lambdaQuery()
.eq(AuthorityUserEntity::getUserSource,1)
.eq(AuthorityUserEntity::getEnableState,1));
if(userList.isEmpty()){
return "正常AD用户数量为0";
}
String sessionId = ehrService.getSessionId();
List<EhrEmployeeDTO> empList = ehrService.empList(sessionId);
if(empList.isEmpty()){
return "员工数量为0";
}
//离职员工编号集合
List<String> empNoList = empList.stream().filter(emp -> Objects.equals("1",emp.getDISABLE()) || Objects.equals("5",emp.getA0191()))
.map(emp -> emp.getEmpNo().toUpperCase()).collect(Collectors.toList());
List<AuthorityUserEntity> disableList = userList.stream()
.filter(user -> empNoList.contains(user.getUserCode().toUpperCase()))
.map(user -> {
user.setEnableState(0);
return user;
}).collect(Collectors.toList());
if(!disableList.isEmpty()){
this.updateBatchById(disableList);
}
return String.format("正常AD用户数%d员工数%d,离职数:%d,更新记录:%d",userList.size(),empList.size(),empNoList.size(),disableList.size());
}
}

View File

@ -0,0 +1,138 @@
package com.nflg.product.material.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.XmlUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.nflg.product.material.pojo.dto.EhrEmployeeDTO;
import com.nflg.product.material.util.CompletableFutureUtil;
import com.nflg.product.material.util.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* packageName com.nflg.product.material.service
*
* @author luohj
* @className EhrService
* @date 2024/8/1 0001
* @description ehr 相关接口
*/
@Service
@Slf4j
@RefreshScope
public class EhrService {
private static HttpUtils httpUtils = new HttpUtils();
@Value("${ehr.url:http://192.168.163.30:8088/hr/DataInterface/WebService.asmx}")
private String ehrUrl;
@Value("${ehr.user:nflj}")
private String ehrUser;
@Value("${ehr.pwd:nfljadmin}")
private String ehrPwd;
/**
* 获取ehr sessionId
* @return
*/
public String getSessionId(){
String sessionId = "";
Map<String,String> mp = Maps.newHashMap();
mp.put("S1",ehrUser);
mp.put("S2",ehrPwd);
try {
String re = httpUtils.doformPost(ehrUrl + "/Login",mp);
Document d = DocumentHelper.parseText(re);
sessionId = d.getRootElement().getStringValue();
} catch (Exception e) {
log.error("ehr 登录异常: {}",e.getMessage());
}
return sessionId;
}
/**
* 获取ehr 员工总数
* @param sessionId
* @return
*/
public Integer empTotal(String sessionId){
int total = -1;
Map<String,String> mp2 = Maps.newHashMap();
mp2.put("SessionID",sessionId);
mp2.put("NewOnly","0");
mp2.put("Time","");
String re2 = null;
try {
re2 = httpUtils.doPost(ehrUrl + "/EmpBeginGetEmployee",mp2);
Document d2 = DocumentHelper.parseText(re2);
total =Integer.valueOf(d2.getRootElement().getStringValue());
} catch (Exception e) {
e.printStackTrace();
}
return total;
}
/**
* ehr 员工列表
* @param sessionId
* @return
*/
public List<EhrEmployeeDTO> empList(String sessionId){
int total = empTotal(sessionId);
List<EhrEmployeeDTO> ehrEmployeeList =Lists.newArrayList();
//每批次数量 1222 6 22 7
int batch = 200;
if(total <= 0){
return ehrEmployeeList;
}
int mul = total / batch;
int rem = total % batch;
int cnt = mul + (rem > 0 ? 1: 0);
List<CompletableFuture<List<EhrEmployeeDTO>>> taskList = Lists.newArrayList();
for (int i = 1; i <= cnt; i++) {
int finalI = i;
CompletableFuture<List<EhrEmployeeDTO>> future = CompletableFuture.supplyAsync(() -> {
Map<String,String> mp3 = Maps.newHashMap();
mp3.put("SessionID",sessionId);
mp3.put("from",String.valueOf( (finalI -1)*batch + 1 ));
mp3.put("to",String.valueOf( (finalI * batch) > total ? total: (finalI * batch) ));
try {
String re3 = httpUtils.doPost(ehrUrl + "/GetSegment",mp3);
Document d3 = DocumentHelper.parseText(re3);
String r3 = d3.getRootElement().getStringValue();
Map<String, Object> m3 = XmlUtil.xmlToMap(r3);
return Convert.toList(EhrEmployeeDTO.class,m3.get("Table"));
} catch (Exception e) {
log.error("eHr获取人员信息异常:{},{}",sessionId,e.getMessage());
}
return Lists.newArrayList();
});
taskList.add(future);
}
try {
CompletableFuture<Void> allTask = CompletableFuture.allOf(taskList.stream().map(CompletableFutureUtil::executeActionAsync).toArray(CompletableFuture[]::new));
allTask.get();
taskList.forEach(t -> ehrEmployeeList.addAll(t.join()));
//清除缓存
Map<String,String> mp4 = Maps.newHashMap();
mp4.put("SessionID",sessionId);
httpUtils.doPost(ehrUrl + "/ClearCache",mp4);
} catch (Exception e) {
log.error("eHr获取人员信息异常:{}",e.getMessage());
}
return ehrEmployeeList;
}
}

View File

@ -0,0 +1,58 @@
package com.nflg.product.material.util;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* packageName com.nflg.product.material.util
*
* @author luohj
* @className CompletableFutureUtil
* @date 2024/8/1 0001
* @description TODO
*/
@Slf4j
public class CompletableFutureUtil {
private static final int MAX_RETRIES = 3;
public static <T> List<T> getAllCompleted(List<CompletableFuture<T>> futuresList) throws Exception {
CompletableFuture<Void> allFuturesResult = CompletableFuture
.allOf(futuresList.stream().map(CompletableFutureUtil::executeActionAsync).toArray(CompletableFuture[]::new));
allFuturesResult.get();
return futuresList
.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
public static <T> CompletableFuture<T> executeActionAsync(CompletableFuture<T> action) {
return action
.thenApply(CompletableFuture::completedFuture)
.exceptionally(t -> retry(t, action, 0))
.thenCompose(Function.identity());
}
private static <T> CompletableFuture<T> retry(Throwable first, CompletableFuture<T> action, int retry) {
if (retry >= MAX_RETRIES) {
return CompletableFutureUtil.failedFuture(first);
}
return action
.thenApply(CompletableFuture::completedFuture)
.exceptionally(t -> {
// first.addSuppressed(t);
log.info("任务:{} , 重试:{}", action, retry);
return retry(first, action, retry + 1); })
.thenCompose(Function.identity());
}
public static <T> CompletableFuture<T> failedFuture(Throwable t) {
final CompletableFuture<T> cf = new CompletableFuture<>();
cf.completeExceptionally(t);
return cf;
}
}