Jiaqi Xia

feat(suspension): only keep latest valid record for each dimension when calculat…

…ing suspension intervals
......@@ -30,12 +30,14 @@ public class SuspensionServiceImpl implements SuspensionService {
final long now = System.currentTimeMillis();
// 学校维度:classId == 0 && dinerId == 0,按 clientId 过滤
List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecordsRaw = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId,
List.of(clientId),
null,
List.of(0)
).stream().filter(r -> r.getEndAt() > now).toList();
);
List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecords = schoolLevelRecordsRaw == null ? List.of() :
schoolLevelRecordsRaw.stream().filter(r -> r.getEndAt() > now).toList();
SingleDinerMealSuspensionRecordRpcResponse latestSchoolLevelRecord = pickLatest(
schoolLevelRecords,
record -> record.getClientId() == clientId && record.getClassId() == 0 && record.getDinerId() == 0
......@@ -45,12 +47,14 @@ public class SuspensionServiceImpl implements SuspensionService {
}
// 班级维度:dinerId == 0,按 clientId + classId 过滤
List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecordsRaw = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId,
List.of(clientId),
List.of(classId),
List.of(0)
).stream().filter(r -> r.getEndAt() > now).toList();
);
List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecords = classLevelRecordsRaw == null ? List.of() :
classLevelRecordsRaw.stream().filter(r -> r.getEndAt() > now).toList();
SingleDinerMealSuspensionRecordRpcResponse latestClassLevelRecord = pickLatest(
classLevelRecords,
record -> record.getClientId() == clientId && record.getClassId() == classId && record.getDinerId() == 0
......@@ -60,12 +64,14 @@ public class SuspensionServiceImpl implements SuspensionService {
}
// 学生维度:三个 id 都不为 0
List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecordsRaw = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId,
List.of(clientId),
List.of(classId),
List.of(dinerId)
).stream().filter(r -> r.getEndAt() > now).toList();
);
List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = dinerLevelRecordsRaw == null ? List.of() :
dinerLevelRecordsRaw.stream().filter(r -> r.getEndAt() > now).toList();
SingleDinerMealSuspensionRecordRpcResponse latestDinerLevelRecord = pickLatest(
dinerLevelRecords,
record -> record.getClientId() == clientId && record.getClassId() == classId && record.getDinerId() == dinerId
......@@ -115,12 +121,19 @@ public class SuspensionServiceImpl implements SuspensionService {
List<Integer> dinerIds = diners.stream().map(SingleDinerRpcResponse::getId).toList();
// 查询三类记录
List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId, clientIds, null, List.of(0)).stream().filter(r -> r.getEndAt() > now).toList();
List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId, clientIds, classIds, List.of(0)).stream().filter(r -> r.getEndAt() > now).toList();
List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId, clientIds, classIds, dinerIds).stream().filter(r -> r.getEndAt() > now).toList();
List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecordsRawBatch = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId, clientIds, null, List.of(0));
List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecordsRawBatch = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId, clientIds, classIds, List.of(0));
List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecordsRawBatch = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId, clientIds, classIds, dinerIds);
List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecords = schoolLevelRecordsRawBatch == null ? List.of() :
schoolLevelRecordsRawBatch.stream().filter(r -> r.getEndAt() > now).toList();
List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecords = classLevelRecordsRawBatch == null ? List.of() :
classLevelRecordsRawBatch.stream().filter(r -> r.getEndAt() > now).toList();
List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = dinerLevelRecordsRawBatch == null ? List.of() :
dinerLevelRecordsRawBatch.stream().filter(r -> r.getEndAt() > now).toList();
// 学校维度:clientId -> latest
Map<Integer, SingleDinerMealSuspensionRecordRpcResponse> clientIdToLatestSchool = schoolLevelRecords == null ? Map.of() :
......@@ -200,7 +213,7 @@ public class SuspensionServiceImpl implements SuspensionService {
public List<SuspensionInterval> getSuspensionIntervalsForDiner(int enterpriseId, int clientId, int classId, int dinerId) {
Map<Integer, List<SuspensionInterval>> map = getSuspensionIntervalsForDiners(enterpriseId, List.of(
SingleDinerRpcResponse.newBuilder()
.setEnterpriseId(clientId) // 注意:此处仅为构造占位,真正需要的是 clientId/classId/dinerId
.setEnterpriseId(enterpriseId)
.setClientId(clientId)
.setClassId(classId)
.setId(dinerId)
......@@ -227,38 +240,51 @@ public class SuspensionServiceImpl implements SuspensionService {
List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId, clientIds, classIds, dinerIds);
Map<Integer, List<SingleDinerMealSuspensionRecordRpcResponse>> clientIdToSchool = schoolLevelRecords == null ? Map.of() :
// 仅保留每个维度“最新一条”有效记录(createdAt 最大)
Map<Integer, SingleDinerMealSuspensionRecordRpcResponse> clientIdToLatestSchool = schoolLevelRecords == null ? Map.of() :
schoolLevelRecords.stream()
.filter(Objects::nonNull)
.filter(r -> r.getClassId() == 0 && r.getDinerId() == 0)
.collect(Collectors.groupingBy(SingleDinerMealSuspensionRecordRpcResponse::getClientId));
.collect(Collectors.toMap(
SingleDinerMealSuspensionRecordRpcResponse::getClientId,
Function.identity(),
(a, b) -> a.getCreatedAt() >= b.getCreatedAt() ? a : b
));
Map<String, List<SingleDinerMealSuspensionRecordRpcResponse>> clientClassToRecords = classLevelRecords == null ? Map.of() :
Map<String, SingleDinerMealSuspensionRecordRpcResponse> clientClassToLatest = classLevelRecords == null ? Map.of() :
classLevelRecords.stream()
.filter(Objects::nonNull)
.filter(r -> r.getDinerId() == 0)
.collect(Collectors.groupingBy(r -> r.getClientId() + "#" + r.getClassId()));
.collect(Collectors.toMap(
r -> r.getClientId() + "#" + r.getClassId(),
Function.identity(),
(a, b) -> a.getCreatedAt() >= b.getCreatedAt() ? a : b
));
Map<Integer, List<SingleDinerMealSuspensionRecordRpcResponse>> dinerIdToRecords = dinerLevelRecords == null ? Map.of() :
Map<Integer, SingleDinerMealSuspensionRecordRpcResponse> dinerIdToLatest = dinerLevelRecords == null ? Map.of() :
dinerLevelRecords.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(SingleDinerMealSuspensionRecordRpcResponse::getDinerId));
.collect(Collectors.toMap(
SingleDinerMealSuspensionRecordRpcResponse::getDinerId,
Function.identity(),
(a, b) -> a.getCreatedAt() >= b.getCreatedAt() ? a : b
));
for (SingleDinerRpcResponse diner : diners) {
List<SuspensionInterval> intervals = new ArrayList<>();
List<SingleDinerMealSuspensionRecordRpcResponse> schoolRecs = clientIdToSchool.getOrDefault(diner.getClientId(), List.of());
List<SingleDinerMealSuspensionRecordRpcResponse> classRecs = clientClassToRecords.getOrDefault(diner.getClientId() + "#" + diner.getClassId(), List.of());
List<SingleDinerMealSuspensionRecordRpcResponse> dinerRecs = dinerIdToRecords.getOrDefault(diner.getId(), List.of());
SingleDinerMealSuspensionRecordRpcResponse latestSchool = clientIdToLatestSchool.get(diner.getClientId());
SingleDinerMealSuspensionRecordRpcResponse latestClass = clientClassToLatest.get(diner.getClientId() + "#" + diner.getClassId());
SingleDinerMealSuspensionRecordRpcResponse latestDiner = dinerIdToLatest.get(diner.getId());
for (SingleDinerMealSuspensionRecordRpcResponse rec : schoolRecs) {
if (rec.getEndAt() > now) intervals.add(new SuspensionInterval(rec.getStartAt(), rec.getEndAt()));
if (latestSchool != null && latestSchool.getEndAt() > now) {
intervals.add(new SuspensionInterval(latestSchool.getStartAt(), latestSchool.getEndAt()));
}
for (SingleDinerMealSuspensionRecordRpcResponse rec : classRecs) {
if (rec.getEndAt() > now) intervals.add(new SuspensionInterval(rec.getStartAt(), rec.getEndAt()));
if (latestClass != null && latestClass.getEndAt() > now) {
intervals.add(new SuspensionInterval(latestClass.getStartAt(), latestClass.getEndAt()));
}
for (SingleDinerMealSuspensionRecordRpcResponse rec : dinerRecs) {
if (rec.getEndAt() > now) intervals.add(new SuspensionInterval(rec.getStartAt(), rec.getEndAt()));
if (latestDiner != null && latestDiner.getEndAt() > now) {
intervals.add(new SuspensionInterval(latestDiner.getStartAt(), latestDiner.getEndAt()));
}
// 归并并集:合并重叠区间、保留间隙
......