package com.xxfc.platform.vehicle.biz;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.admin.feign.dto.UserDTO;
import com.github.wxiaoqi.security.admin.feign.rest.UserRestInterface;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
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.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xxfc.platform.vehicle.common.CustomIllegalParamException;
import com.xxfc.platform.vehicle.common.RestResponse;
import com.xxfc.platform.vehicle.constant.ConstantType;
import com.xxfc.platform.vehicle.constant.RedisKey;
import com.xxfc.platform.vehicle.constant.ResCode.ResCode;
import com.xxfc.platform.vehicle.constant.VehicleBookRecordStatus;
import com.xxfc.platform.vehicle.constant.VehicleStatus;
import com.xxfc.platform.vehicle.entity.*;
import com.xxfc.platform.vehicle.mapper.BookRecordAccItemMapper;
import com.xxfc.platform.vehicle.mapper.VehicleBookInfoMapper;
import com.xxfc.platform.vehicle.mapper.VehicleMapper;
import com.xxfc.platform.vehicle.pojo.*;
import com.xxfc.platform.vehicle.pojo.dto.VehiclePlanDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.weekend.WeekendSqls;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class VehicleBiz extends BaseBiz<VehicleMapper, Vehicle> implements UserRestInterface {

    public static final DateTimeFormatter DEFAULT_DATE_TIME_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd");
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
    public static final DateTimeFormatter YEARMONTH_DATE_TIME_FORMATTER = DateTimeFormat.forPattern("yyyy-MM");
    /**
     * 允许查询预定信息的最大月份数
     */
    public static final Integer MAX_MONTH_COUNT_BOOKED_INFO_QUERY = 3;

    @Autowired
    private VehicleBookInfoMapper vehicleBookInfoMapper;
    @Autowired
    private VehicleBookRecordBiz vehicleBookRecordBiz;
    @Autowired
    private ConstantBiz constantBiz;
    @Autowired
    private BranchCompanyBiz branchCompanyBiz;
    @Autowired
    private BookRecordAccItemMapper bookRecordAccItemMapper;
    @Autowired
    private RedisTemplate customRedisTemplate;
    @Autowired
    private VehicleBookHourInfoBiz vehicleBookHourInfoBiz;
    @Autowired
    UserFeign userFeign;
    @Autowired
    private VehicleBookInfoBiz vehicleBookInfoBiz;


    @Override
    public UserFeign getUserFeign() {
        return userFeign;
    }

    @Value("${vehicle.baseUploadPath}")
    private String baseUploadPath;
    @Value("${vehicle.fristMileage}")
    private Integer fristMileage;

    /**
     * 每批次最大更、插入车辆最大条目数
     */
    public static final Integer MAX_BATCH_SIZE_VEHICLE = 1;//使用了悲观锁，不开放批量更新

    /**
     * 写入上传文件，返回相对路径
     *
     * @param file
     * @return
     */
    public RestResponse<String> uploadDrivingLicense(MultipartFile file) throws Exception {
        //创建本日存放目录
        DateTime now = DateTime.now();
        String dirPathToday = File.separator + now.toString(DEFAULT_DATE_TIME_FORMATTER);
        String redisNoKey = RedisKey.UPLOAD_FILE_NO_PREFIX + now.toString(DEFAULT_DATE_TIME_FORMATTER);
        Long no = customRedisTemplate.opsForValue().increment(redisNoKey);
        if (no.equals(1l)) {
            customRedisTemplate.expire(redisNoKey, 1, TimeUnit.DAYS);
        }
        String fileName = file.getOriginalFilename();
        String realFileRelPath = dirPathToday + File.separator + no + fileName.substring(fileName.lastIndexOf("."));
        //文件存放路径
        String filePath = baseUploadPath + realFileRelPath;
        //将文件写入指定位置
        FileUtils.copyInputStreamToFile(file.getInputStream(), new File(filePath));
        return RestResponse.suc(realFileRelPath);
    }

    /**
     * 下载行驶证图片
     *
     * @param realFileRelPath
     * @return
     * @throws Exception
     */
    public ResponseEntity<byte[]> downloadDrivingLicense(String realFileRelPath) throws Exception {

        String filePath = baseUploadPath + realFileRelPath;
        File file = new File(filePath);//新建一个文件
        HttpHeaders headers = new HttpHeaders();//http头信息
        String downloadFileName = new String(file.getName());//设置编码
        headers.setContentDispositionFormData("attachment", downloadFileName);
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file), headers, HttpStatus.CREATED);
    }

    //    @Cache(key = "vehical")
    public Vehicle get(String id) {
        Vehicle vehicle = mapper.selectByPrimaryKey(id);
        return vehicle;
    }

    /**
     * 获取相关预订记录
     *
     * @param vehicle
     * @param yearMonth
     * @return
     */
    public VehicleBookInfo getByVehicleIdAndYearMonth(String vehicle, String yearMonth) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("vehicle", vehicle);
        params.put("yearMonth", yearMonth);
        List<VehicleBookInfo> vehicleBookInfoList = vehicleBookInfoMapper.getByVehicleIdAndYearMonth(params);
        return CollectionUtils.isEmpty(vehicleBookInfoList) ? null : vehicleBookInfoList.get(0);
    }

    /**
     * 获取相关预订记录
     *
     * @param vehicle
     * @return
     */
    public List<VehicleBookInfo> getByVehicleIdAndYearMonth(String vehicle) {
        Map<String, Object> params = Maps.newHashMap();
        params.put("vehicle", vehicle);
        params.put("yearMonths",
                Lists.newArrayList(
                        DateTime.now().toString(YEARMONTH_DATE_TIME_FORMATTER),
                        DateTime.now().plusMonths(1).toString(YEARMONTH_DATE_TIME_FORMATTER),
                        DateTime.now().plusMonths(2).toString(YEARMONTH_DATE_TIME_FORMATTER)
                )
        );
        List<VehicleBookInfo> vehicleBookInfoList = vehicleBookInfoMapper.getByVehicleIdAndYearMonths(params);
        return vehicleBookInfoList;
    }

    /**
     * 检查常量是否合法
     */
    private void checkIfConstantValid(AddOrUpdateVehicleVo addOrUpdateVehicleVo) {
        //检查常量是否合法
        if (addOrUpdateVehicleVo.getUseType() != null && addOrUpdateVehicleVo.getUseType() > 0) {
            constantBiz.checkIfExists(ConstantType.VEHICLE_USE.getCode(), addOrUpdateVehicleVo.getUseType());
        }
        if (addOrUpdateVehicleVo.getBrand() != null && addOrUpdateVehicleVo.getBrand() > 0) {
            constantBiz.checkIfExists(ConstantType.VEHICLE_BRAND.getCode(), addOrUpdateVehicleVo.getBrand());
        }
    }

    /**
     * 增加车辆
     *
     * @param addOrUpdateVehicleVoList
     */
    @Transactional
    public RestResponse add(List<AddOrUpdateVehicleVo> addOrUpdateVehicleVoList) throws Exception {

        if (addOrUpdateVehicleVoList.size() > MAX_BATCH_SIZE_VEHICLE) {
            throw new BaseException(ResultCode.ADD_VEHICLE_MORE);
        }

        for (AddOrUpdateVehicleVo addOrUpdateVehicleVo : addOrUpdateVehicleVoList) {

            Vehicle vehicle = new Vehicle();
            BeanUtilsBean.getInstance().copyProperties(vehicle, addOrUpdateVehicleVo);

            if (addOrUpdateVehicleVo.getParkBranchCompanyId() == null) {
                throw new BaseException(ResultCode.RET_COMPANY_CAN_NOT_BE_NULL);
            }

            vehicle.setId(UUID.randomUUID().toString());
//            检查车牌或者编码是否已存在，已存在则返回失败
            List<Vehicle> exitsVehicles = lockByCode(addOrUpdateVehicleVo);
            if (CollectionUtils.isNotEmpty(exitsVehicles)) {
                return RestResponse.codeAndMessage(ResCode.VEHICLE_INFO_SAME_NUM_PLATE_EXISTS.getCode(),
                        ResCode.VEHICLE_INFO_SAME_NUM_PLATE_EXISTS.getDesc());

//                if(addOrUpdateVehicleVo.getCode()!= null &&
//                        addOrUpdateVehicleVo.getCode().equals(exitsVehicles.get(0).getCode())){
//                    return RestResponse.code(ResCode.VEHICLE_INFO_CODE_EXIST.getCode());
//                }else{
//                    return RestResponse.code(ResCode.VEHICLE_INFO_SAME_NUM_PLATE_EXISTS.getCode());
//                }
            }
            mapper.insertSelective(vehicle);
        }
        return RestResponse.suc();
    }

    /**
     * 此方法必须置于事务中以使锁生效
     * 符合查询条件记录的修改活且相关记录的锁获取操作将被锁定
     *
     * @param addOrUpdateVehicleVo
     * @return 锁定成功返回
     */
    private List<Vehicle> lockByCode(AddOrUpdateVehicleVo addOrUpdateVehicleVo) throws CustomIllegalParamException {
//        if(addOrUpdateVehicleVo.getCode() == null){
//            return null;
//        }

        if (addOrUpdateVehicleVo.getNumberPlate() == null) {
            throw new BaseException(ResultCode.NUMBER_PLAT_CAN_NOT_BE_NULL);
        }
        List<Vehicle> vehicles = mapper.lockByCode(addOrUpdateVehicleVo);
        return vehicles;
    }

    /**
     * 修改汽车信息
     *
     * @param addOrUpdateVehicleVoList
     * @return
     */
    @Transactional
    public RestResponse update(List<AddOrUpdateVehicleVo> addOrUpdateVehicleVoList) throws Exception {
        if (addOrUpdateVehicleVoList.size() > MAX_BATCH_SIZE_VEHICLE) {
            throw new BaseException(ResultCode.ADD_VEHICLE_MORE);
        }

        for (AddOrUpdateVehicleVo addOrUpdateVehicleVo : addOrUpdateVehicleVoList) {
            Vehicle vehicle = new Vehicle();
            BeanUtilsBean.getInstance().copyProperties(vehicle, addOrUpdateVehicleVo);
            if (addOrUpdateVehicleVo.getParkBranchCompanyId() == null) {
                throw new BaseException(ResultCode.RET_COMPANY_CAN_NOT_BE_NULL);
            }

            //悲观锁，检查是否已存在车牌或编码
            List<Vehicle> exitsVehicles = lockByCode(addOrUpdateVehicleVo);
            if (CollectionUtils.isNotEmpty(exitsVehicles)) {
                for (Vehicle exitsVehicle : exitsVehicles) {
                    if (exitsVehicle.getId().equals(addOrUpdateVehicleVo.getId())) {
                        continue;
                    }
                    return RestResponse.codeAndMessage(ResCode.VEHICLE_INFO_SAME_NUM_PLATE_EXISTS.getCode(),
                            ResCode.VEHICLE_INFO_SAME_NUM_PLATE_EXISTS.getDesc());

//                    if(addOrUpdateVehicleVo.getCode()!= null &&
//                            addOrUpdateVehicleVo.getCode().equals(exitsVehicles.get(0).getCode())){
//                        return RestResponse.code(ResCode.VEHICLE_INFO_CODE_EXIST.getCode());
//                    }else{
//                        return RestResponse.code(ResCode.VEHICLE_INFO_SAME_NUM_PLATE_EXISTS.getCode());
//                    }
                }
            }
            mapper.updateByPrimaryKeySelective(vehicle);
        }
        return RestResponse.suc();
    }

    /**
     * 废弃车辆（状态设置为废弃）
     *
     * @param idList
     * @return
     */
    public RestResponse discard(List<String> idList) {
        if (idList.size() > MAX_BATCH_SIZE_VEHICLE) {
            throw new BaseException(ResultCode.ADD_VEHICLE_MORE);
        }
        Map<String, Object> params = Maps.newHashMap();
        params.put("idList", idList);
        params.put("status", VehicleStatus.DISCARD.getCode());
        mapper.updateStatusById(params);
        return RestResponse.suc();
    }

