|
@@ -0,0 +1,369 @@
|
|
|
+package com.kingdee.eas.custom.synctask;
|
|
|
+
|
|
|
+import com.kingdee.bos.dao.IObjectPK;
|
|
|
+import com.kingdee.bos.metadata.entity.EntityViewInfo;
|
|
|
+import com.kingdee.bos.metadata.entity.FilterInfo;
|
|
|
+import com.kingdee.bos.metadata.entity.FilterItemInfo;
|
|
|
+import com.kingdee.bos.metadata.entity.SelectorItemInfo;
|
|
|
+import com.kingdee.bos.metadata.query.util.CompareType;
|
|
|
+import com.kingdee.eas.base.permission.UserInfo;
|
|
|
+import com.kingdee.eas.basedata.person.PersonCollection;
|
|
|
+import com.kingdee.eas.basedata.person.PersonInfo;
|
|
|
+import com.kingdee.eas.common.EASBizException;
|
|
|
+import com.kingdee.eas.custom.synctask.util.HttpClientUtil;
|
|
|
+import com.kingdee.eas.hr.ats.*;
|
|
|
+import com.kingdee.eas.hr.ats.holidayLimit.generate.util.HRTimeWebUtils;
|
|
|
+
|
|
|
+import org.apache.log4j.Logger;
|
|
|
+import java.io.FileInputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.sql.Timestamp;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.time.Instant;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.*;
|
|
|
+import com.kingdee.bos.*;
|
|
|
+import com.kingdee.eas.basedata.person.IPerson;
|
|
|
+import com.kingdee.eas.basedata.person.PersonFactory;
|
|
|
+import com.kingdee.eas.framework.CoreBaseCollection;
|
|
|
+import com.kingdee.util.StringUtils;
|
|
|
+
|
|
|
+import oadd.org.apache.commons.codec.digest.DigestUtils;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+
|
|
|
+public class SyncTranForAtsFacadeControllerBean extends AbstractSyncTranForAtsFacadeControllerBean
|
|
|
+{
|
|
|
+ private static Logger logger = Logger.getLogger(SyncTranForAtsFacadeControllerBean.class);
|
|
|
+
|
|
|
+ private volatile Properties propt; // 使用 volatile 保证可见性
|
|
|
+ private final Object configLock = new Object();
|
|
|
+
|
|
|
+ private void loadProperties() throws BOSException {
|
|
|
+ if (propt == null) { // 第一次检查
|
|
|
+ synchronized (configLock) {
|
|
|
+ if (propt == null) { // 第二次检查(双重检查锁)
|
|
|
+ Properties temp = new Properties();
|
|
|
+ String path = System.getProperty("EAS_HOME") + "/server/properties/abk/syncAtsConfig.properties";
|
|
|
+ try (FileInputStream fis = new FileInputStream(path)) { // try-with-resources 自动关闭流
|
|
|
+ temp.load(fis);
|
|
|
+ propt = temp; // 原子性赋值
|
|
|
+ } catch (IOException e) {
|
|
|
+ String errorMsg = "加载配置文件失败: " + path + " | " + e.getMessage();
|
|
|
+ logger.error(errorMsg, e); // 使用日志记录代替 e.printStackTrace()
|
|
|
+ throw new BOSException(errorMsg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void _syncPunchRecord(Context ctx, String beginDate, String endDate, int offset) throws BOSException {
|
|
|
+ loadProperties();
|
|
|
+ // 0. 参数校验与日期解析
|
|
|
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ LocalDate start = LocalDate.now();
|
|
|
+ LocalDate end = LocalDate.now();
|
|
|
+ //如果时间参数都为空,则使用offset参数
|
|
|
+ if(StringUtils.isEmpty(beginDate) && StringUtils.isEmpty(endDate)){
|
|
|
+ if( offset > 0 ){
|
|
|
+ //eDate增加offset天
|
|
|
+ end = end.plusDays(offset);
|
|
|
+ }else if( offset < 0 ) {
|
|
|
+ start = start.plusDays(offset);
|
|
|
+ }
|
|
|
+ }else if(StringUtils.isEmpty(beginDate) ){
|
|
|
+ //开始日期字定义,结束日期都是今天
|
|
|
+ start = LocalDate.parse(beginDate, dateFormatter);
|
|
|
+ }else if(StringUtils.isEmpty(endDate)){
|
|
|
+ //开始日期,结束日期都是今天
|
|
|
+ end = LocalDate.parse(endDate, dateFormatter);
|
|
|
+ }
|
|
|
+ if (start.isAfter(end)) {
|
|
|
+ throw new BOSException("开始日期不能晚于结束日期");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 按天循环处理
|
|
|
+ LocalDate currentDay = start;
|
|
|
+ while (!currentDay.isAfter(end)) {
|
|
|
+ try {
|
|
|
+ // 当天日期字符串
|
|
|
+ String dayStr = currentDay.format(dateFormatter);
|
|
|
+
|
|
|
+ // 生成 Token 和 URL 参数
|
|
|
+ long currentTime = Instant.now().getEpochSecond();
|
|
|
+ String secretKey = propt.getProperty("SECRET_KEY");
|
|
|
+ String token = DigestUtils.md5Hex(secretKey + currentTime);
|
|
|
+ // 构建查询参数
|
|
|
+ Map<String, String> queryParams = new HashMap<>();
|
|
|
+ queryParams.put("timestamp", String.valueOf(currentTime));
|
|
|
+ queryParams.put("token", token);
|
|
|
+
|
|
|
+ // 构建请求体(自动处理时间范围)
|
|
|
+ JSONObject requestBody = buildRequestParams(dayStr, dayStr); // 同一天
|
|
|
+
|
|
|
+ // 调用接口
|
|
|
+ String urlPath = propt.getProperty("urlPath");
|
|
|
+ String response = HttpClientUtil.sendPostRequest(urlPath, requestBody, queryParams);
|
|
|
+
|
|
|
+ // 处理响应
|
|
|
+ if (response != null) {
|
|
|
+ JSONObject result = JSONObject.parseObject(response);
|
|
|
+ if ("0".equals(result.getString("ret"))) {
|
|
|
+ JSONObject data = result.getJSONObject("data");
|
|
|
+ JSONArray items = data.getJSONArray("items");
|
|
|
+ logger.error("日期 " + dayStr + " 打卡同步接口,接口共传输记录数: " + items.size()+"条数据");
|
|
|
+ saveMJPunchCardData(ctx, items);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 记录错误但继续执行
|
|
|
+ logger.error("日期 " + currentDay + " 数据同步失败: " + e.getMessage(), e);
|
|
|
+ } finally {
|
|
|
+ currentDay = currentDay.plusDays(1); // 处理下一天
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private JSONObject buildRequestParams(String startDate, String endDate) {
|
|
|
+ JSONObject params = new JSONObject();
|
|
|
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
|
|
+ DateTimeFormatter datetimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+
|
|
|
+ LocalDate today = LocalDate.now();
|
|
|
+ // 处理 startDate:默认为当天 00:00:00
|
|
|
+ if (StringUtils.isEmpty(startDate)) {
|
|
|
+ startDate = today.atStartOfDay().format(datetimeFormatter);
|
|
|
+ } else {
|
|
|
+ startDate = LocalDate.parse(startDate, dateFormatter)
|
|
|
+ .atStartOfDay()
|
|
|
+ .format(datetimeFormatter);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理 endDate:默认为当天 23:59:59
|
|
|
+ if (StringUtils.isEmpty(endDate)) {
|
|
|
+ endDate = today.atTime(23, 59, 59).format(datetimeFormatter);
|
|
|
+ } else {
|
|
|
+ endDate = LocalDate.parse(endDate, dateFormatter)
|
|
|
+ .atTime(23, 59, 59)
|
|
|
+ .format(datetimeFormatter);
|
|
|
+ }
|
|
|
+ params.put("starttime", startDate);
|
|
|
+ params.put("endtime", endDate);
|
|
|
+ return params;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存门禁打卡记录
|
|
|
+ * @param ctx
|
|
|
+ * @param jsonArray
|
|
|
+ */
|
|
|
+ public void saveMJPunchCardData(Context ctx, JSONArray jsonArray) {
|
|
|
+ logger.error("saveMJPunchCardData----");
|
|
|
+ try {
|
|
|
+ int success = 0;
|
|
|
+ Set<String> setCardId = new HashSet();
|
|
|
+
|
|
|
+ CoreBaseCollection cardColl = new CoreBaseCollection();
|
|
|
+ //没有考勤档案的人员数据
|
|
|
+
|
|
|
+ //获取员工编码
|
|
|
+ IPerson iPerson = PersonFactory.getLocalInstance(ctx);
|
|
|
+ PersonCollection personCollection = iPerson.getPersonCollection("select number ");
|
|
|
+ Set<String> persNoSet = new HashSet();
|
|
|
+ for (int i = 0; i < personCollection.size(); i++) {
|
|
|
+ PersonInfo personInfo = personCollection.get(i);
|
|
|
+ persNoSet.add(personInfo.getNumber().toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ //"id": 928547,
|
|
|
+ // "pin": "1234",
|
|
|
+ // "ename": "何霞兰",
|
|
|
+ // "deptnumber": "1",
|
|
|
+ // "deptname": "总部门",
|
|
|
+ // "checktime": "2025-04-25 00:00:12",
|
|
|
+ // "sn": "FAC1234701127",
|
|
|
+ // "alias": "惠州1号楼1层",
|
|
|
+ // "verify": 15,
|
|
|
+ // "stateno": "5",
|
|
|
+ // "state": ""
|
|
|
+
|
|
|
+ //查询本此打卡机过来的数据的id
|
|
|
+ HashSet<String> attIds = new HashSet();
|
|
|
+ for (int j = 0; j < jsonArray.size(); j++) {
|
|
|
+ JSONObject resultData = jsonArray.getJSONObject(j);
|
|
|
+ attIds.add(resultData.get("id").toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ //相同的数据
|
|
|
+ HashSet<String> existedPunchCardRecordCol = getExistedPunchCardRecordCol(ctx, attIds);
|
|
|
+ for (int j = 0; j < jsonArray.size(); j++) {
|
|
|
+ JSONObject resultData = jsonArray.getJSONObject(j);
|
|
|
+ String attId = resultData.getString("id");
|
|
|
+ if (existedPunchCardRecordCol.contains(attId)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ ++success;
|
|
|
+ String personNumber = resultData.getString("pin");//员工编码
|
|
|
+ String checktime = resultData.getString("checktime"); //打卡时间
|
|
|
+ String RecDate = checktime.substring(0, 10);// 考勤日期
|
|
|
+ String address = resultData.getString("alias"); //打卡地点
|
|
|
+ String EquNo = resultData.getString("sn"); //考勤机编码
|
|
|
+ SimpleDateFormat sdfymd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
|
|
+ Date punchDate = sdfymd.parse(RecDate + " 00:00:00");
|
|
|
+// Date punchDate = HRTimeWebUtils.stringToShortDate(RecDate, true);
|
|
|
+ Timestamp punchCardTime = HRTimeWebUtils.stringToTimestamp(checktime, true);
|
|
|
+ // 处理数据
|
|
|
+ if (punchDate != null && punchCardTime != null) {
|
|
|
+ PunchCardRecordInfo cardInfo = new PunchCardRecordInfo();
|
|
|
+ cardInfo.setNumber(attId);
|
|
|
+ //考勤机编码
|
|
|
+ cardInfo.setEquipmentNum(EquNo);
|
|
|
+ //员工编码
|
|
|
+ cardInfo.setAttendanceNum(personNumber);
|
|
|
+ //打卡日期
|
|
|
+ cardInfo.setPunchCardDate(punchDate);
|
|
|
+ //打卡时间
|
|
|
+ cardInfo.setPunchCardTime(punchCardTime);
|
|
|
+ // 打卡来源
|
|
|
+ cardInfo.setPunchCardSource(PunchCardSourceEnum.attenceMachine);
|
|
|
+ // 打卡位置
|
|
|
+ cardInfo.setPunchCardPlace(address);
|
|
|
+ // 有效
|
|
|
+ PunchCardStateEnum punchCardStateEnum;
|
|
|
+ //if ((boolean) resultData.get("isActive")) {
|
|
|
+ // punchCardStateEnum = PunchCardStateEnum.normal;
|
|
|
+ //} else {
|
|
|
+ // punchCardStateEnum = PunchCardStateEnum.cancelled;
|
|
|
+ //}
|
|
|
+ punchCardStateEnum = PunchCardStateEnum.normal;
|
|
|
+ cardInfo.setPunchCardState(punchCardStateEnum);
|
|
|
+ ////简称
|
|
|
+ //cardInfo.setSimpleName(exceptionType);
|
|
|
+ //描述
|
|
|
+ cardInfo.setDescription("考勤机同步");
|
|
|
+ if (!StringUtils.isEmpty(personNumber)) {
|
|
|
+ //shr系统存在的人员才添加;
|
|
|
+ if (!persNoSet.contains(personNumber)) {
|
|
|
+ logger.error("打卡同步:personNumber not exist in shr system, personNumber=" + personNumber);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ setCardId.add(personNumber);
|
|
|
+ cardColl.add(cardInfo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ syncAttendanceRecords(setCardId, cardColl, ctx);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("保存门禁打卡记录失败: " + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ // 考勤档案
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private void syncAttendanceRecords(Set<String> setCardId, CoreBaseCollection cardColl, Context ctx) throws BOSException, EASBizException {
|
|
|
+ // 获取考勤档案
|
|
|
+ Map<String, AttendanceFileInfo> attendanceFileMap = AttendanceFileFactory.getLocalInstance(ctx).getPersonByAttendanceNum(setCardId);
|
|
|
+ logger.error("attendanceFileMap-----" + attendanceFileMap);
|
|
|
+ logger.error("setCardId-----" + setCardId);
|
|
|
+
|
|
|
+ CoreBaseCollection cardCollFinally = new CoreBaseCollection();
|
|
|
+ Collection<Map> seccussArray = new ArrayList<>();
|
|
|
+ Collection<JSONObject> notExistRecords = new ArrayList<>();
|
|
|
+ int success = 0;
|
|
|
+
|
|
|
+ for (int i = 0; i < cardColl.size(); ++i) {
|
|
|
+ Map<String, String> map = new HashMap<>();
|
|
|
+ // 原始打卡记录实体
|
|
|
+ PunchCardRecordInfo cardInfo = (PunchCardRecordInfo) cardColl.get(i);
|
|
|
+ JSONObject jsObject = new JSONObject();
|
|
|
+ jsObject.put("deviceid", cardInfo.getEquipmentNum()); // deviceid --EquNo
|
|
|
+ jsObject.put("userid", cardInfo.getAttendanceNum()); // userid --CardId
|
|
|
+ jsObject.put("RecDate", HRTimeWebUtils.dateShortToString(cardInfo.getPunchCardDate())); //checkin_time --RecDate
|
|
|
+ jsObject.put("RecTime", HRTimeWebUtils.timestampToString(cardInfo.getPunchCardTime()).substring(11)); //checkin_time --RecTime
|
|
|
+ map.put("userid", cardInfo.getAttendanceNum());
|
|
|
+ map.put("RecDate", HRTimeWebUtils.dateShortToString(cardInfo.getPunchCardDate()));
|
|
|
+ map.put("RecTime", HRTimeWebUtils.timestampToString(cardInfo.getPunchCardTime()).substring(11));
|
|
|
+
|
|
|
+ // 员工是否有考勤档案
|
|
|
+ if (attendanceFileMap.containsKey(cardInfo.getAttendanceNum())) {
|
|
|
+ ++success;
|
|
|
+ // 打卡位置 ,考勤机 的地址
|
|
|
+ cardInfo.setPunchCardPlace(cardInfo.getPunchCardPlace());
|
|
|
+ // 打卡来源
|
|
|
+ cardInfo.setPunchCardSource(PunchCardSourceEnum.attenceMachine);
|
|
|
+ // 考勤实体
|
|
|
+ AttendanceFileInfo attendanceFileInfo = attendanceFileMap.get(cardInfo.getAttendanceNum());
|
|
|
+ // 姓名
|
|
|
+ cardInfo.setProposer(attendanceFileInfo.getProposer());
|
|
|
+ // HR 组织
|
|
|
+ cardInfo.setHrOrgUnit(attendanceFileInfo.getHrOrgUnit());
|
|
|
+ // 行政组织
|
|
|
+ cardInfo.setAdminOrgUnit(attendanceFileInfo.getAdminOrgUnit());
|
|
|
+ cardInfo.setCreator((UserInfo) ctx.get("UserInfo"));
|
|
|
+ cardInfo.setLastUpdateUser((UserInfo) ctx.get("UserInfo"));
|
|
|
+ cardInfo.setCreateTime(new Timestamp(new Date().getTime()));
|
|
|
+ cardInfo.setLastUpdateTime(new Timestamp(new Date().getTime()));
|
|
|
+ cardCollFinally.add(cardInfo);
|
|
|
+ seccussArray.add(map);
|
|
|
+ } else {
|
|
|
+ notExistRecords.add(jsObject);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.error("打卡同步保存到hr数量-----" + cardCollFinally.size());
|
|
|
+ logger.error("打卡同步符合条件的写入数量-----" + success);
|
|
|
+ logger.error("打卡同步时不存在考勤档案---" + notExistRecords);
|
|
|
+ logger.error("打卡同步时不存在考勤档案数量-----" + notExistRecords.size());
|
|
|
+ logger.error("seccussArray--Size-----" + seccussArray.size());
|
|
|
+ logger.error("seccussArray-----" + seccussArray);
|
|
|
+
|
|
|
+ if (cardCollFinally.size() > 0) {
|
|
|
+ IObjectPK[] iObjectPKS = PunchCardRecordFactory.getLocalInstance(ctx).saveBatchData(cardCollFinally);
|
|
|
+ logger.error("iObjectPKS----" + iObjectPKS.length);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private HashSet<String> getExistedPunchCardRecordCol(Context ctx, HashSet<String> attId) throws BOSException {
|
|
|
+ HashSet<String> hashSet = new HashSet<String>();
|
|
|
+ EntityViewInfo evi = new EntityViewInfo();
|
|
|
+ FilterInfo fi = new FilterInfo();
|
|
|
+ evi.setFilter(fi);
|
|
|
+ evi.getSelector().add(new SelectorItemInfo("number"));
|
|
|
+ fi.getFilterItems().add(new FilterItemInfo("number", attId, CompareType.INCLUDE));
|
|
|
+ PunchCardRecordCollection existColl = PunchCardRecordFactory.getLocalInstance(ctx).getPunchCardRecordCollection(evi);
|
|
|
+ for(int i =0 ;i<existColl.size();i++) {
|
|
|
+ PunchCardRecordInfo punchCardRecordInfo = existColl.get(i);
|
|
|
+ hashSet.add(punchCardRecordInfo.getNumber());
|
|
|
+ }
|
|
|
+ return hashSet;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}
|