package com.ycwl.basic.controller.viid; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.ycwl.basic.annotation.IgnoreLogReq; import com.ycwl.basic.annotation.IgnoreToken; import com.ycwl.basic.annotation.RequestToFile; import com.ycwl.basic.aspectj.HttpSaver; import com.ycwl.basic.facebody.FaceBodyFactory; import com.ycwl.basic.facebody.adapter.IFaceBodyAdapter; import com.ycwl.basic.facebody.entity.AddFaceResp; import com.ycwl.basic.mapper.DeviceMapper; import com.ycwl.basic.mapper.FaceSampleMapper; import com.ycwl.basic.mapper.SourceMapper; import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity; import com.ycwl.basic.model.pc.device.entity.DeviceEntity; import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity; import com.ycwl.basic.model.pc.scenic.entity.ScenicConfigEntity; import com.ycwl.basic.model.pc.source.entity.SourceEntity; import com.ycwl.basic.model.viid.entity.DeviceIdObject; import com.ycwl.basic.model.viid.entity.FaceListObject; import com.ycwl.basic.model.viid.entity.FaceObject; import com.ycwl.basic.model.viid.entity.FacePositionObject; import com.ycwl.basic.model.viid.entity.ImageListObject; import com.ycwl.basic.model.viid.entity.ImageObject; import com.ycwl.basic.model.viid.entity.ResponseStatusObject; import com.ycwl.basic.model.viid.entity.SubImageInfoObject; import com.ycwl.basic.model.viid.entity.SubImageList; import com.ycwl.basic.model.viid.entity.SystemTimeObject; import com.ycwl.basic.model.viid.req.FaceUploadReq; import com.ycwl.basic.model.viid.req.ImageUploadReq; import com.ycwl.basic.model.viid.req.KeepaliveReq; import com.ycwl.basic.model.viid.req.RegisterReq; import com.ycwl.basic.model.viid.req.UnRegisterReq; import com.ycwl.basic.model.viid.resp.SystemTimeResp; import com.ycwl.basic.model.viid.resp.VIIDBaseResp; import com.ycwl.basic.repository.DeviceRepository; import com.ycwl.basic.repository.ScenicRepository; import com.ycwl.basic.service.pc.ScenicService; import com.ycwl.basic.service.task.TaskFaceService; import com.ycwl.basic.storage.StorageFactory; import com.ycwl.basic.storage.adapters.IStorageAdapter; import com.ycwl.basic.storage.enums.StorageAcl; import com.ycwl.basic.storage.utils.StorageUtil; import com.ycwl.basic.task.DynamicTaskGenerator; import com.ycwl.basic.utils.ImageUtils; import com.ycwl.basic.utils.IpUtils; import com.ycwl.basic.utils.SnowFlakeUtil; import io.swagger.annotations.Api; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import static com.ycwl.basic.constant.StorageConstant.PHOTO_PATH; import static com.ycwl.basic.service.task.impl.TaskFaceServiceImpl.generateEntityId; @IgnoreToken @RestController @Api(tags = "摄像头对接接口") @RequestMapping("/VIID") @Slf4j public class ViidController { @Autowired private DeviceMapper deviceMapper; private static final String serverId = "00000000000000000001"; @Autowired private SourceMapper sourceMapper; @Autowired private DeviceRepository deviceRepository; @Autowired private ScenicRepository scenicRepository; @Autowired private TaskFaceService taskFaceService; private final Map executors = new ConcurrentHashMap<>(); @Autowired private ScenicService scenicService; private ThreadPoolExecutor getExecutor(String deviceId) { ThreadPoolExecutor executor = executors.get(deviceId); if (executor == null) { executor = new ThreadPoolExecutor(4, 4096, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(4096)); executors.put(deviceId, executor); } return executor; } // region 注册注销基础接口 /** * 注册接口 * * @param req 注册的信息 * @param request 请求 * @return 返回 */ @RequestMapping(value = "/System/Register", method = RequestMethod.POST) public VIIDBaseResp register(@RequestBody RegisterReq req, HttpServletRequest request) { DeviceIdObject deviceIdObject = req.getRegisterObject(); log.info("注册的设备信息:{}", deviceIdObject); // 保存设备注册时间 String deviceId = deviceIdObject.getDeviceId(); DeviceEntity device = deviceRepository.getDeviceByDeviceNo(deviceId); if (device == null) { device = new DeviceEntity(); device.setName("未配置设备"); device.setNo(deviceId); device.setOnline(1); } device.setKeepaliveAt(new Date()); device.setIpAddr(IpUtils.getIpAddr(request)); if (device.getId() != null) { deviceMapper.updateEntity(device); } else { device.setId(SnowFlakeUtil.getLongId()); deviceMapper.addEntity(device); deviceRepository.clearDeviceCache(deviceId); } return new VIIDBaseResp( new ResponseStatusObject(serverId, "/VIID/System/Register", "0", "注册成功", sdfTime.format(new Date())) ); } /** * 保活接口 * * @param req 保活的设备信息 * @param request 请求 * @return 返回 */ @IgnoreLogReq @RequestMapping(value = "/System/Keepalive", method = RequestMethod.POST) public VIIDBaseResp keepalive(@RequestBody KeepaliveReq req, HttpServletRequest request) { DeviceIdObject keepaliveObject = req.getKeepaliveObject(); // log.info("对方发送的心跳的信息:{}", keepaliveObject); String deviceId = keepaliveObject.getDeviceId(); DeviceEntity device = deviceRepository.getDeviceByDeviceNo(deviceId); // 判断设备状态 if (device == null) { // 不存在设备就注册 device = new DeviceEntity(); device.setName("未配置设备"); device.setNo(deviceId); device.setOnline(1); device.setKeepaliveAt(new Date()); device.setIpAddr(IpUtils.getIpAddr(request)); device.setId(SnowFlakeUtil.getLongId()); deviceMapper.addEntity(device); deviceRepository.clearDeviceCache(deviceId); } else { deviceRepository.updateOnlineStatus(device.getId(), IpUtils.getIpAddr(request), 1, new Date()); } // log.info("已经解析过的心跳信息:{}", keepaliveObject); return new VIIDBaseResp( new ResponseStatusObject(deviceId, "/VIID/System/Keepalive", "0", "保活", sdfTime.format(new Date())) ); } /** * 注销设备 * * @param req 参数 * @return 返回 */ @RequestMapping(value = "/System/UnRegister", method = RequestMethod.POST) public VIIDBaseResp unRegister(@RequestBody UnRegisterReq req, HttpServletRequest request) { // 获取设备id DeviceIdObject unRegisterObject = req.getUnRegisterObject(); String deviceId = unRegisterObject.getDeviceId(); log.info("获取的注销的请求参数:{}", unRegisterObject); // 首先查询该设备是否存在 DeviceEntity device = deviceRepository.getDeviceByDeviceNo(deviceId); // 判断 if (device != null) { deviceRepository.updateOnlineStatus(device.getId(), IpUtils.getIpAddr(request), 0, new Date()); } return new VIIDBaseResp( new ResponseStatusObject(deviceId, "/VIID/System/UnRegister", "0", "注销成功", sdfTime.format(new Date())) ); } /** * 校时接口 * * @return 返回 */ @RequestMapping(value = "/System/Time", method = RequestMethod.GET) public SystemTimeResp time() { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); return new SystemTimeResp( new SystemTimeObject(serverId, "2", sdf.format(new Date()), TimeZone.getTimeZone("Asia/Shanghai").toString()) ); } // endregion @Autowired private FaceSampleMapper faceSampleMapper; private final SimpleDateFormat sdfTime = new SimpleDateFormat("yyyyMMddHHmmss"); /** * 批量新增人脸 */ @RequestMapping(value = "/Faces", method = RequestMethod.POST) @IgnoreLogReq public VIIDBaseResp faces(@RequestBody FaceUploadReq req) { FaceListObject faceListObject = req.getFaceListObject(); List faceObject = faceListObject.getFaceObject(); String faceId = null; // 遍历人脸列表 for (FaceObject face : faceObject) { // 设置FaceId faceId = face.getFaceID(); // 获取图片信息 SubImageList subImageList = face.getSubImageList(); // 判断人脸对象中的列表是否为空 String deviceID = face.getDeviceID(); DeviceEntity device = deviceRepository.getDeviceByDeviceNo(deviceID); if (device == null) { continue; } DeviceConfigEntity deviceConfig = deviceRepository.getDeviceConfig(device.getId()); int viidMode = 0; if (deviceConfig != null && deviceConfig.getViidType() != null) { viidMode = deviceConfig.getViidType(); } Date shotTime = null; if (StringUtils.isNotBlank(face.getShotTime())) { try { shotTime = sdfTime.parse(face.getShotTime()); } catch (ParseException e) { log.warn("拍摄时间时间转换失败,使用当前时间。错误entity:{}", face); } } if (shotTime == null) { if (StringUtils.isNotBlank(face.getFaceAppearTime())) { try { shotTime = sdfTime.parse(face.getFaceAppearTime()); } catch (ParseException e) { log.warn("拍摄时间时间转换失败,使用当前时间。错误entity:{}", face); } } } if (shotTime == null) { shotTime = new Date(); } else if (Math.abs(shotTime.getTime() - System.currentTimeMillis()) > + 24 * 60 * 60 * 1000) { shotTime = new Date(); } Long scenicId = device.getScenicId(); if (scenicId == null) { continue; } IStorageAdapter scenicStorageAdapter = scenicService.getScenicStorageAdapter(scenicId); IFaceBodyAdapter faceBodyAdapter = scenicService.getScenicFaceBodyAdapter(scenicId); FacePositionObject facePosition = new FacePositionObject(); facePosition.setLtY(face.getLeftTopY()); facePosition.setLtX(face.getLeftTopX()); facePosition.setRbY(face.getRightBtmY()); facePosition.setRbX(face.getRightBtmX()); if (ObjectUtil.isNotEmpty(subImageList) && CollUtil.isNotEmpty(subImageList.getSubImageInfoObject())) { if (viidMode == 0) { // 遍历每个图片对象 // 先找到type14的图片 List type14ImageList = subImageList.getSubImageInfoObject().stream().filter(subImage -> "14".equals(subImage.getType())).collect(Collectors.toList()); for (SubImageInfoObject subImage : subImageList.getSubImageInfoObject()) { // base64转换成MultipartFIle MultipartFile file = ImageUtils.base64ToMultipartFile(subImage.getData()); String ext = subImage.getFileFormat(); if (ext.equalsIgnoreCase("jpeg")) { ext = "jpg"; } IStorageAdapter adapter = StorageFactory.use("faces"); // Type=11 人脸 if (subImage.getType().equals("11")) { // 上传oss Long newFaceSampleId = SnowFlakeUtil.getLongId(); if (Integer.valueOf(1).equals(device.getStatus())) { FaceSampleEntity faceSample = new FaceSampleEntity(); faceSample.setId(newFaceSampleId); faceSample.setScenicId(scenicId); faceSample.setDeviceId(device.getId()); faceSample.setStatus(0); faceSample.setCreateAt(shotTime); String url = adapter.uploadFile(file, "user-face", UUID.randomUUID() + "." + ext); faceSample.setFaceUrl(url); faceSampleMapper.add(faceSample); ThreadPoolExecutor executor = getExecutor(device.getId().toString()); executor.execute(() -> { if (faceBodyAdapter != null) { taskFaceService.assureFaceDb(faceBodyAdapter, scenicId.toString()); AddFaceResp addFaceResp = faceBodyAdapter.addFace(scenicId.toString(), generateEntityId(faceSample), url, newFaceSampleId.toString()); if (addFaceResp != null) { faceSample.setScore(addFaceResp.getScore()); faceSampleMapper.update(faceSample); } } if (deviceConfig != null && Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) { DynamicTaskGenerator.addTask(faceSample.getId()); } }); } for (SubImageInfoObject _subImage : type14ImageList) { facePosition.setImgHeight(_subImage.getHeight()); facePosition.setImgWidth(_subImage.getWidth()); SourceEntity source = new SourceEntity(); source.setId(SnowFlakeUtil.getLongId()); source.setDeviceId(device.getId()); source.setScenicId(device.getScenicId()); source.setFaceSampleId(newFaceSampleId); source.setCreateTime(shotTime); source.setType(2); // 上传oss MultipartFile _file = ImageUtils.base64ToMultipartFile(_subImage.getData()); String filename = StorageUtil.joinPath(PHOTO_PATH, UUID.randomUUID() + "." + ext); String _sourceUrl = scenicStorageAdapter.uploadFile(_file, filename); scenicStorageAdapter.setAcl(StorageAcl.PUBLIC_READ, filename); source.setUrl(_sourceUrl); source.setPosJson(JSON.toJSONString(facePosition)); sourceMapper.add(source); } log.info("人脸信息及原图{}张入库成功!设备ID:{}", type14ImageList.size(), deviceID); } } } else if (viidMode == 1) { for (SubImageInfoObject subImage : subImageList.getSubImageInfoObject()) { // base64转换成MultipartFIle MultipartFile file = ImageUtils.base64ToMultipartFile(subImage.getData()); String ext = subImage.getFileFormat(); if (ext.equalsIgnoreCase("jpeg")) { ext = "jpg"; } IStorageAdapter adapter = StorageFactory.use("faces"); // Type=14 人脸,传™的,有这么传的嘛 if (subImage.getType().equals("14")) { // 上传oss if (Integer.valueOf(1).equals(device.getStatus())) { FaceSampleEntity faceSample = new FaceSampleEntity(); Long newFaceSampleId = SnowFlakeUtil.getLongId(); faceSample.setId(newFaceSampleId); faceSample.setScenicId(scenicId); faceSample.setDeviceId(device.getId()); faceSample.setStatus(0); faceSample.setCreateAt(shotTime); String url = adapter.uploadFile(file, "user-face", UUID.randomUUID() + "." + ext); faceSample.setFaceUrl(url); faceSampleMapper.add(faceSample); DynamicTaskGenerator.addTask(faceSample.getId()); ThreadPoolExecutor executor = getExecutor(device.getId().toString()); executor.execute(() -> { if (faceBodyAdapter != null) { taskFaceService.assureFaceDb(faceBodyAdapter, scenicId.toString()); AddFaceResp addFaceResp = faceBodyAdapter.addFace(scenicId.toString(), generateEntityId(faceSample), url, newFaceSampleId.toString()); if (addFaceResp != null) { faceSample.setScore(addFaceResp.getScore()); faceSampleMapper.update(faceSample); } } if (deviceConfig != null && Integer.valueOf(1).equals(deviceConfig.getEnablePreBook())) { DynamicTaskGenerator.addTask(faceSample.getId()); } }); log.info("模式1人脸信息入库成功!设备ID:{}", deviceID); } } } } } } return new VIIDBaseResp( new ResponseStatusObject(faceId, "/VIID/Faces", "0", "OK", sdfTime.format(new Date())) ); } @RequestMapping(value = "/Images", method = RequestMethod.POST) @IgnoreLogReq public VIIDBaseResp images(HttpServletRequest request, @RequestBody ImageUploadReq req) throws IOException { // log.info("Images:{}", req); HttpSaver.saveRequestToFile(request); return new VIIDBaseResp( new ResponseStatusObject("1", "/VIID/Images", "0", "OK", sdfTime.format(new Date())) ); } }