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;

    @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.NORMAL.getCode())) {
            throw new BaseException(ResCode.VEHICLE_DEPARTURE_VEHICLE_DISABLE.getDesc() + ", 车辆状态是：" + getVehicleStatus(vehicle.getStatus(), vehicle.getId()),
                    ResCode.VEHICLE_DEPARTURE_VEHICLE_DISABLE.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,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(), bookVehicleVo.getNotCheckTimeLegal());
        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 = null;
        if(bookVehicleVo.getVehicleBookRecordId() == null) {
            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());
            vehicleBookRecord.setBookStartDate(startDay.toDate());
            vehicleBookRecord.setBookEndDate(endDay.toDate());
            vehicleBookRecordBiz.updateSelectiveByIdRe(vehicleBookRecord);
        }

//        //添加预定时间记录
        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;
    }


    @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) != andOperationRs)){
            //当天已经被预定检查小时是否也被预定
            return filterHourInfoBooked(vehicleId, hourInfo);
        }
        return Boolean.TRUE;
    }


    public boolean filterHourInfoBooked(String vehicleId, Map<String, Integer> hourInfo) {
        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()) == entry.getValue()) { // 已经被预定
                    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.NORMAL.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);
        if(vehicleBookRecordVos != null && vehicleBookRecordVos.size() > 0) {
            stringBuilder.append("中，使用人：");
            stringBuilder.append(vehicleBookRecordVos.get(0).getVehicleUsername());
            stringBuilder.append(" 使用人电话：");
            stringBuilder.append(vehicleBookRecordVos.get(0).getVehicleUserPhone());
        }
        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 uinbookDateList4DatePeriod(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);
        //转换日期范围为列表，并检查是否合法
        uinbookDateList4DatePeriod(yearMonthAndDate,startDay,endDay);
        //原设计为 读取 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) {
        boolean flag = false;
        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() | 0) == 0) {
                   if(!flag) {
                       Integer effected = vehicleBookInfoMapper.updateBookedInfo(params);
                       if (effected < 1) {
                           return Boolean.FALSE;
                       } else {
                           flag = true;
                           continue;
                       }
                   }
               } else {
                   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);
        }
    }

    /**
     * 根据月份分类到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());
        }

        //若传入预定信息查询条件，则查询对应月份预定信息查询条件（不超过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());
        }

        //若传入预定信息查询条件，则查询对应月份预定信息查询条件（不超过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();
    }
}
