fix: 接收AI数据

This commit is contained in:
tangwei 2026-09-09 11:13:49 +08:00
parent 7fb042387b
commit a69695bafc
11 changed files with 309 additions and 4 deletions

View File

@ -0,0 +1,56 @@
package com.yfd.platform.common.enums;
/**
* @author xyx
* @date 2023-11-27 15:53
*/
public enum AicomTypeEnum {
PFW("AI_5001","漂浮物"),
YYCYX("AI_5002","运鱼车运行"),
YYCCG("AI_5003","运鱼船出港"),
JYCJY("AI_5004","集鱼船集鱼"),
DLMYX("AI_5005","叠梁门运行"),
GYSYX("AI_5006","赶鱼栅运行"),
SYJYX("AI_5007","升鱼机运行"),
AGVXC("AI_5008","AGV小车"),
YLZY("AI_5009","鱼类暂养"),
BASYJ("AI_5010","坝上升鱼机"),
PFWQL("AI_5011","漂浮物清理"),
YDFL("AI_5012","鱼道放流");
private String code;
private String name;
AicomTypeEnum(String code, String name){
this.code = code;
this.name = name;
}
public String getCode(){
return this.code;
}
public String getName(){
return this.name;
}
public enum DlmValueEnum {
CLOSE("close","关闭"),
OPEN("open","开启");
private String code;
private String name;
DlmValueEnum(String code, String name){
this.code = code;
this.name = name;
}
public String getCode(){
return this.code;
}
public String getName(){
return this.name;
}
}
}

View File

