摄像头回放

This commit is contained in:
2025-12-03 20:28:58 +08:00
parent 2f0b323680
commit 0c38c42bfe
26 changed files with 1160 additions and 11 deletions

View File

@ -309,6 +309,8 @@ springdoc:
packages-to-scan: org.dromara.mechanical.jxgl
- group: 32.设备模块
packages-to-scan: org.dromara.device
- group: 33.摄像头模块
packages-to-scan: org.dromara.other
- group: 34.机械安全模块
packages-to-scan: org.dromara.mechanical.jxaqgl
# knife4j的增强配置不需要增强可以不配

View File

@ -0,0 +1,146 @@
package org.dromara.common.core.utils;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
/**
* 时间戳转换工具类(包含时分秒提取)
*/
public class TimestampUtils {
// 默认时区(东八区)
private static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("GMT+8");
// 完整日期格式
private static final String FULL_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
// 时分秒格式
private static final String TIME_FORMAT = "HH:mm:ss";
// 日期格式
private static final String DATE_FORMAT = "yyyy-MM-dd";
/**
* 将日期字符串转为秒级时间戳(优化版)
* 支持格式:"2025-12-03 23:59:59"、"2025-12-03"等
* @param dateStr 日期字符串
* @return 秒级时间戳
*/
public static Long parseDateToTimestamp(String dateStr) {
if (StrUtil.isBlank(dateStr)) {
return null;
}
// 使用Hutool的DateUtil进行智能解析推荐
try {
Date date = DateUtil.parse(dateStr);
return date.getTime() / 1000L; // 转为秒级时间戳
} catch (Exception e) {
throw new IllegalArgumentException(
String.format("日期格式错误:%s支持格式yyyy-MM-dd HH:mm:ss、yyyy-MM-dd等", dateStr)
);
}
}
/**
* 将时间戳转换为完整日期格式yyyy-MM-dd HH:mm:ss
*/
public static String formatTimestamp(Object timestamp) {
if (timestamp == null) {
return null;
}
String timestampStr = timestamp.toString().trim();
if (StrUtil.isBlank(timestampStr)) {
return null;
}
Long time = parseToMilliseconds(timestampStr);
SimpleDateFormat sdf = new SimpleDateFormat(FULL_DATE_FORMAT);
sdf.setTimeZone(DEFAULT_TIME_ZONE);
return sdf.format(new Date(time));
}
/**
* 提取时间戳中的时分秒部分HH:mm:ss
*/
public static String extractTime(Object timestamp) {
if (timestamp == null) {
return null;
}
String timestampStr = timestamp.toString().trim();
if (StrUtil.isBlank(timestampStr)) {
return null;
}
Long time = parseToMilliseconds(timestampStr);
SimpleDateFormat sdf = new SimpleDateFormat(TIME_FORMAT);
sdf.setTimeZone(DEFAULT_TIME_ZONE);
return sdf.format(new Date(time));
}
/**
* 提取时间戳中的日期部分yyyy-MM-dd
*/
public static String extractDate(Object timestamp) {
if (timestamp == null) {
return null;
}
String timestampStr = timestamp.toString().trim();
if (StrUtil.isBlank(timestampStr)) {
return null;
}
Long time = parseToMilliseconds(timestampStr);
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
sdf.setTimeZone(DEFAULT_TIME_ZONE);
return sdf.format(new Date(time));
}
/**
* 解析时间戳字符串为毫秒级时间戳
*/
private static Long parseToMilliseconds(String timestampStr) {
try {
Long time = Long.parseLong(timestampStr);
// 10位秒级时间戳转为13位毫秒级
if (timestampStr.length() == 10) {
time = time * 1000;
}
return time;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("时间戳格式错误:" + timestampStr);
}
}
/**
* 获取时间戳对应的小时
*/
public static int getHour(Object timestamp) {
String timeStr = extractTime(timestamp);
return Integer.parseInt(timeStr.split(":")[0]);
}
/**
* 获取时间戳对应的分钟
*/
public static int getMinute(Object timestamp) {
String timeStr = extractTime(timestamp);
return Integer.parseInt(timeStr.split(":")[1]);
}
/**
* 获取时间戳对应的秒
*/
public static int getSecond(Object timestamp) {
String timeStr = extractTime(timestamp);
return Integer.parseInt(timeStr.split(":")[2]);
}
}

View File

