孟苏慧

Merge branch 'dev/nx-20250526' into 'master'

农信停餐需求



See merge request !4
......@@ -6,10 +6,12 @@ import com.infoloop.tianting.mealorderservice.SingleMealOrderRpcResponse;
import com.infoloop.tianting.model.common.CreatedResult;
import com.infoloop.tianting.model.dto.MealOrderDTO;
import com.infoloop.tianting.model.vo.DeliveryRuleVO;
import com.infoloop.tianting.model.vo.DinerMealSuspensionRecordVO;
import com.infoloop.tianting.model.vo.MealOrderVO;
import com.infoloop.tianting.service.MealOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -20,6 +22,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
......@@ -67,4 +70,11 @@ public class MealOrderController {
return mealOrderService.getDeliveryRule();
}
@ApiOperation(value = "查询停餐信息")
@GetMapping("/dinermealsuspensionrecord")
@ResponseStatus(HttpStatus.OK)
public List<DinerMealSuspensionRecordVO> queryDinerMealSuspensionRecordsByCondition() {
return mealOrderService.queryDinerMealSuspensionRecordsByCondition();
}
}
......
......@@ -66,6 +66,8 @@ public enum ErrorCodeEnum implements BaseEnum {
MP_ACCOUNT_ALREADY_DISABLED(406000024, "账号已禁用"),
DINER_MEAL_SUSPENDED(406000025, "当前就餐人处于停餐状态")
;
private final int code;
......
......@@ -3,14 +3,7 @@ package com.infoloop.tianting.logic.task;
import com.infoloop.tianting.clientinventoryservice.SingleClientRpcResponse;
import com.infoloop.tianting.context.LoginContextHolder;
import com.infoloop.tianting.enums.LoginSourceEnum;
import com.infoloop.tianting.mealorderservice.MealOrderCreation;
import com.infoloop.tianting.mealorderservice.MealOrderOrderMethodEnum;
import com.infoloop.tianting.mealorderservice.MenuSchedule;
import com.infoloop.tianting.mealorderservice.SingleDinerRpcResponse;
import com.infoloop.tianting.mealorderservice.SingleGradeClassRpcResponse;
import com.infoloop.tianting.mealorderservice.SingleMealMenuRecordRpcResponse;
import com.infoloop.tianting.mealorderservice.SingleMealOrderRpcResponse;
import com.infoloop.tianting.mealorderservice.SingleMpAccountDinerRefRpcResponse;
import com.infoloop.tianting.mealorderservice.*;
import com.infoloop.tianting.service.client.ClientInventoryServiceRpcClient;
import com.infoloop.tianting.service.client.DeliveryRuleServiceRpcClient;
import com.infoloop.tianting.service.client.MealOrderServiceRpcClient;
......@@ -31,6 +24,7 @@ import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
......@@ -90,6 +84,24 @@ public class AutoCreateMealOrderTask {
final var thisWeekEnd = today.with(DayOfWeek.SUNDAY).atTime(LocalTime.MAX).atZone(DateUtil.CHINA_ZONE);
final var nextWeekStart = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).atStartOfDay().atZone(DateUtil.CHINA_ZONE);
// 获取下周每天的节假日信息
final var holidayMap = new HashMap<String, Integer>();
for (int i = 0; i < 7; i++) {
final var date = nextWeekStart.plusDays(i);
final var dateStr = date.format(DateUtil.YYYY_MM_DD);
try {
final var url = String.format("https://api.haoshenqi.top/holiday?date=%s", dateStr);
final var response = new java.net.URL(url).openConnection();
final var reader = new java.io.BufferedReader(new java.io.InputStreamReader(response.getInputStream()));
final var jsonResponse = reader.readLine();
final var jsonNode = new com.fasterxml.jackson.databind.ObjectMapper().readTree(jsonResponse);
final var status = jsonNode.get(0).get("status").asInt();
holidayMap.put(dateStr, status);
} catch (Exception e) {
log.error("检查节假日失败: {}", dateStr, e);
}
}
// 查询本周已订餐学生 ID
final var mealOrders = mealOrderServiceRpcClient.queryMealOrdersByCondition(enterpriseId, null, thisWeekStart.toInstant().toEpochMilli(), thisWeekEnd.toInstant().toEpochMilli());
final var dinerIds = mealOrders.stream().map(SingleMealOrderRpcResponse::getDinerId).sorted(Comparator.comparingInt(Integer::intValue)).collect(Collectors.toCollection(LinkedHashSet::new));
......@@ -109,8 +121,86 @@ public class AutoCreateMealOrderTask {
final var clientMap = clientInventoryServiceRpcClient.getClientsByIds(enterpriseId, clientIds).getClientsList().stream().collect(Collectors.toMap(SingleClientRpcResponse::getId, Function.identity()));
final var gradeClassMap = mealOrderServiceRpcClient.getGradeClassesByIds(enterpriseId, classIds).stream().collect(Collectors.toMap(SingleGradeClassRpcResponse::getId, Function.identity()));
final var latestMealMenuRecordMap = mealOrderServiceRpcClient.queryLatestMealMenuRecordsByClientIds(enterpriseId, clientIds).stream().collect(Collectors.toMap(SingleMealMenuRecordRpcResponse::getClientId, Function.identity()));
// 批量查询学校级别的停餐信息
final var schoolSuspensionMap = new HashMap<Integer, SingleDinerMealSuspensionRecordRpcResponse>();
var schoolSuspensionRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId,
clientIds, // 使用0表示查询所有
null,
List.of(0)
);
if (schoolSuspensionRecords != null) {
final var nextWeekStartTime = nextWeekStart.toInstant().toEpochMilli();
final var nextWeekEndTime = nextWeekStart.plusDays(6).with(LocalTime.MAX).toInstant().toEpochMilli();
schoolSuspensionRecords.stream()
.filter(record -> clientIds.contains(record.getClientId()))
.filter(record -> !(record.getEndAt() < nextWeekStartTime || record.getStartAt() > nextWeekEndTime))
.collect(Collectors.groupingBy(
SingleDinerMealSuspensionRecordRpcResponse::getClientId,
Collectors.collectingAndThen(
Collectors.maxBy((a, b) -> Long.compare(a.getCreatedAt(), b.getCreatedAt())),
opt -> opt.orElse(null)
)
))
.forEach((clientId, record) -> {
if (record != null) {
schoolSuspensionMap.put(clientId, record);
}
});
}
// 批量查询学生级别的停餐信息
final var studentSuspensionMap = new HashMap<Integer, SingleDinerMealSuspensionRecordRpcResponse>();
var studentSuspensionRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
enterpriseId,
null, // 使用0表示查询所有
null,
notReservedDiners.stream().map(SingleDinerRpcResponse::getId).toList()
);
if (studentSuspensionRecords != null) {
final var nextWeekStartTime = nextWeekStart.toInstant().toEpochMilli();
final var nextWeekEndTime = nextWeekStart.plusDays(6).with(LocalTime.MAX).toInstant().toEpochMilli();
var dinerIdSet = notReservedDiners.stream()
.map(SingleDinerRpcResponse::getId)
.collect(Collectors.toSet());
studentSuspensionRecords.stream()
.filter(record -> dinerIdSet.contains(record.getDinerId()))
.filter(record -> !(record.getEndAt() < nextWeekStartTime || record.getStartAt() > nextWeekEndTime))
.collect(Collectors.groupingBy(
SingleDinerMealSuspensionRecordRpcResponse::getDinerId,
Collectors.collectingAndThen(
Collectors.maxBy((a, b) -> Long.compare(a.getCreatedAt(), b.getCreatedAt())),
opt -> opt.orElse(null)
)
))
.forEach((dinerId, record) -> {
if (record != null) {
studentSuspensionMap.put(dinerId, record);
}
});
}
// 构建创建请求
final var creations = notReservedDiners.stream().map(diner -> toMealOrderCreation(diner, clientMap, gradeClassMap, latestMealMenuRecordMap, mpAccountDinerRefMap, nextWeekStart)).filter(Objects::nonNull).toList();
final var creations = notReservedDiners.stream()
.map(diner -> toMealOrderCreation(
diner,
clientMap,
gradeClassMap,
latestMealMenuRecordMap,
mpAccountDinerRefMap,
nextWeekStart,
schoolSuspensionMap,
studentSuspensionMap,
holidayMap
))
.filter(Objects::nonNull)
.toList();
if (creations.isEmpty()) {
return;
}
......@@ -132,12 +222,25 @@ public class AutoCreateMealOrderTask {
Map<Integer, SingleGradeClassRpcResponse> classMap,
Map<Integer, SingleMealMenuRecordRpcResponse> menuMap,
Map<Integer, SingleMpAccountDinerRefRpcResponse> mpAccountDinerRefMap,
ZonedDateTime startOfNextWeek) {
ZonedDateTime startOfNextWeek,
Map<Integer, SingleDinerMealSuspensionRecordRpcResponse> schoolSuspensionMap,
Map<Integer, SingleDinerMealSuspensionRecordRpcResponse> studentSuspensionMap,
Map<String, Integer> holidayMap) {
final var menu = menuMap.get(diner.getClientId());
final var client = clientMap.get(diner.getClientId());
final var gradeClass = classMap.get(diner.getClassId());
final var mpAccountDinerRef = mpAccountDinerRefMap.get(diner.getId());
if (menu == null || client == null || gradeClass == null) return null;
// 获取停餐信息,优先使用学校级别的
SingleDinerMealSuspensionRecordRpcResponse suspensionRecord = schoolSuspensionMap.get(diner.getClientId());
if (suspensionRecord == null) {
suspensionRecord = studentSuspensionMap.get(diner.getId());
}
// 构建下周的餐食安排
List<MenuSchedule> meals = buildNextWeekMeals(startOfNextWeek, suspensionRecord, holidayMap);
return MealOrderCreation.newBuilder()
.setClientId(diner.getClientId())
.setMenuId(menu.getId())
......@@ -150,24 +253,50 @@ public class AutoCreateMealOrderTask {
.setClassName(gradeClass.getClassName())
.setStudentNo(diner.getStudentNo())
.setReserveDate(Instant.now().toEpochMilli())
.addAllMeals(buildNextWeekMeals(startOfNextWeek))
.addAllMeals(meals)
.build();
}
private LocalDate getThisWeekDay(LocalDate now, DayOfWeek target) {
LocalDate monday = now.with(DayOfWeek.MONDAY);
return monday.plusDays(target.getValue() - 1L);
}
private List<MenuSchedule> buildNextWeekMeals(ZonedDateTime nextWeekStart) {
return IntStream.range(0, 5)
private List<MenuSchedule> buildNextWeekMeals(ZonedDateTime nextWeekStart,
SingleDinerMealSuspensionRecordRpcResponse suspensionRecord,
Map<String, Integer> holidayMap) {
return IntStream.range(0, 7)
.mapToObj(i -> {
final var date = nextWeekStart.plusDays(i);
final var dateStr = date.format(DateUtil.YYYY_MM_DD);
// 检查是否为节假日
final var status = holidayMap.get(dateStr);
if (status != null && status != 0 && status != 2) {
// 如果不是工作日,返回null
return null;
}
// 如果有停餐信息,检查当前循环的日期是否在停餐时间范围内
if (suspensionRecord != null) {
final var dateStart = date.with(LocalTime.MIN).toInstant().toEpochMilli();
final var dateEnd = date.with(LocalTime.MAX).toInstant().toEpochMilli();
final var suspensionStart = suspensionRecord.getStartAt();
final var suspensionEnd = suspensionRecord.getEndAt();
// 如果停餐时间范围与当前日期有重叠,返回null
if (!(dateEnd < suspensionStart || dateStart > suspensionEnd)) {
return null;
}
}
// 如果是工作日且不在停餐时间内,返回正常菜单
return MenuSchedule.newBuilder()
.setDate(date.format(DateUtil.YYYY_MM_DD_LEFT_SYMBOL))
.setMenu("A")
.build();
})
.filter(Objects::nonNull) // 过滤掉null值
.toList();
}
private LocalDate getThisWeekDay(LocalDate now, DayOfWeek target) {
LocalDate monday = now.with(DayOfWeek.MONDAY);
return monday.plusDays(target.getValue() - 1L);
}
}
......
package com.infoloop.tianting.model.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import java.util.Date;
@Data
@Builder
@ApiModel("停餐记录VO")
public class DinerMealSuspensionRecordVO {
@ApiModelProperty("ID")
private Long id;
@ApiModelProperty("企业ID")
private Long enterpriseId;
@ApiModelProperty("客户ID")
private Long clientId;
@ApiModelProperty("班级ID")
private Long classId;
@ApiModelProperty("年级名称")
private String gradeName;
@ApiModelProperty("班级名称")
private String className;
@ApiModelProperty("用餐人ID")
private Long dinerId;
@ApiModelProperty("用餐人姓名")
private String dinerName;
@ApiModelProperty("学号")
private String studentNo;
@ApiModelProperty("开始时间")
private Date startAt;
@ApiModelProperty("结束时间")
private Date endAt;
@ApiModelProperty("备注")
private String comment;
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ import com.infoloop.tianting.mealorderservice.SingleMealOrderRpcResponse;
import com.infoloop.tianting.model.common.CreatedResult;
import com.infoloop.tianting.model.dto.MealOrderDTO;
import com.infoloop.tianting.model.vo.DeliveryRuleVO;
import com.infoloop.tianting.model.vo.DinerMealSuspensionRecordVO;
import com.infoloop.tianting.model.vo.MealOrderVO;
import java.util.List;
......@@ -17,4 +18,16 @@ public interface MealOrderService {
MealOrderVO getMealOrderById(SingleMealOrderRpcResponse mealOrder);
DeliveryRuleVO getDeliveryRule();
/**
* 查询停餐信息
* 先查询学校的停餐信息,再查询学生的停餐信息,学校的停餐信息优先级高于学生停餐信息
*
* @param enterpriseId 企业ID
* @param clientId 客户ID
* @param classId 班级ID
* @param dinerId 用餐人ID
* @return 停餐信息列表
*/
List<DinerMealSuspensionRecordVO> queryDinerMealSuspensionRecordsByCondition();
}
......
......@@ -47,6 +47,8 @@ import com.infoloop.tianting.mealorderservice.UpdateDinerRpcRequest;
import com.infoloop.tianting.mealorderservice.UpdateDinerRpcResponse;
import com.infoloop.tianting.mealorderservice.UpdateMpAccountRpcRequest;
import com.infoloop.tianting.mealorderservice.UpdateMpAccountRpcResponse;
import com.infoloop.tianting.mealorderservice.QueryDinerMealSuspensionRecordsByConditionRpcRequest;
import com.infoloop.tianting.mealorderservice.SingleDinerMealSuspensionRecordRpcResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
......@@ -274,4 +276,30 @@ public class MealOrderServiceRpcClient {
.setShouldFilterReserveDateEnd(reserveDateEnd != null).setReserveDateEnd(reserveDateEnd == null ? 0 : reserveDateEnd)
.build()).getResponsesList();
}
public List<SingleDinerMealSuspensionRecordRpcResponse> queryDinerMealSuspensionRecordsByCondition(
int enterpriseId,
@Nullable List<Integer> clientIds,
@Nullable List<Integer> classIds,
@Nullable List<Integer> dinerIds) {
var request = QueryDinerMealSuspensionRecordsByConditionRpcRequest.newBuilder()
.setEnterpriseId(enterpriseId);
if (clientIds != null) {
request.setShouldFilterClientIds(true)
.addAllClientIds(clientIds);
}
if (classIds != null) {
request.setShouldFilterClassIds(true)
.addAllClassIds(classIds);
}
if (dinerIds != null) {
request.setShouldFilterDinerIds(true)
.addAllDinerIds(dinerIds);
}
return mealOrderServiceRpcBlockingStub.queryDinerMealSuspensionRecordsByCondition(request.build())
.getResponsesList();
}
}
......
......@@ -4,13 +4,11 @@ import com.infoloop.tianting.context.LoginContextHolder;
import com.infoloop.tianting.enums.QueryMealOrderStatusEnum;
import com.infoloop.tianting.exception.ClientEndExceptions;
import com.infoloop.tianting.exception.ErrorCodeEnum;
import com.infoloop.tianting.mealorderservice.MealOrderCreation;
import com.infoloop.tianting.mealorderservice.MealOrderOrderMethodEnum;
import com.infoloop.tianting.mealorderservice.MenuSchedule;
import com.infoloop.tianting.mealorderservice.SingleMealOrderRpcResponse;
import com.infoloop.tianting.mealorderservice.*;
import com.infoloop.tianting.model.common.CreatedResult;
import com.infoloop.tianting.model.dto.MealOrderDTO;
import com.infoloop.tianting.model.vo.DeliveryRuleVO;
import com.infoloop.tianting.model.vo.DinerMealSuspensionRecordVO;
import com.infoloop.tianting.model.vo.MealOrderVO;
import com.infoloop.tianting.service.MealOrderService;
import com.infoloop.tianting.service.client.ClientInventoryServiceRpcClient;
......@@ -23,12 +21,17 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.DayOfWeek;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@Slf4j
@Service
......@@ -77,30 +80,86 @@ public class MealOrderServiceImpl implements MealOrderService {
if (!nowWithinRange) {
throw ClientEndExceptions.IncorrectRequestValue.build(ErrorCodeEnum.MEAL_ORDER_TIME_NOT_IN_RANGE);
}
// 1. 获取订餐人信息
final var dinerById = mealOrderServiceRpcClient.getDinerById(loginInfo.getEnterpriseId(), createMealOrderDTO.getDinerId());
if (dinerById == null) {
throw ClientEndExceptions.IncorrectRequestValue.build(ErrorCodeEnum.DINER_NOT_FOUND);
}
// 2. 校验订餐人关联关系
final var mpAccountDinerRefs = mealOrderServiceRpcClient.queryMpAccountDinerRefsByCondition(loginInfo.getEnterpriseId(), Collections.singletonList(loginInfo.getId()), Collections.singletonList(createMealOrderDTO.getDinerId()));
if (mpAccountDinerRefs.size() != 1) {
throw ClientEndExceptions.IncorrectRequestValue.build(ErrorCodeEnum.DINER_NOT_FOUND);
}
// 3. 校验客户状态
final var clientById = clientInventoryServiceRpcClient.getClientById(loginInfo.getEnterpriseId(), dinerById.getClientId());
if (clientById.getClient().getStatus() != 1) {
throw ClientEndExceptions.IncorrectRequestValue.build(ErrorCodeEnum.CLIENT_NOT_ENABLED);
}
// 4. 校验班级信息
final var gradeClassById = mealOrderServiceRpcClient.getGradeClassById(loginInfo.getEnterpriseId(), dinerById.getClassId());
if (gradeClassById == null) {
throw ClientEndExceptions.IncorrectRequestValue.build(ErrorCodeEnum.GRADE_CLASS_NOT_FOUND);
}
// 5. 校验停餐记录
var currentTime = System.currentTimeMillis();
// 5.1 先查询学校级别的停餐信息
var schoolSuspensionRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
loginInfo.getEnterpriseId(),
List.of(dinerById.getClientId()),
null,
null
);
if (schoolSuspensionRecords != null && !schoolSuspensionRecords.isEmpty()) {
var latestSchoolSuspension = schoolSuspensionRecords.stream()
.max((a, b) -> Long.compare(a.getCreatedAt(), b.getCreatedAt()))
.orElse(null);
if (latestSchoolSuspension != null &&
currentTime >= latestSchoolSuspension.getStartAt() &&
currentTime <= latestSchoolSuspension.getEndAt()) {
throw ClientEndExceptions.IncorrectRequestValue.build(ErrorCodeEnum.DINER_MEAL_SUSPENDED);
}
}
// 5.2 再查询学生级别的停餐信息
var studentSuspensionRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
loginInfo.getEnterpriseId(),
List.of(dinerById.getClientId()),
List.of(dinerById.getClassId()),
List.of(dinerById.getId())
);
if (studentSuspensionRecords != null && !studentSuspensionRecords.isEmpty()) {
var latestStudentSuspension = studentSuspensionRecords.stream()
.max((a, b) -> Long.compare(a.getCreatedAt(), b.getCreatedAt()))
.orElse(null);
if (latestStudentSuspension != null &&
currentTime >= latestStudentSuspension.getStartAt() &&
currentTime <= latestStudentSuspension.getEndAt()) {
throw ClientEndExceptions.IncorrectRequestValue.build(ErrorCodeEnum.DINER_MEAL_SUSPENDED);
}
}
// 6. 校验菜单信息
final var latestMealMenuRecords = mealOrderServiceRpcClient.queryLatestMealMenuRecordsByClientIds(loginInfo.getEnterpriseId(), Collections.singletonList(dinerById.getClientId()));
if (latestMealMenuRecords.isEmpty()) {
// throw ClientEndExceptions.IncorrectRequestValue.build(ErrorCodeEnum.MEAL_MENU_NOT_FOUND);
}
// 7. 校验是否已预订
final var reserveDinerMealOrders = mealOrderServiceRpcClient.queryReserveMealOrdersByDinerIds(loginInfo.getEnterpriseId(), Collections.singletonList(createMealOrderDTO.getDinerId()));
if (!reserveDinerMealOrders.isEmpty()) {
throw ClientEndExceptions.IncorrectRequestValue.build(ErrorCodeEnum.DINER_ALREADY_RESERVED);
}
// 8. 创建订单
final var mealOrder = mealOrderServiceRpcClient.createMealOrder(loginInfo, MealOrderCreation.newBuilder()
.setClientId(dinerById.getClientId())
.setMenuId(latestMealMenuRecords.isEmpty() ? 0 : latestMealMenuRecords.get(0).getId())
......@@ -180,7 +239,7 @@ public class MealOrderServiceImpl implements MealOrderService {
final var maxDay = Collections.max(enabledDays);
final var today = now.toLocalDate();
final var startDateTime = getThisWeekDate(today, minDay).atTime(startTime);
final var endDateTime = getThisWeekDate(today,maxDay).atTime(endTime);
final var endDateTime = getThisWeekDate(today, maxDay).atTime(endTime);
return !now.isBefore(startDateTime) && !now.isAfter(endDateTime);
}
......@@ -188,4 +247,136 @@ public class MealOrderServiceImpl implements MealOrderService {
LocalDate monday = now.with(DayOfWeek.MONDAY);
return monday.plusDays(day.getValue() - 1L); // MONDAY 是 1,SUNDAY 是 7
}
@Override
public List<DinerMealSuspensionRecordVO> queryDinerMealSuspensionRecordsByCondition() {
final var loginInfo = LoginContextHolder.getLoginInfo();
// 1. 获取当前登录用户关联的所有订餐人
var mpAccountDinerRefs = mealOrderServiceRpcClient.queryMpAccountDinerRefsByCondition(
loginInfo.getEnterpriseId(),
Collections.singletonList(loginInfo.getId()),
Collections.emptyList()
);
if (mpAccountDinerRefs.isEmpty()) {
return Collections.emptyList();
}
// 2. 获取所有订餐人ID
var dinerIds = mpAccountDinerRefs.stream()
.map(SingleMpAccountDinerRefRpcResponse::getDinerId)
.toList();
// 3. 获取所有订餐人信息
var diners = mealOrderServiceRpcClient.getDinersByIds(loginInfo.getEnterpriseId(), dinerIds);
var dinerById = diners.stream().collect(Collectors.toMap(SingleDinerRpcResponse::getId, diner -> diner));
// 4. 获取所有不重复的clientId
var uniqueClientIds = diners.stream()
.map(diner -> Long.valueOf(diner.getClientId()))
.distinct()
.toList();
// 5. 批量获取所有学校的停餐信息
final var now = Instant.now();
var schoolSuspensionRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
loginInfo.getEnterpriseId(),
null,
null,
null
).stream().filter(r -> r.getClassId() == 0).filter(r -> r.getEndAt() > now.toEpochMilli()).toList();
// 6. 批量获取所有学生的停餐信息
var studentSuspensionRecords = mealOrderServiceRpcClient.queryDinerMealSuspensionRecordsByCondition(
loginInfo.getEnterpriseId(),
null,
null,
dinerIds
).stream().filter(r -> r.getEndAt() > now.toEpochMilli()).toList();
// 7. 构建学校停餐信息Map
var schoolSuspensionMap = schoolSuspensionRecords.stream()
.collect(Collectors.groupingBy(
SingleDinerMealSuspensionRecordRpcResponse::getClientId,
Collectors.collectingAndThen(
Collectors.maxBy((a, b) -> Long.compare(a.getCreatedAt(), b.getCreatedAt())),
opt -> opt.map(o -> convertToVO(o, dinerById)).orElse(null)
)
));
// 8. 构建学生停餐信息Map
var studentSuspensionMap = studentSuspensionRecords.stream()
.collect(Collectors.groupingBy(
SingleDinerMealSuspensionRecordRpcResponse::getDinerId,
Collectors.collectingAndThen(
Collectors.maxBy((a, b) -> Long.compare(a.getCreatedAt(), b.getCreatedAt())),
opt -> opt.map(o -> convertToVO(o, dinerById)).orElse(null)
)
));
// 9. 获取每个订餐人的停餐信息
return diners.stream()
.map(diner -> {
// 获取学校级别的停餐信息
var schoolSuspension = schoolSuspensionMap.get(Long.valueOf(diner.getClientId()).intValue());
// 获取学生级别的停餐信息
var studentSuspension = studentSuspensionMap.get(Long.valueOf(diner.getId()).intValue());
// 如果两个都没有停餐信息,返回null
if (schoolSuspension == null && studentSuspension == null) {
return null;
}
// 如果只有学校级别的停餐信息
if (schoolSuspension != null && studentSuspension == null) {
return schoolSuspension;
}
// 如果只有学生级别的停餐信息
if (schoolSuspension == null && studentSuspension != null) {
return studentSuspension;
}
// 如果两个都有停餐信息,合并时间范围
return DinerMealSuspensionRecordVO.builder()
.id(schoolSuspension.getId())
.enterpriseId(schoolSuspension.getEnterpriseId())
.clientId(schoolSuspension.getClientId())
.classId(studentSuspension.getClassId())
.dinerId(studentSuspension.getDinerId())
.dinerName(studentSuspension.getDinerName())
.studentNo(studentSuspension.getStudentNo())
.startAt(new Date(Math.min(schoolSuspension.getStartAt().getTime(), studentSuspension.getStartAt().getTime())))
.endAt(new Date(Math.max(schoolSuspension.getEndAt().getTime(), studentSuspension.getEndAt().getTime())))
.comment("")
.build();
})
.filter(Objects::nonNull)
.distinct()
.toList();
}
/**
* 将RPC响应转换为VO对象
*/
private DinerMealSuspensionRecordVO convertToVO(SingleDinerMealSuspensionRecordRpcResponse response,
Map<Integer, SingleDinerRpcResponse> dinerById) {
if (response == null) {
return null;
}
return DinerMealSuspensionRecordVO.builder()
.id(Long.valueOf(response.getId()))
.enterpriseId(Long.valueOf(response.getEnterpriseId()))
.clientId(Long.valueOf(response.getClientId()))
.classId(Long.valueOf(response.getClassId()))
.dinerId(Long.valueOf(response.getDinerId()))
.dinerName(dinerById.get(response.getDinerId()) != null ? dinerById.get(response.getDinerId()).getName() : null)
.studentNo(dinerById.get(response.getDinerId()) != null ? dinerById.get(response.getDinerId()).getStudentNo() : null)
.startAt(new Date(response.getStartAt()))
.endAt(new Date(response.getEndAt()))
.comment(response.getComment())
.build();
}
}
......
......@@ -31,6 +31,7 @@ service MealOrderServiceRpc {
rpc UpdateDiner (UpdateDinerRpcRequest) returns (UpdateDinerRpcResponse) {}
rpc BatchSaveOrUpdateDiners (BatchSaveOrUpdateDinersRpcRequest) returns (BatchSaveOrUpdateDinersRpcResponse) {}
rpc DeleteDinersByIds (DeleteDinersByIdsRpcRequest) returns (DeleteDinersRpcResponse) {}
rpc DeleteGraduationDinersByClassIds (DeleteGraduationDinersByClassIdsRpcRequest) returns (DeleteGraduationDinersByClassIdsRpcResponse) {}
rpc GetMpAccountDinerRefsByIds (GetMpAccountDinerRefsByIdsRpcRequest) returns (GetMpAccountDinerRefsByIdsRpcResponse) {}
rpc QueryMpAccountDinerRefsByCondition (QueryMpAccountDinerRefsByConditionRpcRequest) returns (QueryMpAccountDinerRefsByConditionRpcResponse) {}
......@@ -46,12 +47,22 @@ service MealOrderServiceRpc {
rpc BatchSaveOrUpdateGradeClasses (BatchSaveOrUpdateGradeClassesRpcRequest) returns (BatchSaveOrUpdateGradeClassesRpcResponse) {}
rpc DeleteGradeClassesByIds (DeleteGradeClassesByIdsRpcRequest) returns (DeleteGradeClassesRpcResponse) {}
rpc DeleteGradeClassesAndDinersByClientIds (DeleteGradeClassesAndDinersByClientIdsRpcRequest) returns (DeleteGradeClassesAndDinersByClientIdsRpcResponse) {}
rpc DeleteGradeClassesAndDinersByClassIds (DeleteGradeClassesAndDinersByClassIdsRpcRequest) returns (DeleteGradeClassesAndDinersByClassIdsRpcResponse) {}
rpc GetMealMenuRecordById (GetMealMenuRecordByIdRpcRequest) returns (GetMealMenuRecordByIdRpcResponse) {}
rpc GetMealMenuRecordsByIds (GetMealMenuRecordsByIdsRpcRequest) returns (GetMealMenuRecordsByIdsRpcResponse) {}
rpc QueryMealMenuRecordsByCondition (QueryMealMenuRecordsByConditionRpcRequest) returns (QueryMealMenuRecordsByConditionRpcResponse) {}
rpc CreateMealMenuRecord (CreateMealMenuRecordRpcRequest) returns (CreateMealMenuRecordRpcResponse) {}
rpc QueryLatestMealMenuRecordsByClientIds (QueryLatestMealMenuRecordsByClientIdsRpcRequest) returns (QueryLatestMealMenuRecordsByClientIdsRpcResponse) {}
// 停餐记录相关服务
rpc getDinerMealSuspensionRecordById(GetDinerMealSuspensionRecordByIdRpcRequest) returns (GetDinerMealSuspensionRecordByIdRpcResponse);
rpc getDinerMealSuspensionRecordsByIds(GetDinerMealSuspensionRecordsByIdsRpcRequest) returns (GetDinerMealSuspensionRecordsByIdsRpcResponse);
rpc queryDinerMealSuspensionRecordsByCondition(QueryDinerMealSuspensionRecordsByConditionRpcRequest) returns (QueryDinerMealSuspensionRecordsByConditionRpcResponse);
rpc createDinerMealSuspensionRecord(CreateDinerMealSuspensionRecordRpcRequest) returns (CreateDinerMealSuspensionRecordRpcResponse);
rpc batchCreateDinerMealSuspensionRecords(BatchCreateDinerMealSuspensionRecordsRpcRequest) returns (BatchCreateDinerMealSuspensionRecordsRpcResponse);
rpc updateDinerMealSuspensionRecord(UpdateDinerMealSuspensionRecordRpcRequest) returns (UpdateDinerMealSuspensionRecordRpcResponse);
rpc deleteDinerMealSuspensionRecordsByIds(DeleteDinerMealSuspensionRecordsByIdsRpcRequest) returns (DeleteDinerMealSuspensionRecordsRpcResponse);
}
enum MealOrderOrderMethodEnum {
......@@ -357,6 +368,17 @@ message DeleteDinersRpcResponse {
bool isDeleted = 1;
}
message DeleteGraduationDinersByClassIdsRpcRequest {
repeated int32 classIds = 1;
int32 enterpriseId = 2;
int32 updatedBy = 3;
int32 updateSource = 4;
}
message DeleteGraduationDinersByClassIdsRpcResponse {
bool isDeleted = 1;
}
enum MpAccountGenderEnum {
MP_ACCOUNT_GENDER_MAN = 0;
MP_ACCOUNT_GENDER_WOMAN = 1;
......@@ -613,8 +635,8 @@ message QueryGradeClassesByConditionRpcRequest {
int32 enterpriseId = 1;
bool shouldFilterClientIds = 2;
repeated int32 clientIds = 3;
bool shouldFilterGradeName = 4;
string gradeName = 5;
bool shouldFilterGradeNames = 4;
repeated string gradeName = 5;
bool includeDeleted = 6;
}
......@@ -721,6 +743,18 @@ message DeleteGradeClassesAndDinersByClientIdsRpcResponse {
bool isDeleted = 1;
}
message DeleteGradeClassesAndDinersByClassIdsRpcRequest {
repeated int32 classIds = 1;
int32 enterpriseId = 2;
int32 updatedBy = 3;
int32 updateSource = 4;
}
message DeleteGradeClassesAndDinersByClassIdsRpcResponse {
bool isDeleted = 1;
}
message SingleMealMenuRecordRpcResponse {
int32 id = 1;
int32 enterpriseId = 2;
......@@ -734,6 +768,7 @@ message SingleMealMenuRecordRpcResponse {
int64 updatedAt = 10;
int32 updateSource = 11;
bool isDeleted = 12;
string operatorName = 13;
}
message GetMealMenuRecordByIdRpcRequest {
......@@ -781,6 +816,7 @@ message CreateMealMenuRecordRpcRequest {
int32 enterpriseId = 2;
int32 createdBy = 3;
int32 creationSource = 4;
string operatorName = 5;
}
message CreateMealMenuRecordRpcResponse {
......@@ -796,3 +832,126 @@ message QueryLatestMealMenuRecordsByClientIdsRpcRequest {
message QueryLatestMealMenuRecordsByClientIdsRpcResponse {
repeated SingleMealMenuRecordRpcResponse responses = 1;
}
// 停餐记录相关消息定义
message SingleDinerMealSuspensionRecordRpcResponse {
int32 id = 1;
int32 enterpriseId = 2;
int32 clientId = 3;
int32 classId = 4;
int32 dinerId = 5;
int64 startAt = 6;
int64 endAt = 7;
string comment = 8;
int64 createdAt = 9;
int32 createdBy = 10;
int32 creationSource = 11;
int64 updatedAt = 12;
int32 updatedBy = 13;
int32 updateSource = 14;
}
message GetDinerMealSuspensionRecordByIdRpcRequest {
int64 id = 1;
}
message GetDinerMealSuspensionRecordByIdRpcResponse {
SingleDinerMealSuspensionRecordRpcResponse response = 1;
}
message GetDinerMealSuspensionRecordsByIdsRpcRequest {
repeated int32 ids = 1;
}
message GetDinerMealSuspensionRecordsByIdsRpcResponse {
repeated SingleDinerMealSuspensionRecordRpcResponse responses = 1;
}
message QueryDinerMealSuspensionRecordsByConditionRpcRequest {
int32 enterpriseId = 1;
bool shouldFilterClientIds = 2;
repeated int32 clientIds = 3;
bool shouldFilterClassIds = 4;
repeated int32 classIds = 5;
bool shouldFilterDinerIds = 6;
repeated int32 dinerIds = 7;
bool shouldFilterStartAt = 8;
int64 startAt = 9;
bool shouldFilterEndAt = 10;
int64 endAt = 11;
}
message QueryDinerMealSuspensionRecordsByConditionRpcResponse {
repeated SingleDinerMealSuspensionRecordRpcResponse responses = 1;
}
message CreateDinerMealSuspensionRecordRpcRequest {
int32 enterpriseId = 1;
int32 createdBy = 2;
int32 creationSource = 3;
DinerMealSuspensionRecordCreation creation = 4;
}
message DinerMealSuspensionRecordCreation {
int32 clientId = 1;
int32 classId = 2;
int32 dinerId = 3;
int64 startAt = 4;
int64 endAt = 5;
string comment = 6;
}
message CreateDinerMealSuspensionRecordRpcResponse {
bool isCreated = 1;
int32 id = 2;
}
message UpdateDinerMealSuspensionRecordRpcRequest {
int32 enterpriseId = 1;
int32 updatedBy = 2;
int32 updateSource = 3;
DinerMealSuspensionRecordModification modification = 4;
}
message DinerMealSuspensionRecordModification {
int32 id = 1;
bool shouldUpdateClientId = 2;
int32 clientId = 3;
bool shouldUpdateClassId = 4;
int32 classId = 5;
bool shouldUpdateDinerId = 6;
int32 dinerId = 7;
bool shouldUpdateStartAt = 8;
int64 startAt = 9;
bool shouldUpdateEndAt = 10;
int64 endAt = 11;
bool shouldUpdateComment = 12;
string comment = 13;
}
message UpdateDinerMealSuspensionRecordRpcResponse {
bool isUpdated = 1;
}
message DeleteDinerMealSuspensionRecordsByIdsRpcRequest {
repeated int32 ids = 1;
int32 enterpriseId = 2;
int32 updatedBy = 3;
int32 updateSource = 4;
}
message DeleteDinerMealSuspensionRecordsRpcResponse {
bool isDeleted = 1;
}
message BatchCreateDinerMealSuspensionRecordsRpcRequest {
int32 enterpriseId = 1;
int32 createdBy = 2;
int32 creationSource = 3;
repeated DinerMealSuspensionRecordCreation creations = 4;
}
message BatchCreateDinerMealSuspensionRecordsRpcResponse {
bool isCreated = 1;
repeated int32 ids = 2;
}
......