feat(suspension): only keep latest valid record for each dimension when calculat…
…ing suspension intervals
Showing
1 changed file
with
54 additions
and
28 deletions
| ... | @@ -30,12 +30,14 @@ public class SuspensionServiceImpl implements SuspensionService { | ... | @@ -30,12 +30,14 @@ public class SuspensionServiceImpl implements SuspensionService { |
| 30 | final long now = System.currentTimeMillis(); | 30 | final long now = System.currentTimeMillis(); |
| 31 | 31 | ||
| 32 | // 学校维度:classId == 0 && dinerId == 0,按 clientId 过滤 | 32 | // 学校维度:classId == 0 && dinerId == 0,按 clientId 过滤 |
| 33 | - List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( | 33 | + List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecordsRaw = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( |
| 34 | enterpriseId, | 34 | enterpriseId, |
| 35 | List.of(clientId), | 35 | List.of(clientId), |
| 36 | null, | 36 | null, |
| 37 | List.of(0) | 37 | List.of(0) |
| 38 | - ).stream().filter(r -> r.getEndAt() > now).toList(); | 38 | + ); |
| 39 | + List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecords = schoolLevelRecordsRaw == null ? List.of() : | ||
| 40 | + schoolLevelRecordsRaw.stream().filter(r -> r.getEndAt() > now).toList(); | ||
| 39 | SingleDinerMealSuspensionRecordRpcResponse latestSchoolLevelRecord = pickLatest( | 41 | SingleDinerMealSuspensionRecordRpcResponse latestSchoolLevelRecord = pickLatest( |
| 40 | schoolLevelRecords, | 42 | schoolLevelRecords, |
| 41 | record -> record.getClientId() == clientId && record.getClassId() == 0 && record.getDinerId() == 0 | 43 | record -> record.getClientId() == clientId && record.getClassId() == 0 && record.getDinerId() == 0 |
| ... | @@ -45,12 +47,14 @@ public class SuspensionServiceImpl implements SuspensionService { | ... | @@ -45,12 +47,14 @@ public class SuspensionServiceImpl implements SuspensionService { |
| 45 | } | 47 | } |
| 46 | 48 | ||
| 47 | // 班级维度:dinerId == 0,按 clientId + classId 过滤 | 49 | // 班级维度:dinerId == 0,按 clientId + classId 过滤 |
| 48 | - List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( | 50 | + List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecordsRaw = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( |
| 49 | enterpriseId, | 51 | enterpriseId, |
| 50 | List.of(clientId), | 52 | List.of(clientId), |
| 51 | List.of(classId), | 53 | List.of(classId), |
| 52 | List.of(0) | 54 | List.of(0) |
| 53 | - ).stream().filter(r -> r.getEndAt() > now).toList(); | 55 | + ); |
| 56 | + List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecords = classLevelRecordsRaw == null ? List.of() : | ||
| 57 | + classLevelRecordsRaw.stream().filter(r -> r.getEndAt() > now).toList(); | ||
| 54 | SingleDinerMealSuspensionRecordRpcResponse latestClassLevelRecord = pickLatest( | 58 | SingleDinerMealSuspensionRecordRpcResponse latestClassLevelRecord = pickLatest( |
| 55 | classLevelRecords, | 59 | classLevelRecords, |
| 56 | record -> record.getClientId() == clientId && record.getClassId() == classId && record.getDinerId() == 0 | 60 | record -> record.getClientId() == clientId && record.getClassId() == classId && record.getDinerId() == 0 |
| ... | @@ -60,12 +64,14 @@ public class SuspensionServiceImpl implements SuspensionService { | ... | @@ -60,12 +64,14 @@ public class SuspensionServiceImpl implements SuspensionService { |
| 60 | } | 64 | } |
| 61 | 65 | ||
| 62 | // 学生维度:三个 id 都不为 0 | 66 | // 学生维度:三个 id 都不为 0 |
| 63 | - List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( | 67 | + List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecordsRaw = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( |
| 64 | enterpriseId, | 68 | enterpriseId, |
| 65 | List.of(clientId), | 69 | List.of(clientId), |
| 66 | List.of(classId), | 70 | List.of(classId), |
| 67 | List.of(dinerId) | 71 | List.of(dinerId) |
| 68 | - ).stream().filter(r -> r.getEndAt() > now).toList(); | 72 | + ); |
| 73 | + List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = dinerLevelRecordsRaw == null ? List.of() : | ||
| 74 | + dinerLevelRecordsRaw.stream().filter(r -> r.getEndAt() > now).toList(); | ||
| 69 | SingleDinerMealSuspensionRecordRpcResponse latestDinerLevelRecord = pickLatest( | 75 | SingleDinerMealSuspensionRecordRpcResponse latestDinerLevelRecord = pickLatest( |
| 70 | dinerLevelRecords, | 76 | dinerLevelRecords, |
| 71 | record -> record.getClientId() == clientId && record.getClassId() == classId && record.getDinerId() == dinerId | 77 | record -> record.getClientId() == clientId && record.getClassId() == classId && record.getDinerId() == dinerId |
| ... | @@ -115,12 +121,19 @@ public class SuspensionServiceImpl implements SuspensionService { | ... | @@ -115,12 +121,19 @@ public class SuspensionServiceImpl implements SuspensionService { |
| 115 | List<Integer> dinerIds = diners.stream().map(SingleDinerRpcResponse::getId).toList(); | 121 | List<Integer> dinerIds = diners.stream().map(SingleDinerRpcResponse::getId).toList(); |
| 116 | 122 | ||
| 117 | // 查询三类记录 | 123 | // 查询三类记录 |
| 118 | - List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( | 124 | + List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecordsRawBatch = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( |
| 119 | - enterpriseId, clientIds, null, List.of(0)).stream().filter(r -> r.getEndAt() > now).toList(); | 125 | + enterpriseId, clientIds, null, List.of(0)); |
| 120 | - List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( | 126 | + List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecordsRawBatch = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( |
| 121 | - enterpriseId, clientIds, classIds, List.of(0)).stream().filter(r -> r.getEndAt() > now).toList(); | 127 | + enterpriseId, clientIds, classIds, List.of(0)); |
| 122 | - List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( | 128 | + List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecordsRawBatch = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( |
| 123 | - enterpriseId, clientIds, classIds, dinerIds).stream().filter(r -> r.getEndAt() > now).toList(); | 129 | + enterpriseId, clientIds, classIds, dinerIds); |
| 130 | + | ||
| 131 | + List<SingleDinerMealSuspensionRecordRpcResponse> schoolLevelRecords = schoolLevelRecordsRawBatch == null ? List.of() : | ||
| 132 | + schoolLevelRecordsRawBatch.stream().filter(r -> r.getEndAt() > now).toList(); | ||
| 133 | + List<SingleDinerMealSuspensionRecordRpcResponse> classLevelRecords = classLevelRecordsRawBatch == null ? List.of() : | ||
| 134 | + classLevelRecordsRawBatch.stream().filter(r -> r.getEndAt() > now).toList(); | ||
| 135 | + List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = dinerLevelRecordsRawBatch == null ? List.of() : | ||
| 136 | + dinerLevelRecordsRawBatch.stream().filter(r -> r.getEndAt() > now).toList(); | ||
| 124 | 137 | ||
| 125 | // 学校维度:clientId -> latest | 138 | // 学校维度:clientId -> latest |
| 126 | Map<Integer, SingleDinerMealSuspensionRecordRpcResponse> clientIdToLatestSchool = schoolLevelRecords == null ? Map.of() : | 139 | Map<Integer, SingleDinerMealSuspensionRecordRpcResponse> clientIdToLatestSchool = schoolLevelRecords == null ? Map.of() : |
| ... | @@ -200,7 +213,7 @@ public class SuspensionServiceImpl implements SuspensionService { | ... | @@ -200,7 +213,7 @@ public class SuspensionServiceImpl implements SuspensionService { |
| 200 | public List<SuspensionInterval> getSuspensionIntervalsForDiner(int enterpriseId, int clientId, int classId, int dinerId) { | 213 | public List<SuspensionInterval> getSuspensionIntervalsForDiner(int enterpriseId, int clientId, int classId, int dinerId) { |
| 201 | Map<Integer, List<SuspensionInterval>> map = getSuspensionIntervalsForDiners(enterpriseId, List.of( | 214 | Map<Integer, List<SuspensionInterval>> map = getSuspensionIntervalsForDiners(enterpriseId, List.of( |
| 202 | SingleDinerRpcResponse.newBuilder() | 215 | SingleDinerRpcResponse.newBuilder() |
| 203 | - .setEnterpriseId(clientId) // 注意:此处仅为构造占位,真正需要的是 clientId/classId/dinerId | 216 | + .setEnterpriseId(enterpriseId) |
| 204 | .setClientId(clientId) | 217 | .setClientId(clientId) |
| 205 | .setClassId(classId) | 218 | .setClassId(classId) |
| 206 | .setId(dinerId) | 219 | .setId(dinerId) |
| ... | @@ -227,38 +240,51 @@ public class SuspensionServiceImpl implements SuspensionService { | ... | @@ -227,38 +240,51 @@ public class SuspensionServiceImpl implements SuspensionService { |
| 227 | List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( | 240 | List<SingleDinerMealSuspensionRecordRpcResponse> dinerLevelRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition( |
| 228 | enterpriseId, clientIds, classIds, dinerIds); | 241 | enterpriseId, clientIds, classIds, dinerIds); |
| 229 | 242 | ||
| 230 | - Map<Integer, List<SingleDinerMealSuspensionRecordRpcResponse>> clientIdToSchool = schoolLevelRecords == null ? Map.of() : | 243 | + // 仅保留每个维度“最新一条”有效记录(createdAt 最大) |
| 244 | + Map<Integer, SingleDinerMealSuspensionRecordRpcResponse> clientIdToLatestSchool = schoolLevelRecords == null ? Map.of() : | ||
| 231 | schoolLevelRecords.stream() | 245 | schoolLevelRecords.stream() |
| 232 | .filter(Objects::nonNull) | 246 | .filter(Objects::nonNull) |
| 233 | .filter(r -> r.getClassId() == 0 && r.getDinerId() == 0) | 247 | .filter(r -> r.getClassId() == 0 && r.getDinerId() == 0) |
| 234 | - .collect(Collectors.groupingBy(SingleDinerMealSuspensionRecordRpcResponse::getClientId)); | 248 | + .collect(Collectors.toMap( |
| 249 | + SingleDinerMealSuspensionRecordRpcResponse::getClientId, | ||
| 250 | + Function.identity(), | ||
| 251 | + (a, b) -> a.getCreatedAt() >= b.getCreatedAt() ? a : b | ||
| 252 | + )); | ||
| 235 | 253 | ||
| 236 | - Map<String, List<SingleDinerMealSuspensionRecordRpcResponse>> clientClassToRecords = classLevelRecords == null ? Map.of() : | 254 | + Map<String, SingleDinerMealSuspensionRecordRpcResponse> clientClassToLatest = classLevelRecords == null ? Map.of() : |
| 237 | classLevelRecords.stream() | 255 | classLevelRecords.stream() |
| 238 | .filter(Objects::nonNull) | 256 | .filter(Objects::nonNull) |
| 239 | .filter(r -> r.getDinerId() == 0) | 257 | .filter(r -> r.getDinerId() == 0) |
| 240 | - .collect(Collectors.groupingBy(r -> r.getClientId() + "#" + r.getClassId())); | 258 | + .collect(Collectors.toMap( |
| 259 | + r -> r.getClientId() + "#" + r.getClassId(), | ||
| 260 | + Function.identity(), | ||
| 261 | + (a, b) -> a.getCreatedAt() >= b.getCreatedAt() ? a : b | ||
| 262 | + )); | ||
| 241 | 263 | ||
| 242 | - Map<Integer, List<SingleDinerMealSuspensionRecordRpcResponse>> dinerIdToRecords = dinerLevelRecords == null ? Map.of() : | 264 | + Map<Integer, SingleDinerMealSuspensionRecordRpcResponse> dinerIdToLatest = dinerLevelRecords == null ? Map.of() : |
| 243 | dinerLevelRecords.stream() | 265 | dinerLevelRecords.stream() |
| 244 | .filter(Objects::nonNull) | 266 | .filter(Objects::nonNull) |
| 245 | - .collect(Collectors.groupingBy(SingleDinerMealSuspensionRecordRpcResponse::getDinerId)); | 267 | + .collect(Collectors.toMap( |
| 268 | + SingleDinerMealSuspensionRecordRpcResponse::getDinerId, | ||
| 269 | + Function.identity(), | ||
| 270 | + (a, b) -> a.getCreatedAt() >= b.getCreatedAt() ? a : b | ||
| 271 | + )); | ||
| 246 | 272 | ||
| 247 | for (SingleDinerRpcResponse diner : diners) { | 273 | for (SingleDinerRpcResponse diner : diners) { |
| 248 | List<SuspensionInterval> intervals = new ArrayList<>(); | 274 | List<SuspensionInterval> intervals = new ArrayList<>(); |
| 249 | 275 | ||
| 250 | - List<SingleDinerMealSuspensionRecordRpcResponse> schoolRecs = clientIdToSchool.getOrDefault(diner.getClientId(), List.of()); | 276 | + SingleDinerMealSuspensionRecordRpcResponse latestSchool = clientIdToLatestSchool.get(diner.getClientId()); |
| 251 | - List<SingleDinerMealSuspensionRecordRpcResponse> classRecs = clientClassToRecords.getOrDefault(diner.getClientId() + "#" + diner.getClassId(), List.of()); | 277 | + SingleDinerMealSuspensionRecordRpcResponse latestClass = clientClassToLatest.get(diner.getClientId() + "#" + diner.getClassId()); |
| 252 | - List<SingleDinerMealSuspensionRecordRpcResponse> dinerRecs = dinerIdToRecords.getOrDefault(diner.getId(), List.of()); | 278 | + SingleDinerMealSuspensionRecordRpcResponse latestDiner = dinerIdToLatest.get(diner.getId()); |
| 253 | 279 | ||
| 254 | - for (SingleDinerMealSuspensionRecordRpcResponse rec : schoolRecs) { | 280 | + if (latestSchool != null && latestSchool.getEndAt() > now) { |
| 255 | - if (rec.getEndAt() > now) intervals.add(new SuspensionInterval(rec.getStartAt(), rec.getEndAt())); | 281 | + intervals.add(new SuspensionInterval(latestSchool.getStartAt(), latestSchool.getEndAt())); |
| 256 | } | 282 | } |
| 257 | - for (SingleDinerMealSuspensionRecordRpcResponse rec : classRecs) { | 283 | + if (latestClass != null && latestClass.getEndAt() > now) { |
| 258 | - if (rec.getEndAt() > now) intervals.add(new SuspensionInterval(rec.getStartAt(), rec.getEndAt())); | 284 | + intervals.add(new SuspensionInterval(latestClass.getStartAt(), latestClass.getEndAt())); |
| 259 | } | 285 | } |
| 260 | - for (SingleDinerMealSuspensionRecordRpcResponse rec : dinerRecs) { | 286 | + if (latestDiner != null && latestDiner.getEndAt() > now) { |
| 261 | - if (rec.getEndAt() > now) intervals.add(new SuspensionInterval(rec.getStartAt(), rec.getEndAt())); | 287 | + intervals.add(new SuspensionInterval(latestDiner.getStartAt(), latestDiner.getEndAt())); |
| 262 | } | 288 | } |
| 263 | 289 | ||
| 264 | // 归并并集:合并重叠区间、保留间隙 | 290 | // 归并并集:合并重叠区间、保留间隙 | ... | ... |
-
Please register or login to post a comment