|
|
@@ -3,22 +3,37 @@ package com.lc.ibps.components.employee.domain;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
|
+import cn.hutool.json.JSONArray;
|
|
|
+import cn.hutool.json.JSONObject;
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.lc.ibps.base.core.constants.StringPool;
|
|
|
+import com.lc.ibps.base.core.util.Collections;
|
|
|
+import com.lc.ibps.base.framework.table.ICommonDao;
|
|
|
+import com.lc.ibps.base.web.context.ContextUtil;
|
|
|
+import com.lc.ibps.components.employee.persistence.dao.StaffScheduleDetailQueryDao;
|
|
|
+import com.lc.ibps.components.employee.persistence.entity.*;
|
|
|
+import com.lc.ibps.components.employee.repository.*;
|
|
|
+import com.lc.ibps.untils.StrUtil;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.context.annotation.Scope;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import com.lc.ibps.base.core.util.BeanUtils;
|
|
|
-import com.lc.ibps.base.core.util.string.StringUtil;
|
|
|
-import com.lc.ibps.base.core.util.AppUtil;
|
|
|
import com.lc.ibps.base.framework.domain.AbstractDomain;
|
|
|
import com.lc.ibps.base.framework.persistence.dao.IDao;
|
|
|
import com.lc.ibps.base.framework.persistence.dao.IQueryDao;
|
|
|
import com.lc.ibps.components.employee.persistence.dao.StaffScheduleDao;
|
|
|
import com.lc.ibps.components.employee.persistence.dao.StaffScheduleQueryDao;
|
|
|
-import com.lc.ibps.components.employee.repository.StaffScheduleRepository;
|
|
|
-import com.lc.ibps.components.employee.persistence.entity.StaffSchedulePo;
|
|
|
-import com.lc.ibps.components.employee.repository.StaffScheduleDetailRepository;
|
|
|
-import com.lc.ibps.components.employee.persistence.entity.StaffScheduleDetailPo;
|
|
|
+
|
|
|
+import java.time.Duration;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.time.format.DateTimeParseException;
|
|
|
+import java.util.*;
|
|
|
|
|
|
/**
|
|
|
* 排班记录表 领域对象实体
|
|
|
@@ -45,6 +60,29 @@ public class StaffSchedule extends AbstractDomain<String, StaffSchedulePo>{
|
|
|
@Resource
|
|
|
private StaffScheduleDetailRepository staffScheduleDetailRepository;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private AttendanceDetailRepository attendanceDetailRepository;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private EmployeeInfoRepository employeeInfoRepository;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ICommonDao commonDao;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private AdjustmentRepository adjustmentRepository;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private StaffScheduleDetailQueryDao scheduleDetailQueryDao;
|
|
|
+
|
|
|
+ private static final Logger logger = LoggerFactory.getLogger(StaffSchedule.class);
|
|
|
+
|
|
|
+ private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(StringPool.DATE_FORMAT_DATETIME_NOSECOND);
|
|
|
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(StringPool.DATE_FORMAT_DATE);
|
|
|
+ private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(StringPool.DATE_FORMAT_DATETIME);
|
|
|
+ private static final String STATUS_APPROVED = "已通过";
|
|
|
+ private static final String ADJUSTMENT_TYPE_SCHEDULE = "paiban";
|
|
|
+
|
|
|
protected void init(){
|
|
|
//
|
|
|
}
|
|
|
@@ -71,21 +109,253 @@ public class StaffSchedule extends AbstractDomain<String, StaffSchedulePo>{
|
|
|
* @since 1.0.0
|
|
|
*/
|
|
|
public void saveCascade(){
|
|
|
+ List<StaffScheduleDetailPo> oldList = staffScheduleDetailRepository.findByMainId(getData().getId());
|
|
|
save();
|
|
|
if(getData().isDelBeforeSave()){
|
|
|
StaffScheduleDetail staffScheduleDetail = staffScheduleDetailRepository.newInstance();
|
|
|
staffScheduleDetail.deleteByMainId(getData().getId());
|
|
|
}
|
|
|
-
|
|
|
+ LocalDate today = LocalDate.now();
|
|
|
+ List<Map<String,String>> config = getScheduleConfig(getData().getConfig());
|
|
|
StaffScheduleDetail staffScheduleDetail = staffScheduleDetailRepository.newInstance();
|
|
|
+ AttendanceDetail attendanceDetail = attendanceDetailRepository.newInstance();
|
|
|
+
|
|
|
if(BeanUtils.isNotEmpty(getData().getStaffScheduleDetailPoList())){
|
|
|
for(StaffScheduleDetailPo staffScheduleDetailPo:getData().getStaffScheduleDetailPoList()){
|
|
|
- //设置外键
|
|
|
staffScheduleDetailPo.setParentId(getData().getId());
|
|
|
staffScheduleDetail.save(staffScheduleDetailPo);
|
|
|
+ for(StaffScheduleDetailPo oldScheduleDetailPo :oldList ){
|
|
|
+ if(oldScheduleDetailPo.getId().equals(staffScheduleDetailPo.getId())){
|
|
|
+ List<Map<String, String>> newDate = extractNonEmptyDValuesAsArray(staffScheduleDetailPo,getData().getStartDate(),getData().getEndDate());
|
|
|
+ List<Map<String, String>> oldDate = extractNonEmptyDValuesAsArray(oldScheduleDetailPo,getData().getStartDate(),getData().getEndDate());
|
|
|
+
|
|
|
+ // 查询主部门 和 用户工号
|
|
|
+ String sql = "SELECT MAIN_PID_ from ibps_party_rel WHERE MAIN_TYPE_='position' and BIZ_='mainPost' and SUB_PID_='%s' limit 1";
|
|
|
+ sql = String.format(sql, staffScheduleDetailPo.getUserId());
|
|
|
+ Map<String,Object> position = commonDao.queryOne(sql);
|
|
|
+ EmployeeInfoPo employeeInfoPo = employeeInfoRepository.get(staffScheduleDetailPo.getUserId());
|
|
|
+
|
|
|
+ // 添加修改记录 即 调班申请记录
|
|
|
+ addAdjustRecord(newDate,oldDate,staffScheduleDetailPo.getId());
|
|
|
+
|
|
|
+ for (Map<String, String> entry : newDate) {
|
|
|
+ String dateStr = entry.get("date");
|
|
|
+ String value = entry.get("value");
|
|
|
+ LocalDate date = LocalDate.parse(dateStr);
|
|
|
+ if (date.isBefore(today)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ String[] values = value.split(StringPool.COMMA);
|
|
|
+
|
|
|
+ // 查询旧考勤记录
|
|
|
+ String sql2 = "SELECT id_ from t_attendance_detail where pai_ban_ji_lu_id_='%s' and yong_hu_id_='%s' and ri_qi_='%s' ";
|
|
|
+ sql2 = String.format(sql2, staffScheduleDetailPo.getId(),staffScheduleDetailPo.getUserId(),dateStr);
|
|
|
+ List<Map<String,Object>> attendanceList = commonDao.query(sql2);
|
|
|
+ if (Collections.isNotEmpty(attendanceList)) {
|
|
|
+ String[] ids = attendanceList.stream().map(map -> String.valueOf(map.get("id_"))).toArray(String[]::new);
|
|
|
+ AttendanceDetail domain = attendanceDetailRepository.newInstance();
|
|
|
+ domain.deleteByIds(ids);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (BeanUtils.isNotEmpty(value)){
|
|
|
+ for (String s : values) {
|
|
|
+ AttendanceDetailPo attendanceDetailPo = new AttendanceDetailPo();
|
|
|
+ attendanceDetailPo.setDiDian(getData().getDiDian());
|
|
|
+ attendanceDetailPo.setPaiBanId(getData().getId());
|
|
|
+ attendanceDetailPo.setPaiBanJiLuId(staffScheduleDetailPo.getId());
|
|
|
+ attendanceDetailPo.setRiQi(dateStr);
|
|
|
+ attendanceDetailPo.setYongHuId(staffScheduleDetailPo.getUserId());
|
|
|
+ attendanceDetailPo.setBuMen(employeeInfoPo.getPositions().split(StringPool.COMMA)[0]);
|
|
|
+ if (BeanUtils.isNotEmpty(position)) {
|
|
|
+ attendanceDetailPo.setBuMen(position.get("MAIN_PID_").toString());
|
|
|
+ }
|
|
|
+ attendanceDetailPo.setGongHao(employeeInfoPo.getJianDingZiGeZ()==null?"":employeeInfoPo.getJianDingZiGeZ());
|
|
|
+ attendanceDetailPo.setPaiBanMingChen(getData().getTitle());
|
|
|
+ for (Map<String,String> map : config) {
|
|
|
+ if (map.get("alias").equals(s)) {
|
|
|
+ String next = map.get("isSecondDay");
|
|
|
+ attendanceDetailPo.setBanCiBieMing(s);
|
|
|
+ attendanceDetailPo.setBanCiMing(map.get("name"));
|
|
|
+ attendanceDetailPo.setBanCiKaiShi(dateStr +StringPool.SPACE+ map.get("startTime"));
|
|
|
+ attendanceDetailPo.setBanCiJieShu(dateStr +StringPool.SPACE+ map.get("endTime"));
|
|
|
+ if (next.equals("Y")){
|
|
|
+ String nextDateStr = LocalDate.parse(dateStr, DATE_FORMATTER).plusDays(1).format(DATE_FORMATTER);
|
|
|
+ attendanceDetailPo.setBanCiJieShu(nextDateStr +StringPool.SPACE+ map.get("endTime"));
|
|
|
+ }
|
|
|
+ attendanceDetailPo.setShiFouKuaRi(map.get("isSecondDay"));
|
|
|
+ attendanceDetailPo.setBanCiShiChang(calculateMinutesBetween(attendanceDetailPo.getBanCiKaiShi(),attendanceDetailPo.getBanCiJieShu()));
|
|
|
+ attendanceDetailPo.setKaoQinZhuangTa("");
|
|
|
+ attendanceDetail.save(attendanceDetailPo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }else {
|
|
|
+ AttendanceDetailPo attendanceDetailPo = new AttendanceDetailPo();
|
|
|
+ attendanceDetailPo.setDiDian(getData().getDiDian());
|
|
|
+ attendanceDetailPo.setPaiBanId(getData().getId());
|
|
|
+ attendanceDetailPo.setPaiBanJiLuId(staffScheduleDetailPo.getId());
|
|
|
+ attendanceDetailPo.setRiQi(dateStr);
|
|
|
+ attendanceDetailPo.setYongHuId(staffScheduleDetailPo.getUserId());
|
|
|
+ attendanceDetailPo.setBuMen(employeeInfoPo.getPositions().split(StringPool.COMMA)[0]);
|
|
|
+ if (BeanUtils.isNotEmpty(position)) {
|
|
|
+ attendanceDetailPo.setBuMen(position.get("MAIN_PID_").toString());
|
|
|
+ }
|
|
|
+ attendanceDetailPo.setGongHao(employeeInfoPo.getJianDingZiGeZ()==null?"":employeeInfoPo.getJianDingZiGeZ());
|
|
|
+ attendanceDetailPo.setPaiBanMingChen(getData().getTitle());
|
|
|
+ attendanceDetailPo.setBanCiMing("休息");
|
|
|
+ attendanceDetailPo.setKaoQinZhuangTa("");
|
|
|
+ attendanceDetail.save(attendanceDetailPo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void addAdjustRecord(List<Map<String,String>> newDateList,List<Map<String,String>> oldDateList,String scheduleId) {
|
|
|
+ boolean areEqualIgnoreOrder = new HashSet<>(newDateList).equals(new HashSet<>(oldDateList))&& newDateList.size() == oldDateList.size();
|
|
|
+ if (!areEqualIgnoreOrder) {
|
|
|
+ AdjustmentPo adjustmentPo = new AdjustmentPo();
|
|
|
+ adjustmentPo.setDiDian(getData().getDiDian());
|
|
|
+ adjustmentPo.setCreateBy(ContextUtil.getCurrentUserId());
|
|
|
+ adjustmentPo.setStatus(STATUS_APPROVED);
|
|
|
+ JSONObject configJson = JSONUtil.parseObj(getData().getConfig());
|
|
|
+ JSONArray approverArray = configJson.getJSONArray("approver");
|
|
|
+ String[] approverList = new String[approverArray.size()];
|
|
|
+ for (int i = 0; i < approverArray.size(); i++) {
|
|
|
+ approverList[i] = approverArray.getStr(i);
|
|
|
+ }
|
|
|
+ String executor = String.join(",", approverList);
|
|
|
+ adjustmentPo.setExecutor(executor);
|
|
|
+ adjustmentPo.setType(ADJUSTMENT_TYPE_SCHEDULE);
|
|
|
+ StringBuilder overview = new StringBuilder();
|
|
|
+ List<AdjustmentDetailPo> adjustmentDetailPoList = new ArrayList<>();
|
|
|
+ adjustmentPo.setReason("修改排班");
|
|
|
+ for (Map<String, String> newMap : newDateList) {
|
|
|
+ for (Map<String, String> oldMap : oldDateList) {
|
|
|
+ String newValue = StrUtil.str(newMap.get("value"));
|
|
|
+ String newDate = StrUtil.str(newMap.get("date"));
|
|
|
+ String oldValue = StrUtil.str(oldMap.get("value"));
|
|
|
+ String oldDate = StrUtil.str(oldMap.get("date"));
|
|
|
+ if (!newValue.equals(oldValue) && oldDate.equals(newDate)) {
|
|
|
+ oldValue = oldValue.isEmpty() ?"无":oldValue;
|
|
|
+ newValue = newValue.isEmpty() ?"无":newValue;
|
|
|
+ AdjustmentDetailPo adjustmentDetailPo = new AdjustmentDetailPo();
|
|
|
+ adjustmentDetailPo.setCreateBy(ContextUtil.getCurrentUserId());
|
|
|
+ adjustmentDetailPo.setRecordId(scheduleId);
|
|
|
+ adjustmentDetailPo.setBeforeAdjust(oldValue);
|
|
|
+ adjustmentDetailPo.setBeforeDate(oldDate);
|
|
|
+ adjustmentDetailPo.setAfterAdjust(newValue);
|
|
|
+ adjustmentDetailPo.setAfterDate(newDate);
|
|
|
+ adjustmentDetailPo.setStatus("已通过");
|
|
|
+ adjustmentDetailPo.setParty(ContextUtil.getCurrentUserId());
|
|
|
+ adjustmentDetailPo.setAuditTime(LocalDateTime.now().format(TIME_FORMATTER));
|
|
|
+ overview.append(oldDate).append("班次【").append(oldValue).append("】调换到").append(newDate).append("班次【").append(newValue).append("】").append("\n");
|
|
|
+ adjustmentDetailPoList.add(adjustmentDetailPo);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ adjustmentPo.setOverview(String.valueOf(overview));
|
|
|
+ adjustmentPo.setAdjustmentDetailPoList(adjustmentDetailPoList);
|
|
|
+ Adjustment adjustment = adjustmentRepository.newInstance(adjustmentPo);
|
|
|
+ adjustment.saveCascade();
|
|
|
}
|
|
|
- }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public static long calculateMinutesBetween(String startTimeStr, String endTimeStr) {
|
|
|
+ if (startTimeStr == null || endTimeStr == null) {
|
|
|
+ throw new IllegalArgumentException("时间参数不能为null");
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ LocalDateTime start = LocalDateTime.parse(startTimeStr, DATE_TIME_FORMATTER);
|
|
|
+ LocalDateTime end = LocalDateTime.parse(endTimeStr, DATE_TIME_FORMATTER);
|
|
|
+ Duration duration = Duration.between(start, end);
|
|
|
+ return Math.abs(duration.toMinutes());
|
|
|
+ } catch (DateTimeParseException e) {
|
|
|
+ throw new IllegalArgumentException("时间格式不正确,应使用 yyyy-MM-dd HH:mm 格式", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<Map<String, String>> getScheduleConfig(String config) {
|
|
|
+ List<Map<String, String>> result = new ArrayList<>();
|
|
|
+ JSONObject configJson = JSONUtil.parseObj(config);
|
|
|
+ JSONArray scheduleShifts = configJson.getJSONArray("scheduleShift");
|
|
|
+ if (scheduleShifts == null || scheduleShifts.isEmpty()) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ for (int i = 0; i < scheduleShifts.size(); i++) {
|
|
|
+ JSONObject shift = scheduleShifts.getJSONObject(i);
|
|
|
+ String name = shift.getStr("name");
|
|
|
+ String alias = shift.getStr("alias");
|
|
|
+ JSONArray dateRanges = shift.getJSONArray("dateRange");
|
|
|
+ if (dateRanges == null || dateRanges.isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for (int j = 0; j < dateRanges.size(); j++) {
|
|
|
+ JSONObject dateRange = dateRanges.getJSONObject(j);
|
|
|
+ Map<String, String> scheduleEntry = new HashMap<>();
|
|
|
+ putIfNotNull(scheduleEntry, "name", name);
|
|
|
+ putIfNotNull(scheduleEntry, "alias", alias);
|
|
|
+ putIfNotNull(scheduleEntry, dateRange, "startTime");
|
|
|
+ putIfNotNull(scheduleEntry, dateRange, "endTime");
|
|
|
+ putIfNotNull(scheduleEntry, dateRange, "isSecondDay");
|
|
|
+ result.add(scheduleEntry);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void putIfNotNull(Map<String, String> map, JSONObject json, String key) {
|
|
|
+ String value = json.getStr(key);
|
|
|
+ putIfNotNull(map, key, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void putIfNotNull(Map<String, String> map, String key, String value) {
|
|
|
+ if (value != null && !value.isEmpty()) {
|
|
|
+ map.put(key, value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static List<Map<String, String>> extractNonEmptyDValuesAsArray(StaffScheduleDetailPo detailPo, String startDate, String endDate) {
|
|
|
+ List<Map<String, String>> result = new ArrayList<>();
|
|
|
+ try {
|
|
|
+ LocalDate start = LocalDate.parse(startDate);
|
|
|
+ LocalDate end = LocalDate.parse(endDate);
|
|
|
+ if (start.isAfter(end) || BeanUtils.isEmpty(detailPo)) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ List<LocalDate> dateList = new ArrayList<>();
|
|
|
+ LocalDate current = start;
|
|
|
+ while (!current.isAfter(end)) {
|
|
|
+ dateList.add(current);
|
|
|
+ current = current.plusDays(1);
|
|
|
+ }
|
|
|
+ int maxDays = Math.min(dateList.size(), 45);
|
|
|
+ for (int i = 0; i < maxDays; i++) {
|
|
|
+ LocalDate date = dateList.get(i);
|
|
|
+ int dNumber = i + 1;
|
|
|
+ String methodName = "getD" + dNumber;
|
|
|
+ try {
|
|
|
+ Method method = StaffScheduleDetailPo.class.getMethod(methodName);
|
|
|
+ String value = (String) method.invoke(detailPo);
|
|
|
+ Map<String, String> entry = new HashMap<>();
|
|
|
+ entry.put("date", date.toString());
|
|
|
+ entry.put("value", value);
|
|
|
+ result.add(entry);
|
|
|
+ } catch (NoSuchMethodException e) {
|
|
|
+ break;
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("Error accessing field d" + dNumber, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("Invalid date format or processing error", e);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
* 主从表一并删除
|