2025-04-07 09:51:03 +08:00

413 lines
20 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<String, ThreadPoolExecutor> 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> 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<SubImageInfoObject> 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()))
);
}
}