@ -46,7 +46,7 @@ public class BusComplaintBoxController extends BaseController {
/**
* 查询意见箱列表
*/
// @SaCheckPermission("complaintBox:complaintBox:list")
@SaCheckPermission("complaintBox:complaintBox:list")
@GetMapping("/list")
public TableDataInfo<BusComplaintBoxVo> list(BusComplaintBoxBo bo, PageQuery pageQuery) {
return busComplaintBoxService.queryPageList(bo, pageQuery);
@ -54,7 +54,7 @@ public class BusComplaintBoxController extends BaseController {
/**
* web获取各个处理状态数量
*/
// @SaCheckPermission("complaintBox:complaintBox:list")
@SaCheckPermission("complaintBox:complaintBox:list")
@GetMapping("/getCount")
public R<List<ComplaintBoxCountVo>> getCount(BusComplaintBoxBo bo) {
return R.ok(busComplaintBoxService.getCount(bo));
@ -75,7 +75,7 @@ public class BusComplaintBoxController extends BaseController {
/**
* 新增意见回复
*/
// @SaCheckPermission("complaintBox:complaintBox:add")
@SaCheckPermission("complaintBox:complaintBox:add")
@Log(title = "意见箱", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/postAReply")
@ -87,7 +87,7 @@ public class BusComplaintBoxController extends BaseController {
/**
* 修改意见阅读状态
*/
// @SaCheckPermission("complaintBox:complaintBox:edit")
@SaCheckPermission("complaintBox:complaintBox:edit")
@Log(title = "意见箱", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/editCheckStatus")
@ -98,7 +98,7 @@ public class BusComplaintBoxController extends BaseController {
/**
* 修改意见阅读状态
*/
// @SaCheckPermission("complaintBox:complaintBox:edit")
@SaCheckPermission("complaintBox:complaintBox:edit")
@Log(title = "意见箱", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/editStatus")

View File

@ -327,7 +327,13 @@ public class BusComplaintBoxServiceImpl extends ServiceImpl<BusComplaintBoxMappe
if ("14".equals(busComplaintBoxVo.getStatus())){
throw new ServiceException("该意见已经关闭,不允许再修改状态");
}
return baseMapper.update(new LambdaUpdateWrapper<BusComplaintBox>().set(BusComplaintBox::getStatus,bo.getStatus()).eq(BusComplaintBox::getId, bo.getId()));
LambdaUpdateWrapper<BusComplaintBox> lqw = new LambdaUpdateWrapper<BusComplaintBox>();
lqw.set(BusComplaintBox::getStatus, bo.getStatus());
lqw.eq(BusComplaintBox::getId, bo.getId());
if ("0".equals(bo.getStatus())){
lqw.set(BusComplaintBox::getCurrentDisposeUserId, null);
}
return baseMapper.update(lqw);
}
}

View File

@ -61,4 +61,14 @@ public interface Ys7Constant {
*/
String setDeviceVideoUrlByPost = "https://open.ys7.com/api/lapp/device/fullday/record/switch/set";
/**
* 摄像头查询设备本地录像 Get
*/
String getDeviceLocalVideoUrlByGet = "https://open.ys7.com/api/v3/device/local/video/unify/query";
/**
* 获取播放地址 POST
*/
String getDeviceLappVideoUrlByPost = "https://open.ys7.com/api/lapp/v2/live/address/get";
}

View File

@ -4,17 +4,20 @@ import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.manager.ys7manager.vo.Ys7NoDataResponseVo;
import org.dromara.manager.ys7manager.vo.Ys7PageResponseVo;
import org.dromara.manager.ys7manager.vo.Ys7QueryDeviceResponseVo;
import org.dromara.manager.ys7manager.vo.Ys7ResponseVo;
import org.dromara.common.core.utils.TimestampUtils;
import org.dromara.manager.ys7manager.dto.DeviceLocalVideoRequstDto;
import org.dromara.manager.ys7manager.dto.DevicePlayBackUrlRequstDto;
import org.dromara.manager.ys7manager.vo.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
/**
* @author lilemy
@ -290,4 +293,159 @@ public class Ys7RequestUtils {
}
}
/**
* 获取播放地址
* @param dto
* @return
*/
public static String getDevicePlayBackUrl(DevicePlayBackUrlRequstDto dto) {
if (StringUtils.isAnyBlank(dto.getAccessToken(), dto.getDeviceSerial())) {
throw new ServiceException("获取回放录像参数为空", HttpStatus.BAD_REQUEST);
}
HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("accessToken", dto.getAccessToken());
paramMap.put("deviceSerial", dto.getDeviceSerial());
if (dto.getChannelNo() != null) {
paramMap.put("channelNo", dto.getChannelNo());
}
if (dto.getQuality() != null) {
paramMap.put("quality", dto.getQuality());
}
if (dto.getProtocol() != null) {
paramMap.put("protocol", dto.getProtocol());
}
if (dto.getStartTime() != null) {
paramMap.put("startTime", dto.getStartTime());
}
if (dto.getEndTime() != null) {
paramMap.put("stopTime", dto.getEndTime());
}
if (dto.getType() != null) {
paramMap.put("type", dto.getType());
}
if (dto.getExpireTime() != null) {
paramMap.put("expireTime", dto.getExpireTime());
}
if (dto.getRecType() != null) {
paramMap.put("recType", dto.getRecType());
}
if (dto.getCode() != null) {
paramMap.put("code", dto.getCode());
}
if (dto.getSupportH265() != null) {
paramMap.put("supportH265", dto.getSupportH265());
}
if (dto.getPlaybackSpeed() != null) {
paramMap.put("playbackSpeed", dto.getPlaybackSpeed());
}
if (dto.getGbchannel() != null) {
paramMap.put("gbchannel", dto.getGbchannel());
}
String errorMsg = "获取回放录像请求失败";
try (HttpResponse response = HttpRequest.post(Ys7Constant.getDeviceLappVideoUrlByPost)
.form(paramMap)
.execute()) {
if (!response.isOk()) {
log.error("{}{}", errorMsg, response.getStatus());
throw new ServiceException(errorMsg + response.getStatus());
}
String body = response.body();
log.info("本地录像body{}",body);
Ys7ResponseVo responseVo = JSONUtil.toBean(body, Ys7ResponseVo.class);
if (!responseVo.getCode().equals("200")) {
log.error("{},状态码:{}{}", errorMsg, responseVo.getCode(), responseVo.getMsg());
throw new ServiceException(errorMsg + responseVo.getMsg());
}
String data = responseVo.getData();
String picUrl = JSONUtil.parseObj(data).getStr("url");
log.info("获取回放录像请求成功,设备[{}]回放成功,通道:{}url{}", dto.getDeviceSerial(), dto.getChannelNo(), picUrl);
return picUrl;
}
}
/**
* 查询设备某天的本地录像
* @param dto
* @return
*/
public static List<DeviceLocalVideoRecordsVo> getDeviceLocalVideo(DeviceLocalVideoRequstDto dto) {
if (StringUtils.isAnyBlank(dto.getAccessToken(), dto.getDeviceSerial(),dto.getStartTime(), dto.getEndTime())) {
throw new ServiceException("查询设备本地录像参数为空", HttpStatus.BAD_REQUEST);
}
dto.setStartTime(Objects.requireNonNull(TimestampUtils.parseDateToTimestamp(dto.getStartTime())).toString());
dto.setEndTime(Objects.requireNonNull(TimestampUtils.parseDateToTimestamp(dto.getEndTime())).toString());
List<DeviceLocalVideoRecordsVo> vos = new ArrayList<>();
getRexords(dto, vos);
return vos;
}
private static void getRexords(DeviceLocalVideoRequstDto dto, List<DeviceLocalVideoRecordsVo> vos) {
String errorMsg = "查询设备本地录像请求失败";
String body = getDevicceLocalVideoBody(dto);
log.info("本地录像body{}",body);
JSONObject jsonObject = JSONUtil.parseObj(body);
DeviceLocalVideoMetaVo meta = JSONUtil.toBean(jsonObject.getJSONObject("meta"), DeviceLocalVideoMetaVo.class);
if (!meta.getCode().equals("200")) {
log.error("{},状态码:{}{}", errorMsg, meta.getCode(), meta.getMessage());
throw new ServiceException(errorMsg + meta.getMessage());
}
DeviceLocalVideoDataVo data = JSONUtil.toBean(jsonObject.getJSONObject("data"), DeviceLocalVideoDataVo.class);
if (data != null) {
List<DeviceLocalVideoRecordsVo> list = JSONUtil.toList(data.getRecords(), DeviceLocalVideoRecordsVo.class);
if (list != null && !list.isEmpty()) {
list.forEach(vo -> {
vo.setStartTime(TimestampUtils.extractTime(vo.getStartTime()));
vo.setEndTime(TimestampUtils.extractTime(vo.getEndTime()));
});
vos.addAll(list);
}
if (data.getHasMore()){
dto.setStartTime(data.getNextFileTime());
if (Long.parseLong(dto.getStartTime()) > Long.parseLong(dto.getEndTime())){
return;
}
getRexords(dto, vos);
}
}
}
private static String getDevicceLocalVideoBody(DeviceLocalVideoRequstDto dto) {
// 2. 构建Header参数接口文档的Header部分
HttpRequest request = HttpRequest.get(Ys7Constant.getDeviceLocalVideoUrlByGet)
.header("accessToken", dto.getAccessToken()) // Header参数accessToken
.header("deviceSerial", dto.getDeviceSerial());// Header参数deviceSerial
// 可选HeaderlocalIndex对应dto的channelNo
if (dto.getChannelNo() != null) {
request.header("channelNo", dto.getChannelNo().toString());
}
// 3. 构建Query参数接口文档的query部分
request.form("startTime", dto.getStartTime()) // Query必传startTime
.form("endTime", dto.getEndTime()); // Query必传endTime
// 可选Query参数
if (dto.getRecordType() != null) {
request.form("recordType", dto.getRecordType().toString());
}
if (dto.getIsQueryByNvr() != null) {
request.form("isQueryByNvr", dto.getIsQueryByNvr().toString());
}
if (dto.getLocation() != null) {
request.form("location", dto.getLocation().toString());
}
if (dto.getPageSize() != null) {
request.form("pageSize", dto.getPageSize().toString());
}
String errorMsg = "查询设备本地录像请求失败";
try (HttpResponse response = request.execute()) {
if (!response.isOk()) {
log.error("{}{}", errorMsg, response.getStatus());
throw new ServiceException(errorMsg + response.getStatus());
}
return response.body();
}
}
}

View File

@ -0,0 +1,18 @@
package org.dromara.manager.ys7manager.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class DeviceLocalVideoRequstDto implements Serializable {
private String accessToken; //toke
private String deviceSerial; //设备序列号
private Integer channelNo; // 通道号默认1
private Integer recordType; // 1:定时录像 2:事件录像 3:智能-车 4:智能-人形 5:自动浓缩录像,不填默认查询所有类型
private String startTime; // 回放开始时间格式yyyy-MM-dd HH:mm:ss
private String endTime; // 回放结束时间格式yyyy-MM-dd HH:mm:ss
private Integer isQueryByNvr; // 是否反查NVR录像0-不反查(默认)1-反查NVR
private Integer location; // 录像检索位置1-本地录像检索(默认)2-CVR中心录像检索
private Integer pageSize; // 分页的页面大小默认50最大200
}

View File

@ -0,0 +1,23 @@
package org.dromara.manager.ys7manager.dto;
import lombok.Data;
import java.io.Serializable;
@Data
public class DevicePlayBackUrlRequstDto implements Serializable {
private String accessToken; //toke
private String deviceSerial; //设备序列号
private Integer channelNo; // 通道号默认1
private Integer protocol; // 流协议1-ezopen、2-hls、3-rtmp、4-flv默认1
private String code; // 视频加密密码
private Integer expireTime; // 过期时长30秒-720天
private String type; // 地址类型1-预览2-本地录像回放3-云存储录像回放默认1
private Integer quality; // 清晰度1-高清2-流畅
private String startTime; // 回放开始时间格式yyyy-MM-dd HH:mm:ss
private String endTime; // 回放结束时间格式yyyy-MM-dd HH:mm:ss
private Integer supportH265; // 是否需要H265编码1-是0-否
private Integer recType; // 回放源0-系统自动选择1-云存储2-本地录像。非必选默认为0
private String playbackSpeed; // 回放倍速:-1、0.5、1、2、4、8、16仅支持flv协议+回放类型)
private String gbchannel; // 国标设备通道编号
}

View File

@ -0,0 +1,29 @@
package org.dromara.manager.ys7manager.vo;
import lombok.Data;
/**
* @author lilemy
* @date 2025/6/12 17:14
*/
@Data
public class DeviceLocalVideoDataVo {
/**
* 响应内容
*/
private String records;
//该录像文件是否来自关联的nvr
private String fromNvr;
// fromNvr为true则返回关联NVR设备序列号否则返回入参填的设备序列号。
private String deviceSerial;
// fromNvr为true则返回关联NVR设备通道号否则返回入参填的设备通道号
private String localIndex;
//是否存在更多录像文件
private Boolean hasMore;
// hasMore为true时该参数值为下一个录像文件的开始时间。如需分页查询该参数值可作为下一页录像文件查询的开始时间。
private String nextFileTime;
}

View File

@ -0,0 +1,27 @@
package org.dromara.manager.ys7manager.vo;
import lombok.Data;
/**
* @author lilemy
* @date 2025/6/12 17:14
*/
@Data
public class DeviceLocalVideoMetaVo {
/**
* 响应码
*/
private String code;
/**
* 响应信息
*/
private String message;
/**
* 响应信息
*/
private String moreInfo;
}

View File

@ -0,0 +1,27 @@
package org.dromara.manager.ys7manager.vo;
import lombok.Data;
/**
* @author lilemy
* @date 2025/6/12 17:14
*/
@Data
public class DeviceLocalVideoRecordsVo {
/**
* 开始时间
*/
private String startTime;
/**
* 结束时间
*/
private String endTime;
//录像类型
private String type;
//录像文件大小,单位:字节
private String size;
}

View File

@ -8,6 +8,7 @@ import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -17,9 +18,20 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.manager.ys7manager.dto.DeviceLocalVideoRequstDto;
import org.dromara.manager.ys7manager.vo.DeviceLocalVideoRecordsVo;
import org.dromara.other.domain.dto.ys7device.*;
import org.dromara.other.domain.dto.ys7deviceimg.AddViolattionRecordReq;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgCaptureReq;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgQueryReq;
import org.dromara.other.domain.vo.ys7device.DateAndDeviceLocalVideoVo;
import org.dromara.other.domain.vo.ys7device.OthYs7DeviceVo;
import org.dromara.other.domain.vo.ys7deviceimg.OthYs7DeviceImgVo;
import org.dromara.other.service.IOthYs7DeviceImgService;
import org.dromara.other.service.IOthYs7DeviceService;
import org.dromara.safety.domain.vo.violationrecord.HseViolationRecordVo;
import org.dromara.safety.service.IHseViolationRecordService;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
@ -46,6 +58,73 @@ public class OthYs7DeviceController extends BaseController {
@Resource
private Ys7Manager ys7Manager;
@Resource
private IOthYs7DeviceImgService othYs7DeviceImgService;
@Lazy
@Resource
private IHseViolationRecordService hseViolationRecordService;
/**
* 获取违规记录详细信息
*
* @param id 主键
*/
@SaCheckPermission("other:ys7Device:list")
@GetMapping("/getViolationRecordInfo/{id}")
public R<HseViolationRecordVo> getViolationRecordInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(hseViolationRecordService.queryById(id));
}
/**
* 萤石摄像头图片抓图
*/
@SaCheckPermission("other:ys7Device:list")
@Log(title = "萤石摄像头图片", businessType = BusinessType.INSERT)
@PostMapping("/capture")
public R<Void> capture(@RequestBody OthYs7DeviceImgCaptureReq req) {
return toAjax(othYs7DeviceImgService.addHMCapturePic(req));
}
/**
* 萤石摄像头图片识别
*/
@SaCheckPermission("other:ys7Device:list")
@Log(title = "萤石摄像头图片", businessType = BusinessType.INSERT)
@PostMapping("/discernImg")
public R<Void> discernImg(@RequestBody OthYs7DeviceImgCaptureReq req) {
return toAjax(othYs7DeviceImgService.discernImg(req));
}
/**
* 下发工单
*/
@SaCheckPermission("other:ys7Device:list")
@Log(title = "萤石摄像头图片", businessType = BusinessType.INSERT)
@PostMapping("/addViolationRecord")
public R<Void> addViolationRecord(@RequestBody AddViolattionRecordReq req) {
return toAjax(othYs7DeviceImgService.addViolationRecord(req));
}
/**
* 查询萤石摄像头图片列表
*/
@SaCheckPermission("other:ys7Device:list")
@GetMapping("/getImgList")
public TableDataInfo<OthYs7DeviceImgVo> list(OthYs7DeviceImgQueryReq req, PageQuery pageQuery) {
return othYs7DeviceImgService.queryPageList(req, pageQuery);
}
/**
* 删除萤石摄像头图片
*
* @param ids 主键串
*/
@SaCheckPermission("other:ys7Device:remove")
@Log(title = "萤石摄像头图片", businessType = BusinessType.DELETE)
@DeleteMapping("/img/{ids}")
public R<Void> imgRemove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
return toAjax(othYs7DeviceImgService.deleteWithValidByIds(List.of(ids)));
}
/**
* 查询萤石摄像头列表
@ -157,4 +236,50 @@ public class OthYs7DeviceController extends BaseController {
return ResponseEntity.ok(JSONUtil.toJsonStr(result));
}
/**
* 获取回放录像播放地址
* @return
*/
@GetMapping("/getPlayBackUrl")
public R<String> getPlayBackUrl(OthYs7DevicePlayBackUrlReq req){
if (req.getDeviceSerial() == null){
throw new ServiceException("设备序列号不能为空!!!");
}
if (req.getStartTime() == null || req.getEndTime() == null){
throw new ServiceException("开始时间和结束时间不能为空!!!");
}
return R.ok(othYs7DeviceService.getPlayBackUrl(req));
}
/**
* 查询设备本地录像
* @return
*/
@GetMapping("/getDeviceLocalVideo")
public R<List<DeviceLocalVideoRecordsVo>> getDeviceLocalVideo(OthYs7DevicePlayBackUrlReq req){
if (req.getDeviceSerial() == null){
throw new ServiceException("设备序列号不能为空!!!");
}
if (req.getStartTime() == null || req.getEndTime() == null){
throw new ServiceException("开始时间和结束时间不能为空!!!");
}
return R.ok(othYs7DeviceService.getDeviceLocalVideo(req));
}
/**
* 查询范围日期和设备本地录像列表
* @return
*/
@GetMapping("/getDateAndDeviceLocalVideo")
public R<DateAndDeviceLocalVideoVo> getDateAndDeviceLocalVideo(OthYs7DevicePlayBackUrlReq req){
if (req.getDeviceSerial() == null){
throw new ServiceException("设备序列号不能为空!!!");
}
if (req.getStartTime() == null || req.getEndTime() == null){
throw new ServiceException("开始时间和结束时间不能为空!!!");
}
return R.ok(othYs7DeviceService.getDateAndDeviceLocalVideo(req));
}
}

View File

@ -72,6 +72,12 @@ public class OthYs7DeviceImg implements Serializable {
*/
private String recognizeUrl;
/**
* 图片状态0、未识别1、已识别未违规2、已识别有违规3、已下发工单
*/
private String imgStatus;
/**
* 备注
*/

View File

@ -0,0 +1,31 @@
package org.dromara.other.domain.dto.ys7device;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author lilemy
* @date 2025/6/13 10:19
*/
@Data
public class OthYs7DevicePlayBackUrlReq implements Serializable {
/**
* 设备序列号
*/
private String deviceSerial;
/**
* 开始时间
*/
private String startTime;
/**
* 结束时间
*/
private String endTime;
}

View File

@ -0,0 +1,36 @@
package org.dromara.other.domain.dto.ys7deviceimg;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lilemy
* @date 2025-10-10 19:14
*/
@Data
public class AddViolattionRecordReq implements Serializable {
/**
* 项目id
*/
@NotNull(message = "图片id不能为空")
private Long projectId;
/**
* 设备序列号
*/
@NotBlank(message = "设备序列号不能为空")
private String deviceSerial;
/**
* 图片id
*/
@NotNull(message = "图片id不能为空")
private Long originalUrlId;
}

View File

@ -21,4 +21,8 @@ public class OthYs7DeviceImgCaptureReq implements Serializable {
*/
@NotBlank(message = "设备序列号不能为空")
private String deviceSerial;
/**
* 图片id
*/
private Long imgId;
}

View File

@ -0,0 +1,29 @@
package org.dromara.other.domain.vo.ys7device;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.manager.ys7manager.vo.DeviceLocalVideoRecordsVo;
import org.dromara.other.domain.OthYs7Device;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 查询范围日期和设备本地录像列表
*/
@Data
public class DateAndDeviceLocalVideoVo implements Serializable {
private List<String> dateList;
private List<DeviceLocalVideoRecordsVo> deviceList;
}

View File

@ -55,6 +55,11 @@ public class OthYs7DeviceImgVo implements Serializable {
@ExcelProperty(value = "图片地址")
private String url;
/**
* 识别结果图片地址
*/
private String recognizeUrl;
/**
* 识别算法模型
*/
@ -77,4 +82,9 @@ public class OthYs7DeviceImgVo implements Serializable {
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 图片状态0、未识别1、已识别2、已下发工单
*/
private String imgStatus;
}

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.other.domain.OthYs7DeviceImg;
import org.dromara.other.domain.dto.ys7deviceimg.AddViolattionRecordReq;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgCaptureReq;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgCreateByCapture;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgQueryReq;
@ -102,4 +103,25 @@ public interface IOthYs7DeviceImgService extends IService<OthYs7DeviceImg> {
* @return 是否抓拍成功
*/
Boolean capturePic(OthYs7DeviceImgCaptureReq req);
/**
* 手动抓拍(不需要识别)
* @param req
* @return
*/
int addHMCapturePic(OthYs7DeviceImgCaptureReq req);
/**
* 识别抓拍图片
* @param req
* @return
*/
int discernImg(OthYs7DeviceImgCaptureReq req);
/**
* 手动下发安全工单
* @param req
* @return
*/
int addViolationRecord(AddViolattionRecordReq req);
}

View File

@ -5,9 +5,11 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.manager.ys7manager.vo.DeviceLocalVideoRecordsVo;
import org.dromara.manager.ys7manager.vo.Ys7QueryDeviceResponseVo;
import org.dromara.other.domain.OthYs7Device;
import org.dromara.other.domain.dto.ys7device.*;
import org.dromara.other.domain.vo.ys7device.DateAndDeviceLocalVideoVo;
import org.dromara.other.domain.vo.ys7device.OthYs7DeviceVo;
import java.util.Collection;
@ -143,4 +145,24 @@ public interface IOthYs7DeviceService extends IService<OthYs7Device> {
*/
void webhook(WebhookMessage receiveMessage);
/**
* 获取回放录像播放地址
* @param req
* @return
*/
String getPlayBackUrl(OthYs7DevicePlayBackUrlReq req);
/**
* 查询设备本地录像
* @param req
* @return
*/
List<DeviceLocalVideoRecordsVo> getDeviceLocalVideo(OthYs7DevicePlayBackUrlReq req);
/**
* 查询范围日期和设备本地录像列表
* @param req
* @return
*/
DateAndDeviceLocalVideoVo getDateAndDeviceLocalVideo(OthYs7DevicePlayBackUrlReq req);
}

View File

@ -24,6 +24,7 @@ import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.other.constant.Ys7DeviceImgConstant;
import org.dromara.other.domain.OthYs7Device;
import org.dromara.other.domain.OthYs7DeviceImg;
import org.dromara.other.domain.dto.ys7deviceimg.AddViolattionRecordReq;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgCaptureReq;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgCreateByCapture;
import org.dromara.other.domain.dto.ys7deviceimg.OthYs7DeviceImgQueryReq;
@ -31,14 +32,18 @@ import org.dromara.other.domain.vo.ys7deviceimg.OthYs7DeviceImgVo;
import org.dromara.other.mapper.OthYs7DeviceImgMapper;
import org.dromara.other.service.IOthYs7DeviceImgService;
import org.dromara.other.service.IOthYs7DeviceService;
import org.dromara.safety.domain.HseRecognizeRecord;
import org.dromara.safety.domain.HseViolationLevel;
import org.dromara.safety.domain.dto.recognizerecord.HseRecognizeRecordCreateDto;
import org.dromara.safety.domain.dto.violationrecord.HseViolationRecordCreateDto;
import org.dromara.safety.domain.enums.HseRecordCategoryEnum;
import org.dromara.safety.service.IHseRecognizeRecordService;
import org.dromara.safety.service.IHseViolationLevelService;
import org.dromara.safety.service.IHseViolationRecordService;
import org.dromara.system.domain.vo.SysOssUploadVo;
import org.dromara.system.service.ISysOssService;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -79,6 +84,10 @@ public class OthYs7DeviceImgServiceImpl extends ServiceImpl<OthYs7DeviceImgMappe
@Resource
private Ys7Manager ys7Manager;
@Lazy
@Resource
private IHseViolationRecordService violationRecordService;
/**
* 查询萤石摄像头图片
*
@ -266,7 +275,12 @@ public class OthYs7DeviceImgServiceImpl extends ServiceImpl<OthYs7DeviceImgMappe
}
if (CollUtil.isEmpty(recTypes)) {
log.error("项目:{},未设置安全等级", img.getProjectId());
boolean save = this.save(othYs7DeviceImg);
if (!save) {
log.error("保存图片失败:{}", othYs7DeviceImg);
} else {
saveList.add(othYs7DeviceImg);
}
continue;
}
RecognizeVo recognizeVo = null;
@ -412,6 +426,175 @@ public class OthYs7DeviceImgServiceImpl extends ServiceImpl<OthYs7DeviceImgMappe
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public int addHMCapturePic(OthYs7DeviceImgCaptureReq req) {
OthYs7Device ys7Device = ys7DeviceService.lambdaQuery()
.eq(OthYs7Device::getDeviceSerial, req.getDeviceSerial())
.last("limit 1")
.one();
if (ys7Device == null) {
throw new ServiceException("设备不存在", HttpStatus.ERROR);
}
String deviceSerial = ys7Device.getDeviceSerial();
String url = null;
try {
url = ys7Manager.getCaptureDevicePic(deviceSerial, 1, 2);
} catch (Exception e) {
log.error("摄像头 {} 抓图失败:{}", deviceSerial, e.getMessage());
throw new ServiceException("摄像头 " + deviceSerial + " 抓图失败:" + e.getMessage(), HttpStatus.ERROR);
}
OthYs7DeviceImg othYs7DeviceImg = new OthYs7DeviceImg();
String originalFilename = extractFilename(url);
String deviceImgOssPath = Ys7DeviceImgConstant.getDeviceImgOssPath(originalFilename, deviceSerial);
SysOssUploadVo uploadVo = ossService.uploadFileUrlWithNoSave(url, deviceImgOssPath);
String ossUrl = uploadVo.getUrl();
if (StringUtils.isNotBlank(ossUrl)) {
othYs7DeviceImg.setProjectId(ys7Device.getProjectId());
othYs7DeviceImg.setDeviceSerial(deviceSerial);
othYs7DeviceImg.setCreateTime(new Date());
othYs7DeviceImg.setDeviceName(ys7Device.getDeviceName());
othYs7DeviceImg.setUrl(ossUrl);
othYs7DeviceImg.setImgStatus("0");
}
return baseMapper.insert(othYs7DeviceImg);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int discernImg(OthYs7DeviceImgCaptureReq req) {
OthYs7DeviceImg img = baseMapper.selectById(req.getImgId());
if (img == null) {
throw new ServiceException("识别图片不存在!!");
}
// 获取安全等级设置
List<HseViolationLevel> levelList = violationLevelService.lambdaQuery()
.eq(HseViolationLevel::getProjectId, img.getProjectId())
.list();
Map<Long, List<RecognizerTypeEnum>> level = new HashMap<>();
if (CollUtil.isNotEmpty(levelList)) {
Map<Long, List<HseViolationLevel>> levelMap = levelList.stream()
.collect(Collectors.groupingBy(HseViolationLevel::getProjectId));
for (Map.Entry<Long, List<HseViolationLevel>> entry : levelMap.entrySet()) {
List<RecognizerTypeEnum> recognizerTypeEnums = entry.getValue().stream().map(l -> {
List<String> levels = StringUtils.splitList(l.getViolationType());
return RecognizerTypeEnum.listFromCodes(levels);
}).filter(CollUtil::isNotEmpty)
.flatMap(Collection::stream)
.distinct()
.toList();
level.put(entry.getKey(), recognizerTypeEnums);
}
}
if (CollUtil.isEmpty(level)) {
log.error("未设置安全等级");
}
// 将抓取的图片进行识别
List<RecognizerTypeEnum> recTypes = new ArrayList<>();
if (CollUtil.isNotEmpty(level)) {
recTypes = level.get(img.getProjectId());
}
if (CollUtil.isEmpty(recTypes)) {
log.error("项目:{},未设置安全等级", img.getProjectId());
throw new ServiceException("项目未设置安全等级无法识别,请设置安全等级之后再进行识别");
}
RecognizeVo recognizeVo = null;
try {
recognizeVo = recognizerManager.recognize(img.getUrl(), recTypes);
} catch (Exception e) {
log.error("图片识别异常", e);
}
if (recognizeVo != null && recognizeVo.getHasTarget().equals(RecognizerHasTargetEnum.YES.getValue())) {
// 记录识别信息
HseRecognizeRecord record = new HseRecognizeRecord();
record.setOriginalPicture(img.getUrl());
record.setOriginalUrlId(img.getId());
record.setCreateTime(new Date());
List<RecognizeTargetVo> targets = recognizeVo.getTargets();
img.setTargets(JSONUtil.toJsonStr(targets));
img.setImgSize(JSONUtil.toJsonStr(recognizeVo.getOriginalImgSize()));
img.setIsRecognize(RecognizerHasTargetEnum.YES.getValue());
List<String> recTypeList = targets.stream().map(RecognizeTargetVo::getType).distinct().toList();
img.setRecType(JSONUtil.toJsonStr(recTypeList));
String targetUrl = null;
try {
RecognizeImageStreamResult imageStreamResult = RecognizerManager.drawImageToStream(img.getUrl(), targets);
InputStream inputStream = imageStreamResult.getInputStream();
String contentType = imageStreamResult.getContentType();
String originalFilename = extractFilename(img.getUrl());
long length = imageStreamResult.getLength();
String targetImgPath = Ys7DeviceImgConstant.getTargetImgOssPath(originalFilename, img.getDeviceSerial());
SysOssUploadVo drawImageUploadVo = ossService.uploadFileUrlWithNoSave(inputStream, targetImgPath, contentType, length);
targetUrl = drawImageUploadVo.getUrl();
if (StringUtils.isNotBlank(targetUrl)) {
img.setRecognizeUrl(targetUrl);
}
} catch (Exception e) {
log.error("图片绘制失败", e);
}
List<String> codeList = targets.stream()
.map(RecognizeTargetVo::getType).distinct()
.map(RecognizerTypeEnum::fromValue).filter(Objects::nonNull)
.map(RecognizerTypeEnum::getCode).filter(Objects::nonNull)
.toList();
String codeStr = String.join(",", codeList);
record.setViolationType(codeStr);
record.setNum(targets.size());
record.setDeviceSerial(img.getDeviceSerial());
record.setDeviceName(img.getDeviceName());
record.setPicture(targetUrl);
record.setOriginalPicture(img.getUrl());
record.setRecordCategory(HseRecordCategoryEnum.MONITOR.getValue());
record.setProjectId(img.getProjectId());
// 保存识别记录
boolean result = recognizeRecordService.save(record);
if (!result) {
throw new ServiceException("保存识别记录失败");
}
}
img.setImgStatus(recognizeVo.getHasTarget().equals(RecognizerHasTargetEnum.YES.getValue()) ? "2" : "1");
img.setUpdateTime(new Date());
return baseMapper.updateById(img);
}
@Override
@Transactional(rollbackFor = Exception.class)
public int addViolationRecord(AddViolattionRecordReq req) {
HseRecognizeRecord recognizeRecord = recognizeRecordService.getBaseMapper().selectOne(new LambdaQueryWrapper<HseRecognizeRecord>()
.eq(HseRecognizeRecord::getDeviceSerial, req.getDeviceSerial())
.eq(HseRecognizeRecord::getProjectId, req.getProjectId())
.eq(HseRecognizeRecord::getOriginalUrlId, req.getOriginalUrlId()));
if (recognizeRecord == null) {
throw new ServiceException("找不到识别记录!");
}
List<HseViolationRecordCreateDto> violationRecordList = new ArrayList<>();
String violationType = recognizeRecord.getViolationType();
if (StringUtils.isNotBlank(violationType)) {
List<String> list = StringUtils.splitList(violationType);
for (String s : list) {
if (recognizeRecord.getProjectId() != null) {
HseViolationRecordCreateDto violationRecord = new HseViolationRecordCreateDto();
violationRecord.setProjectId(recognizeRecord.getProjectId());
violationRecord.setRecognizeId(recognizeRecord.getId());
violationRecord.setViolationType(s);
violationRecord.setViolationTime(recognizeRecord.getCreateTime());
violationRecordList.add(violationRecord);
}
}
}
if (CollUtil.isNotEmpty(violationRecordList)) {
violationRecordService.insertByMonitor(violationRecordList);
}
OthYs7DeviceImg img = baseMapper.selectById(req.getOriginalUrlId());
if (img != null) {
img.setImgStatus("3");
return baseMapper.updateById(img);
}
return 1;
}
/**
* 提取文件名
*

View File

@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
@ -16,15 +17,21 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.manager.ys7manager.Ys7Constant;
import org.dromara.manager.ys7manager.Ys7Manager;
import org.dromara.manager.ys7manager.Ys7RequestUtils;
import org.dromara.manager.ys7manager.dto.DeviceLocalVideoRequstDto;
import org.dromara.manager.ys7manager.dto.DevicePlayBackUrlRequstDto;
import org.dromara.manager.ys7manager.enums.DeviceOnOffLineEnum;
import org.dromara.manager.ys7manager.vo.DeviceLocalVideoRecordsVo;
import org.dromara.manager.ys7manager.vo.Ys7QueryDeviceResponseVo;
import org.dromara.other.domain.OthYs7Device;
import org.dromara.other.domain.dto.ys7device.*;
import org.dromara.other.domain.enums.OthDeviceStatusEnum;
import org.dromara.other.domain.enums.OthVideoEncryptedEnum;
import org.dromara.other.domain.vo.ys7device.DateAndDeviceLocalVideoVo;
import org.dromara.other.domain.vo.ys7device.OthYs7DeviceVo;
import org.dromara.other.mapper.OthYs7DeviceMapper;
import org.dromara.other.service.IOthYs7DeviceService;
import org.dromara.other.utils.DateRangeUtils;
import org.dromara.project.domain.BusProject;
import org.dromara.project.service.IBusProjectService;
import org.springframework.beans.BeanUtils;
@ -433,6 +440,53 @@ public class OthYs7DeviceServiceImpl extends ServiceImpl<OthYs7DeviceMapper, Oth
}
}
@Override
public String getPlayBackUrl(OthYs7DevicePlayBackUrlReq req) {
String token = ys7Manager.getToken();
DevicePlayBackUrlRequstDto dto = new DevicePlayBackUrlRequstDto();
dto.setAccessToken(token);
dto.setDeviceSerial(req.getDeviceSerial());
dto.setStartTime(req.getStartTime());
dto.setEndTime(req.getEndTime());
dto.setProtocol(1);
dto.setQuality(2);
dto.setType("2");
dto.setRecType(0);
dto.setExpireTime(36000);
return Ys7RequestUtils.getDevicePlayBackUrl(dto);
}
@Override
public List<DeviceLocalVideoRecordsVo> getDeviceLocalVideo(OthYs7DevicePlayBackUrlReq req) {
String token = ys7Manager.getToken();
DeviceLocalVideoRequstDto dto = new DeviceLocalVideoRequstDto();
dto.setAccessToken(token);
dto.setDeviceSerial(req.getDeviceSerial());
dto.setStartTime(req.getStartTime());
dto.setEndTime(req.getEndTime());
return Ys7RequestUtils.getDeviceLocalVideo(dto);
}
/**
* 查询范围日期和设备本地录像列表
* @param req
* @return
*/
@Override
public DateAndDeviceLocalVideoVo getDateAndDeviceLocalVideo(OthYs7DevicePlayBackUrlReq req) {
DateAndDeviceLocalVideoVo vo = new DateAndDeviceLocalVideoVo();
List<String> dateList = DateRangeUtils.getDatesBetweenStrings(req.getStartTime(), req.getEndTime());
vo.setDateList(dateList);
if (CollectionUtils.isNotEmpty(dateList) && dateList.size() > 1) {
String first = dateList.getFirst();
req.setStartTime(first+" 00:00:00");
req.setEndTime(first+" 23:59:59");
}
List<DeviceLocalVideoRecordsVo> deviceLocalVideo = getDeviceLocalVideo(req);
vo.setDeviceList(deviceLocalVideo);
return vo;
}
/**
* 验证萤石摄像对象是否发生更改
*

View File

@ -0,0 +1,166 @@
package org.dromara.other.utils;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
/**
* 日期范围工具类
*/
public class DateRangeUtils {
// 默认时区(东八区)
private static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone("GMT+8");
// 日期格式
private static final String DATE_FORMAT = "yyyy-MM-dd";
// 完整日期时间格式
private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
/**
* 获取两个时间戳之间的所有日期(包含起止日期)
* @param startTimestamp 开始时间戳(秒级/毫秒级字符串或数字)
* @param endTimestamp 结束时间戳(秒级/毫秒级字符串或数字)
* @return 日期列表格式yyyy-MM-dd
*/
public static List<String> getDatesBetweenTimestamps(Object startTimestamp, Object endTimestamp) {
// 转换为毫秒级时间戳
long start = convertToMilliseconds(startTimestamp);
long end = convertToMilliseconds(endTimestamp);
return getDatesBetweenDates(new Date(start), new Date(end));
}
/**
* 获取两个日期之间的所有日期(包含起止日期)
* @param startDate 开始日期
* @param endDate 结束日期
* @return 日期列表格式yyyy-MM-dd
*/
public static List<String> getDatesBetweenDates(Date startDate, Date endDate) {
List<String> dates = new ArrayList<>();
Calendar calendar = Calendar.getInstance(DEFAULT_TIME_ZONE);
// 设置开始日期
calendar.setTime(startDate);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
// 循环添加日期直到结束日期
while (!calendar.getTime().after(endDate)) {
dates.add(DateUtil.format(calendar.getTime(), DATE_FORMAT));
// 日期加1天
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
return dates;
}
/**
* 获取两个日期字符串之间的所有日期
* @param startDateStr 开始日期yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss
* @param endDateStr 结束日期yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss
* @return 日期列表
*/
public static List<String> getDatesBetweenStrings(String startDateStr, String endDateStr) {
Date startDate = parseDateString(startDateStr);
Date endDate = parseDateString(endDateStr);
return getDatesBetweenDates(startDate, endDate);
}
/**
* 将时间戳转换为日期yyyy-MM-dd
*/
public static String timestampToDate(Object timestamp) {
long time = convertToMilliseconds(timestamp);
return DateUtil.format(new Date(time), DATE_FORMAT);
}
/**
* 解析日期字符串为Date对象
*/
private static Date parseDateString(String dateStr) {
try {
if (dateStr.contains(" ")) {
SimpleDateFormat sdf = new SimpleDateFormat(DATETIME_FORMAT);
sdf.setTimeZone(DEFAULT_TIME_ZONE);
return sdf.parse(dateStr);
} else {
SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);
sdf.setTimeZone(DEFAULT_TIME_ZONE);
return sdf.parse(dateStr);
}
} catch (ParseException e) {
throw new IllegalArgumentException("日期格式错误:" + dateStr);
}
}
/**
* 将各种时间戳格式转换为毫秒级时间戳
*/
private static long convertToMilliseconds(Object timestamp) {
if (timestamp == null) {
throw new IllegalArgumentException("时间戳不能为空");
}
String timestampStr = timestamp.toString().trim();
if (StrUtil.isBlank(timestampStr)) {
throw new IllegalArgumentException("时间戳不能为空");
}
try {
long time = Long.parseLong(timestampStr);
// 10位秒级时间戳转为13位毫秒级
if (timestampStr.length() == 10) {
time *= 1000;
}
return time;
} catch (NumberFormatException e) {
throw new IllegalArgumentException("时间戳格式错误:" + timestampStr);
}
}
/**
* 获取指定日期的开始时间戳当天00:00:00
*/
public static long getStartOfDayTimestamp(Object timestamp) {
long time = convertToMilliseconds(timestamp);
Calendar calendar = Calendar.getInstance(DEFAULT_TIME_ZONE);
calendar.setTime(new Date(time));
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTimeInMillis() / 1000; // 返回秒级时间戳
}
/**
* 获取指定日期的结束时间戳当天23:59:59
*/
public static long getEndOfDayTimestamp(Object timestamp) {
long time = convertToMilliseconds(timestamp);
Calendar calendar = Calendar.getInstance(DEFAULT_TIME_ZONE);
calendar.setTime(new Date(time));
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 59);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MILLISECOND, 999);
return calendar.getTimeInMillis() / 1000; // 返回秒级时间戳
}
}

View File

@ -52,6 +52,11 @@ public class HseRecognizeRecord implements Serializable {
*/
private String violationType;
/**
* 原始图片id
*/
private Long originalUrlId;
/**
* 原始图片
*/

View File

@ -37,6 +37,11 @@ public class HseRecognizeRecordCreateDto {
*/
private String recordCategory;
/**
* 原始图片id
*/
private Long originalUrlId;
/**
* 原始图片
*/

View File

@ -66,6 +66,11 @@ public class HseRecognizeRecordVo implements Serializable {
@ExcelDictFormat(dictType = "violation_level_type")
private String violationType;
/**
* 原始图片id
*/
private Long originalUrlId;
/**
* 原始图片
*/