package com.objecteye.service.impl; import cn.hutool.core.date.DateUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.objecteye.entity.*; import com.objecteye.pojo.RabbitMQVehicle; import com.objecteye.service.IVehicleFileService; import com.objecteye.service.SpecialtyServices; import com.objecteye.utils.CompareDistance; import com.objecteye.utils.GlobalUtil; import com.objecteye.utils.RelationMappingUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.aggregation.Aggregation; import org.springframework.data.mongodb.core.aggregation.AggregationResults; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * 一车一档实现类 * * @author liuhaoyu */ @Component public class VehicleFileServiceImpl implements IVehicleFileService { /** * redis中保存的key-value集合 */ private final String VEHICLE_FILE_PLATE_NUMBER = "vehicleFilePlateNumber"; private final String URL = "http://192.168.10.4:10002/vehicle/analysisFile"; private final double FEATURE_LOWER_LIMIT = 0.8; /** * 车牌号所在字段 */ private final String vehiclePlateHphm = "vehicle_plate_hphm"; @Autowired private MongoTemplate mongoTemplate; @Autowired private RedisTemplate redisTemplate; @Autowired private CompareDistance compareDistance; @Autowired private SpecialtyServices specialtyServices; /** * 档案浏览查询(分页) * * @param vehicleFileParam 请求参数 * @return 结果集 */ @Override public PageResult fileQueryByPage(VehicleFileParam vehicleFileParam) { // 实际需要返回的数据 List resultList = fileResultHandle(vehicleFileParam); int pageNum = vehicleFileParam.getCurrentpage(); int pageSize = vehicleFileParam.getPagevolume(); if (pageSize <= 0) { return new PageResult<>(0, new ArrayList<>()); } // 总页数 int pageTotal = (resultList.size() / pageSize) + ((resultList.size() % pageSize > 0) ? 1 : 0); if (pageTotal == 0) { return new PageResult<>(pageTotal, new ArrayList<>()); } int fromIndex = (pageNum - 1) * pageSize; int toIndex; if (pageNum == pageTotal) { toIndex = resultList.size(); } else { toIndex = pageNum * pageSize; if (toIndex > resultList.size()) { toIndex = resultList.size(); } if (fromIndex >= toIndex) { return new PageResult<>(pageTotal, new ArrayList<>()); } } return new PageResult<>(pageTotal, resultList.subList(fromIndex, toIndex)); } /** * 档案浏览查询(不分页) * * @param vehicleFileParam 请求参数 * @return 结果集 */ @Override public List fileQuery(VehicleFileParam vehicleFileParam) { return fileResultHandle(vehicleFileParam); } /** * 档案浏览查询 最终数据处理 * * @param vehicleFileParam 请求参数 * @return 结果集 */ private List fileResultHandle(VehicleFileParam vehicleFileParam) { List plateNumberList = getRightDataByCondition(vehicleFileParam); Map vehicleCountMap = getVehicleFileCountMap(plateNumberList); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); int fileMinNumber = vehicleFileParam.getFileMinNumber(); // 把符合条件的数据汇总 List resultList = new ArrayList<>(); Map vehicleFileBaseMap = redisTemplate.opsForHash().entries(VEHICLE_FILE_PLATE_NUMBER); for (Map.Entry entry : vehicleFileBaseMap.entrySet()) { if (!plateNumberList.contains(entry.getKey())) { continue; } RabbitMQVehicle rabbitMqVehicle = JSON.parseObject(entry.getValue(), RabbitMQVehicle.class); // 是否最后出现时间 if (vehicleFileParam.getLastAppear() == 1) { Long lastAppearTime = rabbitMqVehicle.getPictime(); // 最新的出现时间在时间范围内的显示出来, 不在范围内的不做查询 if (null != lastAppearTime) { // 起始时间 if (null != vehicleFileParam.getStartTime() && lastAppearTime < vehicleFileParam.getStartTime()) { continue; } // 结束时间 if (null != vehicleFileParam.getEndTime() && lastAppearTime > vehicleFileParam.getEndTime()) { continue; } } } // 最小出现数 if (fileMinNumber > vehicleCountMap.get(entry.getKey())) { continue; } VvehicleFileQueryResult vehicleFileQueryResult = new VvehicleFileQueryResult(); vehicleFileQueryResult.setAppearNum(vehicleCountMap.get(entry.getKey())); vehicleFileQueryResult.setId(rabbitMqVehicle.getVehicle_plate_hphm()); vehicleFileQueryResult.setPictime(simpleDateFormat.format(new Date(rabbitMqVehicle.getPictime()))); vehicleFileQueryResult.setPicurl(rabbitMqVehicle.getSnapshoturl()); resultList.add(vehicleFileQueryResult); } // 时间倒序 resultList.sort((o1, o2) -> { if (DateUtil.parse(o2.getPictime()).getTime() - DateUtil.parse(o1.getPictime()).getTime() > 0) { return 1; } else if (DateUtil.parse(o2.getPictime()).getTime() - DateUtil.parse(o1.getPictime()).getTime() < 0) { return -1; } else { return 0; } }); return resultList; } /** * 获取符合条件的车辆数据 * * @param vehicleFileParam 请求参数 * @return 结果集- 车牌号 */ private List getRightDataByCondition(VehicleFileParam vehicleFileParam) { // 拼接过滤条件 获取条件下指定车牌号的数量 // 起始时间 // 结束时间 Criteria criteria = new Criteria(); if (null != vehicleFileParam.getStartTime() && null != vehicleFileParam.getEndTime()) { criteria.and("pictime").gte(vehicleFileParam.getStartTime()).lte(vehicleFileParam.getEndTime()); } else if (null != vehicleFileParam.getStartTime() && null == vehicleFileParam.getEndTime()) { criteria.and("pictime").gte(vehicleFileParam.getStartTime()); } else if (null == vehicleFileParam.getStartTime() && null != vehicleFileParam.getEndTime()) { criteria.and("pictime").lte(vehicleFileParam.getEndTime()); } // 车牌号 if (null != vehicleFileParam.getPlateNumber() && !"".equals(vehicleFileParam.getPlateNumber())) { String plateNumber = vehicleFileParam.getPlateNumber(); plateNumber = plateNumber.replaceAll("\\?", "\\\\S").replaceAll("\\*", ".*"); Pattern pattern = Pattern.compile(plateNumber, Pattern.CASE_INSENSITIVE); criteria.and(vehiclePlateHphm).regex(pattern); } Query query = Query.query(criteria); query.fields().include(vehiclePlateHphm); List rabbitMqVehicles = mongoTemplate.find(query, RabbitMQVehicle.class); return rabbitMqVehicles.stream().map(RabbitMQVehicle::getVehicle_plate_hphm).distinct().collect(Collectors.toList()); } /** * 档案浏览查询 获取过滤条件下数据的档案出现次数 * * @param plateNumberList 需要查询数量的车牌号 * @return 结果集 */ private Map getVehicleFileCountMap(List plateNumberList) { Aggregation aggregation = Aggregation.newAggregation( Aggregation.project(vehiclePlateHphm), Aggregation.match(Criteria.where(vehiclePlateHphm).in(plateNumberList)), Aggregation.group(vehiclePlateHphm).count().as("count"), Aggregation.project(vehiclePlateHphm, "count")); AggregationResults rabbitMqVehicles = mongoTemplate.aggregate(aggregation, "rabbitMQVehicle", RabbitMQVehicle.class); // 符合时间范围的档案出现次数结果集 List countVehicles = rabbitMqVehicles.getMappedResults(); Map vehicleFileCountMap = new HashMap<>(); for (RabbitMQVehicle rabbitMqVehicle : countVehicles) { if (null == rabbitMqVehicle) { continue; } vehicleFileCountMap.put(rabbitMqVehicle.getId(), Long.parseLong(String.valueOf(rabbitMqVehicle.getCount()))); } return vehicleFileCountMap; } /** * 车牌搜档(分页) * * @param filePlateNumParam 页面容量 * @return 结果集 */ @Override public PageResult plateNumberQueryByPage(VehicleFilePlateNumParam filePlateNumParam) { String plateNumber = filePlateNumParam.getPlateNumber(); int pageNum = filePlateNumParam.getCurrentpage(); int pageSize = filePlateNumParam.getPagevolume(); if (pageSize <= 0) { return new PageResult<>(0, new ArrayList<>()); } // 数据获取 List rabbitMqVehicles = mongoTemplate.find(Query.query(Criteria.where(vehiclePlateHphm).is(plateNumber)) .with(Sort.by(Sort.Order.desc("pictime"))) .limit(pageSize).skip((pageNum - 1) * pageSize), RabbitMQVehicle.class); // 数据条数获取 long totalRows = mongoTemplate.count(Query.query(Criteria.where(vehiclePlateHphm).is(plateNumber)), RabbitMQVehicle.class); // 总页数 long pageTotal = (totalRows / pageSize) + ((totalRows % pageSize > 0) ? 1 : 0); return new PageResult<>(pageTotal, cleanDateToSimple(rabbitMqVehicles)); } /** * 车牌搜档(不分页) * * @param filePlateNumParam 请求参数 * @return 结果集 */ @Override public List plateNumberQuery(VehicleFilePlateNumParam filePlateNumParam) { List rabbitMqVehicles = mongoTemplate.find(Query.query(Criteria.where(vehiclePlateHphm).is(filePlateNumParam.getPlateNumber())), RabbitMQVehicle.class); return cleanDateToSimple(rabbitMqVehicles); } /** * 把RabbitMQVehicle格式的数据转换为输出视图数据模型数据 * * @param rabbitMqVehicles RabbitMQVehicle类型数据集合 * @return 结果集 */ private List cleanDateToSimple(List rabbitMqVehicles) { List resultList = new ArrayList<>(); String dateFormat = "yyyy-MM-dd HH:mm:ss"; for (RabbitMQVehicle rabbitMqVehicle : rabbitMqVehicles) { if (null == rabbitMqVehicle) { continue; } VvehicleFilePlateNumberQueryResult tempResult = new VvehicleFilePlateNumberQueryResult(); tempResult.setPicurl(rabbitMqVehicle.getSnapshoturl()); tempResult.setPlateNumber(rabbitMqVehicle.getVehicle_plate_hphm()); tempResult.setType(rabbitMqVehicle.getVehicle_recg_type()); tempResult.setColor(RelationMappingUtil.getVehicleColor(rabbitMqVehicle.getVehicle_color_index())); tempResult.setLatitude(rabbitMqVehicle.getLatitude()); tempResult.setLongitude(rabbitMqVehicle.getLongitude()); tempResult.setBrand(rabbitMqVehicle.getVehicle_recg_brand()); List styleList = new ArrayList<>(); if (null != rabbitMqVehicle.getVehicle_recg_brand() && !"".equals(rabbitMqVehicle.getVehicle_recg_brand())) { styleList.add(rabbitMqVehicle.getVehicle_recg_brand()); } if (null != rabbitMqVehicle.getVehicle_recg_subbrand() && !"".equals(rabbitMqVehicle.getVehicle_recg_subbrand())) { styleList.add(rabbitMqVehicle.getVehicle_recg_subbrand()); } if (null != rabbitMqVehicle.getVehicle_recg_issue_year() && !"".equals(rabbitMqVehicle.getVehicle_recg_issue_year())) { styleList.add(rabbitMqVehicle.getVehicle_recg_issue_year()); } String style = String.join("-", styleList); tempResult.setStyle(style); tempResult.setPictime(DateUtil.format(new Date(rabbitMqVehicle.getPictime()), dateFormat)); tempResult.setId(rabbitMqVehicle.getId()); resultList.add(tempResult); } return resultList; } /** * 以图搜档 * * @param gcxh 车辆序号 * @param multipartFile 请求参数 * @return 结果集 */ @Override public VvehicleFilePicQueryResult picQuery(int gcxh, MultipartFile multipartFile) { String fileName = multipartFile.getOriginalFilename(); String picPath = GlobalUtil.dbPath1() + File.separator + "picture" + File.separator + fileName; File newFile = new File(picPath); try { multipartFile.transferTo(newFile); } catch (IOException e) { e.printStackTrace(); } String response = GlobalUtil.httpExecute(URL, newFile); newFile.delete(); if (null == response || "".equals(response)) { return null; } JSONObject finalObj = getMostSimilarOne(gcxh, response); if (finalObj == null) { return null; } String plateNumber = finalObj.getString(vehiclePlateHphm); // 获取redis中保存的最新数据 String lastData = redisTemplate.opsForHash().get(VEHICLE_FILE_PLATE_NUMBER, plateNumber).toString(); RabbitMQVehicle rabbitMqVehicle = JSON.parseObject(lastData, RabbitMQVehicle.class); VvehicleFilePicQueryResult picQueryResult = new VvehicleFilePicQueryResult(); // 相似度 picQueryResult.setFeature(finalObj.getString("threshold")); // 图片地址 picQueryResult.setUrl(rabbitMqVehicle.getPicurl()); // 抓拍时间 picQueryResult.setPictime(rabbitMqVehicle.getPictime()); // 车牌号 picQueryResult.setPlateNumber(plateNumber); return picQueryResult; } /** * 获取最相似的车辆信息 * * @param gcxh 车辆序号 * @param response 图片文件 * @return 车辆对应数据 */ @Override public JSONObject getMostSimilarOne(int gcxh, String response) { JSONObject jsonObject = JSON.parseObject(response); JSONArray infoArr = jsonObject.getJSONObject("result").getJSONArray("info"); if (null == infoArr || infoArr.size() == 0) { return null; } List infoList = infoArr.toJavaList(JSONObject.class); JSONObject infoObj = infoList.get(gcxh); //获取车辆特征值 JSONObject vehicleFeaRes = infoObj.getJSONObject("vehicle_fea_res"); JSONArray featureArr = vehicleFeaRes.getJSONArray("feature"); double[] featureNew = new double[featureArr.size()]; for (int i = 0; i < featureArr.size(); i++) { featureNew[i] = featureArr.getDoubleValue(i); } // 选中车辆的特征值信息 double currentFeature = compareDistance.getDistance(featureNew); Map redisMap = redisTemplate.opsForHash().entries(VEHICLE_FILE_PLATE_NUMBER); List tempInfoList = new ArrayList<>(); // 相似度字段名 final String thresholdStr = "threshold"; for (Map.Entry entry : redisMap.entrySet()) { RabbitMQVehicle rabbitMqVehicle = JSON.parseObject(entry.getValue(), RabbitMQVehicle.class); if (null == rabbitMqVehicle) { continue; } double[] dbFeature = rabbitMqVehicle.getVehicle_fea_feature(); // 缓存中车辆的特征值信息 double dbDistance = compareDistance.getDistance(dbFeature); // 计算相似度 double threshold = compareDistance.simDistance(featureNew, currentFeature, dbFeature, dbDistance); // 计算结果是NaN的情况不计入数据 if (Double.isNaN(threshold)) { continue; } JSONObject tempObj = JSON.parseObject(JSON.toJSONString(rabbitMqVehicle)); // 相似度保留2位小数 DecimalFormat decimalFormat = new DecimalFormat("#0.00"); if (Double.parseDouble(decimalFormat.format(threshold)) >= 1) { tempObj.put(thresholdStr, "1"); } else { tempObj.put(thresholdStr, decimalFormat.format(threshold)); } tempInfoList.add(tempObj); } // 获取相似度最高的一项 JSONObject finalObj = Collections.max(tempInfoList, Comparator.comparingDouble(value -> value.getDoubleValue(thresholdStr))); // 低于相似值下限则认为没有相关档案 if (FEATURE_LOWER_LIMIT > finalObj.getDoubleValue(thresholdStr)) { return null; } return finalObj; } /** * 通过过车序号查询车辆的信息(转base64解析图片) * * @param gcxh 车辆序号 * @param picfile 图片文件 * @return 结果集 */ @Override public JSONObject findByGcxh(int gcxh, MultipartFile picfile) { return specialtyServices.findByGcxh(gcxh, picfile); } /** * 司机信息 * * @param params 请求参数 * @return 结果集 */ @Override public PageResult driverFiles(VehicleFilePlateNumParam params) { Criteria criteria = new Criteria(); criteria.and(vehiclePlateHphm).is(params.getPlateNumber()); criteria.and("personUrl").ne(null); Query query = Query.query(criteria); query.fields().include("pictime"); query.fields().include(vehiclePlateHphm); query.fields().include("longitude"); query.fields().include("latitude"); query.fields().include("personUrl"); query.fields().include("equipmentName"); query.fields().include("id"); List rabbitMqVehicles = mongoTemplate.find(query.with(Sort.by(Sort.Order.desc("pictime"))) .limit(params.getPagevolume()) .skip((params.getCurrentpage() - 1) * params.getPagevolume()), RabbitMQVehicle.class); List resultList = new ArrayList<>(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (RabbitMQVehicle rabbitMqVehicle : rabbitMqVehicles) { if (null == rabbitMqVehicle) { continue; } VvehicleFileDriverResult tempResult = JSON.parseObject(JSON.toJSONString(rabbitMqVehicle), VvehicleFileDriverResult.class); tempResult.setPicurl(rabbitMqVehicle.getPersonUrl()); tempResult.setPlateNumber(rabbitMqVehicle.getVehicle_plate_hphm()); tempResult.setPictime(simpleDateFormat.format(new Date(rabbitMqVehicle.getPictime()))); resultList.add(tempResult); } // 总条数 long total = mongoTemplate.count(query, RabbitMQVehicle.class); // 总页数 int totalPage = (int) Math.ceil((double) total / params.getPagevolume()); return new PageResult<>(totalPage, resultList); } }