Commit 27cad387 authored by 周健威's avatar 周健威

Merge branch 'master-vehicle-price' into dev-tiande

parents f801100d 326697dd
package com.github.wxiaoqi.security.common.vo;
import com.github.wxiaoqi.security.common.util.Query;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
......@@ -10,5 +11,7 @@ public class PageParam {
@ApiModelProperty("每页限制")
Integer limit;
public Query initQuery() {
return new Query(this);
}
}
......@@ -158,4 +158,18 @@ public class ShuntApply implements Serializable {
@ApiModelProperty(value = "超时时间戳", hidden = true )
private Long overTime;
/**
* 开始城市编码
*/
@Column(name = "start_city_code")
@ApiModelProperty(value = "开始城市编码")
private Integer startCityCode;
/**
* 结束城市编码
*/
@Column(name = "end_city_code")
@ApiModelProperty(value = "结束城市编码", hidden = true )
private Integer endCityCode;
}
......@@ -5,6 +5,7 @@ import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFacto
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
......@@ -23,37 +24,71 @@ public class RabbitDelayConfig {
public static final String ORDER_CANCEL_EXC = "order_cancel_delay_exchange";
public static final String ORDER_CANCEL_QUE = "order_cancel_delay_queue";
public static final String ORDER_CANCEL_KEY = "order_cancel_delay_key";
public static final String APPLY_CANCEL_EXC = "apply_cancel_delay_exchange";
public static final String APPLY_CANCEL_QUE = "apply_cancel_delay_queue";
public static final String APPLY_CANCEL_KEY = "apply_cancel_delay_key";
/**
* 延时队列交换机
* 注意这里的交换机类型:CustomExchange
* @return
*/
@Bean
public CustomExchange delayExchange(){
@Bean(ORDER_CANCEL_EXC)
public CustomExchange orderDelayExchange(){
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange(ORDER_CANCEL_EXC,"x-delayed-message",true, false, args);
}
/**
* 延时队列交换机
* 注意这里的交换机类型:CustomExchange
* @return
*/
@Bean(APPLY_CANCEL_EXC)
public CustomExchange applyDelayExchange(){
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange(APPLY_CANCEL_EXC,"x-delayed-message",true, false, args);
}
/**
* 延时队列
* @return
*/
@Bean
public Queue delayQueue(){
@Bean(ORDER_CANCEL_QUE)
public Queue orderDelayQueue(){
return new Queue(ORDER_CANCEL_QUE,true);
}
/**
* 延时队列
* @return
*/
@Bean(APPLY_CANCEL_QUE)
public Queue applyDelayQueue(){
return new Queue(APPLY_CANCEL_QUE,true);
}
/**
* 给延时队列绑定交换机
* @return
*/
@Bean
public Binding cfgDelayBinding(Queue cfgDelayQueue, CustomExchange cfgUserDelayExchange){
@Bean("cfgDelayBinding")
public Binding cfgDelayBinding(@Qualifier(ORDER_CANCEL_QUE) Queue cfgDelayQueue, @Qualifier(ORDER_CANCEL_EXC) CustomExchange cfgUserDelayExchange ){
return BindingBuilder.bind(cfgDelayQueue).to(cfgUserDelayExchange).with(ORDER_CANCEL_KEY).noargs();
}
/**
* 给延时队列绑定交换机
* @return
*/
@Bean("acqDelayBinding")
public Binding acqDelayBinding(@Qualifier(APPLY_CANCEL_QUE) Queue acqDelayQueue, @Qualifier(APPLY_CANCEL_EXC) CustomExchange acqUserDelayExchange){
return BindingBuilder.bind(acqDelayQueue).to(acqUserDelayExchange).with(APPLY_CANCEL_KEY).noargs();
}
// @Bean
// public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
// SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
......
......@@ -3,8 +3,10 @@ package com.xxfc.platform.order.mqhandler;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.github.wxiaoqi.security.common.util.process.ResultCode;
import com.rabbitmq.client.Channel;
import com.xxfc.platform.order.biz.ShuntApplyBiz;
import com.xxfc.platform.order.biz.inner.OrderCancelBiz;
import com.xxfc.platform.order.entity.BaseOrder;
import com.xxfc.platform.order.entity.ShuntApply;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
......@@ -14,6 +16,7 @@ import org.springframework.stereotype.Component;
import java.io.IOException;
import java.time.LocalDateTime;
import static com.xxfc.platform.order.config.RabbitDelayConfig.APPLY_CANCEL_QUE;
import static com.xxfc.platform.order.config.RabbitDelayConfig.ORDER_CANCEL_QUE;
/**
......@@ -28,6 +31,9 @@ public class RabbitConsumer {
@Autowired
OrderCancelBiz orderCancelBiz;
@Autowired
ShuntApplyBiz shuntApplyBiz;
/**
* 默认情况下,如果没有配置手动ACK, 那么Spring Data AMQP 会在消息消费完毕后自动帮我们去ACK
......@@ -60,4 +66,45 @@ public class RabbitConsumer {
channel.basicRecover(true);
}
}
/**
* 默认情况下,如果没有配置手动ACK, 那么Spring Data AMQP 会在消息消费完毕后自动帮我们去ACK
* 存在问题:如果报错了,消息不会丢失,但是会无限循环消费,一直报错,如果开启了错误日志很容易就吧磁盘空间耗完
* 解决方案:手动ACK,或者try-catch 然后在 catch 里面将错误的消息转移到其它的系列中去
* spring.rabbitmq.listener.simple.acknowledge-mode = manual
*/
@RabbitListener(queues = APPLY_CANCEL_QUE)
public void applyCancelReceiveDealy(ShuntApply shuntApply, Message message, Channel channel) throws IOException {
log.info("===============接收队列接收消息====================");
log.info("接收时间:{},接受内容:{}", LocalDateTime.now(), shuntApply.toString());
//通知 MQ 消息已被接收,可以ACK(从队列中删除)了
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
try {
//设置自动取消标识
// baseOrder.setCancelReason("超时未付款,系统自动取消订单");
//orderCancelBiz.cancel(baseOrder, null);
//判断updateTime 是否一致(即乐观锁)
ShuntApply dbApply = shuntApplyBiz.selectById(shuntApply.getId());
if(dbApply.getUpdTime().equals(shuntApply.getUpdTime())) {
shuntApplyBiz.updateSelectiveById(new ShuntApply(){{
setId(shuntApply.getId());
setStatus(ShuntApply.STATUS_AUTOCNL);
}});
}
}catch (BaseException e) {
if(ResultCode.DB_OPERATION_FAIL_CODE == e.getStatus()) {
log.info("取消操作被取消;订单id:"+ shuntApply.getId());
}
}catch (Exception e) {
log.error("============消费失败,尝试消息补发再次消费!==============");
log.error(e.getMessage());
/**
* basicRecover方法是进行补发操作,
* 其中的参数如果为true是把消息退回到queue但是有可能被其它的consumer(集群)接收到,
* 设置为false是只补发给当前的consumer
*/
channel.basicRecover(true);
}
}
}
package com.xxfc.platform.order.mqhandler;
import com.xxfc.platform.order.entity.BaseOrder;
import com.xxfc.platform.order.entity.ShuntApply;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -8,8 +9,7 @@ import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import static com.xxfc.platform.order.config.RabbitDelayConfig.ORDER_CANCEL_EXC;
import static com.xxfc.platform.order.config.RabbitDelayConfig.ORDER_CANCEL_KEY;
import static com.xxfc.platform.order.config.RabbitDelayConfig.*;
/**
* rabbitMq生产者类
......@@ -39,5 +39,22 @@ public class RabbitProduct {
);
log.info("{}ms后执行", delayTime);
}
public void sendApplyDelayMessage(ShuntApply shuntApply, Long delayTime) {
//这里的消息可以是任意对象,无需额外配置,直接传即可
log.info("===============延时队列生产消息====================");
log.info("发送时间:{},发送内容:{}", LocalDateTime.now(), shuntApply.toString());
this.rabbitTemplate.convertAndSend(
APPLY_CANCEL_EXC,
APPLY_CANCEL_KEY,
shuntApply,
message -> {
//注意这里时间可以使long,而且是设置header
message.getMessageProperties().setHeader("x-delay", delayTime);
return message;
}
);
log.info("{}ms后执行", delayTime);
}
}
......@@ -51,7 +51,7 @@ public class ShuntApplyController extends BaseController<ShuntApplyBiz, ShuntApp
@RequestMapping(value = "/addApply", method = RequestMethod.POST)
@ResponseBody
@ApiOperation(value = "申请列表")
@ApiOperation(value = "添加申请")
public ObjectRestResponse addApply(@RequestBody ShuntApply shuntApply) {
//查询列表数据
......
package com.xxfc.platform.order.rest.background;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.common.context.BaseContextHandler;
import com.github.wxiaoqi.security.admin.feign.rest.UserRestInterface;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.github.wxiaoqi.security.common.rest.BaseController;
import com.github.wxiaoqi.security.common.util.OrderUtil;
import com.github.wxiaoqi.security.common.util.Query;
import com.github.wxiaoqi.security.common.util.process.ResultCode;
import com.github.wxiaoqi.security.common.vo.PageDataVO;
import com.github.wxiaoqi.security.common.vo.PageParam;
......@@ -16,23 +18,31 @@ import com.xxfc.platform.order.biz.ShuntApplyBiz;
import com.xxfc.platform.order.biz.inner.OrderCalculateBiz;
import com.xxfc.platform.order.entity.BaseOrder;
import com.xxfc.platform.order.entity.ShuntApply;
import com.xxfc.platform.order.mqhandler.RabbitProduct;
import com.xxfc.platform.order.pojo.order.RentVehicleBO;
import com.xxfc.platform.order.pojo.order.add.BgAddRentDTO;
import com.xxfc.platform.order.service.OrderRentVehicleService;
import com.xxfc.platform.vehicle.constant.VehicleBookRecordStatus;
import com.xxfc.platform.vehicle.entity.VehicleModel;
import com.xxfc.platform.vehicle.feign.VehicleFeign;
import com.xxfc.platform.vehicle.pojo.RentVehicleBookDTO;
import com.xxfc.platform.vehicle.pojo.vo.UsableVeicleVO;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.jsoup.helper.DataUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static com.github.wxiaoqi.security.common.constant.CommonConstants.SYS_FALSE;
import static com.github.wxiaoqi.security.common.constant.CommonConstants.SYS_TRUE;
import static com.xxfc.platform.order.entity.ShuntApply.STATUS_CONFIRM;
import static com.xxfc.platform.order.entity.ShuntApply.STATUS_CRT;
@RestController
@RequestMapping("/background/shuntApply")
public class BgShuntApplyController extends BaseController<ShuntApplyBiz, ShuntApply> {
public class BgShuntApplyController extends BaseController<ShuntApplyBiz, ShuntApply> implements UserRestInterface {
@Autowired
......@@ -50,10 +60,92 @@ public class BgShuntApplyController extends BaseController<ShuntApplyBiz, ShuntA
@Autowired
OrderRentVehicleService orderRentVehicleService;
@RequestMapping(value = "/confirmApply", method = RequestMethod.POST)
@Autowired
RabbitProduct rabbitProduct;
@Override
public UserFeign getUserFeign() {
return this.userFeign;
}
@RequestMapping(value = "/steward/list", method = RequestMethod.GET)
@ResponseBody
@ApiOperation(value = "根据城市查询待确认申请列表")
public ObjectRestResponse<List<StewardShuntApply>> stewardList(ListDTO dto) {
//获取该城市的申请最大和最小时间
//获取该店铺
List<ShuntApply> list = baseBiz.selectByWeekend(w -> {
w.andEqualTo(ShuntApply::getStartCityCode, dto.getCityCode());
w.andEqualTo(ShuntApply::getStatus, STATUS_CRT);
return w;
}, " crt_time desc ");
List<StewardShuntApply> returnList = CollUtil.newArrayList();
Integer companyId = getAdminUserInfo().getCompanyId();
if(CollUtil.isNotEmpty(list)) {
//group by 车型
//List<Integer> modelList = list.parallelStream().map(ShuntApply::getModelId).distinct().collect(Collectors.toList());
//.collect(Collectors.groupingBy(ShuntApply::getModelId));
Map<Integer, List<ShuntApply>> modelMap = list.parallelStream().collect(Collectors.groupingBy(ShuntApply::getModelId));
Long startTime = list.parallelStream().map(ShuntApply::getStartTime).min(Long::compareTo).get();
Long endTime = list.parallelStream().map(ShuntApply::getEndTime).max(Long::compareTo).get();
for (Integer modelId : modelMap.keySet()) {
RentVehicleBookDTO rbd = new RentVehicleBookDTO();
rbd.setModelId(modelId);
rbd.setParkBranchCompanyId(companyId);
rbd.setStartCompanyId(companyId);
rbd.setEndCompanyId(companyId);
rbd.setBookStartDate(DateUtil.date(startTime).toDateStr());
rbd.setBookEndDate(DateUtil.date(endTime).toDateStr());
rbd.setBookStartDateTime(DateUtil.date(startTime).toTimeStr());
rbd.setBookEndDateTime(DateUtil.date(endTime).toTimeStr());
//查询本公司时间段内可用车辆
ObjectRestResponse<PageDataVO<UsableVeicleVO>> usableVehiclePage = vehicleFeign.applyUsableVehicle(rbd);
if(CollUtil.isNotEmpty(usableVehiclePage.getData().getData())) {
modelMap.get(modelId).forEach( shuntApply -> {
List<UsableVeicleVO> vehicles = CollUtil.newArrayList();
usableVehiclePage.getData().getData().forEach(usableVeicleVO -> {
//判断是否预定时间内可租
usableVeicleVO.setAllowRent(SYS_TRUE);
//如果"必须在这时间后预定" 存在 并且 比预定开始时间 要晚,那么不允许预定
if(null != usableVeicleVO.getToLiftMustAfterDate() && DateUtil.date(shuntApply.getStartTime()).isBeforeOrEquals(usableVeicleVO.getToLiftMustAfterDate())) {
usableVeicleVO.setAllowRent(SYS_FALSE);
}
//如果"必须在这时间前预定" 存在 并且 比预定结束时间 要早,那么不允许预定
if(null != usableVeicleVO.getToReturnMustBeforeDate() && DateUtil.date(shuntApply.getEndTime()).isAfterOrEquals(usableVeicleVO.getToReturnMustBeforeDate())) {
usableVeicleVO.setAllowRent(SYS_FALSE);
}
if(SYS_TRUE.equals(usableVeicleVO.getAllowRent())) {
vehicles.add(BeanUtil.toBean(usableVeicleVO, UsableVeicleVO.class));
}
});
if(CollUtil.isNotEmpty(vehicles)) {
StewardShuntApply stewardShuntApply = BeanUtil.toBean(shuntApply, StewardShuntApply.class);
stewardShuntApply.setVehicles(vehicles);
returnList.add(stewardShuntApply);
}
});
}
}
}
return ObjectRestResponse.succ(returnList);
}
@RequestMapping(value = "/steward/confirmApply", method = RequestMethod.POST)
@ResponseBody
@ApiOperation(value = "后台确认申请")
public ObjectRestResponse confirmApply(@RequestBody confirmApplyDTO dto) {
public ObjectRestResponse stewardConfirmApply(@RequestBody ConfirmApplyDTO dto) {
//查询列表数据
if (StrUtil.isBlank(getCurrentUserId())) {
......@@ -85,14 +177,27 @@ public class BgShuntApplyController extends BaseController<ShuntApplyBiz, ShuntA
shuntApply.setVehicleId(dto.getVehicleId());
shuntApply.setStatus(STATUS_CONFIRM);
shuntApply.setOrderNo(detail.getOrder().getNo());
baseBiz.updateSelectiveById(shuntApply);
shuntApply.setOverTime(DateUtil.offsetHour(DateUtil.date(), 1).getTime());
baseBiz.updateSelectiveByIdRe(shuntApply);
rabbitProduct.sendApplyDelayMessage(baseBiz.selectById(shuntApply.getId()), 1000L * 3601L);
return ObjectRestResponse.succ();
}
@Data
public static class confirmApplyDTO {
public static class ConfirmApplyDTO {
Integer applyId;
String vehicleId;
}
@Data
public static class ListDTO {
Integer cityCode;
}
@Data
public static class StewardShuntApply extends ShuntApply {
List<UsableVeicleVO> vehicles;
}
}
\ No newline at end of file
......@@ -13,6 +13,7 @@ import com.xxfc.platform.vehicle.pojo.dto.VehicleModelCalendarPriceDTO;
import com.xxfc.platform.vehicle.pojo.vo.AccompanyingItemVo;
import com.xxfc.platform.vehicle.pojo.vo.BranComanyLeaderVo;
import com.xxfc.platform.vehicle.pojo.vo.BranchCompanyListVO;
import com.xxfc.platform.vehicle.pojo.vo.UsableVeicleVO;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType;
......@@ -243,4 +244,7 @@ public interface VehicleFeign {
@RequestMapping(value = "/vehicleInfo/selectByCompanyIds", method = RequestMethod.GET)
public ObjectRestResponse<List<Vehicle>> vehicleSelectByCompanyIds(@RequestParam("companyIds") String companyIds);
@RequestMapping(value = "/vehicleInfo/rent/apply/usable-vehicle", method = RequestMethod.POST)
public ObjectRestResponse<PageDataVO<UsableVeicleVO>> applyUsableVehicle(@RequestBody RentVehicleBookDTO rbd);
}
......@@ -195,6 +195,47 @@ public class RentVehicleController extends BaseController<VehicleBiz> implements
return ObjectRestResponse.succ(uvmvpdvs);
}
@ApiOperation("调车申请列表-可用车辆查询")
@RequestMapping(value = "/rent/apply/usable-vehicle", method = RequestMethod.POST)
@IgnoreUserToken
public ObjectRestResponse<PageDataVO<UsableVeicleVO>> applyUsableVehicle(@RequestBody RentVehicleBookDTO rbd) {
// rbd.setModelId(data.getVehicleModel().getId());
// rbd.setParkBranchCompanyId(dto.getStartCompanyId());
// rbd.setStartCompanyId(dto.getStartCompanyId());
// rbd.setEndCompanyId(dto.getEndCompanyId());
// rbd.setBookStartDate(dto.getStartDate());
// rbd.setBookEndDate(dto.getEndDate());
// rbd.setBookStartDateTime(dto.getStartDateTime());
// rbd.setBookEndDateTime(dto.getEndDateTime());
rbd.setRecordIntersection(Boolean.TRUE);
rbd.setLimit(100);
rbd.setPage(1);
rbd.setYearNo4Where(Boolean.TRUE);
//查询可车辆信息
PageDataVO<UsableVeicleVO> pageDataVO = vehicleBiz.searchUsableVehicle(rbd);
if(CollUtil.isNotEmpty(pageDataVO.getData())) {
for (UsableVeicleVO usableVeicleVO : pageDataVO.getData()) {
//判断是否预定时间内可租
usableVeicleVO.setAllowRent(SYS_TRUE);
//如果"必须在这时间后预定" 存在 并且 比预定开始时间 要晚,那么不允许预定
if(null != usableVeicleVO.getToLiftMustAfterDate() && DateUtil.parse(rbd.getBookStartDateTime()).isBeforeOrEquals(usableVeicleVO.getToLiftMustAfterDate())) {
usableVeicleVO.setAllowRent(SYS_FALSE);
}
//如果"必须在这时间前预定" 存在 并且 比预定结束时间 要早,那么不允许预定
if(null != usableVeicleVO.getToReturnMustBeforeDate() && DateUtil.parse(rbd.getBookEndDateTime()).isAfterOrEquals(usableVeicleVO.getToReturnMustBeforeDate())) {
usableVeicleVO.setAllowRent(SYS_FALSE);
}
}
}
return ObjectRestResponse.succ(pageDataVO);
}
/**
* 后台查询可用车辆(车型)
*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment