package com.chuanghai.ihotel.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.chuanghai.ihotel.aop.LoginCheckAspect; import com.chuanghai.ihotel.common.exception.BizCodeEnume; import com.chuanghai.ihotel.common.exception.RRException; import com.chuanghai.ihotel.common.utils.MyQuery; import com.chuanghai.ihotel.common.utils.PageParam; import com.chuanghai.ihotel.common.utils.PageUtils; import com.chuanghai.ihotel.component.PayComponent; import com.chuanghai.ihotel.config.JXNXSPayConfig; import com.chuanghai.ihotel.config.RabbitMQConfig; import com.chuanghai.ihotel.constant.TimeConstant; import com.chuanghai.ihotel.controller.request.ConfrimOrderRequest; import com.chuanghai.ihotel.controller.request.OrderQueryRequest; import com.chuanghai.ihotel.controller.request.SubmitOrderRequest; import com.chuanghai.ihotel.dao.HotelOrderDao; import com.chuanghai.ihotel.dto.BillHandleResultDTO; import com.chuanghai.ihotel.dto.EventMessageDTO; import com.chuanghai.ihotel.dto.LockRoomDTO; import com.chuanghai.ihotel.entity.HotelOrderBillEntity; import com.chuanghai.ihotel.entity.HotelOrderEntity; import com.chuanghai.ihotel.entity.HotelUserEntity; import com.chuanghai.ihotel.entity.RoomDoorLockDataEntity; import com.chuanghai.ihotel.entity.RoomEntity; import com.chuanghai.ihotel.entity.RoomTypeEntity; import com.chuanghai.ihotel.entity.SystemSettingEntity; import com.chuanghai.ihotel.enums.EventMessageTypeEnum; import com.chuanghai.ihotel.enums.OrderBillStatuEnum; import com.chuanghai.ihotel.enums.OrderStatuEnum; import com.chuanghai.ihotel.enums.UserIdentityTypeEnum; import com.chuanghai.ihotel.service.HotelOrderBillService; import com.chuanghai.ihotel.service.HotelOrderService; import com.chuanghai.ihotel.service.HotelUserService; import com.chuanghai.ihotel.service.RoomDoorLockDataService; import com.chuanghai.ihotel.service.RoomRealtimeStatuService; import com.chuanghai.ihotel.service.RoomService; import com.chuanghai.ihotel.service.RoomTypeService; import com.chuanghai.ihotel.service.SystemSettingService; import com.chuanghai.ihotel.util.CommonUtil; import com.chuanghai.ihotel.vo.ConfirmOrderVO; import com.chuanghai.ihotel.vo.LoginUserVO; import com.chuanghai.ihotel.vo.OrderBillHandleVO; import com.chuanghai.ihotel.vo.OrderSubmitVO; import com.chuanghai.ihotel.vo.UserOrderDetailVO; import com.chuanghai.ihotel.vo.UserOrderIndexVO; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @Slf4j @Service("hotelOrderService") public class HotelOrderServiceImpl extends ServiceImpl implements HotelOrderService { @Autowired private SystemSettingService systemSettingService; @Autowired private RoomTypeService roomTypeService; @Autowired private RoomRealtimeStatuService roomRealtimeStatuService; @Autowired private HotelUserService hotelUserService; @Autowired private RoomService roomService; @Autowired private HotelOrderBillService orderBillService; @Autowired private RoomDoorLockDataService doorLockService; @Autowired private RoomDoorLockDataService roomDoorLockDataService; @Autowired private PayComponent payComponent; @Autowired private JXNXSPayConfig jxnxsPayConfig; @Autowired private RabbitTemplate rabbitTemplate; @Autowired private RabbitMQConfig rabbitMQConfig; @Override public PageUtils queryPage(PageParam pageParam, OrderQueryRequest request) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq(request.getOrderId() != null, "id", request.getOrderId()); queryWrapper.eq(StringUtils.hasText(request.getDeleteFlag()), "delete_flag", request.getDeleteFlag()); queryWrapper.eq(StringUtils.hasText(request.getOrderNo()), "order_no", request.getOrderNo()); queryWrapper.eq(StringUtils.hasText(request.getUserFlag()), "user_flag", request.getUserFlag()); queryWrapper.eq(StringUtils.hasText(request.getUserName()), "user_name", request.getUserName()); queryWrapper.eq(StringUtils.hasText(request.getUserPhone()), "user_phone", request.getUserPhone()); queryWrapper.eq(StringUtils.hasText(request.getRoomNo()), "room_no", request.getRoomNo()); queryWrapper.eq(request.getRoomTypeId() != null, "room_type_id", request.getRoomTypeId()); String statu = request.getStatu(); if (StringUtils.hasText(statu)) { if (statu.contains("-")) { String[] split = statu.split("-"); queryWrapper.eq("order_statu", split[0]); queryWrapper.eq("biz_statu", split[1]); } else { queryWrapper.eq("order_statu", statu); } } queryWrapper.ge(request.getEnableStartTime() != null, "enable_start_time", request.getEnableStartTime()); queryWrapper.le(request.getEnableStartTime() != null, "enable_end_time", request.getEnableStartTime()); IPage page = this.page( new MyQuery().getPage(pageParam), queryWrapper ); return new PageUtils(page); } @Override public PageUtils userOrderPage(PageParam pageParam, OrderQueryRequest request) { pageParam.setOrderField("create_time"); pageParam.setOrder("desc"); LoginUserVO loginUserVO = LoginCheckAspect.threadLocal.get(); request.setUserFlag(loginUserVO.getCardNumber()); request.setDeleteFlag("1"); // 1未删除 PageUtils pageUtils = this.queryPage(pageParam, request); List list = pageUtils.getList(); List collect = list.stream().map(e -> { UserOrderIndexVO vo = new UserOrderIndexVO(); BeanUtils.copyProperties(e, vo); return vo; }).collect(Collectors.toList()); pageUtils.setList(collect); return pageUtils; } @Override public HotelOrderEntity findByOrderNo(String orderNo) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("order_no", orderNo); queryWrapper.last("limit 1"); return this.getOne(queryWrapper); } @Override public ConfirmOrderVO confirmOrder(ConfrimOrderRequest request) { SystemSettingEntity systemSetting = systemSettingService.get(); // 入住日期校验 Integer preDay = systemSetting.getPreDay(); if (Math.abs(ChronoUnit.DAYS.between(request.getStartTime(), LocalDate.now())) > preDay) { throw new RRException(BizCodeEnume.PARAMETER_ERROR, "只能预定" + preDay + "天之内的房源"); } // 查询房源是否充足 Long roomTypeId = request.getRoomTypeId(); RoomTypeEntity roomType = roomTypeService.myGetById(roomTypeId); if (roomType == null) { throw new RRException(BizCodeEnume.PARAMETER_ERROR, "房型不存在"); } LocalDateTime startTime = CommonUtil.localDateToTime(request.getStartTime(), TimeConstant.INTO_TIME); LocalDateTime endTime = CommonUtil.localDateToTime(request.getEndTime(), TimeConstant.OUT_TIME); long days = ChronoUnit.DAYS.between(request.getStartTime(), request.getEndTime()); int busyNum = roomRealtimeStatuService.getBusyNum(roomTypeId, startTime, endTime); int enableNum = roomType.getRoomNum() - busyNum; // 查询房型价格 BigDecimal discountPrice = roomType.getDiscountPrice(); // 查询押金 BigDecimal deposit = systemSetting.getDeposit(); BigDecimal totalAmount = discountPrice.add(deposit).multiply(new BigDecimal(Long.toString(days))); // 封装确认订单vo ConfirmOrderVO vo = new ConfirmOrderVO(); vo.setStartTime(startTime); vo.setEndTime(endTime); vo.setNums(Integer.valueOf((int)days)); vo.setRoomTypeId(roomTypeId); vo.setRoomTypeName(roomType.getTypeName()); vo.setFreeNum(enableNum >= 0 ? enableNum : 0); vo.setDiscountPrice(roomType.getDiscountPrice()); vo.setDeposit(systemSetting.getDeposit()); vo.setPriceOfWater(systemSetting.getPriceOfWater()); vo.setPriceOfElectric(systemSetting.getPriceOfElectric()); vo.setFreeQuotaOfElectric(systemSetting.getPriceOfElectric()); vo.setFreeQuotaOfWater(systemSetting.getFreeQuotaOfWater()); vo.setFreeQuotaOfElectric(systemSetting.getFreeQuotaOfElectric()); vo.setFreeTotal(systemSetting.getFreeTotal()); vo.setTotalAmount(totalAmount); return vo; } @Override public void orderPaySuccess(Long orderId, String orderNo, String realPayAmount, LocalDateTime payTime) { HotelOrderEntity order = null; if (orderId != null) { order = this.getById(orderId); } else { if (StringUtils.hasText(orderNo)) { order = this.findByOrderNo(orderNo); } } if (order == null) { log.warn("修改订单状态:订单【{}】不存在", orderId); return; } if (OrderStatuEnum.WAIT_PAY.getCode().equalsIgnoreCase(order.getOrderStatu())) { if (Math.abs(order.getPayAmount().subtract(new BigDecimal(realPayAmount)).doubleValue()) < 0.01) { // 订单最终支付金额验证成功 if (payTime == null) { order.setPayTime(LocalDateTime.now()); } else { order.setPayTime(payTime); } order.setOrderStatu(OrderStatuEnum.FINISH_PAY.getCode()); this.updateById(order); } } } @Transactional @Override public OrderSubmitVO submitOrder(SubmitOrderRequest request) { LoginUserVO loginUserVO = LoginCheckAspect.threadLocal.get(); String cardNumber = loginUserVO.getCardNumber(); // 身份校验 HotelUserEntity hotelUser = hotelUserService.findByCardNumber(cardNumber); if (hotelUser == null) { throw new RRException(BizCodeEnume.PERMISSION_DENIED, "非法用户"); } String identityType = hotelUser.getIdentityType(); if (!UserIdentityTypeEnum.STAFF_OF_TEACHER.getCode().equals(identityType)) { throw new RRException(BizCodeEnume.PERMISSION_DENIED, "非法用户-非教职工"); } SystemSettingEntity systemSetting = systemSettingService.get(); // 入住时间校验 Integer preDay = systemSetting.getPreDay(); if (Math.abs(ChronoUnit.DAYS.between(request.getEnableStartTime().toLocalDate(), LocalDate.now())) > preDay) { throw new RRException(BizCodeEnume.PARAMETER_ERROR, "只能预定" + preDay + "天之内的房源"); } // 校验房型 RoomTypeEntity roomType = roomTypeService.myGetById(request.getRoomTypeId()); if (roomType == null) { throw new RRException(BizCodeEnume.PARAMETER_ERROR, "房型不存在"); } // 金额校验 BigDecimal discountPrice = roomType.getDiscountPrice(); BigDecimal deposit = systemSetting.getDeposit(); long days = ChronoUnit.DAYS.between(request.getEnableStartTime().toLocalDate(), request.getEnableEndTime().toLocalDate()); BigDecimal totalAmount = discountPrice.add(deposit).multiply(new BigDecimal(Long.toString(days))); if (Math.abs(totalAmount.subtract(request.getPayAmount()).doubleValue()) > 0.01) { throw new RRException(BizCodeEnume.ORDER_SUBMIT_ERROR, "订单金额校验失败"); } // 锁定房源 long orderId = IdWorker.getId(); LockRoomDTO lockRoomDTO = LockRoomDTO.builder() .roomTypeId(roomType.getId()) .bizId(orderId) .startTime(request.getEnableStartTime()) .endTime(request.getEnableEndTime()) .build(); Long roomId = roomRealtimeStatuService.lockRoomByRoomTypeId(lockRoomDTO); if (roomId == null) { throw new RRException(BizCodeEnume.ORDER_SUBMIT_ERROR, "【" + roomType.getTypeName() + "】房型房源不足,锁定房源失败"); } RoomEntity room = roomService.getById(roomId); if (room == null) { throw new RRException(BizCodeEnume.UNKNOW_EXCEPTION, "系统异常,根据id查询房间失败"); } // 订单落库 HotelOrderEntity hotelOrder = new HotelOrderEntity(); BeanUtils.copyProperties(request, hotelOrder); hotelOrder.setId(orderId); hotelOrder.setOrderNo(IdWorker.getTimeId()); hotelOrder.setUserFlag(cardNumber); hotelOrder.setRoomId(roomId); hotelOrder.setRoomNo(room.getRoomNo()); hotelOrder.setRoomTypeName(roomType.getTypeName()); hotelOrder.setRoomTypeMasterImg(roomType.getTypeImage().split(",")[0]); hotelOrder.setCreateTime(LocalDateTime.now()); hotelOrder.setOrderStatu(OrderStatuEnum.WAIT_PAY.getCode()); hotelOrder.setBizStatu(OrderBillStatuEnum.WAIT_HANDLE.getCode()); hotelOrder.setDeleteFlag("1"); // 0删除、1正常 this.save(hotelOrder); // 发送延迟消息 EventMessageDTO eventMessage = EventMessageDTO.builder() .eventMessageType(EventMessageTypeEnum.PRODUCT_ORDER_NEW.name()) .accountNo(loginUserVO.getCardNumber()) .bizId(orderId) .build(); rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(), eventMessage); // 返回支付参数 String payUrl = String.format(jxnxsPayConfig.getPayUrl(), hotelOrder.getOrderNo(), request.getPayAmount()); return OrderSubmitVO.builder().orderId(hotelOrder.getId()).payUrl(payUrl).build(); } @Override public void handleOrderMessage(EventMessageDTO eventMessageDTO) { String messageType = eventMessageDTO.getEventMessageType(); try { if(EventMessageTypeEnum.PRODUCT_ORDER_NEW.name().equalsIgnoreCase(messageType)) { // 关单消息 this.closeOrder(eventMessageDTO); } } catch (Exception e) { log.error("订单消费者消费失败:{}", eventMessageDTO); throw new RRException(BizCodeEnume.MQ_CONSUME_EXCEPTION); } } @Override public void closeOrder(EventMessageDTO eventMessageDTO) { Long orderId = eventMessageDTO.getBizId(); String cardNumber = eventMessageDTO.getAccountNo(); HotelOrderEntity orderEntity = this.getById(orderId); if (orderEntity == null) { log.warn("关单:订单【{}】不存在", orderId); return; } // 判断订单状态 if (!orderEntity.getOrderStatu().equalsIgnoreCase(OrderStatuEnum.WAIT_PAY.getCode())) { log.info("关单:订单不是初始状态待支付:{}", eventMessageDTO); return; } // 未支付,向第三方支付平台查询状态 Map map = payComponent.queryOrderStatus(orderEntity.getOrderNo()); log.info("关单:订单支付状态查询结果,【{}】", map); String payStatus = map.get("status"); if ("1".equalsIgnoreCase(payStatus)) { // 已经完成支付 String amount = map.get("trade_amount"); String payTimeStr = map.get("trade_pay_time"); LocalDateTime payTime = LocalDateTime.parse(payTimeStr, TimeConstant.DEFAULT_DTF); String realPayAmount = new BigDecimal(amount).divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_UP).toString(); // 分转成元 this.orderPaySuccess(orderId, orderEntity.getOrderNo(), realPayAmount, payTime); } else { // 未支付,取消订单 this.cancelOrder(cardNumber, orderId); } } @Override public boolean cancelOrder(String userFlag, Long orderId) { HotelOrderEntity orderEntity = this.getById(orderId); if (orderEntity == null) { log.warn("取消订单:订单【{}】不存在", orderId); return false; } if (!orderEntity.getUserFlag().equalsIgnoreCase(userFlag)) { log.warn("取消订单:操作越权,非法取消订单,订单原用户【】,操作用户【】", orderEntity.getUserFlag(), userFlag); throw new RRException(BizCodeEnume.PERMISSION_DENIED, "不能取消非自己的订单"); } orderEntity.setOrderStatu(OrderStatuEnum.CANCEL.getCode()); this.updateById(orderEntity); // 释放库存 roomRealtimeStatuService.releaseByBizId(orderEntity.getId()); return true; } @Override public UserOrderDetailVO userOrderDetail(Long orderId) { HotelOrderEntity orderEntity = userGetOrderById(orderId); UserOrderDetailVO detailVO = new UserOrderDetailVO(); BeanUtils.copyProperties(orderEntity, detailVO); RoomTypeEntity roomType = roomTypeService.myGetById(orderEntity.getRoomTypeId()); detailVO.setRoomTypeUsualPrice(roomType.getUsualPrice()); detailVO.setRoomTypeDiscountPrice(roomType.getDiscountPrice()); // 订单状态为已入住之后,查询水电等相关信息 String orderStatu = orderEntity.getOrderStatu(); if (Integer.valueOf(orderStatu).intValue() >= Integer.valueOf(OrderStatuEnum.HOLD_ON.getCode()).intValue()) { HotelOrderBillEntity orderBill = orderBillService.findByOrderId(orderId); if (orderBill == null) { throw new RRException(BizCodeEnume.UNKNOW_EXCEPTION, "用户订单详情-订单状态错误"); } BeanUtils.copyProperties(orderBill, detailVO); } // 订单状态为已入住,查询密码锁等相关信息 if (OrderStatuEnum.HOLD_ON.getCode().equals(orderStatu)) { RoomDoorLockDataEntity lockData = roomDoorLockDataService.queryByOrderId(orderId); if (lockData == null) { throw new RRException(BizCodeEnume.UNKNOW_EXCEPTION, "用户订单详情-锁密码获取失败"); } detailVO.setLockRealtimePassword(lockData.getLockRealtimePassword()); detailVO.setPasswordStartTime(lockData.getStartTime()); detailVO.setPasswordEndTime(lockData.getEndTime()); } return detailVO; } @Override public OrderSubmitVO getOrderPayParam(Long orderId) { HotelOrderEntity orderEntity = userGetOrderById(orderId); if (!OrderStatuEnum.WAIT_PAY.getCode().equalsIgnoreCase(orderEntity.getOrderStatu())) { throw new RRException(BizCodeEnume.PARAMETER_ERROR, "当前订单状态为非待支付状态"); } // 支付参数 String payUrl = String.format(jxnxsPayConfig.getPayUrl(), orderEntity.getOrderNo(), orderEntity.getPayAmount()); return OrderSubmitVO.builder().orderId(orderId).payUrl(payUrl).build(); } @Override public void userDeleteOrder(Long orderId) { HotelOrderEntity order = userGetOrderById(orderId); String orderStatu = order.getOrderStatu(); // 已取消、待支付、已完成订单才可以被删除 if (!(OrderStatuEnum.CANCEL.getCode().equalsIgnoreCase(orderStatu) || OrderStatuEnum.WAIT_PAY.getCode().equalsIgnoreCase(orderStatu) || OrderStatuEnum.FINISH_PAY.getCode().equalsIgnoreCase(orderStatu))) { throw new RRException(BizCodeEnume.PERMISSION_DENIED, "当前订单不可删除"); } UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", orderId); updateWrapper.set("delete_flag", "0"); // 0表示删除 updateWrapper.last("limit 1"); this.update(null, updateWrapper); } @Override public void userCancelOrder(Long orderId) { HotelOrderEntity order = userGetOrderById(orderId); // 已取消、待支付、已完成订单才可以被删除 if (!OrderStatuEnum.WAIT_PAY.getCode().equalsIgnoreCase(order.getOrderStatu())) { throw new RRException(BizCodeEnume.PERMISSION_DENIED, "当前订单不可取消"); } UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", orderId); updateWrapper.set("order_statu", OrderStatuEnum.WAIT_PAY.getCode()); updateWrapper.last("limit 1"); this.update(null, updateWrapper); } @Transactional @Override public void userHoldOrder(Long orderId) { HotelOrderEntity order = userGetOrderById(orderId); // 支付完成状态的订单才可以办理入住 if (!OrderStatuEnum.FINISH_PAY.getCode().equalsIgnoreCase(order.getOrderStatu())) { throw new RRException(BizCodeEnume.PERMISSION_DENIED, "订单状态异常"); } // 订单有效时间校验 LocalDateTime now = LocalDateTime.now(); if (now.isAfter(order.getEnableEndTime())) { throw new RRException(BizCodeEnume.PERMISSION_DENIED, "订单超过有效时间"); } Long roomId = order.getRoomId(); // 水电抄表 => 生成结账单 orderBillService.generateBill(orderId, roomId); // 门锁密码 doorLockService.generatePassword(order); // 更新订单状态 UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", orderId); updateWrapper.set("order_statu", OrderStatuEnum.HOLD_ON.getCode()); updateWrapper.last("limit 1"); this.update(null, updateWrapper); } @Override public OrderBillHandleVO userReturnOrder(Long orderId) { HotelOrderEntity order = userGetOrderById(orderId); // 入住状态的订单才可以办理退房 if (!OrderStatuEnum.HOLD_ON.getCode().equalsIgnoreCase(order.getOrderStatu())) { throw new RRException(BizCodeEnume.PERMISSION_DENIED, "订单状态异常"); } // 结算订单 BillHandleResultDTO billHandleResult = orderBillService.calcBill(orderId); if ("1".equals(billHandleResult.getResultFlag()) && billHandleResult.getReturnFee().doubleValue() > 0) { // TODO 发起退款 } // 密码锁密码删除 roomDoorLockDataService.deleteByOrderId(orderId); // 更新订单状态 UpdateWrapper updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("id", orderId); updateWrapper.set("order_statu", OrderStatuEnum.WAIT_BILL.getCode()); updateWrapper.last("limit 1"); this.update(null, updateWrapper); OrderBillHandleVO vo = new OrderBillHandleVO(); vo.setOrderId(orderId); vo.setBillId(billHandleResult.getBillId()); vo.setFlag(billHandleResult.getResultFlag()); vo.setRefundFee(billHandleResult.getReturnFee()); vo.setSupperFee(billHandleResult.getRealFee()); return vo; } /** * 用户根据订单id获取订单 * @param orderId * @return */ private HotelOrderEntity userGetOrderById(Long orderId) { HotelOrderEntity orderEntity = this.getById(orderId); if ("0".equalsIgnoreCase(orderEntity.getDeleteFlag())) { throw new RRException(BizCodeEnume.PARAMETER_ERROR, "订单不存在,订单已删除"); } if (orderEntity == null) { throw new RRException(BizCodeEnume.PARAMETER_ERROR, "订单不存在,无效的orderId"); } LoginUserVO loginUserVO = LoginCheckAspect.threadLocal.get(); String userFlag = loginUserVO.getCardNumber(); if (!orderEntity.getUserFlag().equalsIgnoreCase(userFlag)) { throw new RRException(BizCodeEnume.PERMISSION_DENIED, "非自己的订单"); } return orderEntity; } }