@ -15,6 +15,9 @@ public class ImeVmsstbprptVo {
@Schema(description = "站类编码")
private String sttpCode;
@Schema(description = "站类编码")
private String sttp;
@Schema(description = "描点状态")
private String anchoPointState;

View File

@ -433,6 +433,7 @@ public class ImeServiceImpl implements ImeService {
return "SELECT " +
"src.STCD AS id, " +
"src.STTP_CODE AS sttpCode, " +
"src.STTP AS sttp, " +
"CONCAT('ZHZB_', src.STTP_CODE) AS anchoPointState, " +
"src.STLC AS stlc, " +
"src.STCD AS stcd, " +
@ -477,6 +478,7 @@ public class ImeServiceImpl implements ImeService {
Map<String, String> columns = new LinkedHashMap<>();
columns.put("id", "t.id AS id");
columns.put("sttpCode", "t.sttpCode AS sttpCode");
columns.put("sttp", "t.sttp AS sttp");
columns.put("stcd", "t.stcd AS stcd");
columns.put("stnm", "t.stnm AS stnm");
columns.put("rstcd", "t.rstcd AS rstcd");

View File

@ -2,15 +2,15 @@ package com.yfd.platform.qgc_env.warn.controller;
import com.yfd.platform.common.DataSourceRequest;
import com.yfd.platform.config.ResponseResult;
import com.yfd.platform.qgc_env.warn.entity.ao.ReceiveAiDataAo;
import com.yfd.platform.qgc_env.warn.service.WarnDataService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/warn")
@ -80,4 +80,11 @@ public class WarnDataController {
public ResponseResult getAiFileKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) {
return ResponseResult.successData(warnDataService.getAiFileKendoListCust(dataSourceRequest));
}
@PostMapping("/receiveAiData")
@Operation(summary = "接收AI数据")
public ResponseResult receiveAiData(@RequestBody List<ReceiveAiDataAo> aoList) {
warnDataService.receiveAiData(aoList);
return ResponseResult.success();
}
}

View File

@ -0,0 +1,41 @@
package com.yfd.platform.qgc_env.warn.entity.ao;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
/**
* AI识别数据AI设备上抛识别的 aidata 载荷
*/
@Data
@Schema(description = "AI识别数据")
public class AiDataAo {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(description = "识别时间")
private Date time;
@Schema(description = "漂浮物面积(平方米)")
private BigDecimal area;
@Schema(description = "视频流量")
private BigDecimal flow;
@Schema(description = "水位")
private BigDecimal water_level;
@Schema(description = "断面流速分布")
private Map<String, AiFlowAo> flow_info;
/**
* 公共识别数据字段各类型通用取数写入 SD_AICOM_R.AI_VAL
*/
@Schema(description = "公共识别数据各类型通用写入AI_VAL")
private String value;
}

View File

@ -0,0 +1,17 @@
package com.yfd.platform.qgc_env.warn.entity.ao;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Data
@Schema(description = "AI视频流量数据")
public class AiFlowAo {
@Schema(description = "流速")
private BigDecimal v;
@Schema(description = "面积")
private BigDecimal area;
}

View File

@ -0,0 +1,39 @@
package com.yfd.platform.qgc_env.warn.entity.ao;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* 接收AI数据入参
*/
@Data
@Schema(description = "接收AI数据入参")
public class ReceiveAiDataAo {
@Schema(description = "设备编号")
private String dvcd;
@Schema(description = "事件类型0000=心跳 5001=漂浮物 5011=漂浮物清理 5021=视频流量 AI_RIV=河道识别 AI_FISHING=捕鱼识别")
private String type;
@Schema(description = "AI识别数据")
private AiDataAo aidata;
@Schema(description = "文件id,多个逗号分隔")
private String files;
@Schema(description = "AI识别发生时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date aitime;
@Schema(description = "状态")
private Boolean status;
@Schema(description = "消息")
private String message;
}

View File

@ -2,6 +2,7 @@ package com.yfd.platform.qgc_env.warn.service;
import com.yfd.platform.common.DataSourceRequest;
import com.yfd.platform.common.DataSourceResult;
import com.yfd.platform.qgc_env.warn.entity.ao.ReceiveAiDataAo;
import com.yfd.platform.qgc_env.warn.entity.vo.AlarmPointVo;
import com.yfd.platform.qgc_env.warn.entity.vo.AiRecordVo;
import com.yfd.platform.qgc_env.warn.entity.vo.AiProtectVo;
@ -35,4 +36,11 @@ public interface WarnDataService {
DataSourceResult<StcdWarnStateVo> getWarnStcdKendoListCust(DataSourceRequest dataSourceRequest);
DataSourceResult<AiFileVo> getAiFileKendoListCust(DataSourceRequest dataSourceRequest);
/**
* 接收AI设备上抛的识别数据合并写入 SD_AICOM_R / SD_AIHEARTBEAT_R
*
* @param aoList AI数据列表
*/
void receiveAiData(List<ReceiveAiDataAo> aoList);
}

View File

@ -11,6 +11,8 @@ import com.yfd.platform.common.DataSourceResult;
import com.yfd.platform.common.GroupHelper;
import com.yfd.platform.common.GroupingInfo;
import com.yfd.platform.common.MicroservicDynamicSQLMapper;
import com.yfd.platform.common.exception.BizException;
import com.yfd.platform.qgc_env.warn.entity.ao.ReceiveAiDataAo;
import com.yfd.platform.qgc_env.warn.entity.vo.AlarmPointVo;
import com.yfd.platform.qgc_env.warn.entity.vo.AiComListVo;
import com.yfd.platform.qgc_env.warn.entity.vo.AiDmRunVo;
@ -22,9 +24,12 @@ import com.yfd.platform.qgc_env.warn.entity.vo.EngWarnCountVo;
import com.yfd.platform.qgc_env.warn.entity.vo.StcdWarnStateVo;
import com.yfd.platform.qgc_env.warn.service.WarnDataService;
import com.yfd.platform.utils.QgcQueryWrapperUtil;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.ArrayList;
@ -43,6 +48,9 @@ public class WarnDataServiceImpl implements WarnDataService {
@Resource
private MicroservicDynamicSQLMapper microservicDynamicSQLMapper;
@Resource
private JdbcTemplate jdbcTemplate;
@Override
public DataSourceResult<EngWarnCountVo> getEngWarnCount(DataSourceRequest dataSourceRequest) {
DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest();
@ -2586,4 +2594,123 @@ public class WarnDataServiceImpl implements WarnDataService {
default -> null;
};
}
// ==================== 接收AI数据(SD_AICOM_R / SD_AIHEARTBEAT_R) ====================
@Override
@Transactional(rollbackFor = Exception.class)
public void receiveAiData(List<ReceiveAiDataAo> aoList) {
if (CollUtil.isEmpty(aoList)) {
throw new BizException("数据不能为空");
}
List<Object[]> aicomRows = new ArrayList<>();
List<Object[]> heartbeatRows = new ArrayList<>();
for (ReceiveAiDataAo ao : aoList) {
if (ao == null) {
continue;
}
if (StrUtil.isBlank(ao.getDvcd())) {
throw new BizException("站码不能为空");
}
if (StrUtil.isBlank(ao.getType())) {
throw new BizException("AI数据类型不能为空");
}
if (ao.getAitime() == null) {
throw new BizException("AI数据发生时间不能为空");
}
String stcd = ao.getDvcd().trim();
String tm = DateUtil.formatDateTime(ao.getAitime());
String remarkJson = JSONUtil.toJsonStr(ao.getAidata());
switch (ao.getType()) {
case "0000":
// ai心跳数据
heartbeatRows.add(new Object[]{stcd, tm, ao.getFiles()});
break;
case "5001":
// 漂浮物识别
if (ao.getAidata() == null || ao.getAidata().getArea() == null) {
throw new BizException("漂浮物面积不能为空");
}
aicomRows.add(new Object[]{
stcd, tm, "AI_5001",
ao.getAidata().getArea().toPlainString(),
ao.getFiles(), remarkJson
});
break;
case "5011":
// 漂浮物清理
aicomRows.add(new Object[]{stcd, tm, "AI_5011", null, ao.getFiles(), remarkJson});
break;
case "5021":
// 视频流量
aicomRows.add(new Object[]{
stcd, tm, "AI_5014",
ao.getAidata() != null && ao.getAidata().getFlow() != null
? ao.getAidata().getFlow().toPlainString() : null,
ao.getFiles(), remarkJson
});
break;
case "AI_RIV":
case "AI_FISHING":
// 新增类型AI_VAL 取自 aidata 公共识别字段 value
aicomRows.add(new Object[]{
stcd, tm, ao.getType(),
ao.getAidata() != null ? ao.getAidata().getValue() : null,
ao.getFiles(), remarkJson
});
break;
default:
throw new BizException("未知的AI数据类型");
}
}
if (!aicomRows.isEmpty()) {
mergeAiCom(aicomRows);
}
if (!heartbeatRows.isEmpty()) {
mergeAiHeartbeat(heartbeatRows);
}
}
private void mergeAiCom(List<Object[]> rows) {
StringBuilder sql = new StringBuilder("MERGE INTO SD_AICOM_R target USING (");
for (int i = 0; i < rows.size(); i++) {
if (i > 0) {
sql.append(" UNION ALL ");
}
sql.append("SELECT ? AS STCD, TO_DATE(?, 'YYYY-MM-DD HH24:MI:SS') AS TM, ")
.append("? AS TYPE, ? AS AI_VAL, ? AS FID, ? AS REMARK FROM DUAL");
}
sql.append(") source ON (target.STCD = source.STCD AND target.TM = source.TM AND target.TYPE = source.TYPE) ")
.append("WHEN NOT MATCHED THEN INSERT (STCD, TM, TYPE, AI_VAL, FID, REMARK, RECORD_TIME, IS_DELETED) ")
.append("VALUES (source.STCD, source.TM, source.TYPE, source.AI_VAL, source.FID, source.REMARK, SYSDATE, 0) ")
.append("WHEN MATCHED THEN UPDATE SET ")
.append("target.AI_VAL = source.AI_VAL, ")
.append("target.FID = source.FID, ")
.append("target.REMARK = source.REMARK, ")
.append("target.MODIFY_TIME = SYSDATE");
List<Object> params = new ArrayList<>();
for (Object[] row : rows) {
params.addAll(Arrays.asList(row));
}
jdbcTemplate.update(sql.toString(), params.toArray());
}
private void mergeAiHeartbeat(List<Object[]> rows) {
StringBuilder sql = new StringBuilder("MERGE INTO SD_AIHEARTBEAT_R target USING (");
for (int i = 0; i < rows.size(); i++) {
if (i > 0) {
sql.append(" UNION ALL ");
}
sql.append("SELECT ? AS STCD, TO_DATE(?, 'YYYY-MM-DD HH24:MI:SS') AS TM, ? AS FID FROM DUAL");
}
sql.append(") source ON (target.STCD = source.STCD AND target.TM = source.TM) ")
.append("WHEN NOT MATCHED THEN INSERT (STCD, TM, FID, RECORD_TIME, IS_DELETED) ")
.append("VALUES (source.STCD, source.TM, source.FID, SYSDATE, 0) ")
.append("WHEN MATCHED THEN UPDATE SET target.FID = source.FID, target.MODIFY_TIME = SYSDATE");
List<Object> params = new ArrayList<>();
for (Object[] row : rows) {
params.addAll(Arrays.asList(row));
}
jdbcTemplate.update(sql.toString(), params.toArray());
}
}

View File

@ -12,6 +12,9 @@ public class WeVmsstbprptVo {
@Schema(description = "主键")
private String id;
@Schema(description = "站类编码")
private String sttp;
@Schema(description = "站类编码")
private String sttpCode;

View File

@ -4232,6 +4232,7 @@ public class WeFishServiceImpl implements WeFishService {
private String buildWeVmsstbprptViewSql() {
return "SELECT " +
"src.STCD AS id, " +
"src.STTP AS sttp, " +
"src.STTP_CODE AS sttpCode, " +
"src.STLC AS stlc, " +
"src.STCD AS stcd, " +
@ -4370,6 +4371,7 @@ public class WeFishServiceImpl implements WeFishService {
Map<String, String> columns = new LinkedHashMap<>();
columns.put("id", "t.id AS id");
columns.put("sttpCode", "t.sttpCode AS sttpCode");
columns.put("sttp", "t.sttp AS sttp");
columns.put("stcd", "t.stcd AS stcd");
columns.put("stnm", "t.stnm AS stnm");
columns.put("rstcd", "t.rstcd AS rstcd");