package com.mgm.mcrm.approval.service; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.Feature; import com.auth0.jwt.JWT; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.mgm.mcrm.allotment.models.CfgAppChannel; import com.mgm.mcrm.allotment.models.CfgOtherAllotment; import com.mgm.mcrm.allotment.models.CfgOtherAppChannel; import com.mgm.mcrm.allotment.models.CfgRoomCriterias; import com.mgm.mcrm.allotment.repositories.OtherAllotmentDao; import com.mgm.mcrm.allotment.repositories.RoomCriteriaDao; import com.mgm.mcrm.approval.dtos.GetApproverRequest; import com.mgm.mcrm.approval.dtos.RejectApprovalResponse; import com.mgm.mcrm.approval.dtos.SubmitApprovalResponse; import com.mgm.mcrm.approval.mappers.ApprovalMapper; import com.mgm.mcrm.approval.models.ApprovalDetail; import com.mgm.mcrm.approval.models.ApprovalGamingData; import com.mgm.mcrm.approval.models.CfgApprovalRouting; import com.mgm.mcrm.approval.models.CfgRequestReason; import com.mgm.mcrm.approval.repositories.ApprovalRoutingDao; import com.mgm.mcrm.approval.repositories.RequestReasonDao; import com.mgm.mcrm.appsetting.models.CfgSettings; import com.mgm.mcrm.appsetting.services.SettingService; import com.mgm.mcrm.appsetting.services.SysSettingService; import com.mgm.mcrm.authorization.dtos.LoginUserInfo; import com.mgm.mcrm.authorization.services.MgmUserService; import com.mgm.mcrm.authorization.services.SysGroupPermissionService; import com.mgm.mcrm.basesetting.property.models.CfgProperties; import com.mgm.mcrm.basesetting.property.repositories.PropertyDao; import com.mgm.mcrm.basesetting.room.models.CfgOtherRoomStatus; import com.mgm.mcrm.basesetting.room.repositories.OtherRoomStatusDao; import com.mgm.mcrm.basesetting.team.repositories.TeamStructureDao; import com.mgm.mcrm.booking.dtos.BookingDto; import com.mgm.mcrm.booking.mappers.BookingMapper; import com.mgm.mcrm.booking.models.Bookings; import com.mgm.mcrm.booking.models.BookingsView; import com.mgm.mcrm.booking.repositories.BookingDao; import com.mgm.mcrm.booking.repositories.BookingViewDao; import com.mgm.mcrm.booking.services.BookingService; import com.mgm.mcrm.booking.services.NonMGMBookingService; import com.mgm.mcrm.booking.services.OperaDBBookingService; import com.mgm.mcrm.booking.services.OperaService; import com.mgm.mcrm.common.constants.SysCommon; import com.mgm.mcrm.common.constants.SysConstants; import com.mgm.mcrm.common.dtos.BaseApiResponse; import com.mgm.mcrm.common.dtos.EumnApprovalStatus; import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; import com.mgm.mcrm.common.dtos.mgm_api.ApprovalGamingDataResult; import com.mgm.mcrm.common.dtos.mgm_api.ApproverRecordResult; import com.mgm.mcrm.common.dtos.mgm_api.ApproverResult; import com.mgm.mcrm.common.dtos.mgm_api.PlayerProfileEntry; import com.mgm.mcrm.common.dtos.mgm_api.UserInfoApiRecord; import com.mgm.mcrm.common.jms.BookingControlMsgSender; import com.mgm.mcrm.common.services.MGMEsbApiService; import com.mgm.mcrm.common.utils.DateTimeUtil; import com.mgm.mcrm.common.utils.LogUtil; import com.mgm.mcrm.logging.models.SysLogBooking; import com.mgm.mcrm.logging.repositories.SysLogBookingDao; import com.mgm.mcrm.logging.services.SysLogBookingService; import com.mgm.mcrm.notification.models.Email; import com.mgm.mcrm.notification.repositories.EmailDao; import com.mgm.mcrm.notification.services.SendEmailService; import com.mgm.mcrm.player.services.PlayerService; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; import org.hibernate.StaleObjectStateException; import org.slf4j.Logger; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; @Service public class ApprovalService { @Resource private SysSettingService sysSettingService; @Resource private BookingViewDao bookingViewDao; @Resource private BookingDao bookingDao ; @Resource private SysLogBookingService sysLogBookingService ; @Resource private RequestReasonDao requestReasonDao; @Resource private MGMEsbApiService mgmEsbApiService; @Resource private PropertyDao propertyDao; @Resource private OtherRoomStatusDao roomStatusDao; @Resource private OperaService operaService; @Resource private SysLogBookingDao sysLogBookingDao; @Resource private OperaDBBookingService operaDBBookingService; @Resource private SysGroupPermissionService sysGroupPermissionService; @Resource private BookingMapper bookingMapper; @Resource private ApprovalMapper approvalMapper; private final Logger log = LogUtil.getLogger(ApprovalService.class); @Autowired private ApprovalRoutingDao approvalRoutingDao; @Autowired private BookingControlMsgSender ctrlMsgSender; @Lazy @Autowired private BookingService bookingService; @Autowired private EmailDao emailDao; @Autowired private MgmUserService mgmUserService; @Autowired private SettingService settingService; @Autowired private PlayerService playerService; @Autowired private RoomCriteriaDao roomCriteriaDao; @Autowired private OtherRoomStatusDao otherRoomStatusDao; @Autowired private OtherAllotmentDao otherAllotmentDao; /** 与 NonMGMBookingService 中的 ApprovalService 形成环,延迟注入打破循环 */ @Lazy @Autowired private NonMGMBookingService nonMGMBookingService; @Autowired private TeamStructureDao teamStructureDao; @Resource private SendEmailService sendEmailService; public BaseApiResponse> getRequestReason() throws Exception { // 新建對象 BaseApiResponse> response = new BaseApiResponse>(); // 獲取ApprovalReason List requestReasons = requestReasonDao.findAll(); if (!requestReasons.isEmpty()) { response.setSuccess(true); response.setData(requestReasons); }else{ response.setSuccess(false); response.setMessage("No request reason found"); } // 返回數據 return response; } public BaseApiResponse getApprover(HttpServletRequest request, GetApproverRequest approvalRequest) throws Exception { String playerId = approvalRequest.getPlayerId(); String bookingType = approvalRequest.getBookingType(); String propertyName =approvalRequest.getPropertyName(); String roomCategory =approvalRequest.getRoomCategory(); String checkInDate = approvalRequest.getCheckInDate(); String checkOutDate =approvalRequest.getCheckOutDate(); String token = request.getHeader("Authorization"); String userId = JWT.decode(token).getClaim("login_id").asString(); BaseApiResponse response = new BaseApiResponse<>(); if(!StringUtils.isEmpty(userId)) { userId = StringUtils.replace(userId, "@mgm.mo", ""); } if (propertyName == null && roomCategory == null) { response.setSuccess(false); response.setMessage("Missing required parameter 'propertyName' or 'roomCategory'"); return response; } try { HttpResponseResult httpResult = mgmEsbApiService.invokeGetApprover(playerId,bookingType,propertyName,roomCategory,checkInDate,checkOutDate,userId); try { JSONObject rawResult = JSONObject.parseObject(httpResult.getResponseContent()); ApproverResult resultObj = JSONObject.parseObject(httpResult.getResponseContent(), ApproverResult.class, Feature.SupportNonPublicField,Feature.IgnoreNotMatch); fillApproverListFromRawJson(resultObj, rawResult); if(Strings.CS.equals(resultObj.getStatusCode(), "OK")) { if (resultObj.getRecords() != null && !resultObj.getRecords().isEmpty()) { response.setSuccess(true); // 只返回 Records[0] response.setData(resultObj); } else { response.setSuccess(false); response.setMessage("NO_RECORDS"); LogUtil.info(ApprovalService.class, "[GetApprover Error] Records is empty"); } }else { response.setSuccess(false); response.setMessage(resultObj.getStatusDescription()); LogUtil.info(ApprovalService.class, "[GetApprover Error]" + resultObj.getStatusDescription()); } }catch(Exception err) { response.setSuccess(false); response.setMessage("RETURN_DATA_ERROR"); LogUtil.info(ApprovalService.class, "[GetApprover Error]" + err.getMessage()); } }catch(Exception err) { response.setSuccess(false); response.setMessage("INVOKE_GETAPPROVER_API_ERROR"); LogUtil.info(ApprovalService.class, "[GetApprover Error]" + err.getMessage()); } return response; } private void fillApproverListFromRawJson(ApproverResult resultObj, JSONObject rawResult) { if (resultObj == null || resultObj.getRecords() == null || rawResult == null) { return; } JSONArray rawRecords = rawResult.getJSONArray("Records"); if (rawRecords == null) { return; } List records = resultObj.getRecords(); int size = Math.min(records.size(), rawRecords.size()); for (int i = 0; i < size; i++) { JSONObject rawRecord = rawRecords.getJSONObject(i); if (rawRecord == null) { continue; } records.get(i).setApproverList(rawRecord.get("ApproverList")); } } public BaseApiResponse getApprovalData(String bookingId) throws Exception { BaseApiResponse response = new BaseApiResponse<>(); ApprovalGamingDataResult approvalGamingData; try { // String bookingCategory = bookingDao.findCategoryById(Long.valueOf(bookingId)); // HttpResponseResult httpResult; // if ("DAYLIGHT".equalsIgnoreCase(bookingCategory)) { // httpResult = mgmApiService.invokeGetApprovalData(bookingId, 1); // } else { // httpResult = mgmApiService.invokeGetApprovalData(bookingId, 0); // } HttpResponseResult httpResult = mgmEsbApiService.invokeGetApprovalData(bookingId, 1); try { approvalGamingData = JSONObject.parseObject(httpResult.getResponseContent(), ApprovalGamingDataResult.class, Feature.SupportNonPublicField,Feature.IgnoreNotMatch); if(StringUtils.equalsIgnoreCase(approvalGamingData.getStatusCode(),"OK")) { response.setData(approvalGamingData); response.setSuccess(true); return response; }else { response.setSuccess(false); response.setMessage(approvalGamingData.getStatusDescription()); LogUtil.info(ApprovalService.class, "[Get ApprovalData Error]" + approvalGamingData.getStatusDescription()); } }catch(Exception err) { response.setSuccess(false); response.setMessage("RETURN_DATA_ERROR"); LogUtil.info(ApprovalService.class, "[Get ApprovalData Error]" + err.getMessage()); } }catch(Exception err) { response.setSuccess(false); response.setMessage("Get Approval Data Error"); LogUtil.info(ApprovalService.class, "[Get ApprovalData Error]" + err.getMessage()); } return response; } public void createApprovalRouting(Bookings booking, String approverId, String approverName, String approvalReasonId, String routingReason) throws Exception{ // 获取ApprovalReason LogUtil.info(ApprovalService.class, "[createApprovalRouting] Booking Id :" + booking.getId()); CfgRequestReason cfgRequestReason = requestReasonDao.getById(Long.valueOf(approvalReasonId)); Date now = new Date(); //将 ApprovalRouting 数据插入表中 CfgApprovalRouting newRouting = new CfgApprovalRouting(); newRouting.setBookingId(booking.getId()); newRouting.setApproverId(approverId); newRouting.setApproverName(approverName); newRouting.setApprovalReason(cfgRequestReason.getCode()); newRouting.setCreatedAt(now); newRouting.setUpdatedAt(now); newRouting.setBookingStatus("AP"); newRouting.setRoutingReason(routingReason); newRouting.setCreatedBy(booking.getCreatedByName()); newRouting.setUpdatedBy(null); booking.setApprovalReason(cfgRequestReason.getCode()); bookingDao.save(booking); CfgApprovalRouting saveRouting = approvalRoutingDao.save(newRouting); } public List getSearchRecordPendingApproval(String checkinDate, String checkoutDate, String playerId) { // 获得配置的过期分钟 暂无 // AppSetting approvalExpMin = appSettingRepository.getByKeys(SettingConstants.APPROVAL_EXPIRE_MINUTES); // if (approvalExpMin != null && StringUtils.isNotBlank(approvalExpMin.getKeyValues())) { // expMin = Integer.valueOf(approvalExpMin.getKeyValues()); // } Boolean isMock = sysSettingService.isAPPMock(); int expMin = 20000; expMin = expMin * -1; Date dtNow = DateTimeUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss"); dtNow = DateTimeUtil.addMin(dtNow, expMin); String nowStr = DateTimeUtil.getDateByFormat(dtNow, "yyyy-MM-dd HH:mm:ss"); List bookingsViews = bookingViewDao.findRecordSearchPendingApproval(playerId, checkinDate, checkoutDate, nowStr); List newResult = new ArrayList<>(); SimpleDateFormat dateOnlyFormat = new SimpleDateFormat("yyyy-MM-dd"); dateOnlyFormat.setTimeZone(TimeZone.getTimeZone("GMT+8")); Date now = new Date(); for (BookingsView booking : bookingsViews) { if (booking.getCheckinDate() != null) { LocalDate today = LocalDate.parse(dateOnlyFormat.format(now)); LocalDate checkinDateOnly = LocalDate.parse(dateOnlyFormat.format(booking.getCheckinDate())); } newResult.add(booking); } return bookingMapper.viewToBookingDtoList(newResult); } public boolean isExpiredPendingApprovalBooking(BookingsView booking){ SimpleDateFormat dateOnlyFormat = new SimpleDateFormat("yyyy-MM-dd"); dateOnlyFormat.setTimeZone(TimeZone.getTimeZone("GMT+8")); CfgSettings cutoffStartTime = settingService.getSettingByKey("VIEW_YESTERDAY_TODOLIST_START"); CfgSettings cutoffEndTime = settingService.getSettingByKey("VIEW_YESTERDAY_TODOLIST_END"); String cutoffStartTimeValue = cutoffStartTime != null ? cutoffStartTime.getKeyValues() : null; String cutoffEndTimeValue = cutoffEndTime != null ? cutoffEndTime.getKeyValues() : null; Date now = new Date(); boolean isExpired = false; if (booking.getCheckinDate() != null) { LocalDate today = LocalDate.parse(dateOnlyFormat.format(now)); LocalDate checkinDateOnly = LocalDate.parse(dateOnlyFormat.format(booking.getCheckinDate())); // 已超过入住日(不含当天),且当前时间不在配置的 HH:mm 窗口内 → AE if (checkinDateOnly.isBefore(today) && !isCurrentTimeWithinCutoffWindowGmt8(cutoffStartTimeValue, cutoffEndTimeValue)) { Bookings entity = bookingDao.findById(booking.getId()).orElse(null); if (entity != null) { entity.setApproveStatus("AE"); entity.setUpdatedAt(now); bookingDao.save(entity); CfgApprovalRouting approvalRouting = approvalRoutingDao.getByBookingId(entity.getId()); if (approvalRouting != null) { approvalRouting.setBookingStatus("AE"); approvalRouting.setUpdatedAt(now); approvalRoutingDao.save(approvalRouting); } isExpired = true; } } } return isExpired; } /** * 判断当前时间(GMT+8)是否在 [start, end] 内(含起止),配置为 HH:mm(如 09:30)。 * 约定 start 不晚于 end;配置缺失或解析失败时返回 false(视为不在窗口内)。 */ private boolean isCurrentTimeWithinCutoffWindowGmt8(String startValue, String endValue) { if (StringUtils.isBlank(startValue) || StringUtils.isBlank(endValue)) { return false; } // 注意:Java 里分钟必须用小写 m;大写 M 表示「月份」。HH = 两位小时,H = 一位或两位小时 DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm"); try { LocalTime start = LocalTime.parse(startValue.trim(), fmt); LocalTime end = LocalTime.parse(endValue.trim(), fmt); ZoneId zone = ZoneId.of("GMT+8"); LocalTime current = ZonedDateTime.now(zone).toLocalTime(); return !current.isBefore(start) && !current.isAfter(end); } catch (DateTimeParseException e) { return false; } } public BaseApiResponse cancelApprovalBooking(Map param,String userId, HttpServletRequest request) { BaseApiResponse response = new BaseApiResponse<>(); Date createDate = new Date(); Bookings booking = new Bookings(); Long bookId = Long.valueOf(String.valueOf(param.get("id"))); ArrayList booksList = new ArrayList<>(); booking = bookingDao.findById(bookId).orElse(null); String status = booking.getApproveStatus(); if (Strings.CS.equals(booking.getCreatedBy(),userId)){ if ("AP".equals(status)) { booking.setApproveStatus("AC"); booking.setUpdatedAt(createDate); booking.setUpdatedBy(userId); bookingDao.save(booking); response.setSuccess(true); response.setData(bookId); booksList.add(booking); String oldStatus = EumnApprovalStatus.getApprovalStatusByDBValue(status); String newStatus = EumnApprovalStatus.getApprovalStatusByDBValue("AC"); sysLogBookingService.saveLogBooking(booksList, request,oldStatus,newStatus,"Cancel Approval Booking",""); CfgApprovalRouting approvalRouting = approvalRoutingDao.getByBookingId(bookId); approvalRouting.setBookingStatus("AC"); approvalRouting.setUpdatedAt(createDate); approvalRouting.setUpdatedBy(userId); approvalRoutingDao.save(approvalRouting); }else{ response.setSuccess(false); response.setLogMessage("BOOKING_STATUS_CHANGED"); response.setMessage("Cannot Cancel Status is not Pending Approval Booking"); } }else{ response.setSuccess(false); response.setLogMessage("NO_AUTHORIZED"); response.setMessage("You have not authorized to Cancel this Booking"); return response; } return response; } public BaseApiResponse batchCancelApprovalBooking(Map param,String userId, HttpServletRequest request) { BaseApiResponse response = new BaseApiResponse<>(); Date createDate = new Date(); Bookings booking = new Bookings(); String[] bookIds = (String.valueOf(param.get("bookId"))).split(";"); ArrayList booksList = new ArrayList(); StringBuilder failId = new StringBuilder(); for (String id : bookIds) { booking = bookingDao.findById(Long.parseLong(id)).orElse(null); String status = booking.getApproveStatus(); if ("AP".equals(status)) { booking.setApproveStatus("AC"); booking.setUpdatedAt(createDate); booking.setUpdatedBy(userId); bookingDao.save(booking); Date date = new Date(); CfgApprovalRouting newRouting = approvalRoutingDao.getByBookingId(booking.getId()); newRouting.setBookingStatus("AC"); newRouting.setUpdatedAt(date); newRouting.setUpdatedBy(userId); approvalRoutingDao.save(newRouting); String oldStatus = EumnApprovalStatus.getApprovalStatusByDBValue(status); String newStatus = EumnApprovalStatus.getApprovalStatusByDBValue("AC"); sysLogBookingService.saveLogBooking(booksList, request,oldStatus,newStatus,"Cancel Approval Booking",""); response.setSuccess(true); response.setData(booking); booksList.add(booking); }else{ failId.append(id); } } if (!failId.isEmpty()){ response.setSuccess(false); response.setMessage("Cancel Pending Approval Booking Fail,BookId : "+failId.toString()); } return response; } public BaseApiResponse> getPendingApprovalList(HttpServletRequest request,Map params) throws Exception { String token = request.getHeader("Authorization"); String userId = JWT.decode(token).getClaim("login_id").asString(); BaseApiResponse> response = new BaseApiResponse<>(); String playerId = params.get("playerId") == null ? null : params.get("playerId").toString(); List rangeDate = params.get("rangeDate") == null ? null : (ArrayList) params.get("rangeDate"); String groupName = JWT.decode(token).getClaim("group_name").asString(); boolean viewApproval = sysGroupPermissionService.checkGroupPermission(groupName, "10001_05"); //TODO viewApproval = true; if (!viewApproval){ response.setSuccess(Boolean.FALSE); response.setLogMessage("No Permission"); response.setMessage("No Permission"); return response; } List bookingList = new ArrayList<>(); List approvalList = approvalRoutingDao.getApprovalList(userId); List bookingIds = approvalList.stream().map(CfgApprovalRouting::getBookingId).toList(); String bookingIdsParam = bookingIds.stream().map(String::valueOf).collect(Collectors.joining(";")); ApprovalGamingDataResult approvalData = bookingIds.isEmpty() ? new ApprovalGamingDataResult() : this.getApprovalData(bookingIdsParam).getData(); List waitListData = approvalData.getRecords(); boolean hasPlayerId = playerId != null && !playerId.trim().isEmpty(); String trimmedPlayerId = hasPlayerId ? playerId.trim() : null; // 到期判定:仅当当前日期已超过 checkinDate(nowDate > checkinDate)才设为 AE Date now = new Date(); SimpleDateFormat dateOnlyFormat = new SimpleDateFormat("yyyy-MM-dd"); // 处理日期范围:rangeDate 可能为 null / 空 / 1 个 / 多个(且连续) Date startDate = null; Date endDate = null; if (rangeDate != null && !rangeDate.isEmpty()) { try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 假设传入的日期字符串已按从小到大排好,如无保证也可以先 Collections.sort(rangeDate) Collections.sort(rangeDate); startDate = sdf.parse(rangeDate.get(0)); endDate = sdf.parse(rangeDate.get(rangeDate.size() - 1)); } catch (Exception e) { LogUtil.error(ApprovalService.class, "[getPendingApprovalList] parse rangeDate error: " + e.getMessage()); } } for (CfgApprovalRouting cfgApprovalRouting : approvalList) { BookingsView booking = hasPlayerId ? bookingViewDao.findByIdAndPlayerIdContainingIgnoreCase(cfgApprovalRouting.getBookingId(), trimmedPlayerId) : bookingViewDao.findByid(cfgApprovalRouting.getBookingId()); if (booking == null) { continue; } // 如果已经到期:把路由与 booking 状态都更新为 AE,并从返回列表中剔除 if (booking.getCheckinDate() != null) { LocalDate nowDate = LocalDate.parse(dateOnlyFormat.format(now)); LocalDate checkinDateOnly = LocalDate.parse(dateOnlyFormat.format(booking.getCheckinDate())); long diffDays = ChronoUnit.DAYS.between(checkinDateOnly, nowDate); // now - checkin // 新规则: // diffDays <= 0:未超过 checkinDate(含当天),不剔除 // diffDays > 0:日期已超过 checkinDate,直接剔除(AE) boolean shouldExpire = diffDays > 0; dateOnlyFormat.setTimeZone(TimeZone.getTimeZone("GMT+8")); CfgSettings cutoffStartTime = settingService.getSettingByKey("VIEW_YESTERDAY_TODOLIST_START"); CfgSettings cutoffEndTime = settingService.getSettingByKey("VIEW_YESTERDAY_TODOLIST_END"); String cutoffStartTimeValue = cutoffStartTime != null ? cutoffStartTime.getKeyValues() : null; String cutoffEndTimeValue = cutoffEndTime != null ? cutoffEndTime.getKeyValues() : null; if (shouldExpire && !isCurrentTimeWithinCutoffWindowGmt8(cutoffStartTimeValue, cutoffEndTimeValue)) { cfgApprovalRouting.setBookingStatus("AE"); cfgApprovalRouting.setUpdatedAt(now); cfgApprovalRouting.setUpdatedBy(userId); approvalRoutingDao.save(cfgApprovalRouting); Bookings entity = bookingDao.findById(cfgApprovalRouting.getBookingId()).orElse(null); if (entity != null) { entity.setApproveStatus("AE"); entity.setUpdatedAt(now); entity.setUpdatedBy(userId); bookingDao.save(entity); } continue; } } if (waitListData != null) { // 根据bookingId匹配对应的ApprovalGamingData for (ApprovalGamingData gamingData : waitListData) { if (gamingData.getBookingId() != null && booking.getId() != null && gamingData.getBookingId().equals(booking.getId().toString())) { booking.setR6Tpd(gamingData.getR6TPD()); break; } } } if (startDate != null && endDate != null && booking.getCheckinDate() != null) { Date checkin = booking.getCheckinDate(); if (checkin.before(startDate) || checkin.after(endDate)) { continue; } } bookingList.add(booking); } // 对bookingList进行排序: // 1) 按 BookingView.checkinDate 日期部分升序(忽略时间) // 2) 如果同一天,再按 R6TPD 降序 Collections.sort(bookingList, (b1, b2) -> { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date firstCheckInDate = b1.getCheckinDate(); Date secondCheckInDate = b2.getCheckinDate(); if (firstCheckInDate == null && secondCheckInDate == null) { // 同时为空,按 R6TPD 排 } else if (firstCheckInDate == null) { return 1; // d1 放后面 } else if (secondCheckInDate == null) { return -1; // d2 放后面 } else { int dateCompare = dateFormat.format(firstCheckInDate).compareTo(dateFormat.format(secondCheckInDate)); if (dateCompare != 0) { return dateCompare; } } // 同一天比较 R6TPD值 double firstR6Tpd = 0.0; double secondR6Tpd = 0.0; try { if (b1.getR6Tpd() != null && !b1.getR6Tpd().isEmpty()) { String r6TpdStr1 = b1.getR6Tpd().replace(",", ""); firstR6Tpd = Double.parseDouble(r6TpdStr1); } if (b2.getR6Tpd() != null && !b2.getR6Tpd().isEmpty()) { String r6TpdStr2 = b2.getR6Tpd().replace(",", ""); secondR6Tpd = Double.parseDouble(r6TpdStr2); } } catch (NumberFormatException e) { // 解析错误视为0 } // 按R6TPD降序排序 return Double.compare(secondR6Tpd, firstR6Tpd); }); response.setSuccess(true); response.setData(bookingList); return response; } public BaseApiResponse getCountPendingApproval(HttpServletRequest request) { String token = request.getHeader("Authorization"); String userId = JWT.decode(token).getClaim("login_id").asString(); BaseApiResponse response = new BaseApiResponse<>(); response.setSuccess(true); response.setData(approvalRoutingDao.countApprovalList(userId)); return response; } public BaseApiResponse rejectApprovalBooking(Long bookingId,String userId) throws StaleObjectStateException { BaseApiResponse response = new BaseApiResponse<>(); List bookingList = new ArrayList<>(); CfgApprovalRouting approvalRouting = approvalRoutingDao.getByBookingId(bookingId); Date now = new Date(); System.out.println("Version"+approvalRouting.getVersion()); if (approvalRouting != null && isCurrentUserInApproverList(approvalRouting.getApproverId(), userId) && "AP".equals(approvalRouting.getBookingStatus())) { LogUtil.info(ApprovalService.class,"Reject Approval.UserId:"+userId); approvalRouting.setBookingStatus("AR"); CfgApprovalRouting changeRecord = approvalRoutingDao.save(approvalRouting); if (changeRecord==null) { response.setSuccess(false); response.setLogMessage("SUBMIT_APPROVAL_FAIL"); response.setMessage("Submit approval fail,Record status has been changed"); return response; } Bookings booking = bookingDao.findById(approvalRouting.getBookingId()).orElse(null); booking.setApprovedId(Long.valueOf(userId)); booking.setApprovedAt(now); booking.setApproveStatus("AR"); bookingDao.save(booking); response.setSuccess(true); response.setData(approvalMapper.toRejectApprovalResponse(booking)); }else{ response.setSuccess(false); response.setLogMessage("REJECT BOOKING FALSE"); response.setMessage("Reject booking fail,please refresh record and retry"); } return response; } public BaseApiResponse getApprovalDetail(HttpServletRequest request, Map params) throws Exception { String token = request.getHeader("Authorization"); String playerId = params.get("playerId") == null ? null : params.get("playerId").toString(); String userId = JWT.decode(token).getClaim("login_id").asString(); BaseApiResponse response = new BaseApiResponse<>(); // 获取对应 playerId 且 approvalStatus 为 AP 的 booking 记录 List bookingList = bookingViewDao.getPendingApprovalBookingList(playerId,userId); ObjectMapper mapper = new ObjectMapper(); List idList = new ArrayList<>(); // 循环配置每个Record的Player for (BookingsView booking : bookingList) { // 收集booking.getId() idList.add(String.valueOf(booking.getId())); String sharer = booking.getSharer(); JsonNode jsonArray = mapper.readTree(sharer); List playerInfoList = new ArrayList<>(); for (JsonNode person : jsonArray) { String sharerId = person.get("sharerId")!=null?person.get("sharerId").asText():""; String name = person.get("name")!=null?person.get("name").asText():""; playerInfoList.add(sharerId + " " + name); } String joinedResult = String.join(";", playerInfoList); booking.setSharer(joinedResult); } // 用分号拼接所有ID // Call Eab api 获取 player 的 GamingData ApprovalGamingDataResult approvalData = this.getApprovalData(String.join(";", idList)).getData(); List approvalRoutingList = new ArrayList<>(); for (BookingsView booking : bookingList) { approvalRoutingList.add(approvalRoutingDao.getByBookingId(booking.getId())); } ApprovalDetail approvalDetail = new ApprovalDetail(); approvalDetail.setCfgApprovalRoutingList(approvalRoutingList); approvalDetail.setApprovalGamingData(approvalData.getRecords()); approvalDetail.setBookingsViewList(bookingList); if (!approvalDetail.getBookingsViewList().isEmpty()) { response.setSuccess(true); response.setData(approvalDetail); }else { response.setSuccess(false); response.setLogMessage("GET_APPROVAL_DETAIL_FALSE"); } return response; } public BaseApiResponse submitApproval(String bookingId,String checkinDate,String checkoutDate,String userId,HttpServletRequest request) throws Exception { BaseApiResponse response = new BaseApiResponse<>(); ArrayList addList = new ArrayList<>(); Gson gson = new Gson(); CfgApprovalRouting approvalRouting = approvalRoutingDao.getByBookingId(Long.valueOf(bookingId)); LogUtil.info(ApprovalService.class,"Submitting Approval.UserId:"+userId+"BookingId"+bookingId +"CheckinDate"+checkinDate+"CheckoutDate"+checkoutDate); Date now = new Date(); //判断approverId 和 当前userId是否相同 if (approvalRouting != null && isCurrentUserInApproverList(approvalRouting.getApproverId(), userId) && "AP".equals(approvalRouting.getBookingStatus())) { LogUtil.info(ApprovalService.class,"Submit Approval.UserId:"+userId); //相同 处理approvalRouting记录 并调用 Call OWS createbooking approvalRouting.setBookingStatus("AA"); approvalRoutingDao.save(approvalRouting); //处理Pending Approval Booking 记录 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); Date checkin = dateFormat.parse(checkinDate); Date checkout = dateFormat.parse(checkoutDate); String userName = null; CfgSettings emailAddress = settingService.getSettingByKey(SysConstants.MGM_EMAIL_ADDRESS); if(request!=null) { LoginUserInfo userInfo = SysCommon.parseUserFromToken(String.valueOf(request)); if (userInfo != null) { userId = userInfo.getLoginId(); if(!StringUtils.isEmpty(userId)) { userId = Strings.CS.replace(userId, "@mgm.mo", ""); } userName = userInfo.getDisplayName(); if(userId!=null) { try { Long checkUserId=Long.valueOf(userId); BaseApiResponse userInfoByUserId = mgmUserService.findUserInfoByUserId(userId); UserInfoApiRecord newUserInfo = userInfoByUserId.getData(); userName = newUserInfo.getUserName(); } catch (Exception e) { userName = userId; userId = null; } } } } Date creatdDate = DateTimeUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss"); Bookings originBooking = bookingDao.findById(approvalRouting.getBookingId()).orElse(null); originBooking.setApproveStatus("AA"); originBooking.setApprovedAt(now); originBooking.setApprovedBy(userName); if (userId != null) { originBooking.setApprovedId(Long.valueOf(userId)); } if (!Strings.CS.equals(originBooking.getCheckinDate().toString(),checkinDate)){ SysLogBooking newLogBooking = new SysLogBooking(); newLogBooking.setOccurTime(now); newLogBooking.setBookId(Long.valueOf(bookingId)); newLogBooking.setEventCode("Check In Date"); newLogBooking.setChangeFrom(DateTimeUtil.getDateByFormat(originBooking.getCheckinDate(), "yyyy-MM-dd")); newLogBooking.setChangeTo(checkinDate); newLogBooking.setLoginId(userId); newLogBooking.setLoginName(userId); addList.add(newLogBooking); originBooking.setCheckinDate(checkin); } if(!Strings.CS.equals(originBooking.getCheckoutDate().toString(),checkoutDate)){ SysLogBooking newLogBooking = new SysLogBooking(); newLogBooking.setOccurTime(now); newLogBooking.setBookId(Long.valueOf(bookingId)); newLogBooking.setEventCode("Check Out Date"); newLogBooking.setChangeFrom(DateTimeUtil.getDateByFormat(originBooking.getCheckoutDate(), "yyyy-MM-dd")); newLogBooking.setChangeTo(checkoutDate); newLogBooking.setLoginId(userId); newLogBooking.setLoginName(userId); addList.add(newLogBooking); originBooking.setCheckoutDate(checkout); } BookingsView bookingView = bookingViewDao.findById(Long.parseLong(bookingId)); // 获取Property CfgProperties properties = propertyDao.findById(originBooking.getPropertyId()).orElse(null); Boolean isOverBook; if ("DAYLIGHT".equalsIgnoreCase(bookingView.getPropertyCategory())) { isOverBook = false; } else { isOverBook = this.checkIsOverbook(bookingView.getRoomId(), originBooking.getCheckinDate(), originBooking.getCheckoutDate(), bookingView.getPropertyId(), bookingView.getBlockId()); } Boolean isOtherOverBook = this.checkOtherIsOverbook(Math.toIntExact(originBooking.getRoomId()), originBooking.getCheckoutDate(), originBooking.getCheckinDate(), originBooking.getPropertyId(), originBooking.getSegmentId()); if (bookingView != null) { bookingView.setCheckinDate(originBooking.getCheckinDate()); bookingView.setCheckoutDate(originBooking.getCheckoutDate()); Integer numOfNight = Integer.valueOf(DateTimeUtil.getDiffDate( bookingView.getCheckinDate(), bookingView.getCheckoutDate(), "day")); List dates = new ArrayList<>(); List dateList = new ArrayList<>(); JSONObject obj = new JSONObject(); for (int j = 0; j < numOfNight + 1; j++) { dates.add(DateTimeUtil.getDateByFormat(DateTimeUtil.addDay(bookingView.getCheckinDate(), j), "yyyy-MM-dd")); } obj.put("date", dates); obj.put("numOfNight", numOfNight); obj.put("numOfRoom", 1); obj.put("checked", true); dateList.add(obj); bookingView.setDateList(dateList); bookingView.setNumOfNight(numOfNight); originBooking.setDateList(JSONArray.toJSONString(dateList)); originBooking.setNumOfNight(numOfNight); bookingView.setApproveStatus(originBooking.getApproveStatus()); bookingView.setApprovedAt(originBooking.getApprovedAt()); bookingView.setApprovedBy(originBooking.getApprovedBy()); bookingView.setApprovedId(originBooking.getApprovedId()); } bookingDao.saveAndFlush(originBooking); LogUtil.info(ApprovalService.class,"Submitting Approval.BookingID:"+bookingId+"|Save CheckinDate = "+originBooking.getCheckinDate()+" Save CheckoutDate = "+originBooking.getCheckoutDate()); if (Strings.CS.equals(originBooking.getBookType(),"0")){ //create 的情况 LogUtil.info(ApprovalService.class,"Submitting Approval.BookingID:"+bookingId+"|bookingType:Create"); BookingsView originBookingView = bookingView; originBookingView = this.getEmailInfo(originBookingView,originBookingView.getCreatedBy()).getData(); // 判断是OPETA或OTHER Property if(Strings.CS.equals(properties.getCategory(),"OPERA")) { // OPERA 先创建 newBooking 再 call OWS,避免 callOWSCreateBooking 内部把同一笔记录的 status 改成 N 覆盖掉 origin 的 AA Bookings newBooking = new Bookings(); BeanUtils.copyProperties(originBookingView, newBooking, "id"); // BookingsView 中 roomId/blockId 为 Long,Bookings 为 Integer,copyProperties 会跳过;View 无 bookingTypeId newBooking.setApprovalBookingId(String.valueOf(originBooking.getId())); newBooking.setCategory("OPERA"); if (originBookingView.getRoomId() != null) { newBooking.setRoomId(originBookingView.getRoomId()); } else if (originBooking.getRoomId() != null) { newBooking.setRoomId(originBooking.getRoomId()); } if (originBookingView.getBlockId() != null) { newBooking.setBlockId(originBookingView.getBlockId()); } else if (originBooking.getBlockId() != null) { newBooking.setBlockId(originBooking.getBlockId()); } if (originBooking.getBookingTypeId() != null) { newBooking.setBookingTypeId(originBooking.getBookingTypeId()); } newBooking.setApproveStatus("PN"); // newBooking.setLastOperaStatus("RESERVED"); newBooking.setApprovedBy(userName); if (userId != null) { newBooking.setApprovedId(Long.valueOf(userId)); } newBooking.setApprovalReason(approvalRouting.getApprovalReason()); bookingDao.save(newBooking); if (ctrlMsgSender != null) { if (SysCommon.jmsRunning) { ctrlMsgSender.sendMessage("CREATE_OPERA_BOOKING", newBooking); } else { bookingService.callOWSCreateBooking(newBooking); } } } else if (Strings.CS.equals(properties.getCategory(),"DAYLIGHT")) { Bookings newBooking = new Bookings(); BeanUtils.copyProperties(originBookingView, newBooking, "id"); // BookingsView 中 roomId/blockId 为 Long,Bookings 为 Integer,copyProperties 会跳过;View 无 bookingTypeId newBooking.setApprovalBookingId(String.valueOf(originBooking.getId())); newBooking.setDateList(originBooking.getDateList()); newBooking.setMember(originBooking.getMember()); newBooking.setCategory("DAYLIGHT"); if (originBookingView.getRoomId() != null) { newBooking.setRoomId(originBookingView.getRoomId()); } else if (originBooking.getRoomId() != null) { newBooking.setRoomId(originBooking.getRoomId()); } if (originBookingView.getBlockId() != null) { newBooking.setBlockId(originBookingView.getBlockId()); } else if (originBooking.getBlockId() != null) { newBooking.setBlockId(originBooking.getBlockId()); } if (originBooking.getBookingTypeId() != null) { newBooking.setBookingTypeId(originBooking.getBookingTypeId()); } newBooking.setApproveStatus("PN"); // newBooking.setLastOperaStatus("RESERVED"); newBooking.setApprovedBy(userName); if (userId != null) { newBooking.setApprovedId(Long.valueOf(userId)); } newBooking.setApprovalReason(approvalRouting.getApprovalReason()); bookingDao.save(newBooking); if (ctrlMsgSender != null) { if (SysCommon.jmsRunning) { ctrlMsgSender.sendMessage("CREATE_DAYLIGHT_BOOKING", newBooking); } else { bookingService.callDaylightUpsertReservation(newBooking, "CREATE_BOOKING"); } } }else { //判断是否OverBooking if((isOverBook&&originBooking.getBlockId()!=null)||(isOtherOverBook&&originBooking.getSegmentId()!=null)) { Bookings emailBook = originBooking; Email email = new Email(); email.setBookId(originBooking.getId()); email.setCreatedAt(creatdDate); email.setCreatedBy(originBooking.getCreatedBy()); emailBook.setCategory("OTHER"); email = sendEmailService.sendApprovalNewEmail(emailBook, originBookingView,email,userId,userName,true,false,emailAddress.getKeyValues()); if(email.getStatus()==1) { originBooking.setApproveStatus("AA"); bookingDao.save(originBooking); } emailDao.save(email); }else{ //Other Property 去本地createBooking Bookings newBooking = new Bookings(); BeanUtils.copyProperties(originBooking, newBooking,"id"); newBooking.setApproveStatus("PN"); newBooking.setCheckinDate(checkin); newBooking.setCheckoutDate(checkout); newBooking.setLastOperaStatus("PENDING"); newBooking.setApprovedBy(userName); newBooking.setApprovedId(Long.valueOf(userId)); newBooking.setCategory("OTHER"); newBooking.setApprovalRemark(originBooking.getApprovalReason()); //获取other room status CfgOtherRoomStatus roomStatus = roomStatusDao.findByStatusName("Pending"); if(roomStatus!=null) { newBooking.setOtherRoomStatusId(roomStatus.getId()); } bookingDao.save(newBooking); //扣减房间 nonMGMBookingService.processOtherAllotment(newBooking.getRoomId(), newBooking.getCheckoutDate(), newBooking.getCheckinDate(), newBooking.getPropertyId(), newBooking.getSegmentId()); } } // 判断是否为 R2 或者 Y2房型 是的话 发Email if((StringUtils.equals(originBooking.getRoomTypeCode(), "R2"))|| StringUtils.equals(originBooking.getRoomTypeCode(), "Y2")) { Bookings emailBook = originBooking; Email email = new Email(); email.setBookId(originBooking.getId()); email.setCreatedAt(creatdDate); email.setCreatedBy(originBooking.getCreatedBy()); email = sendEmailService.sendApprovalNewEmail(emailBook, originBookingView,email,userId,userName,false,true,emailAddress.getKeyValues()); emailDao.save(email); } response.setSuccess(true); response.setData(approvalMapper.toSubmitApprovalResponse(originBooking)); }else{ LogUtil.info(ApprovalService.class,"Submitting Approval.BookingID:"+bookingId+"|bookingType:Extend"); //获取Property if(Strings.CS.equals(properties.getCategory(),"OPERA")){ //Send mgm booking email BookingsView originBookingView = bookingViewDao.findById(Long.parseLong(originBooking.getApprovalBookingId())); originBookingView = this.getEmailInfo(originBookingView,originBookingView.getCreatedBy()).getData(); Bookings emailBook = originBooking; Email email = new Email(); email.setBookId(originBooking.getId()); email.setCreatedAt(creatdDate); email.setCreatedBy(originBooking.getCreatedBy()); email = sendEmailService.sendApprovalExtendEmail(emailBook, originBookingView,email,userId,userName,originBookingView,false,emailAddress.getKeyValues()); LogUtil.info(ApprovalService.class,"Send Email Approved Id = "+originBooking.getId()); if(email.getStatus()==1) { originBooking.setApproveStatus("AA"); bookingDao.save(originBooking); emailDao.save(email); response.setSuccess(true); response.setData(approvalMapper.toSubmitApprovalResponse(originBooking)); }else{ response.setSuccess(false); response.setLogMessage("Send Email Failed"); response.setMessage("Send Email Failed"); } }else if (Strings.CS.equals(properties.getCategory(),"DAYLIGHT")) { BaseApiResponse findPlayerResult = playerService .findPlayerProfileByPlayerIdAndUserId(originBooking.getPlayerId(), userId); if (!findPlayerResult.isSuccess() || findPlayerResult.getData() == null) { response.setSuccess(false); response.setCode("Player Profile Not Found"); } else { BaseApiResponse> extendResult = bookingService.extendDaylightApprovalBooking( originBooking, findPlayerResult.getData(), userId, userName); if (extendResult.isSuccess()) { originBooking.setApproveStatus("AA"); bookingDao.save(originBooking); response.setSuccess(true); response.setData(approvalMapper.toSubmitApprovalResponse(originBooking)); } else { response.setSuccess(false); response.setMessage(extendResult.getMessage()); response.setLogMessage(extendResult.getLogMessage()); } } }else{ //send email instead of sync other booking BookingsView originBookingView = bookingViewDao.findById(Long.parseLong(originBooking.getApprovalBookingId())); originBookingView = this.getEmailInfo(originBookingView,originBookingView.getCreatedBy()).getData(); Email email = new Email(); email.setBookId(originBooking.getId()); email.setCreatedAt(creatdDate); email.setCreatedBy(originBooking.getCreatedBy()); email = sendEmailService.sendApprovalExtendEmail(originBooking, originBookingView,email,userId,userName,bookingView,isOtherOverBook,emailAddress.getKeyValues()); if(email.getStatus()==1) { originBooking.setApproveStatus("AA"); bookingDao.save(originBooking); emailDao.save(email); response.setSuccess(true); response.setData(approvalMapper.toSubmitApprovalResponse(originBooking)); }else{ response.setSuccess(false); response.setMessage("Send Email Failed"); response.setLogMessage("Send Email Failed"); } } } if(addList.size()>0) sysLogBookingDao.saveAll(addList); }else { response.setSuccess(false); response.setMessage("Submit Approval Failed,BookingId:"+bookingId); response.setLogMessage("Approve record fail,please refresh record and retry"); } return response; } public BaseApiResponse>> batchApproval(HttpServletRequest request,Map params) throws Exception { String token = request.getHeader("Authorization"); String userId = JWT.decode(token).getClaim("login_id").asString(); List> submitInfoList = this.convertToList(params.get("submitInfoList")); BaseApiResponse>> response = new BaseApiResponse<>(); List failApprovalList = new ArrayList<>(); LogUtil.info(ApprovalService.class,"Batch Approval.UserId:"+userId+" submitInfoList:"+submitInfoList); // 循环拿到每个提交的信息 for (Map submitInfo : submitInfoList) { String bookingId = String.valueOf(submitInfo.get("bookingId")); String checkinDate = submitInfo.get("checkinDate"); String checkoutDate = submitInfo.get("checkoutDate"); if (bookingId != null && checkinDate != null && checkoutDate != null) { // 调用submitApproval方法 BaseApiResponse funcResponse = this.submitApproval(bookingId, checkinDate, checkoutDate, userId, request); // 如果submitApproval失败了 放入failApprovalList中 if (!funcResponse.isSuccess()) failApprovalList.add(bookingId); }else{ failApprovalList.add(bookingId); } } // 根据failApprovalList 判断是否成功 if (failApprovalList.isEmpty()) { response.setSuccess(true); response.setData(submitInfoList); }else{ response.setSuccess(false); response.setLogMessage("Failed to Approval Booking,BookingIds:"+failApprovalList.toString()); LogUtil.error(ApprovalService.class,"Batch Approval Fail,FailApprovalList:"+failApprovalList); } return response; } // public ServiceResult changeMisCharge(Long bookingId,String misCharge,String userId) throws Exception { // ServiceResult processResult = new ServiceResult(); // List failApprovalList = new ArrayList<>(); //// 获取到对应的booking // Bookings bookings = bookingDao.getById(bookingId); // Boolean isMock = sysSettingService.isMockReplicate(); // // // 与 createBooking 中 operaComment 日期段一致(英文 Mar 30,非 Date#toString()) // String dateRange = DateTimeUtil.formatOperaCommentStayDateRange( // bookings.getCheckinDate(), bookings.getCheckoutDate(), bookings.getNumOfNight()); // // // String newOperaComment = null; // // 获取Booking的Comment // // Integer bookingCommentId = bookings.getOperaCommentId()!=null?Integer.valueOf(bookings.getOperaCommentId()):null; // Integer resvNameId = bookings.getResvId()!=null?Integer.valueOf(bookings.getResvId()):null; // // LogUtil.info(ApprovalService.class,"Start GetOperaComment CommentId:"+bookingCommentId+" resvNameId:"+resvNameId); // // List bookingCommentByCommentId = operaDBBookingService.getBookingCommentByCommentId(bookingCommentId,resvNameId); // // LogUtil.info(ApprovalService.class,"End GetOperaComment"); // // String commentType = null; // // if (bookingCommentByCommentId != null && !bookingCommentByCommentId.isEmpty()) { // // //TODO 先用get 在升级到JDK21后 使用getLast替换掉 // OperaComment operaComment = bookingCommentByCommentId.getLast(); // bookings.setOperaComment(operaComment.getComments()); // bookings.setOperaCommentId(operaComment.getResvCommentId()); // commentType = operaComment.getCommentType(); // LogUtil.info(ApprovalService.class,"Get Comment Success:"+operaComment.getComments()+"CommentId:"+operaComment.getResvCommentId()); // // }else{ // // LogUtil.info(ApprovalService.class,"Get Comment Failed,bookingCommentByCommentId is Empty"); // // } // // String operaComment = bookings != null ? bookings.getOperaComment() : null; // newOperaComment = operaComment == null ? "" : operaComment; //// 判断CreateBooking时是否有传入MisCharge //// 没有 -> operaComment 是固定模板((RM)COMP ..APP\n\nSTAY DATE,OD,BOOKER) //// 有 -> operaComment 里已经包含 mischarge 对应的 INC 行((RM+INC$*** )... / INC.CHRG$***...) // //// 修改misCharge + 修改 operaComment // String misChargeValue = misCharge == null ? "" : misCharge.trim(); // // // 保存旧 misCharge,用于区分“情况 1 / 情况 2” // String oldMisCharge = bookings.getMiscCharge(); // boolean hadOldMisCharge = oldMisCharge != null && !oldMisCharge.trim().isEmpty(); // // // 统一换行符,避免 \r\n 或 ' ' 导致 indexOf/replace 命中失败 // if (newOperaComment != null) { // newOperaComment = newOperaComment.replace("\r\n", "\n").replace("\r", "\n"); // // DB/OPERA 里可能已是 ' ',先还原成 '\n' 供下方逻辑判断 // newOperaComment = newOperaComment.replace(" ", "\n"); // newOperaComment = newOperaComment.replace("$", "$"); // } // // bookings.setMiscCharge(misChargeValue); // // if (!misChargeValue.isEmpty() && !newOperaComment.isEmpty()) { // // 若 misCharge 以前为空,且当前 comment 仍是 createBooking 的默认模板,则视为情况 1 // boolean isBaseTemplate = // newOperaComment.contains("(RM)COMP ..APP") // && newOperaComment.contains("\n\n"+dateRange+","+bookings.getSourceCode()+","+userId) // && !newOperaComment.contains("INC.CHRG$"); // // String amendLine = "AMEND INC.CHRG $" + misChargeValue + " AS "+userId+" ..APP"; // // if (!hadOldMisCharge && isBaseTemplate) { // newOperaComment = newOperaComment // .replace("(RM)COMP ..APP", "(RM+INC$" + misChargeValue + ")COMP ..APP"); // // // 在 \n\n 后、STAY DATE 行前插入 // String target = "\n\n"+dateRange+","+bookings.getSourceCode()+","+userId; // if (newOperaComment.contains(target)) { // newOperaComment = newOperaComment.replace( // target, // "\n\n" + amendLine + "\n" + target.substring(2) // ); // } else { // int nnIdx = newOperaComment.indexOf("\n\n"); // if (nnIdx >= 0) { // newOperaComment = newOperaComment.substring(0, nnIdx + 2) + amendLine + "\n" + newOperaComment.substring(nnIdx + 2); // } else { // newOperaComment = newOperaComment + "\n" + amendLine; // } // } // } else { // // 情况 2:operaComment 已经包含 INC.CHRG$***,需要把 misCharge 替换为新值,并在第二个换行符后的下一行插入 amendLine // // // 替换 RM 里的 INC$ 值 // int rmIdxFull = newOperaComment.indexOf("(RM+INC$"); // if (rmIdxFull >= 0) { // int valStart = rmIdxFull + "(RM+INC$".length(); // int valEnd = newOperaComment.indexOf(")COMP", valStart); // if (valEnd > valStart) { // newOperaComment = newOperaComment.substring(0, valStart) + misChargeValue + newOperaComment.substring(valEnd); // } // } // int rmIdxHalf = newOperaComment.indexOf("(RM+INC$"); // if (rmIdxHalf >= 0) { // int valStart = rmIdxHalf + "(RM+INC$".length(); // int valEnd = newOperaComment.indexOf(")COMP", valStart); // if (valEnd > valStart) { // newOperaComment = newOperaComment.substring(0, valStart) + misChargeValue + newOperaComment.substring(valEnd); // } // } // // // 不移除旧的 AMEND 行:每次修改都在旧 AMEND 行上方新增一行 // // 在 \n\n 后插入一行(\n\n 下一行即为原本第一条 AMEND 行) // String doubleNl = "\n\n"; // int nnIdx = newOperaComment.indexOf(doubleNl); // if (nnIdx >= 0) { // newOperaComment = newOperaComment.substring(0, nnIdx + doubleNl.length()) // + amendLine + "\n" // + newOperaComment.substring(nnIdx + doubleNl.length()); // } else { // newOperaComment = newOperaComment + "\n" + amendLine; // } // } // // // }else{ // newOperaComment = "(RM+INC$" + misCharge + ")COMP ..APP\n\n" // + "INC.CHRG$" + misCharge + "COMP BY "+bookings.getCreatedBy()+" ..APP\n" // + dateRange+","+bookings.getSourceCode()+","+userId; // } // // // 最终落库 / 发送到 OPERA 前,将内部换行符从 '\n' 转成 ' ' // if (newOperaComment != null && !newOperaComment.isEmpty()) { // newOperaComment = newOperaComment.replace("\n", " "); // newOperaComment = newOperaComment.replace("$", "$"); // } // if (StringUtils.isNotBlank(userId)) { // bookings.setUpdatedBy(userId); // bookings.setUpdatedAt(new Date()); // } // Bookings changedBooking = null; // if (isMock) { // bookings.setOperaComment(newOperaComment); // changedBooking = bookingDao.save(bookings); // } else { // // if(bookings.getConfirmationNo()!=null){ // //调用OWS方法去OPERA修改MisCharge // ModifyBookingRequest misChargeRequest = new ModifyBookingRequest(); // misChargeRequest.setMisCharge(misChargeValue); // misChargeRequest.setBooking(bookings); // operaService.modifyMisCharge(misChargeRequest); // LogUtil.info(ApprovalService.class,"new Comment"+newOperaComment); // // //调用OWS方法去OPERA修改Comment // CfgProperties property = propertyDao.getById(bookings.getPropertyId()); // GuestRequestsRequest cashCommentRequest = new GuestRequestsRequest(); // JSONObject cashCommentResponse = new JSONObject(); // cashCommentRequest.setProperty(property); // cashCommentRequest.setComment(newOperaComment); // cashCommentRequest.setConfirmationNo(bookings.getConfirmationNo()); // cashCommentRequest.setCommentId(bookings.getOperaCommentId()); // LogUtil.info(ApprovalService.class,"CommentId"+bookings.getOperaCommentId()+"CommentType"+commentType); //// 判断如果没有Cooment就ADD 如果有的话 就去UPDATE // // if (bookings.getOperaCommentId()!=null){ // cashCommentRequest.setActionType("UPDATE"); // cashCommentRequest.setCommentId(bookings.getOperaCommentId()); // if (commentType!=null){ // cashCommentRequest.setCommentType(commentType); // } // }else { // cashCommentRequest.setActionType("ADD"); // cashCommentRequest.setCommentType("CASHIER"); // } // LogUtil.info(ApprovalService.class,"Action Type:"+cashCommentRequest.getActionType()); // LogUtil.info(ApprovalService.class,"start opera change comment"); // cashCommentResponse = operaService.guestRequests(cashCommentRequest); // LogUtil.info(ApprovalService.class,"end opera change comment"); // // if (cashCommentResponse!=null){ // String commentId = cashCommentResponse.get("commentId").toString(); // if (commentId != null && !commentId.isEmpty()) { // bookings.setOperaCommentId(commentId); // } // }else{ // // } // // } // // bookings.setOperaComment(newOperaComment); // changedBooking = bookingDao.save(bookings); // // } // // // processResult.setSuccess(true); // processResult.setReturnData(changedBooking); // return processResult; // } public BaseApiResponse> batchReject(List submitBookingIds,String userId) throws Exception { BaseApiResponse> response = new BaseApiResponse<>(); List failApprovalList = new ArrayList<>(); // 循环拿到每个提交的信息 for (String bookingId : submitBookingIds) { if (bookingId != null) { // 调用submitApproval方法 BaseApiResponse rejectResponse = this.rejectApprovalBooking(Long.valueOf(bookingId), userId); // 如果submitApproval失败了 放入failApprovalList中 if (!rejectResponse.isSuccess()) failApprovalList.add(bookingId); }else{ failApprovalList.add(bookingId); } } // 根据failApprovalList 判断是否成功 if (failApprovalList.isEmpty()) { response.setSuccess(true); response.setData(submitBookingIds); }else{ response.setSuccess(false); response.setMessage("Failed to Reject Booking,BookingIds:"+failApprovalList.toString()); } return response; } /** * approver_id 可能为单个工号,或以英文逗号/中文逗号/分号分隔的多个工号。 * 分隔后逐项 trim 再与 userId 精确比较,避免用 contains 导致 "800002" 误匹配 "80002"。 */ private static boolean isCurrentUserInApproverList(String approverIdField, String userId) { if (StringUtils.isBlank(approverIdField) || StringUtils.isBlank(userId)) { return false; } String uid = userId.trim(); for (String token : approverIdField.split("[,,;;]")) { LogUtil.info(ApprovalService.class,"approverIdField:"+token); LogUtil.info(ApprovalService.class,"Current Id:"+uid); if (uid.equals(token.trim())) { LogUtil.info(ApprovalService.class,"=-=---=-=-=-=-== TRUE"); return true; } } LogUtil.info(ApprovalService.class,"=-=---=-=-=-=-== FALSE"); return false; } @SuppressWarnings("unchecked") public List> convertToList(Object obj) { if (obj instanceof List list) { if (list.isEmpty() || list.getFirst() instanceof Map) { return (List>) list; } } return new ArrayList<>(); } private BaseApiResponse getEmailInfo(BookingsView bookView, String userId) { BaseApiResponse response = new BaseApiResponse<>(); if(bookView!=null) { JSONArray dynList = !StringUtils.isEmpty(bookView.getSharer())&&!Strings.CS.equals("[]", bookView.getSharer())? JSONArray.parseArray(bookView.getSharer()) :new JSONArray(); StringBuilder sharerString = new StringBuilder(); if (!dynList.isEmpty()) { for (Object o : dynList) { Gson gson = new Gson(); JSONObject shareObj = (JSONObject) o; String sharePlayerId = shareObj.getString("playerId"); String shareName = shareObj.getString("name"); LogUtil.info(BookingService.class, "[editBooking] sharePlayerId " + gson.toJson(sharePlayerId)); LogUtil.info(BookingService.class, "[editBooking] shareName " + gson.toJson(shareName)); if (!StringUtils.isEmpty(sharePlayerId)) { sharerString.append(sharePlayerId).append(" ").append(shareName).append(" "); } else if (!StringUtils.isEmpty(shareName)) { sharerString.append(shareName).append(" "); } else { continue; } } } bookView.setSharerName(sharerString.toString()); String playerId = bookView.getPlayerId()!=null? bookView.getPlayerId():null; BaseApiResponse webPlayerProfileByPlayerId = playerService.findWebPlayerProfileByPlayerId(playerId); if(webPlayerProfileByPlayerId.isSuccess()) { try { PlayerProfileEntry profile = webPlayerProfileByPlayerId.getData(); bookView.setMemberTier(profile.getTierName()); bookView.setMemberLastName(profile.getName()); } catch (Exception e) { LogUtil.error(ApprovalService.class,String.valueOf(e.getMessage())); } } } response.setData(bookView); return response; } //检查other booking是否overbook public Boolean checkOtherIsOverbook(int roomId,Date checkoutDate,Date checkinDate,Long propertyId,Long segmentId) { boolean isOverbook = false; List roomIds = new ArrayList<>(); roomIds.add(Long.valueOf(String.valueOf(roomId))); Date endDate = DateTimeUtil.addDay(checkoutDate, -1); List criterias = otherAllotmentDao.findByRoomIdInAndPropertyIdAndBookDateBetween (roomIds,propertyId,checkinDate,endDate); if(criterias.isEmpty()) isOverbook = true; Long[] roomStatusId = otherRoomStatusDao.findIsPickUpId(); for(CfgOtherAllotment c:criterias) { CfgOtherAppChannel channel = new CfgOtherAppChannel(); Set channels = c.getChannels(); if(channels.isEmpty()) isOverbook = true; for(CfgOtherAppChannel a:channels) { if(a.getSegmentId()!=null&&segmentId!=null) { if(a.getSegmentId().longValue()==segmentId.longValue()) { channel = a; int pickUp = 0; Integer appChannel = 0; pickUp = bookingDao.getOtherPickupBooking(propertyId, roomId, segmentId, c.getBookDate(), roomStatusId); pickUp = pickUp+1; if(channel.getAppChannel()!=null) { appChannel = channel.getAppChannel(); } if(appChannel-pickUp<0) { isOverbook = true; break; } } } } } return isOverbook; } //检查other booking是否overbook public Boolean checkIsOverbook(Long roomId,Date checkinDate ,Date checkoutDate,Long propertyId,Long blockId) { boolean isOverbook = false; List roomIds = new ArrayList<>(); roomIds.add(roomId); Date endDate = DateTimeUtil.addDay(checkoutDate, -1); List criterias = roomCriteriaDao.findByRoomIdInAndPropertyIdAndBookDateBetween (roomIds,propertyId,checkinDate,endDate); if(criterias.isEmpty()) isOverbook = true; for(CfgRoomCriterias c:criterias) { CfgAppChannel channel = new CfgAppChannel(); Set channels = c.getChannels(); if(channels.isEmpty()) isOverbook = true; for(CfgAppChannel a:channels) { if(a.getBlockId()!=null&&blockId!=null) { if(a.getBlockId().longValue()==blockId.longValue()) { channel = a; int pickUp = 0; int appChannel = 0; pickUp = bookingDao.getPickupBooking(propertyId, roomId, blockId, c.getBookDate()); pickUp = pickUp+1; if(channel.getAppChannel()!=null) { appChannel = channel.getAppChannel(); } if(appChannel-pickUp<0) { isOverbook = true; break; } } } } } return isOverbook; } public void setFailApprovalRouting(String bookingId){ Bookings booking = bookingDao.findById(Long.valueOf(bookingId)).orElse(null); Date now = new Date(); booking.setApproveStatus("F"); booking.setTryTime(5); booking.setUpdatedAt(now); bookingDao.save(booking); } }