以前做HR时候由于是copy的sap hr的表结构,sap有定界的概念,定界根据时间约束来表示数据开始结束日期在表中什么样的,无交叉,无重叠,连续等。以前根据业务场景写,每个表都写了一遍,没有用泛型和反射去实现公用写法。现在从另一个角度来写,带有些许算法的味儿。
1.普通写法,声明一个TimeEntity,业务代码去复制给TimeEntity就好。然后都围绕这个TimeEntity实现。最后再处理相关业务。工具类都是普通的工具类,所以不在这里贴出来了。
根据各种场景截取日期时间段,得到自己想要的。看注释里就明白了。
import cn.hutool.json.JSONUtil;
import com.xhj.framework.web.common.DateUtils;
import com.xhj.framework.web.common.StringUtils;
import lombok.Data;
import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class TestDate1 {
public static void main(String[] args) throws ParseException {
List<TimeEntity> list =new ArrayList<TimeEntity>();
// //场景1 测试通过
// |------| |--------| 表数据(需要修改开始结束日期)
// |----------| 前端返回带有开始结束日期点位置数据(需要保证整条完整)
TimeEntity oldE = new TimeEntity();
oldE.setId("1");
oldE.setStartDate("2023-12-31");
oldE.setEndDate("2024-02-10");
list.add(oldE);
TimeEntity oldE2 = new TimeEntity();
oldE2.setId("2");
oldE2.setStartDate("2024-02-11");
oldE2.setEndDate("2028-12-01");
list.add(oldE2);
TimeEntity newE = new TimeEntity();
newE.setId("");
newE.setStartDate("2024-01-01");
newE.setEndDate("2024-04-20");
//场景2 测试通过
// // |--------------|
// // |--------------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-12-31");
// oldE.setEndDate("2024-02-10");
// list.add(oldE);
//场景3 测试通过
// // |----------------|
// //|---------|
// TimeEntity oldE2 = new TimeEntity();
// oldE2.setId("2");
// oldE2.setStartDate("2024-02-11");
// oldE2.setEndDate("2028-12-01");
// list.add(oldE2);
//场景4 测试通过
// // |--------------------|
// // |---------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-12-31");
// oldE.setEndDate("2024-04-10");
// list.add(oldE);
//场景5
// |--------------|
// |--------------------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-12-31");
// oldE.setEndDate("2024-04-10");
// list.add(oldE);
//
// TimeEntity newE = new TimeEntity();
// newE.setId("");
// newE.setStartDate("2023-11-31");
// newE.setEndDate("2024-04-20");
//场景6 测试通过,返回无交叉不用处理数据
// |----------| |----------|
// |---|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-10-31");
// oldE.setEndDate("2023-11-10");
// list.add(oldE);
//
// TimeEntity oldE1 = new TimeEntity();
// oldE1.setId("2");
// oldE1.setStartDate("2024-04-20");
// oldE1.setEndDate("2024-11-10");
// list.add(oldE1);
//
// TimeEntity newE = new TimeEntity();
// newE.setId("");
// newE.setStartDate("2023-11-31");
// newE.setEndDate("2024-03-20");
//场景7 通过
// |-------| |--------|
// |-------------------------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-10-31");
// oldE.setEndDate("2023-11-10");
// list.add(oldE);
//
// TimeEntity oldE1 = new TimeEntity();
// oldE1.setId("2");
// oldE1.setStartDate("2024-04-20");
// oldE1.setEndDate("2024-11-10");
// list.add(oldE1);
//
// TimeEntity newE = new TimeEntity();
// newE.setId("");
// newE.setStartDate("2023-09-30");
// newE.setEndDate("2024-11-15");
//场景8 通过
// |-------| |--------|
// |-------------------------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-10-31");
// oldE.setEndDate("2023-11-10");
// list.add(oldE);
//
// TimeEntity oldE1 = new TimeEntity();
// oldE1.setId("2");
// oldE1.setStartDate("2023-12-20");
// oldE1.setEndDate("2024-01-10");
// list.add(oldE1);
//
// TimeEntity newE = new TimeEntity();
// newE.setId("");
// newE.setStartDate("2023-11-05");
// newE.setEndDate("2024-11-15");
//场景9 通过
// |-------| |--------|
// |-------------------------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-10-31");
// oldE.setEndDate("2023-11-10");
// list.add(oldE);
//
// TimeEntity oldE1 = new TimeEntity();
// oldE1.setId("2");
// oldE1.setStartDate("2023-12-20");
// oldE1.setEndDate("2024-01-10");
// list.add(oldE1);
//
// TimeEntity newE = new TimeEntity();
// newE.setId("");
// newE.setStartDate("2023-09-05");
// newE.setEndDate("2024-01-05");
//场景10通过
// |-------| |---------|
// |--------------------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-10-31");
// oldE.setEndDate("2023-11-10");
// list.add(oldE);
//
// TimeEntity oldE1 = new TimeEntity();
// oldE1.setId("2");
// oldE1.setStartDate("2023-12-20");
// oldE1.setEndDate("2024-01-10");
// list.add(oldE1);
//
// TimeEntity newE = new TimeEntity();
// newE.setId("");
// newE.setStartDate("2023-10-31");
// newE.setEndDate("2024-01-10");
//场景11通过
// |-------| |---------|
// |--------------------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-10-31");
// oldE.setEndDate("2023-11-10");
// list.add(oldE);
//
// TimeEntity oldE1 = new TimeEntity();
// oldE1.setId("2");
// oldE1.setStartDate("2023-12-20");
// oldE1.setEndDate("2024-01-10");
// list.add(oldE1);
//
// TimeEntity newE = new TimeEntity();
// newE.setId("");
// newE.setStartDate("2023-11-01");
// newE.setEndDate("2023-12-20");
//场景12通过
// |-------|
// |--------------------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-10-31");
// oldE.setEndDate("2023-11-10");
// list.add(oldE);
//
// TimeEntity newE = new TimeEntity();
// newE.setId("");
// newE.setStartDate("2023-11-10");
// newE.setEndDate("2023-12-20");
//场景13通过
// |-------|
// |---------|
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-10-31");
// oldE.setEndDate("2023-11-10");
// list.add(oldE);
//
// TimeEntity newE = new TimeEntity();
// newE.setId("");
// newE.setStartDate("2023-10-10");
// newE.setEndDate("2023-10-31");
getDates(list, newE);
}
public static List<TimeEntity> getDates(List<TimeEntity> oldEList, TimeEntity newE) throws ParseException {
//即将插入日期 newE和数据库表数据有交叉
boolean b = isOverlap(oldEList,newE);
if(!b)
{
System.out.println("没有交叉,直接插入:" + JSONUtil.toJsonStr(newE));
//插入数据库
return new ArrayList<>();
}
//放到list排序
List<TimeEntity> list = new ArrayList<>();
list.add( new TimeEntity(newE.getId(),newE.getStartDate()));
list.add( new TimeEntity(newE.getId(),newE.getEndDate()));
for (TimeEntity oldEntity : oldEList) {
list.add(new TimeEntity(oldEntity.getId(),oldEntity.getStartDate()));
list.add(new TimeEntity(oldEntity.getId(), oldEntity.getEndDate()));
}
//去掉重复日期
Map<String, TimeEntity> map = list.stream()
.collect(Collectors.toMap(
TimeEntity::getDateStr,
Function.identity(),
(existing, replacement) -> StringUtils.isBlank(existing.getId()) ? existing : replacement
));
List<TimeEntity> filteredList = new ArrayList<>(map.values());
// 输出满足条件的 Entity 对象
filteredList.forEach(System.out::println);
list.clear();
list.addAll(filteredList);
list.sort(Comparator.comparing(TimeEntity::getDateStr));
System.out.println("排序后的日期点:" + list);
String newEStartDate = newE.getStartDate();
String newEEndDate = newE.getEndDate();
List<TimeEntity> dateRanges = new ArrayList<>();
for (int i = 0; i < list.size()-1 ; i++) {
TimeEntity dateVo = new TimeEntity();
// end > getDateStr >= start 分段,根据新老归属,划分id
if(list.get(i).getDateStr().compareTo(newEStartDate)>=0&&list.get(i).getDateStr().compareTo(newEEndDate)<0){
dateVo.setId(newE.getId());
}else{
if(!list.get(i).getId().equals(newE.getId())){
dateVo.setId(list.get(i).getId());
}
if(!list.get(i+1).getId().equals(newE.getId())){
dateVo.setId(list.get(i+1).getId());
}
}
dateVo.setId(list.get(i).getId());
// list.get(i) is new end point +1
if( list.get(i).getDateStr().equals(newEEndDate)){
//+1天
dateVo.setStartDate(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD,DateUtils.addDays(DateUtils.parseDate(newEEndDate), 1)));
}else{
dateVo.setStartDate(list.get(i).getDateStr());
}
// if list.get(i+1) is new end point +0
// else -1
if( list.size() == i+2 || list.get(i+1).getDateStr().equals(newEEndDate)){
dateVo.setEndDate(list.get(i+1).getDateStr());
}else{
//-1天
dateVo.setEndDate(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD,DateUtils.addDays(DateUtils.parseDate(list.get(i+1).getDateStr()), -1)));
}
dateRanges.add(dateVo);
}
System.out.println("按照时间分段数据: " + dateRanges);
//去除和即将入库开始结束日期有交叉的数据
dateRanges.removeIf(dateVo -> dateVo.getStartDate().compareTo(newE.getEndDate())<=0
&& newE.getStartDate().compareTo(dateVo.getEndDate())<=0);
dateRanges.add(newE);
dateRanges.sort(Comparator.comparing(TimeEntity::getStartDate));
System.out.println("最终数据数据: " + dateRanges);
//对库操作
return dateRanges;
}
public static boolean isOverlap(List<TimeEntity> timeEntityList, TimeEntity newEntity) {
return timeEntityList.stream()
.anyMatch(entity -> {
return entity.getStartDate().compareTo(newEntity.getEndDate())<=0 && newEntity.getStartDate().compareTo(entity.getEndDate())<=0;
});
}
@Data
public static class TimeEntity implements Serializable {
String id;
private String dateStr;
private String startDate;
public TimeEntity( ){
}
private String endDate;
public TimeEntity(String id,String dateStr ){
this.id=id;
this.dateStr =dateStr;
}
public TimeEntity(String id,String startDate,String endDate ){
this.id=id;
this.startDate =startDate;
this.endDate = endDate;
}
}
}
2.针对上边也可以用泛型+发射,来写一遍。TimeEntity在这里就相当于表对应的实体类。两种都可以,就看怎么处理业务数据。
import com.xhj.framework.web.common.DateUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class GenericEntityProcessor<T> {
public List<T> getDates(List<T> oldEList, T newE) throws ParseException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
boolean b = isOverlap(oldEList, newE);
if (!b) {
System.out.println("No overlap, directly insert: " + newE.toString());
return new ArrayList<>();
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// Extracting fields using reflection
Field idField = getField("id", newE.getClass());
Field startDateField = getField("startDate", newE.getClass());
Field endDateField = getField("endDate", newE.getClass());
List<T> list = new ArrayList<>();
list.add(createEntity(newE, idField, startDateField));
list.add(createEntity(newE, idField, endDateField));
for (T oldEntity : oldEList) {
list.add(createEntity(oldEntity, idField, startDateField));
list.add(createEntity(oldEntity, idField, endDateField));
}
// Remove duplicates based on the dateStr field
Map<String, T> map = list.stream()
.collect(Collectors.toMap(
entity -> (String) getFieldValue(entity, "dateStr"),
Function.identity(),
(existing, replacement) -> getFieldValue(existing, "id").equals("") ? existing : replacement
));
List<T> filteredList = new ArrayList<>(map.values());
filteredList.forEach(System.out::println);
list.clear();
list.addAll(filteredList);
list.sort(Comparator.comparing(entity -> (String) getFieldValue(entity, "dateStr")));
System.out.println("最终按照日期排序后的list: " + list);
List<T> dateRanges = processDateRanges(list, newE);
// Further processing and database operations
return dateRanges;
}
public List<T> processDateRanges(List<T> list, T newE) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String newEStartDate = (String) getFieldValue(newE, "startDate");
String newEEndDate = (String) getFieldValue(newE, "endDate");
List<T> dateRanges = new ArrayList<>();
for (int i = 0; i < list.size() - 1; i++) {
T dateVo = createInstance(newE);
String currentDateStr = (String) getFieldValue(list.get(i), "dateStr");
String nextDateStr = (String) getFieldValue(list.get(i + 1), "dateStr");
if (currentDateStr.compareTo(newEStartDate) >= 0 && currentDateStr.compareTo(newEEndDate) < 0) {
setFieldValue(dateVo, "id", getFieldValue(newE, "id"));
} else {
if (!getFieldValue(list.get(i), "id").equals(getFieldValue(newE, "id"))) {
setFieldValue(dateVo, "id", getFieldValue(list.get(i), "id"));
}
if (!getFieldValue(list.get(i + 1), "id").equals(getFieldValue(newE, "id"))) {
setFieldValue(dateVo, "id", getFieldValue(list.get(i + 1), "id"));
}
}
if (currentDateStr.equals(newEEndDate)) {
//+1天
setFieldValue(dateVo, "startDate", DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD,DateUtils.addDays(DateUtils.parseDate(newEEndDate), 1)));
} else {
setFieldValue(dateVo, "startDate", currentDateStr);
}
if (list.size() == i + 2 || nextDateStr.equals(newEEndDate)) {
setFieldValue(dateVo, "endDate", nextDateStr);
} else {
//-1天
setFieldValue(dateVo, "endDate",DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD,DateUtils.addDays(DateUtils.parseDate(nextDateStr), -1)));
}
dateRanges.add(dateVo);
}
System.out.println("按照时间分段数据: " + dateRanges);
// Remove date ranges with overlap
dateRanges.removeIf(dateVo -> {
return ((String) getFieldValue(dateVo, "startDate")).compareTo(newEEndDate) <= 0
&& newEStartDate.compareTo((String) getFieldValue(dateVo, "endDate")) <= 0;
});
dateRanges.add(newE);
dateRanges.sort(Comparator.comparing(entity -> (String) getFieldValue(entity, "startDate")));
return dateRanges;
}
public boolean isOverlap(List<T> entityList, T newEntity) throws IllegalAccessException {
String newStartDate = (String) getFieldValue(newEntity, "startDate");
String newEndDate = (String) getFieldValue(newEntity, "endDate");
return entityList.stream()
.anyMatch(entity -> {
return ((String) getFieldValue(entity, "startDate")).compareTo(newEndDate) <= 0 &&
newStartDate.compareTo((String) getFieldValue(entity, "endDate")) <= 0;
});
}
private Field getField(String fieldName, Class<?> clazz) {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
e.printStackTrace();
return null;
}
}
private Object getFieldValue(T entity, String fieldName) {
try {
Field field = entity.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(entity);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
private void setFieldValue(T entity, String fieldName, Object value) {
try {
Field field = entity.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(entity, value);
} catch (Exception e) {
e.printStackTrace();
}
}
private T createInstance(T entity) {
try {
return (T) entity.getClass().getConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private T createEntity(T entity, Field idField, Field dateField) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
Object idValue = idField.get(entity);
Object dateValue = dateField.get(entity);
Constructor<?> constructor = entity.getClass().getConstructor(idField.getType(), dateField.getType());
T newEntity = (T) constructor.newInstance(idValue, dateValue);
return newEntity;
}
// Example of usage
public static void main(String[] args) throws ParseException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {
List<TimeEntity> list = new ArrayList<>();
// TimeEntity oldE = new TimeEntity();
// oldE.setId("1");
// oldE.setStartDate("2023-10-31");
// oldE.setEndDate("2023-11-10");
// list.add(oldE);
TimeEntity oldE1 = new TimeEntity();
oldE1.setId("2");
oldE1.setStartDate("2024-04-20");
oldE1.setEndDate("2024-11-10");
list.add(oldE1);
TimeEntity newE = new TimeEntity();
newE.setId("");
newE.setStartDate("2024-04-30");
newE.setEndDate("2024-11-15");
GenericEntityProcessor<TimeEntity> processor = new GenericEntityProcessor<>();
List<TimeEntity> dateRanges = processor.getDates(list, newE);
System.out.println("最终数据数据: " + dateRanges);
// Further processing with the result
}
}
两种写法最终实现截取时间段,联系不间断,无重叠。
最讨厌处理日期了......做个笔记记录下实现.