//    /**
//     * 申请汽车预定（内部）
//     * 检查是否可预定，修改相关预定记录状态
//     * @param userId
//     * @param bookVehicleVo
//     * @return
//     */
//    @Transactional
//    public VehicleBookRecord applyVehicle4Employee(Integer userId, BookVehicleVO bookVehicleVo, String userName) throws Exception{
//        //检查车辆信息是否合法
//        checkIfVehicleExists(bookVehicleVo.getVehicleId());
//        //提取日期和相应的预定目标日期
//        Map<String,List<String>> yearMonthAndDate = Maps.newHashMap();
//        //预定时间不能为空
//        if(StringUtils.isBlank(bookVehicleVo.getBookStartDate()) || StringUtils.isBlank(bookVehicleVo.getBookEndDate())){
//            throw new BaseException(ResultCode.DATE_TIME_IS_NULL);
//        }
//        String[] bookStartDateArray = bookVehicleVo.getBookStartDate().split(" ");
//        String[] bookEndDateArray = bookVehicleVo.getBookEndDate().split(" ");
//
//        DateTime startDay =DateTime.parse(bookStartDateArray[0],DEFAULT_DATE_TIME_FORMATTER);
//        DateTime endDay =DateTime.parse(bookEndDateArray[0], DEFAULT_DATE_TIME_FORMATTER);
//
//
//        //转换日期范围为列表，并检查是否合法
//        fillDateList4DatePeriod(yearMonthAndDate,startDay,endDay);
//        if(yearMonthAndDate.size()>3){//连续的日期最多夸3个月
//            throw new BaseException(ResultCode.ONLY_BOOK_TWO_MONTH);
//        }
//        Map<String, Integer> map = vehicleBookHourInfoBiz.getPredictableHours(bookVehicleVo.getBookStartDate(), bookVehicleVo.getBookEndDate());
//        //检查车辆是否可以预定
//        for(Map.Entry<String,List<String>> entry:yearMonthAndDate.entrySet()){
//            Boolean rsEach = applyVehicle4EmployeePerMonth(bookVehicleVo.getVehicleId(),entry.getValue(),entry.getKey(), map);
//            if(Boolean.FALSE.equals(rsEach)){
//                throw new BaseException(ResultCode.VEHICLE_IS_BOOKED);
//            }
//        }
//
//        //加入预定申请记录
//        VehicleBookRecord vehicleBookRecord = new VehicleBookRecord();
//        vehicleBookRecord.setVehicleId(bookVehicleVo.getVehicleId());
//        vehicleBookRecord.setBookType(bookVehicleVo.getBookType());
//        vehicleBookRecord.setStatus(VehicleBookRecordStatus.APPLY.getCode());
//        vehicleBookRecord.setBookUser(userId);
//        vehicleBookRecord.setBookUserName(userName);
//        vehicleBookRecord.setBookStartDate(DateTime.
//                parse(bookStartDateArray[0],DEFAULT_DATE_TIME_FORMATTER).toDate());
//        vehicleBookRecord.setBookEndDate(DateTime.
//                parse(bookEndDateArray[0],DEFAULT_DATE_TIME_FORMATTER).toDate());
//        vehicleBookRecord.setLiftAddr(bookVehicleVo.getLiftAddr());
//        vehicleBookRecord.setRemark(bookVehicleVo.getRemark());
//        vehicleBookRecord.setDestination(bookVehicleVo.getDestination());
//        vehicleBookRecord.setLiftCompany(bookVehicleVo.getLiftCompany());
//        vehicleBookRecord.setRetCompany(bookVehicleVo.getRetCompany());
//        vehicleBookRecord.setOrderNo(bookVehicleVo.getOrderNo());
//        vehicleBookRecordBiz.save(vehicleBookRecord);
////        //添加预定时间记录
////
////            Map<String, Integer> map = vehicleBookHourInfoBiz.getPredictableHours(bookVehicleVo.getBookStartDate(), bookVehicleVo.getBookEndDate());
////            for(Map.Entry<String, Integer> entry : map.entrySet()) {
////                VehicleBookHourInfoDto vehicleBookHourInfoDto = new VehicleBookHourInfoDto();
////                vehicleBookHourInfoDto.setVehicleId(bookVehicleVo.getVehicleId());
////                vehicleBookHourInfoDto.setYearMonthDay(entry.getKey());
////                vehicleBookHourInfoDto.setBookedHour(entry.getValue());
////                vehicleBookHourInfoBiz.save(vehicleBookHourInfoDto);
////            }
//
//        //修改相关车辆预定记录
//        Boolean hasSuc = bookedVehicle(bookVehicleVo);
//        if(!hasSuc){
//            throw new BaseException(ResultCode.BOOKED_FAILED_CODE);
//        }
//
//        //添加随车物品
//        List<Map<String,Object>> params = Lists.newArrayList();
//        if(MapUtils.isNotEmpty(bookVehicleVo.getSelectedAccItem())){
//            for(Map.Entry<Integer,Integer> idAndAmount : bookVehicleVo.getSelectedAccItem().entrySet()){
//                Map<String,Object> row = Maps.newHashMap();
//                row.put("id",idAndAmount.getKey());
//                row.put("amount",idAndAmount.getValue());
//                row.put("bookRecordId",vehicleBookRecord.getId());
//                params.add(row);
//            }
//            bookRecordAccItemMapper.batchAdd(params);
//        }
//
//        return vehicleBookRecord;
//    }

    /**
     * 不需要审核
     *
     * @param userId
     * @param bookVehicleVo
     * @param userName
     * @return
     * @throws Exception
     */
    @Transactional
    public VehicleBookRecord applyVehicle(Integer userId, BookVehicleVO bookVehicleVo, String userName) throws Exception {
        log.info("预定车辆参数：userId = {}, bookVehicleVo = {},username = {}", userId, bookVehicleVo, userName);
        //检查车辆信息是否合法
        Vehicle vehicle = mapper.selectByPrimaryKey(bookVehicleVo.getVehicleId());
        if (vehicle == null) {
            throw new BaseException(ResCode.VEHICLE_DEPARTURE_VEHICLE_UNEXIST.getDesc(), ResCode.VEHICLE_DEPARTURE_VEHICLE_UNEXIST.getCode());
        }
        if (vehicle.getStatus().equals(VehicleStatus.DISCARD.getCode())) {
            throw new BaseException(ResCode.VEHICLE_DEPARTURE_VEHICLE_DISABLE.getDesc() + ", 车辆状态是：" + getVehicleStatus(vehicle.getStatus(), vehicle.getId()),
                    ResCode.VEHICLE_DEPARTURE_VEHICLE_DISABLE.getCode());
        }
        if (vehicle.getIsDel() == 1) {
            throw new BaseException(ResCode.VEHICLE_DEPARTURE_VEHICLE_DELETE.getDesc(),
                    ResCode.VEHICLE_DEPARTURE_VEHICLE_DELETE.getCode());
        }
        if (StringUtils.isBlank(bookVehicleVo.getBookStartDate()) || StringUtils.isBlank(bookVehicleVo.getBookEndDate())) {
            throw new BaseException(ResultCode.DATE_TIME_IS_NULL);
        }
        //提取日期和相应的预定目标日期
        Map<String, List<String>> yearMonthAndDate = Maps.newHashMap();
        DateTime startDay = DateTime.parse(bookVehicleVo.getBookStartDate(), DATE_TIME_FORMATTER);
        DateTime endDay = DateTime.parse(bookVehicleVo.getBookEndDate(), DATE_TIME_FORMATTER);
        //转换日期范围为列表，并检查是否合法
        fillDateList4DatePeriod(yearMonthAndDate, DateTime.parse(startDay.toString(DEFAULT_DATE_TIME_FORMATTER), DEFAULT_DATE_TIME_FORMATTER), DateTime.parse(endDay.toString(DEFAULT_DATE_TIME_FORMATTER), DEFAULT_DATE_TIME_FORMATTER));
               if (yearMonthAndDate.size() > 3) {//连续的日期最多夸3个月
            throw new BaseException(ResultCode.ONLY_BOOK_TWO_MONTH);
        }


        Map<String, Integer> map = new HashMap<>();
        //加入预定申请记录
        VehicleBookRecord vehicleBookRecord = null;
        if (bookVehicleVo.getVehicleBookRecordId() == null) {
            //检验时间是否可以预定
            map = vehicleBookHourInfoBiz.getPredictableHours(bookVehicleVo.getBookStartDate(), bookVehicleVo.getBookEndDate(), bookVehicleVo.getNotCheckTimeLegal());
            vehicleBookRecord = new VehicleBookRecord();
            BeanUtil.copyProperties(bookVehicleVo, vehicleBookRecord, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
            vehicleBookRecord.setBookStartDate(startDay.toDate());
            vehicleBookRecord.setBookEndDate(endDay.toDate());
            vehicleBookRecord.setStatus(bookVehicleVo.getStatus());
            vehicleBookRecord.setBookUser(userId);
            vehicleBookRecord.setBookUserName(userName);
            vehicleBookRecord.setLiftCompany(vehicle.getParkBranchCompanyId());
            vehicleBookRecordBiz.save(vehicleBookRecord);
        } else {
            vehicleBookRecord = vehicleBookRecordBiz.selectById(bookVehicleVo.getVehicleBookRecordId());
            if (vehicleBookRecord == null) {
                throw new BaseException(ResCode.VEHICLE_BOOKED_RECORD_NOT_EXIST.getDesc(), ResCode.VEHICLE_BOOKED_RECORD_NOT_EXIST.getCode());
            } else {
                //先取消预定，然后再修改
                //先取消之前预定时间，然后再修改
                bookVehicleVo.setUnbookStartDate(new DateTime(vehicleBookRecord.getBookStartDate()).toString(DATE_TIME_FORMATTER));
                bookVehicleVo.setUnbookEndDate(new DateTime(vehicleBookRecord.getBookEndDate()).toString(DATE_TIME_FORMATTER));
                unbookVehicle(bookVehicleVo);
                //检验时间是否可以预定
                map = vehicleBookHourInfoBiz.getPredictableHours(bookVehicleVo.getBookStartDate(), bookVehicleVo.getBookEndDate(), bookVehicleVo.getNotCheckTimeLegal());
                vehicleBookRecord.setBookStartDate(startDay.toDate());
                vehicleBookRecord.setBookEndDate(endDay.toDate());
                vehicleBookRecordBiz.updateSelectiveByIdRe(vehicleBookRecord);
            }
        }
        for (Map.Entry<String, List<String>> entry : yearMonthAndDate.entrySet()) {
            Boolean rsEach = applyVehicle4EmployeePerMonth(bookVehicleVo.getVehicleId(), entry.getValue(), entry.getKey(), map);
            if (Boolean.FALSE.equals(rsEach)) {
                throw new BaseException(ResultCode.VEHICLE_IS_BOOKED);
            }
        }
        //添加预定日期记录
        Boolean hasSuc = bookedVehicle(bookVehicleVo);
        if (!hasSuc) {
            throw new BaseException(ResultCode.BOOKED_FAILED_CODE);
        }

//        //添加预定时间记录
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            VehicleBookHourInfoDto vehicleBookHourInfoDto = new VehicleBookHourInfoDto();
            vehicleBookHourInfoDto.setVehicleId(bookVehicleVo.getVehicleId());
            vehicleBookHourInfoDto.setYearMonthDay(entry.getKey());
            vehicleBookHourInfoDto.setBookedHour(entry.getValue());
            vehicleBookHourInfoBiz.save(vehicleBookHourInfoDto);
        }

        //添加随车物品
        List<Map<String, Object>> params = Lists.newArrayList();
        if (MapUtils.isNotEmpty(bookVehicleVo.getSelectedAccItem())) {
            for (Map.Entry<Integer, Integer> idAndAmount : bookVehicleVo.getSelectedAccItem().entrySet()) {
                Map<String, Object> row = Maps.newHashMap();
                row.put("id", idAndAmount.getKey());
                row.put("amount", idAndAmount.getValue());
                row.put("bookRecordId", vehicleBookRecord.getId());
                params.add(row);
            }
            bookRecordAccItemMapper.batchAdd(params);
        }

        return vehicleBookRecord;
    }


    @Transactional
    public Boolean applyVehicle4EmployeePerMonth(String vehicleId, List<String> bookedDates, String yearMonth, Map<String, Integer> hourInfo) {
        //检查车辆是否有空档
        //获得当月预定记录
        VehicleBookInfo vehicleBookInfo = getByVehicleIdAndYearMonth(vehicleId, yearMonth);
        //位操作确定目标日期是否可预订
        Map<String, Object> params = Maps.newHashMap();
        Map<String, List<String>> yearMonthAndDate = new HashMap<>();
        yearMonthAndDate.put(yearMonth, bookedDates);
        fillBookedDateSearchParam(params, null, yearMonthAndDate);//转换为查询对应日期未预定的条件
        Map<String, Map<String, Integer>> yearMonthAndParam = (Map<String, Map<String, Integer>>) params.get("yearMonthAndParam");
        Map<String, Integer> andOpratorParam = yearMonthAndParam.get(yearMonth);
        Integer andOperationFactor = andOpratorParam.get("andOperationFactor");
        Integer andOperationRs = andOpratorParam.get("andOperationRs");
        if (vehicleBookInfo != null && vehicleBookInfo.getBookedDate() != null &&
                ((vehicleBookInfo.getBookedDate() & andOperationFactor) != 0)) {//已经被预定
            //当天已经被预定检查小时是否也被预定
            return filterHourInfoBooked(vehicleId, hourInfo);
        }
        return Boolean.TRUE;
    }


    public boolean filterHourInfoBooked(String vehicleId, Map<String, Integer> hourInfo) {
        if (MapUtils.isEmpty(hourInfo)) {
            return false;
        }
        for (Map.Entry<String, Integer> entry : hourInfo.entrySet()) {
            if (entry.getValue() == 0) {//0点 查询是否有已经预约的记录
                Map<String, Object> param = new HashMap<>();
                param.put("vehicleId", vehicleId);
                param.put("bookedEndDate", DateTime.parse(entry.getKey() + " 00:00:00", DATE_TIME_FORMATTER));
                List<VehicleBookRecordVo> vehicleBookRecordVos = vehicleBookRecordBiz.selectZeroHourRecord(param);
                if (vehicleBookRecordVos != null && vehicleBookRecordVos.size() > 0) {
                    return false;
                }
            }
            VehicleBookHourInfoDto vehicleBookHourInfoDto = new VehicleBookHourInfoDto();
            vehicleBookHourInfoDto.setYearMonthDay(entry.getKey());
            vehicleBookHourInfoDto.setVehicleId(vehicleId);
            List<VehicleBookHourInfo> vehicleBookHourInfos = vehicleBookHourInfoBiz.selectByVehicleAndDate(vehicleBookHourInfoDto);
            if (vehicleBookHourInfos != null && vehicleBookHourInfos.size() > 0) {
                if ((vehicleBookHourInfos.get(0).getBookedHour() & entry.getValue()) != 0) { // 已经被预定
                    log.info(entry.getKey() + "预定的时间段已经被预约！");
                    return Boolean.FALSE;
                }
            }
        }
        return Boolean.TRUE;
    }

    /**
     * 批准预定车辆预定
     *
     * @param operatorId
     * @param bookRecordId
     * @return
     */
    @Transactional
    public RestResponse<Integer> reviewVehicleBooking(Integer operatorId, Long bookRecordId, Integer rsStatus, String userName, String rejectRemark, Boolean notCheckTimeLegal) throws Exception {
        try {
            //获取相关申请记录
            VehicleBookRecord vehicleBookRecord = vehicleBookRecordBiz.selectById(bookRecordId);
            //申请记录验证
            if (vehicleBookRecord == null) {
                return RestResponse.codeAndMessage(ResCode.VEHICLE_BOOK_RECORD_IS_NOT_EXIST.getCode(), ResCode.VEHICLE_BOOK_RECORD_IS_NOT_EXIST.getDesc());
            }
            if (VehicleBookRecordStatus.APPROVE.getCode().equals(vehicleBookRecord.getStatus())) {
                return RestResponse.codeAndMessage(ResCode.VEHICLE_BOOKED_RECORD_ALREADY_CHANGED.getCode(), ResCode.VEHICLE_BOOKED_RECORD_ALREADY_CHANGED.getDesc());
            }
            //转换为相应预定参数
            BookVehicleVO bookVehicleVo = new BookVehicleVO();
            BeanUtils.copyProperties(bookVehicleVo, vehicleBookRecord);
            bookVehicleVo.setBookStartDate(new DateTime(vehicleBookRecord.getBookStartDate()).toString(DATE_TIME_FORMATTER));
            bookVehicleVo.setBookEndDate(new DateTime(vehicleBookRecord.getBookEndDate()).toString(DATE_TIME_FORMATTER));

            //成功后修改预定记录状态
            Map<String, Object> updateParam = Maps.newHashMap();
            updateParam.put("id", bookRecordId);
            updateParam.put("status", rsStatus);
            updateParam.put("reviewerApply", operatorId);
            updateParam.put("reviewerNameApply", userName);
            updateParam.put("statusCondition", VehicleBookRecordStatus.APPLY.getCode());
            if (StringUtils.isNotBlank(rejectRemark)) {
                updateParam.put("rejectRemark", rejectRemark);
            }
            Integer effected = vehicleBookRecordBiz.changeRecordStatus(updateParam);
            if (effected == 0) {//修改失败，手动回滚
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚
                return RestResponse.code(ResCode.VEHICLE_BOOKED_RECORD_ALREADY_CHANGED.getCode());
            } else if (VehicleBookRecordStatus.REJECTED.getCode().equals(rsStatus)) {
                //拒绝，则释放bookinfo
                bookVehicleVo.setUnbookStartDate(bookVehicleVo.getBookStartDate());
                bookVehicleVo.setUnbookEndDate(bookVehicleVo.getBookEndDate());
                Boolean hasSuc = unbookVehicle(bookVehicleVo);
                if (hasSuc) {
                    return RestResponse.suc();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return RestResponse.codeAndMessage(1325, e.getMessage());
        }
        return RestResponse.codeAndMessage(1325, "取消预定失败");
    }

    /**
     * 取消预定
     *
     * @return
     */
    @Transactional
    public RestResponse unbookVehicle4Employee(Integer operatorId, Long bookRecordId, String userName, String rejectRemark, Boolean notCheckTimeLegal) throws Exception {
        //获取相关申请记录
        VehicleBookRecord vehicleBookRecord = vehicleBookRecordBiz.selectById(bookRecordId);
        //申请记录验证
        if (vehicleBookRecord == null) {
            return RestResponse.codeAndMessage(ResCode.VEHICLE_BOOK_RECORD_IS_NOT_EXIST.getCode(), ResCode.VEHICLE_BOOK_RECORD_IS_NOT_EXIST.getDesc());
        }
//        //已通过审核的可以取消预定
//        if (!VehicleBookRecordStatus.APPROVE.getCode().equals(vehicleBookRecord.getStatus())) {
//            return RestResponse.code(ResCode.VEHICLE_BOOKED_RECORD_ALREADY_CHANGED.getCode());
//        }
        //转换为相应取消预定参数
        BookVehicleVO bookVehicleVo = new BookVehicleVO();
        BeanUtils.copyProperties(bookVehicleVo, vehicleBookRecord);
        bookVehicleVo.setNotCheckTimeLegal(notCheckTimeLegal);
        bookVehicleVo.setBookStartDate(null);
        bookVehicleVo.setBookEndDate(null);
        bookVehicleVo.setUnbookStartDate(new DateTime(vehicleBookRecord.getBookStartDate()).toString(DATE_TIME_FORMATTER));
        bookVehicleVo.setUnbookEndDate(new DateTime(vehicleBookRecord.getBookEndDate()).toString(DATE_TIME_FORMATTER));
        //取消预定
        Boolean hasSuc = unbookVehicle(bookVehicleVo);
        if (!hasSuc) {
            return RestResponse.codeAndMessage(ResCode.VEHICLE_UNBOOK_FAIL.getCode(), ResCode.VEHICLE_UNBOOK_FAIL.getDesc());
        }
        //修改预定状态，写入取消人
        Map<String, Object> updateParam = Maps.newHashMap();
        updateParam.put("id", bookRecordId);
        updateParam.put("status", VehicleBookRecordStatus.CANCEL_APPLY.getCode());
        updateParam.put("reviewerCancel", operatorId);
        updateParam.put("reviewerNameCancel", userName);
        updateParam.put("statusCondition", VehicleBookRecordStatus.APPROVE.getCode());
        if (StringUtils.isNotBlank(rejectRemark)) {
            updateParam.put("rejectRemark", rejectRemark);
        }
        Integer effected = vehicleBookRecordBiz.changeRecordStatus(updateParam);
        if (effected == 0) {//修改失败，手动回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚
            return RestResponse.code(ResCode.VEHICLE_BOOKED_RECORD_ALREADY_CHANGED.getCode());
        }
//        //删除预定时间记录
//        List<String> list = Lists.newArrayList();
//        DateTime startDay = DateTime.parse(bookVehicleVo.getUnbookStartDate().split(" ")[0], DEFAULT_DATE_TIME_FORMATTER);
//        DateTime endDay = DateTime.parse(bookVehicleVo.getUnbookEndDate().split(" ")[0], DEFAULT_DATE_TIME_FORMATTER);
//        for( DateTime curDate = startDay;curDate.compareTo(endDay) <= 0; curDate=curDate.plusDays(1)) {
//            log.info("当前日期：" + curDate);
//            String date = curDate.toString(DATE_TIME_FORMATTER);
//            list.add(date);
//        }
//         vehicleBookHourInfoBiz.delete(vehicleBookRecord.getVehicleId(), list);
        return RestResponse.suc();
    }

    /**
     * 获取预定车辆相关与操作参数
     *
     * @return
     */
    private Integer getBitOpratorFactor4Booked(List<String> dates) {
        Integer andOperationFactor = 0;
        for (String dateStr : dates) {//已预定作为条件，该位与1作与运算必定为1
            DateTime dateTime = DateTime.parse(dateStr, DEFAULT_DATE_TIME_FORMATTER);
            //仅对应位为1的整形值
            andOperationFactor |= 1 << (dateTime.dayOfMonth().get() - 1);
        }
        return andOperationFactor;
    }

    /**
     * 获取预定车辆相关与操作参数
     *
     * @return
     */
    private Integer getBitOpratorFactor4UnBooked(List<String> dates) {
        Integer andOperationFactor = Integer.MAX_VALUE;
        for (String dateStr : dates) {//已预定作为条件，该位与1作与运算必定为1
            DateTime dateTime = DateTime.parse(dateStr, DEFAULT_DATE_TIME_FORMATTER);
            //仅对应位为1的整形值
            andOperationFactor ^= 1 << (dateTime.dayOfMonth().get() - 1);
        }
        return andOperationFactor;
    }


    /**
     * 根据预定日期逐条修改预定记录
     *
     * @param bookVehicleVo
     * @return
     */
    @Transactional
    public Boolean bookedVehicle(BookVehicleVO bookVehicleVo) throws Exception {
        //提取日期和相应的预定目标日期
        Map<String, List<String>> yearMonthAndDate = Maps.newHashMap();
        DateTime startDay = DateTime.parse(bookVehicleVo.getBookStartDate(), DATE_TIME_FORMATTER);
        DateTime endDay = DateTime.parse(bookVehicleVo.getBookEndDate(), DATE_TIME_FORMATTER);
        //转换日期范围为列表，并检查是否合法
        fillDateList4DatePeriod(yearMonthAndDate, startDay, endDay);
        if (yearMonthAndDate.size() > 3) {//连续的日期最多夸3个月
            throw new BaseException(ResultCode.ONLY_BOOK_TWO_MONTH);
        }
        Boolean rs = Boolean.TRUE;
        for (Map.Entry<String, List<String>> entry : yearMonthAndDate.entrySet()) {
            Boolean rsEach = bookedVehiclePerMonth(bookVehicleVo.getVehicleId(), entry.getValue(), entry.getKey());
            if (Boolean.FALSE.equals(rsEach)) {
                rs = Boolean.FALSE;
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚
            }
        }
        return rs;
    }

    /**
     * 不存在插入、存在更新，利用唯一索引确保相同车辆以及年月序列化提交事务
     *
     * @param vehicleId
     * @param bookedDates
     * @param yearMonth
     * @return 是否更新或插入成功
     */
    @Transactional
    public Boolean bookedVehiclePerMonth(String vehicleId, List<String> bookedDates, String yearMonth) throws Exception {
        VehicleBookInfo vehicleBookInfo = new VehicleBookInfo();
        vehicleBookInfo.setVehicle(vehicleId);
        vehicleBookInfo.setYearMonth(yearMonth);
        //检查是否存在相关车辆
        checkIfVehicleExists(vehicleId);
        Integer orRsOperationFactor = getBitOpratorFactor4Booked(bookedDates);//预定的相关或运算因子，当前月份没有预定记录时同时也是结果
        vehicleBookInfo.setBookedDate(orRsOperationFactor);
        Map<String, Object> map = new HashMap<>();
        map.put("vehicle", vehicleId);
        map.put("yearMonth", yearMonth);
        List<VehicleBookInfo> vehicleBookInfos = vehicleBookInfoMapper.getByVehicleIdAndYearMonth(map);
        if (vehicleBookInfos != null && vehicleBookInfos.size() > 0) {
            if ((vehicleBookInfos.get(0).getBookedDate() & orRsOperationFactor) == orRsOperationFactor) {
                return Boolean.TRUE;
            }
        }
        Integer effected = vehicleBookInfoMapper.insertIgnore(vehicleBookInfo);
        if (effected == 0) {//已存在则需要更新
            Map<String, Object> params = Maps.newHashMap();
            params.put("vehicleId", vehicleBookInfo.getVehicle());
            params.put("yearMonth", yearMonth);
            //加入更新条件
            if (CollectionUtils.isEmpty(bookedDates)) {
                throw new BaseException(ResultCode.BOOKED_DATE_IS_EMPTY);
            }
            Map<String, List<String>> yearMonthAndDate = new HashMap<>();
            yearMonthAndDate.put(vehicleBookInfo.getYearMonth(), bookedDates);
            //转换为查询对应日期未预定的条件
            fillBookedDateSearchParam(params, null, yearMonthAndDate);
            //提取相关参数组装更细条件
            Map<String, Map<String, Integer>> yearMonthAndParam = (Map<String, Map<String, Integer>>) params.get("yearMonthAndParam");
            Map<String, Integer> andOpratorParam = yearMonthAndParam.get(vehicleBookInfo.getYearMonth());
            params.putAll(andOpratorParam);
            params.put("orRsOperationFactor",
                    orRsOperationFactor);
            effected = vehicleBookInfoMapper.updateBookedInfo(params);
        }
        return effected > 0 ? Boolean.TRUE : Boolean.FALSE;
    }

    /**
     * 检查车辆是否存在
     *
     * @param id
     */
    public boolean checkIfVehicleExists(String id) {

        Vehicle vehicle = mapper.selectByPrimaryKey(id);
        if (vehicle == null) {
            return false;
        }
//        if (vehicle.getStatus().equals(VehicleStatus.DISCARD.getCode())) {
//            throw new BaseException(ResCode.VEHICLE_DEPARTURE_VEHICLE_DISABLE.getDesc() + ", 车辆状态是：" + getVehicleStatus(vehicle.getStatus(), vehicle.getId()),
//                    ResCode.VEHICLE_DEPARTURE_VEHICLE_DISABLE.getCode());
//        }
        return true;
    }

    public String getVehicleStatus(Integer status, String vehicleId) {
        StringBuilder stringBuilder = new StringBuilder();
        switch (status) {
            case 1:
                stringBuilder.append("正常运行");
                break;
            case 2:
                stringBuilder.append("维修");
                break;
            case 3:
                stringBuilder.append("报废");
                break;
            case 4:
                stringBuilder.append("出车");
                break;
            case 5:
                stringBuilder.append("保养");
                break;
        }
        List<VehicleBookRecordVo> vehicleBookRecordVos = vehicleBookRecordBiz.selectByVehicleId(vehicleId);
        vehicleBookRecordBiz.removeStatus2(vehicleBookRecordVos);
        if (vehicleBookRecordVos != null && vehicleBookRecordVos.size() > 0 && vehicleBookRecordVos.get(0).getBookEndDate() != null && vehicleBookRecordVos.get(0).getBookEndDate().after(new Date())) {
            stringBuilder.append("中，使用人：");
            stringBuilder.append(vehicleBookRecordVos.get(0).getVehicleUsername());
            stringBuilder.append(" 使用人电话：");
            stringBuilder.append(vehicleBookRecordVos.get(0).getVehicleUserPhone());
        } else {
            stringBuilder.append(", 请联系管理员修改车辆状态为正常状态");
        }
        return stringBuilder.toString();
    }

    /**
     * 把日期范围转换为列表，并检查操作范围是否合法
     *
     * @param yearMonthAndDate
     * @param startDay
     * @param endDay
     */
    private void fillDateList4DatePeriod(Map<String, List<String>> yearMonthAndDate, DateTime startDay, DateTime endDay) {
        for (DateTime curDate = startDay; curDate.compareTo(endDay) <= 0; curDate = curDate.plusDays(1)) {
            String curDateStr = curDate.toString(DEFAULT_DATE_TIME_FORMATTER);
            if (curDateStr.compareTo(DateTime.now().toString(DEFAULT_DATE_TIME_FORMATTER)) < 0) {
                throw new BaseException(ResultCode.ONLY_BOOK_FROM_TODAY);
            }
            String curYearMonth = curDate.toString(YEARMONTH_DATE_TIME_FORMATTER);
            if (!yearMonthAndDate.containsKey(curYearMonth)) {
                yearMonthAndDate.put(curYearMonth, Lists.newArrayList());
            }
            List<String> curBookedDateList = yearMonthAndDate.get(curYearMonth);
            curBookedDateList.add(curDateStr);
        }

        if (yearMonthAndDate.size() > 3) {//连续的日期最多夸3个月
            throw new BaseException(" 只可以预约两个月内的车辆");
        }
    }

    private void unbookDateList4DatePeriod(Map<String, List<String>> yearMonthAndDate, DateTime startDay, DateTime endDay) {

        for (DateTime curDate = startDay; curDate.compareTo(endDay) <= 0; curDate = curDate.plusDays(1)) {
            String curDateStr = curDate.toString(DEFAULT_DATE_TIME_FORMATTER);
//            if(curDateStr.compareTo(DateTime.now().toString(DEFAULT_DATE_TIME_FORMATTER))<0){
//                throw new BaseException("只可以取消当前时间之后的车辆");
//            }
            String curYearMonth = curDate.toString(YEARMONTH_DATE_TIME_FORMATTER);
            if (!yearMonthAndDate.containsKey(curYearMonth)) {
                yearMonthAndDate.put(curYearMonth, Lists.newArrayList());
            }
            List<String> curBookedDateList = yearMonthAndDate.get(curYearMonth);
            curBookedDateList.add(curDateStr);
        }

        if (yearMonthAndDate.size() > 3) {//连续的日期最多夸3个月
            throw new BaseException(ResultCode.ONLY_BOOK_TWO_MONTH);
        }
    }

    /**
     * 取消预定（释放bookinfo）
     *
     * @return
     */
    @Transactional
    public Boolean unbookVehicle(BookVehicleVO bookVehicleVo) throws Exception {
        //提取日期参数，改为每月一份
        //提取日期和相应的预定目标日期
        Map<String, List<String>> yearMonthAndDate = Maps.newHashMap();
        DateTime startDay = DateTime.parse(bookVehicleVo.getUnbookStartDate(), DATE_TIME_FORMATTER);
        DateTime endDay = DateTime.parse(bookVehicleVo.getUnbookEndDate(), DATE_TIME_FORMATTER);
        //转换日期范围为列表，并检查是否合法
        unbookDateList4DatePeriod(yearMonthAndDate, DateTime.parse(startDay.toString(DEFAULT_DATE_TIME_FORMATTER), DEFAULT_DATE_TIME_FORMATTER), DateTime.parse(endDay.toString(DEFAULT_DATE_TIME_FORMATTER), DEFAULT_DATE_TIME_FORMATTER));
        //原设计为 读取 bookVehicleVo.getNotCheckTimeLegal()， 现在取消/拒绝 true
        Map<String, Integer> map = vehicleBookHourInfoBiz.getPredictableHours(bookVehicleVo.getUnbookStartDate(), bookVehicleVo.getUnbookEndDate(), Boolean.TRUE);
        if (yearMonthAndDate.size() > 3) {//连续的日期最多夸3个月
            throw new BaseException(ResultCode.ONLY_UNBOOK_TWO_MONTH);
        }
        Boolean rs = Boolean.TRUE;
        for (Map.Entry<String, List<String>> entry : yearMonthAndDate.entrySet()) {
            Boolean rsEach = unbookVehiclePerMonth(bookVehicleVo.getVehicleId(), entry.getValue(), entry.getKey(), map);
            if (Boolean.FALSE.equals(rsEach)) {
                rs = Boolean.FALSE;
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//手动回滚
            }
        }
        return rs;
    }


    @Transactional
    public Boolean unbookVehiclePerMonth(String vehicleId, List<String> unbookDates, String yearMonth, Map<String, Integer> hourInfo) {
        checkIfVehicleExists(vehicleId);
        Map<String, Object> params = Maps.newHashMap();
        params.put("vehicleId", vehicleId);
        params.put("yearMonth", yearMonth);
        //加入更新条件
        if (CollectionUtils.isEmpty(unbookDates)) {
            throw new BaseException(ResultCode.VEHICLE_CAN_NOT_UNBOOK);
        }
        Map<String, List<String>> yearMonthAndDate = new HashMap<>();
        yearMonthAndDate.put(yearMonth, unbookDates);
        //转换为查询对应日期未预定的条件
        fillBookedDateSearchParam(params, yearMonthAndDate, null);
        //提取相关参数组装更细条件
        Map<String, Map<String, Integer>> yearMonthAndParam = (Map<String, Map<String, Integer>>) params.get("yearMonthAndParam");
        Map<String, Integer> andOpratorParam = yearMonthAndParam.get(yearMonth);
        params.putAll(andOpratorParam);
        Integer andRsOperationFactor = getBitOpratorFactor4UnBooked(unbookDates);//预定的相关或运算因子，当前月份没有预定记录时同时也是结果
        params.put("andRsOperationFactor",
                andRsOperationFactor);
        return unbookHourInfo(vehicleId, hourInfo, params);
    }

    public boolean unbookHourInfo(String vehicleId, Map<String, Integer> hourInfo, Map<String, Object> params) {
        for (Map.Entry<String, Integer> entry : hourInfo.entrySet()) {
            VehicleBookHourInfoDto vehicleBookHourInfoDto = new VehicleBookHourInfoDto();
            vehicleBookHourInfoDto.setYearMonthDay(entry.getKey());
            vehicleBookHourInfoDto.setVehicleId(vehicleId);
            List<VehicleBookHourInfo> vehicleBookHourInfos = vehicleBookHourInfoBiz.selectByVehicleAndDate(vehicleBookHourInfoDto);
            if (vehicleBookHourInfos != null && vehicleBookHourInfos.size() > 0) {
                if ((vehicleBookHourInfos.get(0).getBookedHour() & ~entry.getValue()) == 0) { //时间取与运算为0说明当天全部取消预定
                    DateTime dateTime = DateTime.parse(entry.getKey(), DEFAULT_DATE_TIME_FORMATTER);
                    Integer andOperationFactor = 0;
                    andOperationFactor |= 1 << (dateTime.dayOfMonth().get() - 1);
                    VehicleBookInfo vehicleBookInfo = getByVehicleIdAndYearMonth(vehicleId, dateTime.toString(YEARMONTH_DATE_TIME_FORMATTER));
                    if (vehicleBookInfo != null) {
                        vehicleBookInfo.setBookedDate(vehicleBookInfo.getBookedDate() & ~andOperationFactor);
                        int effected = vehicleBookInfoBiz.update(vehicleBookInfo);
                        if (effected < 1) {
                            return Boolean.FALSE;
                        }
                    }
                }
                vehicleBookHourInfos.get(0).setBookedHour((vehicleBookHourInfos.get(0).getBookedHour() & ~entry.getValue()));
                int effect = vehicleBookHourInfoBiz.updateByIdRe(vehicleBookHourInfos.get(0));
                if (effect < 1) {
                    return Boolean.FALSE;
                } else {
                    continue;
                }
            } else {
                return Boolean.FALSE;
            }
        }
        return Boolean.TRUE;
    }

    /**
     * 获取某月份相应预定日期查询条件
     *
     * @param yearMonthAndDate          年月 - 预定日期条件字符串（yyyy-MM-dd）
     * @param yearMonthAndDateNotBooked 年月 - 未预定日期条件字符串
     * @return
     */
    public void fillBookedDateSearchParam(Map<String, Object> params,
                                          Map<String, List<String>> yearMonthAndDate, Map<String, List<String>> yearMonthAndDateNotBooked) {
        if (MapUtils.isEmpty(yearMonthAndDate) && MapUtils.isEmpty(yearMonthAndDateNotBooked)) {//没有预定信息查询条件
            throw new BaseException(ResultCode.BOOKED_DATE_IS_EMPTY);
        }

        Map<String, Map<String, Integer>> yearMonthAndParam = new HashMap<>();
        Set<String> bookedYearMonth = new HashSet<>();//记录未预定查询条件，供检查是否是否存在同一天既是已预定，又是没有预定
        if (MapUtils.isNotEmpty(yearMonthAndDate)) {
            for (Map.Entry<String, List<String>> entry : yearMonthAndDate.entrySet()) {
                String curYearMonth = entry.getKey();
                if (CollectionUtils.isEmpty(entry.getValue())) {
                    continue;
                }
                Integer andOperationFactor = 0;//查找条件中作与运算的参数
                Integer andOperationRs = 0;//与运算结果
                for (String dateStr : entry.getValue()) {//已预定作为条件，该位与1作与运算必定为1
                    DateTime dateTime = DateTime.parse(dateStr, DEFAULT_DATE_TIME_FORMATTER);
                    bookedYearMonth.add(dateStr);
                    //仅对应位为1的整形值
                    andOperationFactor |= 1 << (dateTime.dayOfMonth().get() - 1);
                    andOperationRs |= 1 << (dateTime.dayOfMonth().get() - 1);
                }
                Map<String, Integer> andOperationParam = Maps.newHashMap();
                andOperationParam.put("andOperationFactor", andOperationFactor);
                andOperationParam.put("andOperationRs", andOperationRs);
                yearMonthAndParam.put(curYearMonth, andOperationParam);
            }
        }

        if (MapUtils.isNotEmpty(yearMonthAndDateNotBooked)) {
            for (Map.Entry<String, List<String>> entry : yearMonthAndDateNotBooked.entrySet()) {
                String curYearMonth = entry.getKey();
                if (CollectionUtils.isEmpty(entry.getValue())) {
                    continue;
                }
                Map<String, Integer> andOperationParam = Maps.newHashMap();
                andOperationParam.put("andOperationFactor", 0);
                andOperationParam.put("andOperationRs", 0);
                Integer andOperationFactor = 0;//查找条件中作与运算的参数
                if (yearMonthAndParam.containsKey(curYearMonth)) {
                    andOperationParam = yearMonthAndParam.get(curYearMonth);
                    andOperationFactor = andOperationParam.get("andOperationFactor");
                } else {
                    yearMonthAndParam.put(curYearMonth, andOperationParam);
                }
                for (String dateStr : entry.getValue()) {//已预定作为条件，该位与1作与运算必定为1
                    if (bookedYearMonth.contains(dateStr)) {
                        log.info("当天不可预定，日期为：dateStr = {}", dateStr);
                        throw new BaseException(ResultCode.TODAY_CAN_NOT_BOOK);
                    }
                    DateTime dateTime = DateTime.parse(dateStr, DEFAULT_DATE_TIME_FORMATTER);
                    //仅对应位为1的整形值
                    andOperationFactor |= 1 << (dateTime.dayOfMonth().get() - 1);
                }
                andOperationParam.put("andOperationFactor", andOperationFactor);
//                if(!bookedYearMonth.contains(curYearMonth)){//仅以未预定日期条件查询时，不存在预定记录即为没有预定
//                    andOperationParam.put("notBookedOnly",1);
//                }
            }

        }
        if (MapUtils.isNotEmpty(yearMonthAndParam)) {
            params.put("yearMonthAndParam", yearMonthAndParam);
            String bitStr = "";
            for (String key : yearMonthAndParam.keySet()) {
                bitStr += "1";
            }
            params.put("yearMonthAndParamBitStr", bitStr);
        }
    }

    /**
     * 根据月份分类到map中
     */
    private void classifyByYearMonth(List<String> target, Map<String, List<String>> yearMonthAndDate, Set<String> allYearMonth) {
        if (CollectionUtils.isNotEmpty(target)) {//已预约
            for (String dateStr : target) {
                String curYearMonth = DateTime.parse(dateStr, DEFAULT_DATE_TIME_FORMATTER).toString(YEARMONTH_DATE_TIME_FORMATTER);
                allYearMonth.add(curYearMonth);
                List<String> dateStrList = yearMonthAndDate.get(curYearMonth);
                if (dateStrList == null) {
                    dateStrList = Lists.newArrayList();
                    yearMonthAndDate.put(curYearMonth, dateStrList);
                }
                dateStrList.add(dateStr);
            }
        }
    }

    /**
     * 把日期范围转换成列表
     *
     * @param startDate
     * @param endDate
     * @return
     */
    private List<String> convertDatePeriod2List(String startDate, String endDate) {
        DateTime startDay = DateTime.parse(startDate, DEFAULT_DATE_TIME_FORMATTER);
        DateTime endDay = DateTime.parse(endDate, DEFAULT_DATE_TIME_FORMATTER);
        List<String> rs = Lists.newArrayList();
        for (DateTime curDate = startDay; curDate.compareTo(endDay) <= 0; curDate = curDate.plusDays(1)) {
            String curDateStr = curDate.toString(DEFAULT_DATE_TIME_FORMATTER);
            rs.add(curDateStr);
        }
        return rs;
    }

    /**
     * 根据前端预定日期查询条件转换为实际查询条件
     *
     * @param params
     * @return
     */
    public void adjustBookedInfoParam(Map<String, Object> params, VehiclePageQueryVo vehiclePageQueryVo) {
        //加入每月预定信息查询条件
        List<String> bookedDates = Lists.newArrayList();
        if (StringUtils.isNotBlank(vehiclePageQueryVo.getBookedStartDate()) &&
                StringUtils.isNotBlank(vehiclePageQueryVo.getBookedEndDate())) {
            bookedDates = convertDatePeriod2List(vehiclePageQueryVo.getBookedStartDate(),
                    vehiclePageQueryVo.getBookedEndDate());
        }
        params.remove("bookStartDate");
        params.remove("bookEndDate");

        List<String> notBookedDates = Lists.newArrayList();
        if (StringUtils.isNotBlank(vehiclePageQueryVo.getNotBookedStartDate()) &&
                StringUtils.isNotBlank(vehiclePageQueryVo.getNotBookedEndDate())) {
            notBookedDates = convertDatePeriod2List(vehiclePageQueryVo.getNotBookedStartDate(),
                    vehiclePageQueryVo.getNotBookedEndDate());
        }
        params.remove("notBookedStartDate");
        params.remove("norBookedEndDate");

        //若传入预定信息查询条件，则查询对应月份预定信息查询条件（不超过3个月）
        if (CollectionUtils.isEmpty(bookedDates) && CollectionUtils.isEmpty(notBookedDates)) {
            return;
        }
        //筛选出查询条件所在月份
        Set<String> allYearMonth = new HashSet<>();//记录所有年月
        Map<String, List<String>> yearMonthAndDate = new HashMap<>();//预定年月 - 预定日期列表
        classifyByYearMonth(bookedDates, yearMonthAndDate, allYearMonth);
        Map<String, List<String>> yearMonthAndDateNotBooked = new HashMap<>();//未预定年月 - 未预定日期列表
        classifyByYearMonth(notBookedDates, yearMonthAndDateNotBooked, allYearMonth);
        if (allYearMonth.size() > 0 && params.get("subordinateBranch") == null) {
            throw new BaseException(" <subordinateBranch> must included in params while using <bookedInfo> param. ");
        }
        if (allYearMonth.size() > MAX_MONTH_COUNT_BOOKED_INFO_QUERY) {
            throw new BaseException(" only 3 month can be included <bookedInfo> param.");
        }
        //加入预定信息查询条件
        fillBookedDateSearchParam(params, yearMonthAndDate, yearMonthAndDateNotBooked);
    }

    /**
     * 按页查询
     *
     * @param vehiclePageQueryVo
     * @return
     * @throws Exception
     */
    public PageDataVO<ResultVehicleVo> getByPage(VehiclePageQueryVo vehiclePageQueryVo) throws Exception {
        Map<String, Object> params = PropertyUtils.describe(vehiclePageQueryVo);
        Integer pageSize = (Integer) params.get("limit");
        params.remove("pageSize");
        Integer pageNo = (Integer) params.get("page");
        params.remove("pageNo");
        //处理预定日期相关参数
        adjustBookedInfoParam(params, vehiclePageQueryVo);
        params.get("yearMonthAndParam");
        if (vehiclePageQueryVo.getModelId() == null) {
            params.remove("modelId");
        }
        PageHelper.startPage(pageNo, pageSize);
        List<ResultVehicleVo> vehicles = mapper.getByPage(params);
        PageInfo<ResultVehicleVo> vehiclePageInfo = new PageInfo<>(vehicles);
        return PageDataVO.pageInfo(vehiclePageInfo);
    }

    /**
     * 按页查询(非所有数据，过滤分公司信息)
     *
     * @param vehiclePageQueryVo
     * @return
     * @throws Exception
     */
    public PageDataVO<ResultVehicleVo> getByPageNotAllData(VehiclePageQueryVo vehiclePageQueryVo, List<Integer> companyList) throws Exception {
        Map<String, Object> params = PropertyUtils.describe(vehiclePageQueryVo);
        Integer pageSize = (Integer) params.get("limit");
        params.remove("pageSize");
        Integer pageNo = (Integer) params.get("page");
        params.remove("pageNo");
        //处理预定日期相关参数
        adjustBookedInfoParam(params, vehiclePageQueryVo);
        if (companyList != null && companyList.size() > 0) {
            params.put("companyList", companyList);
        } else {
            params.put("companyList", Arrays.asList(-1));
        }
        PageHelper.startPage(pageNo, pageSize);
        List<ResultVehicleVo> vehicles = mapper.getByPageNotAllData(params);
        PageInfo<ResultVehicleVo> vehiclePageInfo = new PageInfo<>(vehicles);
        return PageDataVO.pageInfo(vehiclePageInfo);
    }

    public List<Integer> dataCompany(String dataZone, String dataCompany) {
        List<BranchCompany> zoneCompanys = branchCompanyBiz.dataCompany(dataZone);
        //((VehicleBiz)AopContext.currentProxy()).dataCompany(dataZone);
        List<Integer> zoneCompanyIds = zoneCompanys.parallelStream().map(BranchCompany::getId).collect(Collectors.toList());
        if (StringUtils.isNotBlank(dataCompany)) {
            List<Integer> dataCompanyIds = Arrays.asList(dataCompany.split(",")).parallelStream().map(s -> Integer.valueOf(s)).collect(Collectors.toList());
            zoneCompanyIds.addAll(dataCompanyIds);
        }
        //去重
        return zoneCompanyIds.parallelStream().distinct().collect(Collectors.toList());
    }

    public PageDataVO<UsableVehicleModelVO> searchUsableModel(UsableVeicleDTO dto) {
        Map<String, Object> params = BeanUtil.beanToMap(dto);
        if (StrUtil.isNotBlank(dto.getStartDate()) && StrUtil.isNotBlank(dto.getEndDate())) {
            initBookSearchParam(dto, params);
        }
        return PageDataVO.pageInfo(dto.getPage(), dto.getLimit(), () -> mapper.searchUsableModel(params));
    }

    public PageDataVO<UsableVehicleModelVO> hotModel(HotVeicleDTO dto) {
        dto.setHotSign(1);
        Map<String, Object> params = BeanUtil.beanToMap(dto);
        return PageDataVO.pageInfo(dto.getPage(), dto.getLimit(), () -> mapper.searchUsableModel(params));
    }

    private void initBookSearchParam(UsableVeicleDTO dto, Map<String, Object> params) {
        List<String> notBookedDates = Lists.newArrayList();
        if (StringUtils.isNotBlank(dto.getStartDate()) &&
                StringUtils.isNotBlank(dto.getEndDate())) {
//            notBookedDates = convertDatePeriod2List(dto.getStartDate(),
//                    dto.getEndDate());
            notBookedDates = convertDatePeriod2List(dto.getStartDateExtend(),
                    dto.getEndDateExtend());
        }

        //若传入预定信息查询条件，则查询对应月份预定信息查询条件（不超过3个月）
        if (CollectionUtils.isEmpty(notBookedDates)) {
            throw new BaseException(ResultCode.NOTEXIST_CODE);
        }

        //筛选出查询条件所在月份 如 想预定 8月29 ~ 9月 4日
        Set<String> allYearMonth = new HashSet<>();//记录所有年月 "2019-08","2019-09"
        Map<String, List<String>> yearMonthAndDate = new HashMap<>();//需比较已预定的年月 - 预定日期列表 为空（拒绝 预约记录时用到）
        Map<String, List<String>> yearMonthAndDateNotBooked = new HashMap<>();//需比较未预定的年月 - 未预定日期列表 "2019-08-29"...."2019-09-04",
        classifyByYearMonth(notBookedDates, yearMonthAndDateNotBooked, allYearMonth);
        if (allYearMonth.size() > MAX_MONTH_COUNT_BOOKED_INFO_QUERY) {
            throw new BaseException(" only 3 month can be included <bookedInfo> param.");
        }

        //加入预定信息查询条件
        fillBookedDateSearchParam(params, yearMonthAndDate, yearMonthAndDateNotBooked);
    }

    public PageDataVO<Vehicle> searchUsableVehicle(RentVehicleBookDTO dto) {
        Map<String, Object> params = BeanUtil.beanToMap(dto);
        List<String> notBookedDates = Lists.newArrayList();
        if (StringUtils.isNotBlank(dto.getBookEndDate()) &&
                StringUtils.isNotBlank(dto.getBookEndDate())) {
//            notBookedDates = convertDatePeriod2List(dto.getBookStartDate(),
//                    dto.getBookEndDate());
            notBookedDates = convertDatePeriod2List(dto.getStartDateExtend(),
                    dto.getEndDateExtend());
        }

        //若传入预定信息查询条件，则查询对应月份预定信息查询条件（不超过3个月）
        if (CollectionUtils.isEmpty(notBookedDates)) {
            //return;
        }
        //筛选出查询条件所在月份
        Set<String> allYearMonth = new HashSet<>();//记录所有年月
        Map<String, List<String>> yearMonthAndDate = new HashMap<>();//预定年月 - 预定日期列表
        Map<String, List<String>> yearMonthAndDateNotBooked = new HashMap<>();//未预定年月 - 未预定日期列表
        classifyByYearMonth(notBookedDates, yearMonthAndDateNotBooked, allYearMonth);
        if (allYearMonth.size() > MAX_MONTH_COUNT_BOOKED_INFO_QUERY) {
            throw new BaseException(" only 3 month can be included <bookedInfo> param.");
        }
        //加入预定信息查询条件
        fillBookedDateSearchParam(params, yearMonthAndDate, yearMonthAndDateNotBooked);

        PageHelper.startPage(dto.getPage(), dto.getLimit());
        List<Vehicle> lists = mapper.searchUsableVehicle(params);
        PageInfo<Vehicle> usableVehicleInfo = new PageInfo<>(lists);
        return PageDataVO.pageInfo(usableVehicleInfo);
    }

    /**
     * 保存
     *
     * @param vehicle
     * @return
     */
    @Transactional
    public ObjectRestResponse save(Vehicle vehicle) {
        if (vehicle == null) {
            ObjectRestResponse.paramIsEmpty();
        }

        String id = vehicle.getId();
        if (StringUtils.isBlank(id)) {
            insertSelective(vehicle);
            insertSelective(vehicle);
        } else {
            updateSelectiveById(vehicle);
        }
        return ObjectRestResponse.succ();
    }

    /**
     * 查找
     *
     * @param vehicleVo
     * @param page
     * @param limit
     * @return
     */
    public ObjectRestResponse findListByVehicle(VehicleVo vehicleVo, Integer page, Integer limit) {
        Vehicle vehicle = new Vehicle();
        // vehicleVo复制一份到vehicle

        try {
            BeanUtilsBean.getInstance().copyProperties(vehicle, vehicleVo);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            log.info("获取车辆列表转换对象失败： vehicleVo = {}", vehicleVo);
        }


        WeekendSqls<Vehicle> sqls = WeekendSqls.<Vehicle>custom();
        //车辆状态
        if (vehicle.getStatus() != null || vehicle.getStatus() != 0) {
            sqls.andEqualTo(Vehicle::getStatus, vehicle.getStatus());
        }
        //车牌号
        if (StringUtils.isBlank(vehicle.getNumberPlate())) {
            sqls.andEqualTo(Vehicle::getNumberPlate, vehicle.getNumberPlate());
            Function<Vehicle, Integer> getStatus = Vehicle::getStatus;
        }

        //公司
        if (vehicle.getSubordinateBranch() == null || vehicle.getSubordinateBranch() == 0) {
            sqls.andEqualTo(Vehicle::getSubordinateBranch, vehicle.getSubordinateBranch());
        }

        //大区
        if (vehicleVo.getZoneId() == null || vehicleVo.getZoneId() == 0) {
            //获取大区内的公司id
            Example example = Example.builder(BranchCompany.class).where(WeekendSqls.<BranchCompany>custom()
                    .andEqualTo(BranchCompany::getZoneId, vehicleVo.getZoneId())).build();
            List<BranchCompany> branchCompanies = branchCompanyBiz.selectByExample(example);
            ArrayList<Integer> bIds = new ArrayList<>();
            for (BranchCompany branchCompany : branchCompanies) {
                bIds.add(branchCompany.getId());
            }
            sqls.andIn(Vehicle::getSubordinateBranch, bIds);
        }
        sqls.andEqualTo(Vehicle::getStatus, 0);
        Example exa = Example.builder(Vehicle.class).where(sqls).build();
        PageDataVO<Object> objectPageDataVO = PageDataVO.pageInfo(page, limit, () -> selectByExample(exa));
        return ObjectRestResponse.succ(objectPageDataVO);
    }

    /**
     * 查询条
     *
     * @param id
     * @return
     */
    public ObjectRestResponse getOneById(Integer id) {

        return ObjectRestResponse.succ(selectByExample(
                Example.builder(Vehicle.class)
                        .where(WeekendSqls.<Vehicle>custom()
                                .andEqualTo(Vehicle::getId, id)
                                .andEqualTo(Vehicle::getStatus, 0))
                        .build()));

    }

    public List<VehicleCountVo> countVehicleByParam(VehiclePlanDto vehiclePlanDto) {
        return mapper.countVehicleByParam(vehiclePlanDto);
    }

    public PageDataVO<VehicleAndModelInfoVo> getAllVehicle(VehiclePlanDto vehiclePlanDto) {
        Query query = new Query(vehiclePlanDto);
        PageDataVO<VehicleAndModelInfoVo> pageDataVO = PageDataVO.pageInfo(query, () -> mapper.getAllVehicle(query.getSuper()));
        return pageDataVO;
    }

    public ObjectRestResponse<PageDataVO<VehicleAndModelInfoVo>> getVehicle(VehiclePlanDto vehiclePlanDto) {
        Integer page = vehiclePlanDto.getPage() == null ? 1 : vehiclePlanDto.getPage();
        Integer limit = vehiclePlanDto.getLimit() == null ? 10 : vehiclePlanDto.getLimit();
        vehiclePlanDto.setPage(page);
        vehiclePlanDto.setLimit(limit);
        UserDTO userDTO = getAdminUserInfo();
        if (userDTO == null) {
            return ObjectRestResponse.createFailedResult(ResultCode.RSTOKEN_EXPIRED_CODE, "token失效");
        }
        List<Integer> companyList = Lists.newArrayList();
        List<BranchCompany> branchCompany = branchCompanyBiz.getListByUser(userDTO);
        companyList = branchCompany.stream().map(BranchCompany::getId).collect(Collectors.toList());

        if (vehiclePlanDto.getParkBranchCompanyId() != null) {
            if (companyList.size() > 0) {
                if (companyList.contains(vehiclePlanDto.getParkBranchCompanyId())) {
                    companyList.clear();
                    companyList.add(vehiclePlanDto.getParkBranchCompanyId());
                } else {
                    return ObjectRestResponse.succ();
                }
            } else {
                companyList.add(vehiclePlanDto.getParkBranchCompanyId());
            }
        }
        log.info("用户权限公司ID: companyList = {}", companyList);
        vehiclePlanDto.setCompanyIds(companyList);
        Query query = new Query(vehiclePlanDto);
        PageDataVO<VehicleAndModelInfoVo> pageDataVO = PageDataVO.pageInfo(query, () -> mapper.getVehicle(query.getSuper()));
        return ObjectRestResponse.succ(pageDataVO);
    }

    //删除车辆信息
    public RestResponse delVehicleById(String id) {
        Vehicle vehicle = new Vehicle();
        vehicle.setId(id);
        vehicle.setIsDel(1);
        updateSelectiveById(vehicle);
        return RestResponse.suc();
    }


    public List<String> getHourList(String result) {
        StringBuffer stringBuffer = new StringBuffer();
        if (result.length() < 24) {//如果不足24位补0
            for (int i = 0; i < 24 - result.length(); i++) {
                stringBuffer.append("0");
            }
            log.info(stringBuffer.toString());
        }
        stringBuffer.append(result);
        List<String> list = new ArrayList<>(24);
        for (int i = 0; i < stringBuffer.toString().length(); i++) {
            if ("1".equals(stringBuffer.toString().charAt(i) + "")) {
                list.add(i, "1");
            } else {
                list.add(i, "0");
            }
        }
        return list;
    }

    public List<String> findExistVehicleIds() {
        return mapper.findExistVehicleIds();
    }

    public List<String> findVehicleIdsByPlateNumber(String plateNumber) {
         List<String> vehicleIds = Lists.newArrayList();
        Example example = new Example(Vehicle.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andLike("numberPlate",String.format("%%%s%%",plateNumber.trim()));
        List<Vehicle> vehicles = mapper.selectByExample(example);
        if (CollectionUtils.isEmpty(vehicles)){
            return vehicleIds;
        }
        return vehicles.stream().map(Vehicle::getId).collect(Collectors.toList());
    }
}
