Files
mgm-daylight-mcrm-api-mobile/src/main/java/com/mgm/mcrm/approval/service/ApprovalService.java
T
2026-10-26 09:31:05 +08:00

1504 lines
74 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<List<CfgRequestReason>> getRequestReason() throws Exception {
// 新建對象
BaseApiResponse<List<CfgRequestReason>> response = new BaseApiResponse<List<CfgRequestReason>>();
// 獲取ApprovalReason
List<CfgRequestReason> 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<ApproverResult> 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<ApproverResult> 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<ApproverRecordResult> 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<ApprovalGamingDataResult> getApprovalData(String bookingId) throws Exception {
BaseApiResponse<ApprovalGamingDataResult> 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<BookingDto> 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<BookingsView> bookingsViews = bookingViewDao.findRecordSearchPendingApproval(playerId, checkinDate,
checkoutDate, nowStr);
List<BookingsView> 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<Long> cancelApprovalBooking(Map<String,Object> param,String userId, HttpServletRequest request) {
BaseApiResponse<Long> response = new BaseApiResponse<>();
Date createDate = new Date();
Bookings booking = new Bookings();
Long bookId = Long.valueOf(String.valueOf(param.get("id")));
ArrayList<Bookings> 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<Bookings> batchCancelApprovalBooking(Map<String,Object> param,String userId, HttpServletRequest request) {
BaseApiResponse<Bookings> response = new BaseApiResponse<>();
Date createDate = new Date();
Bookings booking = new Bookings();
String[] bookIds = (String.valueOf(param.get("bookId"))).split(";");
ArrayList<Bookings> 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<List<BookingsView>> getPendingApprovalList(HttpServletRequest request,Map<String, Object> params) throws Exception {
String token = request.getHeader("Authorization");
String userId = JWT.decode(token).getClaim("login_id").asString();
BaseApiResponse<List<BookingsView>> response = new BaseApiResponse<>();
String playerId = params.get("playerId") == null ? null : params.get("playerId").toString();
List<String> rangeDate = params.get("rangeDate") == null ? null : (ArrayList<String>) 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<BookingsView> bookingList = new ArrayList<>();
List<CfgApprovalRouting> approvalList = approvalRoutingDao.getApprovalList(userId);
List<Long> 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<ApprovalGamingData> 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<Long> getCountPendingApproval(HttpServletRequest request) {
String token = request.getHeader("Authorization");
String userId = JWT.decode(token).getClaim("login_id").asString();
BaseApiResponse<Long> response = new BaseApiResponse<>();
response.setSuccess(true);
response.setData(approvalRoutingDao.countApprovalList(userId));
return response;
}
public BaseApiResponse<RejectApprovalResponse> rejectApprovalBooking(Long bookingId,String userId) throws StaleObjectStateException {
BaseApiResponse<RejectApprovalResponse> response = new BaseApiResponse<>();
List<BookingsView> 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<ApprovalDetail> getApprovalDetail(HttpServletRequest request, Map<String, Object> 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<ApprovalDetail> response = new BaseApiResponse<>();
// 获取对应 playerId 且 approvalStatus 为 AP 的 booking 记录
List<BookingsView> bookingList = bookingViewDao.getPendingApprovalBookingList(playerId,userId);
ObjectMapper mapper = new ObjectMapper();
List<String> 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<String> 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<CfgApprovalRouting> 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<SubmitApprovalResponse> submitApproval(String bookingId,String checkinDate,String checkoutDate,String userId,HttpServletRequest request) throws Exception {
BaseApiResponse<SubmitApprovalResponse> response = new BaseApiResponse<>();
ArrayList<SysLogBooking> 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<UserInfoApiRecord> 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<String> dates = new ArrayList<>();
List<JSONObject> 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<PlayerProfileEntry> findPlayerResult = playerService
.findPlayerProfileByPlayerIdAndUserId(originBooking.getPlayerId(), userId);
if (!findPlayerResult.isSuccess() || findPlayerResult.getData() == null) {
response.setSuccess(false);
response.setCode("Player Profile Not Found");
} else {
BaseApiResponse<List<Bookings>> 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<List<Map<String,String>>> batchApproval(HttpServletRequest request,Map<String, Object> params) throws Exception {
String token = request.getHeader("Authorization");
String userId = JWT.decode(token).getClaim("login_id").asString();
List<Map<String, String>> submitInfoList = this.convertToList(params.get("submitInfoList"));
BaseApiResponse<List<Map<String,String>>> response = new BaseApiResponse<>();
List<String> failApprovalList = new ArrayList<>();
LogUtil.info(ApprovalService.class,"Batch Approval.UserId:"+userId+" submitInfoList:"+submitInfoList);
// 循环拿到每个提交的信息
for (Map<String, String> 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<SubmitApprovalResponse> 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<String> 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<OperaComment> 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 或 '&#xA;' 导致 indexOf/replace 命中失败
// if (newOperaComment != null) {
// newOperaComment = newOperaComment.replace("\r\n", "\n").replace("\r", "\n");
// // DB/OPERA 里可能已是 '&#xA;',先还原成 '\n' 供下方逻辑判断
// newOperaComment = newOperaComment.replace("&#xA;", "\n");
// newOperaComment = newOperaComment.replace("&#x24;", "$");
// }
//
// 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' 转成 '&#xA;'
// if (newOperaComment != null && !newOperaComment.isEmpty()) {
// newOperaComment = newOperaComment.replace("\n", "&#xA;");
// newOperaComment = newOperaComment.replace("$", "&#x24;");
// }
// 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<List<String>> batchReject(List<String> submitBookingIds,String userId) throws Exception {
BaseApiResponse<List<String>> response = new BaseApiResponse<>();
List<String> failApprovalList = new ArrayList<>();
// 循环拿到每个提交的信息
for (String bookingId : submitBookingIds) {
if (bookingId != null) {
// 调用submitApproval方法
BaseApiResponse<RejectApprovalResponse> 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<Map<String, String>> convertToList(Object obj) {
if (obj instanceof List<?> list) {
if (list.isEmpty() || list.getFirst() instanceof Map) {
return (List<Map<String, String>>) list;
}
}
return new ArrayList<>();
}
private BaseApiResponse<BookingsView> getEmailInfo(BookingsView bookView, String userId) {
BaseApiResponse<BookingsView> 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<PlayerProfileEntry> 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<Long> roomIds = new ArrayList<>();
roomIds.add(Long.valueOf(String.valueOf(roomId)));
Date endDate = DateTimeUtil.addDay(checkoutDate, -1);
List<CfgOtherAllotment> 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<CfgOtherAppChannel> 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<Long> roomIds = new ArrayList<>();
roomIds.add(roomId);
Date endDate = DateTimeUtil.addDay(checkoutDate, -1);
List<CfgRoomCriterias> criterias = roomCriteriaDao.findByRoomIdInAndPropertyIdAndBookDateBetween
(roomIds,propertyId,checkinDate,endDate);
if(criterias.isEmpty()) isOverbook = true;
for(CfgRoomCriterias c:criterias) {
CfgAppChannel channel = new CfgAppChannel();
Set<CfgAppChannel> 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);
}
}