diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlAiRController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlAiRController.java
new file mode 100644
index 00000000..951a9772
--- /dev/null
+++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlAiRController.java
@@ -0,0 +1,314 @@
+package com.yfd.platform.qgc_base.controller;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yfd.platform.annotation.Log;
+import com.yfd.platform.common.DataSourceRequest;
+import com.yfd.platform.config.ResponseResult;
+import com.yfd.platform.qgc_base.domain.SdEngInfoBHOperateRequest;
+import com.yfd.platform.qgc_base.domain.SdFishDictoryB;
+import com.yfd.platform.qgc_base.domain.SdFpssrlAiR;
+import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest;
+import com.yfd.platform.qgc_base.service.ISdFishDictoryBService;
+import com.yfd.platform.qgc_base.service.ISdFpssrlAiRService;
+import com.yfd.platform.qgc_data.service.AttachmentUploadService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * 过鱼设施AI盒子自动数据表 前端控制器
+ *
+ */
+@Slf4j
+@RestController
+@RequestMapping("/base/fpssrlAiR")
+@Tag(name = "过鱼设施AI自动数据管理")
+public class SdFpssrlAiRController {
+
+ @Resource
+ private ISdFpssrlAiRService sdFpssrlAiRService;
+
+ @Resource
+ private ObjectMapper objectMapper;
+
+ @Resource
+ private AttachmentUploadService attachmentUploadService;
+
+ @Resource
+ private ISdFishDictoryBService sdFishDictoryBService;
+
+ // ==================== CRUD 方法 ====================
+
+ @PostMapping("/queryPageList")
+ @Operation(summary = "分页查询过鱼AI自动数据列表(支持动态过滤和排序)")
+ public ResponseResult queryPageList(@RequestBody DataSourceRequest request) {
+ Page page = sdFpssrlAiRService.queryPageList(request);
+ return ResponseResult.successData(page);
+ }
+
+ @GetMapping("/getById")
+ @Operation(summary = "根据ID查询过鱼AI自动数据")
+ public ResponseResult getById(@RequestParam String id) {
+ return ResponseResult.successData(sdFpssrlAiRService.getById(id));
+ }
+
+ @Log(module = "过鱼设施AI自动数据管理", value = "新增过鱼AI自动数据")
+ @PostMapping("/add")
+ @Operation(summary = "新增过鱼AI自动数据")
+ public ResponseResult add(@RequestParam("data") String dataStr,
+ @RequestParam(value = "picFiles", required = false) List picFiles,
+ @RequestParam(value = "vdFiles", required = false) List vdFiles) {
+ try {
+ SdEngInfoBHOperateRequest request = objectMapper.readValue(dataStr, SdEngInfoBHOperateRequest.class);
+ SdFpssrlAiR entity = request == null || request.getEngInfo() == null ? null
+ : objectMapper.convertValue(request.getEngInfo(), SdFpssrlAiR.class);
+ if (entity == null) {
+ return ResponseResult.error("数据不能为空");
+ }
+
+ // 上传附件并设置到实体(图片→firstImgUrl,视频→videoUrl)
+ applyUploadedFiles(entity, picFiles, vdFiles);
+
+ boolean result = sdFpssrlAiRService.add(entity, request.getSource());
+ return result ? ResponseResult.success("新增成功") : ResponseResult.error("新增失败");
+ } catch (Exception e) {
+ log.error("新增过鱼AI自动数据失败", e);
+ return ResponseResult.error("新增失败: " + e.getMessage());
+ }
+ }
+
+ @Log(module = "过鱼设施AI自动数据管理", value = "修改过鱼AI自动数据")
+ @PostMapping("/update")
+ @Operation(summary = "修改过鱼AI自动数据")
+ public ResponseResult update(@RequestParam("data") String dataStr,
+ @RequestParam(value = "picFiles", required = false) List picFiles,
+ @RequestParam(value = "vdFiles", required = false) List vdFiles) {
+ try {
+ SdEngInfoBHOperateRequest request = objectMapper.readValue(dataStr, SdEngInfoBHOperateRequest.class);
+ Map engInfo = request == null ? null : request.getEngInfo();
+ if (engInfo == null || engInfo.get("id") == null) {
+ return ResponseResult.error("数据或ID不能为空");
+ }
+
+ // 获取修改前的实体,用于后续清理被替换的旧附件
+ SdFpssrlAiR before = sdFpssrlAiRService.getById((String) engInfo.get("id"));
+ List oldAttachmentIds = before != null ? collectAttachmentIds(before) : Collections.emptyList();
+
+ // 上传新附件并合并回 patch map(仅当本次有文件上传时才覆盖对应字段)
+ SdFpssrlAiR tempEntity = objectMapper.convertValue(engInfo, SdFpssrlAiR.class);
+ applyUploadedFiles(tempEntity, picFiles, vdFiles);
+ if (picFiles != null && !picFiles.isEmpty()) {
+ engInfo.put("firstimgurl", tempEntity.getFirstImgUrl());
+ }
+ if (vdFiles != null && !vdFiles.isEmpty()) {
+ engInfo.put("videourl", tempEntity.getVideoUrl());
+ }
+
+ boolean result = sdFpssrlAiRService.update(engInfo, request.getSource());
+ if (result) {
+ // 清理被替换的旧附件
+ SdFpssrlAiR after = sdFpssrlAiRService.getById((String) engInfo.get("id"));
+ deleteReplacedFiles(oldAttachmentIds, collectAttachmentIds(after));
+ }
+ return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败");
+ } catch (Exception e) {
+ log.error("修改过鱼AI自动数据失败", e);
+ return ResponseResult.error("修改失败: " + e.getMessage());
+ }
+ }
+
+ @Log(module = "过鱼设施AI自动数据管理", value = "删除过鱼AI自动数据")
+ @PostMapping("/delete")
+ @Operation(summary = "删除过鱼AI自动数据")
+ public ResponseResult delete(@RequestBody SdEngInfoBHOperateRequest request) {
+ List ids = request == null ? null : request.getIds();
+ if (ids == null || ids.isEmpty()) {
+ return ResponseResult.error("ID不能为空");
+ }
+
+ // 删除前先收集所有需要清理的附件ID
+ List allAttachmentIds = new ArrayList<>();
+ for (String id : ids) {
+ SdFpssrlAiR entity = sdFpssrlAiRService.getById(id);
+ if (entity != null) {
+ allAttachmentIds.addAll(collectAttachmentIds(entity));
+ }
+ }
+
+ boolean result = sdFpssrlAiRService.delete(ids, request.getSource());
+ if (result) {
+ // 删除关联的附件
+ deleteAttachments(allAttachmentIds);
+ }
+ return result ? ResponseResult.success("删除成功") : ResponseResult.error("删除失败");
+ }
+
+ // ==================== AI 设备识别数据上报接口 ====================
+
+ /**
+ * AI设备图片/视频上传接口
+ * 步骤1:先上传图片获取附件URL,再将URL填入数据上报接口
+ */
+ @PostMapping("/ai/uploadImage")
+ @Operation(summary = "AI设备图片上传")
+ public ResponseResult aiUploadImage(@RequestParam("file") MultipartFile file) {
+ if (file == null || file.isEmpty()) {
+ return ResponseResult.error("文件不能为空");
+ }
+ try {
+ List files = new ArrayList<>();
+ files.add(file);
+ List attachmentIds = attachmentUploadService.uploadMultipartFiles(files);
+ if (attachmentIds != null && !attachmentIds.isEmpty()) {
+ return ResponseResult.successData(attachmentIds);
+ } else {
+ return ResponseResult.error("图片上传失败");
+ }
+ } catch (Exception e) {
+ log.error("AI图片上传失败", e);
+ return ResponseResult.error("图片上传失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * AI设备识别数据上报接口(支持单条和批量)
+ * 步骤2:上传完图片后,将附件ID填入JSON数组调用此接口
+ */
+ @PostMapping("/ai/report")
+ @Operation(summary = "AI设备识别数据上报(支持批量)")
+ public ResponseResult aiReport(@RequestBody List requests) {
+ if (requests == null || requests.isEmpty()) {
+ return ResponseResult.error("请求数据不能为空");
+ }
+ List list = sdFishDictoryBService.list(new LambdaQueryWrapper().eq(SdFishDictoryB::getIsDeleted, "0").select(SdFishDictoryB::getId, SdFishDictoryB::getName));
+ Map idNameMap = list.stream()
+ .filter(item -> item.getId() != null && item.getName() != null)
+ .collect(Collectors.toMap(
+ SdFishDictoryB::getName,
+ SdFishDictoryB::getId,
+ (oldValue, newValue) -> oldValue // 如果有重复 key 保留旧值(实际不会重复)
+ ));
+ // 逐条校验
+ for (int i = 0; i < requests.size(); i++) {
+ SdFpssrlRAiRequest req = requests.get(i);
+ if (req == null) {
+ return ResponseResult.error("第" + (i + 1) + "条数据为空");
+ }
+ if (req.getStcd() == null || req.getStcd().isEmpty()) {
+ return ResponseResult.error("第" + (i + 1) + "条过鱼设施编码(stcd)不能为空");
+ }
+ if (req.getFwdx() == null || req.getFwdx().isEmpty()) {
+ return ResponseResult.error("第" + (i + 1) + "条服务对象(fwdx)不能为空");
+ }
+ if (req.getTm() == null) {
+ return ResponseResult.error("第" + (i + 1) + "条识别时间(tm)不能为空");
+ }
+ if (req.getFtp() == null || req.getFtp().isEmpty()) {
+ return ResponseResult.error("第" + (i + 1) + "条鱼种类(ftp)不能为空");
+ }
+ if (StrUtil.isNotBlank(req.getFtp())) {
+ req.setFtp(idNameMap.getOrDefault(req.getFtp(), req.getFtp()));
+ }
+ }
+
+ try {
+ List result = sdFpssrlAiRService.processAiReportBatch(requests);
+ log.info("AI批量上报成功,共{}条", result.size());
+ return ResponseResult.success();
+ } catch (Exception e) {
+ log.error("AI识别数据批量上报失败", e);
+ return ResponseResult.error("上报失败: " + e.getMessage());
+ }
+ }
+
+ // ==================== 附件处理辅助方法 ====================
+
+ /**
+ * 上传图片/视频附件并追加附件ID到 firstImgUrl/videoUrl 字段(与已有ID以逗号拼接)
+ */
+ private void applyUploadedFiles(SdFpssrlAiR entity, List picFiles, List vdFiles) {
+ if (picFiles != null && !picFiles.isEmpty()) {
+ List ids = attachmentUploadService.uploadMultipartFiles(picFiles);
+ if (!ids.isEmpty()) {
+ entity.setFirstImgUrl(mergeIds(entity.getFirstImgUrl(), ids));
+ }
+ }
+ if (vdFiles != null && !vdFiles.isEmpty()) {
+ List ids = attachmentUploadService.uploadMultipartFiles(vdFiles);
+ if (!ids.isEmpty()) {
+ entity.setVideoUrl(mergeIds(entity.getVideoUrl(), ids));
+ }
+ }
+ }
+
+ /**
+ * 将新上传的附件ID列表追加到已有ID字符串后面(逗号分隔)
+ */
+ private String mergeIds(String existing, List ids) {
+ String newIds = ids.stream()
+ .filter(StrUtil::isNotBlank)
+ .collect(Collectors.joining(","));
+ if (StrUtil.isBlank(newIds)) {
+ return existing;
+ }
+ return StrUtil.isBlank(existing) ? newIds : existing + "," + newIds;
+ }
+
+ /**
+ * 收集实体中 firstImgUrl/videoUrl 字段的附件ID
+ */
+ private List collectAttachmentIds(SdFpssrlAiR entity) {
+ List ids = new ArrayList<>();
+ if (entity == null) {
+ return ids;
+ }
+ if (StrUtil.isNotBlank(entity.getFirstImgUrl())) {
+ Arrays.stream(entity.getFirstImgUrl().split(","))
+ .map(String::trim)
+ .filter(StrUtil::isNotBlank)
+ .forEach(ids::add);
+ }
+ if (StrUtil.isNotBlank(entity.getVideoUrl())) {
+ Arrays.stream(entity.getVideoUrl().split(","))
+ .map(String::trim)
+ .filter(StrUtil::isNotBlank)
+ .forEach(ids::add);
+ }
+ return ids;
+ }
+
+ /**
+ * 删除被替换的旧附件(old中有但new中没有的)
+ */
+ private void deleteReplacedFiles(List oldIds, List newIds) {
+ for (String oldId : oldIds) {
+ if (StrUtil.isBlank(oldId)) {
+ continue;
+ }
+ if (!newIds.contains(oldId)) {
+ attachmentUploadService.deleteFile(oldId);
+ }
+ }
+ }
+
+ /**
+ * 批量删除附件
+ */
+ private void deleteAttachments(List attachmentIds) {
+ for (String id : attachmentIds) {
+ if (StrUtil.isNotBlank(id)) {
+ attachmentUploadService.deleteFile(id);
+ }
+ }
+ }
+}
diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlAiR.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlAiR.java
index 2a85652d..8fcd28b5 100644
--- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlAiR.java
+++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlAiR.java
@@ -115,6 +115,10 @@ public class SdFpssrlAiR implements Serializable {
/** 备注 */
private String remark;
+ /** 鱼种类名称(非表字段,用于列表展示) */
+ @TableField(exist = false)
+ private String ftpName;
+
/** 过鱼设施名称(非表字段,用于列表展示) */
@TableField(exist = false)
private String stnm;
diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlAiRService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlAiRService.java
new file mode 100644
index 00000000..4b61c399
--- /dev/null
+++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlAiRService.java
@@ -0,0 +1,48 @@
+package com.yfd.platform.qgc_base.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.yfd.platform.common.DataSourceRequest;
+import com.yfd.platform.qgc_base.domain.SdFpssrlAiR;
+import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * 过鱼设施AI盒子自动数据表 服务接口
+ *
+ */
+public interface ISdFpssrlAiRService extends IService {
+
+ /**
+ * 分页查询(Kendo Grid)
+ */
+ Page queryPageList(DataSourceRequest request);
+
+ /**
+ * 新增
+ */
+ boolean add(SdFpssrlAiR entity, String source);
+
+ /**
+ * 修改(按前端 patch 显式更新字段,含 null 置空)
+ */
+ boolean update(Map engInfoPatch, String source) throws Exception;
+
+ /**
+ * 删除(逻辑删除)
+ */
+ boolean delete(List ids, String source);
+
+ /**
+ * 根据ID查询(排除已删除的)
+ */
+ SdFpssrlAiR getById(String id);
+
+ /**
+ * 批量处理AI设备上报的识别数据
+ */
+ List processAiReportBatch(List requests);
+}
diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlAiRServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlAiRServiceImpl.java
new file mode 100644
index 00000000..b77f6b17
--- /dev/null
+++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlAiRServiceImpl.java
@@ -0,0 +1,153 @@
+package com.yfd.platform.qgc_base.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.yfd.platform.common.DataSourceRequest;
+import com.yfd.platform.qgc_base.domain.SdFpssrlAiR;
+import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest;
+import com.yfd.platform.qgc_base.mapper.SdFpssrlAiRMapper;
+import com.yfd.platform.qgc_base.service.IMsOperationLogService;
+import com.yfd.platform.qgc_base.service.ISdFpssrlAiRService;
+import com.yfd.platform.utils.CodeToNameMetadataBo;
+import com.yfd.platform.utils.DataSourceRequestUtil;
+import com.yfd.platform.utils.DictCodeToNameConverter;
+import com.yfd.platform.utils.SecurityUtils;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * 过鱼设施AI盒子自动数据表 服务实现类
+ *
+ */
+@Slf4j
+@Service
+public class SdFpssrlAiRServiceImpl extends ServiceImpl implements ISdFpssrlAiRService {
+
+ private static final String TABLE_NAME = "SD_FPSSRL_AI_R";
+
+ @Resource
+ private IMsOperationLogService msOperationLogService;
+
+ @Resource
+ private DictCodeToNameConverter dictCodeToNameConverter;
+
+ @Resource
+ private PatchUpdateHelper patchUpdateHelper;
+
+ private static final Set TRANSIENT_FIELDS = Set.of("stnm", "ftpName", "serialVersionUID");
+
+ /**
+ * 代码转名称元数据配置列表
+ */
+ private static final List CODE_TO_NAME_META_LIST = new ArrayList<>();
+
+ static {
+ CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("ftp").modifyProperty("ftpName")
+ .dictType("DYNAMIC").dictSource("SD_FISHDICTORY_B").codeColumn("ID").nameColumn("NAME")
+ .filter("NVL(IS_DELETED, 0) = 0 AND NVL(ENABLE, 1) = 1").build());
+ }
+
+ @Override
+ public Page queryPageList(DataSourceRequest request) {
+ return DataSourceRequestUtil.executeQuery(request, SdFpssrlAiR.class, this);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean add(SdFpssrlAiR entity, String source) {
+ boolean result = this.save(entity);
+ if (result) {
+ dictCodeToNameConverter.convertCodeToName(entity, CODE_TO_NAME_META_LIST);
+ msOperationLogService.recordAddDetailLog(entity.getId(), TABLE_NAME, entity, source, CODE_TO_NAME_META_LIST);
+ }
+ return result;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean update(Map engInfoPatch, String source) throws Exception {
+ return patchUpdateHelper.execute(this, engInfoPatch, source, TABLE_NAME, SdFpssrlAiR.class,
+ TRANSIENT_FIELDS, CODE_TO_NAME_META_LIST, "id");
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean delete(List ids, String source) {
+ if (ids == null || ids.isEmpty()) return false;
+ int count = 0;
+ for (String id : ids) {
+ SdFpssrlAiR entity = getById(id);
+ LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>();
+ wrapper.eq(SdFpssrlAiR::getId, id);
+ wrapper.set(SdFpssrlAiR::getIsDeleted, 1);
+ wrapper.set(SdFpssrlAiR::getDeleteUser, SecurityUtils.getCurrentUsername());
+ wrapper.set(SdFpssrlAiR::getDeleteTime, new Date());
+ if (this.update(wrapper)) {
+ msOperationLogService.recordDeleteDetailLog(id, TABLE_NAME, entity, source);
+ count++;
+ }
+ }
+ return count > 0;
+ }
+
+ @Override
+ public SdFpssrlAiR getById(String id) {
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(SdFpssrlAiR::getId, id)
+ .eq(SdFpssrlAiR::getIsDeleted, 0);
+ return getOne(wrapper);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public List processAiReportBatch(List requests) {
+ List entities = new ArrayList<>();
+ Date now = new Date();
+ for (SdFpssrlRAiRequest req : requests) {
+ SdFpssrlAiR entity = new SdFpssrlAiR();
+ entity.setStcd(req.getFwdx());
+ entity.setAiBoxCode(req.getStcd());
+ entity.setTm(req.getTm());
+ entity.setFtp(req.getFtp());
+ entity.setFcnt(req.getFcnt() != null ? req.getFcnt() : 1);
+ entity.setFsz(req.getFsz());
+ entity.setLength(req.getLength());
+ entity.setWidth(req.getWidth());
+ entity.setFishspeed(req.getFishspeed());
+ entity.setDirection(req.getDirection());
+ entity.setFishposition(req.getFishposition());
+ entity.setFirstImgUrl(req.getFirstImgUrl());
+ entity.setSecondImgUrl(req.getSecondImgUrl());
+ entity.setVideoUrl(req.getVideoUrl());
+ entity.setTemperature(req.getTemperature());
+ entity.setWaterlevel(req.getWaterlevel());
+ entity.setSpeed(req.getSpeed());
+ entity.setQ(req.getQ());
+ entity.setDox(req.getDox());
+ entity.setTu(req.getTu());
+ entity.setChannelno(StrUtil.isNotBlank(req.getChannelno()) ? req.getChannelno() : "主通道");
+ entity.setFid(req.getFid());
+ entity.setRemark(req.getRemark());
+ entity.setRecordUser("AI_SYSTEM");
+ entity.setRecordTime(now);
+ entity.setModifyUser("AI_SYSTEM");
+ entity.setModifyTime(now);
+ entity.setIsDeleted(0);
+ entities.add(entity);
+ }
+ this.baseMapper.mergeFishRecords(entities);
+ return entities;
+ }
+}
diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java
index e04595d3..dcdc553d 100644
--- a/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java
+++ b/backend/src/main/java/com/yfd/platform/qgc_env/fp/service/impl/FpRunServiceImpl.java
@@ -1232,7 +1232,7 @@ public class FpRunServiceImpl implements FpRunService {
DataSourceLoadOptionsBase loadOptions) {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ")
- .append(buildQgcFpssrlDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect()))
+ .append(buildQgcFpssrlDetailAISelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect()))
.append(" FROM (")
.append(buildQgcFpssrlAiRCoreSql())
.append(") t WHERE 1 = 1 ");
@@ -1704,12 +1704,12 @@ public class FpRunServiceImpl implements FpRunService {
") src";
}
-
private String buildQgcFpssrlAiRCoreSql() {
return "SELECT " +
"src.ID AS id, " +
"src.STCD AS stcd, " +
"src.STNM AS stnm, " +
+ "src.ISFS AS isfs, " +
"src.BASEID AS baseId, " +
"src.BASENAME AS baseName, " +
"src.RSTCD AS rstcd, " +
@@ -1729,6 +1729,12 @@ public class FpRunServiceImpl implements FpRunService {
"src.FISHSPEED AS fishspeed, " +
"src.DIRECTION AS direction, " +
"src.TEMPERATURE AS temperature, " +
+ "src.WATERLEVEL AS waterlevel, " +
+ "src.FISHPOSITION AS fishposition, " +
+ "src.REMARK AS remark, " +
+ "src.DOX AS dox, " +
+ "src.Q AS q, " +
+ "src.TU AS tu, " +
"src.SPEED AS speed, " +
"src.CHANNELNO AS channelno, " +
"src.FCNT AS fcnt, " +
@@ -1757,11 +1763,18 @@ public class FpRunServiceImpl implements FpRunService {
" COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " +
" t.FTP AS FISHID, " +
" t.FSZ AS FSZ, " +
+ " null AS ISFS, " +
" TO_CHAR(t.LENGTH) AS LENGTH, " +
" TO_CHAR(t.WIDTH) AS WIDTH, " +
" TO_CHAR(t.FISHSPEED) AS FISHSPEED, " +
" TO_CHAR(t.DIRECTION) AS DIRECTION, " +
" TO_CHAR(t.TEMPERATURE) AS TEMPERATURE, " +
+ " TO_CHAR(t.WATERLEVEL) AS WATERLEVEL, " +
+ " TO_CHAR(t.FISHPOSITION) AS FISHPOSITION, " +
+ " TO_CHAR(t.REMARK) AS REMARK, " +
+ " TO_CHAR(t.DOX) AS DOX, " +
+ " TO_CHAR(t.Q) AS Q, " +
+ " TO_CHAR(t.TU) AS TU, " +
" TO_CHAR(t.SPEED) AS SPEED, " +
" t.CHANNELNO AS CHANNELNO, " +
" TO_CHAR(t.FCNT) AS FCNT, " +
@@ -1773,64 +1786,6 @@ public class FpRunServiceImpl implements FpRunService {
" fp.STTP AS STCODE, " +
" sttp.STTP_NAME AS STNAME " +
" FROM SD_FPSSRL_AI_R t " +
- " INNER JOIN V_MS_STBPRP_T fp ON fp.STCD = t.STCD " +
- " LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = fp.STTP AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " +
- " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " +
- " LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " +
- " LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD " +
- " AND hbrv.BASEID = eng.BASE_ID " +
- " AND NVL(hbrv.IS_DELETED, 0) = 0 " +
- " AND NVL(hbrv.ENABLED, 1) = 1 " +
- " LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = t.FTP " +
- " AND relRv.RVCD = eng.HBRVCD " +
- " AND NVL(relRv.IS_DELETED, 0) = 0 " +
- " LEFT JOIN SD_FISHDICTORY_B fishRv ON fishRv.ID = relRv.ZY_FISH_ID " +
- " AND NVL(fishRv.IS_DELETED, 0) = 0 " +
- " AND NVL(fishRv.ENABLE, 1) = 1 " +
- " LEFT JOIN SD_FISHDICTORY_RLTN_B relZy ON relZy.FISH_ID = t.FTP " +
- " AND relZy.RVCD = 'ZY' " +
- " AND NVL(relZy.IS_DELETED, 0) = 0 " +
- " LEFT JOIN SD_FISHDICTORY_B fishZy ON fishZy.ID = relZy.ZY_FISH_ID " +
- " AND NVL(fishZy.IS_DELETED, 0) = 0 " +
- " AND NVL(fishZy.ENABLE, 1) = 1 " +
- " LEFT JOIN SD_FISHDICTORY_B fishDirect ON fishDirect.ID = t.FTP " +
- " AND NVL(fishDirect.IS_DELETED, 0) = 0 " +
- " AND NVL(fishDirect.ENABLE, 1) = 1 " +
- " WHERE NVL(t.IS_DELETED, 0) = 0 " +
- " UNION ALL " +
- " SELECT t.ID AS ID, " +
- " t.STCD AS STCD, " +
- " fp.STNM AS STNM, " +
- " eng.BASE_ID AS BASEID, " +
- " hb.BASENAME AS BASENAME, " +
- " fp.RSTCD AS RSTCD, " +
- " eng.ENNM AS ENNM, " +
- " eng.HBRVCD AS HBRVCD, " +
- " NVL(hb.ORDER_INDEX, 999999) AS BASESTEPSORT, " +
- " NVL(hbrv.ORDER_INDEX, 999999) AS RVCDSTEPSORT, " +
- " NVL(fp.ORDER_INDEX, 999999) AS SITESTEPSORT, " +
- " NVL(eng.ORDER_INDEX, 999999) AS RSTCDSTEPSORT, " +
- " t.STRDT AS TM, " +
- " TO_CHAR(t.STRDT, 'YYYY') AS YR, " +
- " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " +
- " t.FTP AS FISHID, " +
- " t.FSZ AS FSZ, " +
- " CAST(NULL AS VARCHAR2(50)) AS LENGTH, " +
- " CAST(NULL AS VARCHAR2(50)) AS WIDTH, " +
- " CAST(NULL AS VARCHAR2(50)) AS FISHSPEED, " +
- " TO_CHAR(t.DIRECTION) AS DIRECTION, " +
- " CAST(NULL AS VARCHAR2(50)) AS TEMPERATURE, " +
- " CAST(NULL AS VARCHAR2(50)) AS SPEED, " +
- " CAST(NULL AS VARCHAR2(100)) AS CHANNELNO, " +
- " TO_CHAR(t.FCNT) AS FCNT, " +
- " t.PICPTH AS FIRSTIMGURL, " +
- " t.VDPTH AS VIDEOURL, " +
- " 1 AS MWAY, " +
- " t.STRDT AS STRDT, " +
- " t.ENDDT AS ENDDT, " +
- " fp.STTP AS STCODE, " +
- " sttp.STTP_NAME AS STNAME " +
- " FROM SD_FPSS_R t " +
" INNER JOIN SD_FPSS_B_H fp ON fp.STCD = t.STCD " +
" LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = fp.STTP AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " +
" LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " +
@@ -1855,10 +1810,164 @@ public class FpRunServiceImpl implements FpRunService {
" AND NVL(fishDirect.IS_DELETED, 0) = 0 " +
" AND NVL(fishDirect.ENABLE, 1) = 1 " +
" WHERE NVL(t.IS_DELETED, 0) = 0 " +
-// " AND (t.TASK_STATUS = 'Approved' OR t.TASK_STATUS IS NULL) " +
") src";
}
+
+// private String buildQgcFpssrlAiRCoreSql() {
+// return "SELECT " +
+// "src.ID AS id, " +
+// "src.STCD AS stcd, " +
+// "src.STNM AS stnm, " +
+// "src.BASEID AS baseId, " +
+// "src.BASENAME AS baseName, " +
+// "src.RSTCD AS rstcd, " +
+// "src.ENNM AS ennm, " +
+// "src.HBRVCD AS hbrvcd, " +
+// "src.BASESTEPSORT AS baseStepSort, " +
+// "src.RVCDSTEPSORT AS rvcdStepSort, " +
+// "src.SITESTEPSORT AS siteStepSort, " +
+// "src.RSTCDSTEPSORT AS rstcdStepSort, " +
+// "src.TM AS tm, " +
+// "src.YR AS yr, " +
+// "src.FTP AS ftp, " +
+// "src.FISHID AS fishId, " +
+// "src.FSZ AS fsz, " +
+// "src.LENGTH AS length, " +
+// "src.WIDTH AS width, " +
+// "src.FISHSPEED AS fishspeed, " +
+// "src.DIRECTION AS direction, " +
+// "src.TEMPERATURE AS temperature, " +
+// "src.SPEED AS speed, " +
+// "src.CHANNELNO AS channelno, " +
+// "src.FCNT AS fcnt, " +
+// "src.FIRSTIMGURL AS firstimgurl, " +
+// "src.VIDEOURL AS videourl, " +
+// "src.MWAY AS mway, " +
+// "src.STRDT AS strdt, " +
+// "src.ENDDT AS enddt, " +
+// "src.STCODE AS stCode, " +
+// "src.STNAME AS stName " +
+// "FROM ( " +
+// " SELECT t.ID AS ID, " +
+// " t.STCD AS STCD, " +
+// " fp.STNM AS STNM, " +
+// " eng.BASE_ID AS BASEID, " +
+// " hb.BASENAME AS BASENAME, " +
+// " fp.RSTCD AS RSTCD, " +
+// " eng.ENNM AS ENNM, " +
+// " eng.HBRVCD AS HBRVCD, " +
+// " NVL(hb.ORDER_INDEX, 999999) AS BASESTEPSORT, " +
+// " NVL(hbrv.ORDER_INDEX, 999999) AS RVCDSTEPSORT, " +
+// " NVL(fp.ORDER_INDEX, 999999) AS SITESTEPSORT, " +
+// " NVL(eng.ORDER_INDEX, 999999) AS RSTCDSTEPSORT, " +
+// " t.TM AS TM, " +
+// " TO_CHAR(t.TM, 'YYYY') AS YR, " +
+// " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " +
+// " t.FTP AS FISHID, " +
+// " t.FSZ AS FSZ, " +
+// " TO_CHAR(t.LENGTH) AS LENGTH, " +
+// " TO_CHAR(t.WIDTH) AS WIDTH, " +
+// " TO_CHAR(t.FISHSPEED) AS FISHSPEED, " +
+// " TO_CHAR(t.DIRECTION) AS DIRECTION, " +
+// " TO_CHAR(t.TEMPERATURE) AS TEMPERATURE, " +
+// " TO_CHAR(t.SPEED) AS SPEED, " +
+// " t.CHANNELNO AS CHANNELNO, " +
+// " TO_CHAR(t.FCNT) AS FCNT, " +
+// " t.FIRSTIMGURL AS FIRSTIMGURL, " +
+// " t.VIDEOURL AS VIDEOURL, " +
+// " 2 AS MWAY, " +
+// " CAST(NULL AS DATE) AS STRDT, " +
+// " CAST(NULL AS DATE) AS ENDDT, " +
+// " fp.STTP AS STCODE, " +
+// " sttp.STTP_NAME AS STNAME " +
+// " FROM SD_FPSSRL_AI_R t " +
+// " INNER JOIN V_MS_STBPRP_T fp ON fp.STCD = t.STCD " +
+// " LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = fp.STTP AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " +
+// " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " +
+// " LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " +
+// " LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD " +
+// " AND hbrv.BASEID = eng.BASE_ID " +
+// " AND NVL(hbrv.IS_DELETED, 0) = 0 " +
+// " AND NVL(hbrv.ENABLED, 1) = 1 " +
+// " LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = t.FTP " +
+// " AND relRv.RVCD = eng.HBRVCD " +
+// " AND NVL(relRv.IS_DELETED, 0) = 0 " +
+// " LEFT JOIN SD_FISHDICTORY_B fishRv ON fishRv.ID = relRv.ZY_FISH_ID " +
+// " AND NVL(fishRv.IS_DELETED, 0) = 0 " +
+// " AND NVL(fishRv.ENABLE, 1) = 1 " +
+// " LEFT JOIN SD_FISHDICTORY_RLTN_B relZy ON relZy.FISH_ID = t.FTP " +
+// " AND relZy.RVCD = 'ZY' " +
+// " AND NVL(relZy.IS_DELETED, 0) = 0 " +
+// " LEFT JOIN SD_FISHDICTORY_B fishZy ON fishZy.ID = relZy.ZY_FISH_ID " +
+// " AND NVL(fishZy.IS_DELETED, 0) = 0 " +
+// " AND NVL(fishZy.ENABLE, 1) = 1 " +
+// " LEFT JOIN SD_FISHDICTORY_B fishDirect ON fishDirect.ID = t.FTP " +
+// " AND NVL(fishDirect.IS_DELETED, 0) = 0 " +
+// " AND NVL(fishDirect.ENABLE, 1) = 1 " +
+// " WHERE NVL(t.IS_DELETED, 0) = 0 " +
+//// " UNION ALL " +
+//// " SELECT t.ID AS ID, " +
+//// " t.STCD AS STCD, " +
+//// " fp.STNM AS STNM, " +
+//// " eng.BASE_ID AS BASEID, " +
+//// " hb.BASENAME AS BASENAME, " +
+//// " fp.RSTCD AS RSTCD, " +
+//// " eng.ENNM AS ENNM, " +
+//// " eng.HBRVCD AS HBRVCD, " +
+//// " NVL(hb.ORDER_INDEX, 999999) AS BASESTEPSORT, " +
+//// " NVL(hbrv.ORDER_INDEX, 999999) AS RVCDSTEPSORT, " +
+//// " NVL(fp.ORDER_INDEX, 999999) AS SITESTEPSORT, " +
+//// " NVL(eng.ORDER_INDEX, 999999) AS RSTCDSTEPSORT, " +
+//// " t.STRDT AS TM, " +
+//// " TO_CHAR(t.STRDT, 'YYYY') AS YR, " +
+//// " COALESCE(fishRv.NAME, relRv.FISH_NAME, fishZy.NAME, relZy.FISH_NAME, fishDirect.NAME, t.FTP) AS FTP, " +
+//// " t.FTP AS FISHID, " +
+//// " t.FSZ AS FSZ, " +
+//// " CAST(NULL AS VARCHAR2(50)) AS LENGTH, " +
+//// " CAST(NULL AS VARCHAR2(50)) AS WIDTH, " +
+//// " CAST(NULL AS VARCHAR2(50)) AS FISHSPEED, " +
+//// " TO_CHAR(t.DIRECTION) AS DIRECTION, " +
+//// " CAST(NULL AS VARCHAR2(50)) AS TEMPERATURE, " +
+//// " CAST(NULL AS VARCHAR2(50)) AS SPEED, " +
+//// " CAST(NULL AS VARCHAR2(100)) AS CHANNELNO, " +
+//// " TO_CHAR(t.FCNT) AS FCNT, " +
+//// " t.PICPTH AS FIRSTIMGURL, " +
+//// " t.VDPTH AS VIDEOURL, " +
+//// " 1 AS MWAY, " +
+//// " t.STRDT AS STRDT, " +
+//// " t.ENDDT AS ENDDT, " +
+//// " fp.STTP AS STCODE, " +
+//// " sttp.STTP_NAME AS STNAME " +
+//// " FROM SD_FPSS_R t " +
+//// " INNER JOIN SD_FPSS_B_H fp ON fp.STCD = t.STCD " +
+//// " LEFT JOIN SD_STTP_B sttp ON sttp.STTP_CODE = fp.STTP AND NVL(sttp.IS_DELETED, 0) = 0 AND NVL(sttp.ENABLE, 1) = 1 " +
+//// " LEFT JOIN SD_ENGINFO_B_H eng ON eng.STCD = fp.RSTCD AND NVL(eng.IS_DELETED, 0) = 0 " +
+//// " LEFT JOIN SD_HYDROBASE hb ON hb.BASEID = eng.BASE_ID AND NVL(hb.IS_DELETED, 0) = 0 " +
+//// " LEFT JOIN SD_HBRV_DIC hbrv ON hbrv.HBRVCD = eng.HBRVCD " +
+//// " AND hbrv.BASEID = eng.BASE_ID " +
+//// " AND NVL(hbrv.IS_DELETED, 0) = 0 " +
+//// " AND NVL(hbrv.ENABLED, 1) = 1 " +
+//// " LEFT JOIN SD_FISHDICTORY_RLTN_B relRv ON relRv.FISH_ID = t.FTP " +
+//// " AND relRv.RVCD = eng.HBRVCD " +
+//// " AND NVL(relRv.IS_DELETED, 0) = 0 " +
+//// " LEFT JOIN SD_FISHDICTORY_B fishRv ON fishRv.ID = relRv.ZY_FISH_ID " +
+//// " AND NVL(fishRv.IS_DELETED, 0) = 0 " +
+//// " AND NVL(fishRv.ENABLE, 1) = 1 " +
+//// " LEFT JOIN SD_FISHDICTORY_RLTN_B relZy ON relZy.FISH_ID = t.FTP " +
+//// " AND relZy.RVCD = 'ZY' " +
+//// " AND NVL(relZy.IS_DELETED, 0) = 0 " +
+//// " LEFT JOIN SD_FISHDICTORY_B fishZy ON fishZy.ID = relZy.ZY_FISH_ID " +
+//// " AND NVL(fishZy.IS_DELETED, 0) = 0 " +
+//// " AND NVL(fishZy.ENABLE, 1) = 1 " +
+//// " LEFT JOIN SD_FISHDICTORY_B fishDirect ON fishDirect.ID = t.FTP " +
+//// " AND NVL(fishDirect.IS_DELETED, 0) = 0 " +
+//// " AND NVL(fishDirect.ENABLE, 1) = 1 " +
+//// " WHERE NVL(t.IS_DELETED, 0) = 0 " +
+//// " AND (t.TASK_STATUS = 'Approved' OR t.TASK_STATUS IS NULL) " +
+// ") src";
+// }
+
private String buildQgcSdfpssrDetailSelectSql(List selectFields) {
Map selectMap = new LinkedHashMap<>();
selectMap.put("stcd", "t.stcd AS stcd");
@@ -1966,6 +2075,68 @@ public class FpRunServiceImpl implements FpRunService {
return String.join(", ", actualColumns);
}
+
+ private String buildQgcFpssrlDetailAISelectSql(List selectFields) {
+ Map selectMap = new LinkedHashMap<>();
+ selectMap.put("id", "t.id AS id");
+ selectMap.put("yr", "t.yr AS yr");
+ selectMap.put("stcd", "t.stcd AS stcd");
+ selectMap.put("stnm", "CASE WHEN t.stName IS NULL THEN t.stnm ELSE t.stName || t.stnm END AS stnm");
+ selectMap.put("baseId", "t.baseId AS baseId");
+ selectMap.put("baseName", "t.baseName AS baseName");
+ selectMap.put("rstcd", "t.rstcd AS rstcd");
+ selectMap.put("ennm", "t.ennm AS ennm");
+ selectMap.put("isfs", "t.isfs AS isfs");
+ selectMap.put("hbrvcd", "t.hbrvcd AS hbrvcd");
+ selectMap.put("baseStepSort", "t.baseStepSort AS baseStepSort");
+ selectMap.put("rvcdStepSort", "t.rvcdStepSort AS rvcdStepSort");
+ selectMap.put("siteStepSort", "t.siteStepSort AS siteStepSort");
+ selectMap.put("rstcdStepSort", "t.rstcdStepSort AS rstcdStepSort");
+ selectMap.put("tm", "t.tm AS tm");
+ selectMap.put("ftp", "t.ftp AS ftp");
+ selectMap.put("fishId", "t.fishId AS fishId");
+ selectMap.put("fsz", "t.fsz AS fsz");
+ selectMap.put("length", "t.length AS length");
+ selectMap.put("width", "t.width AS width");
+ selectMap.put("fishspeed", "t.fishspeed AS fishspeed");
+
+ selectMap.put("waterlevel", "t.waterlevel AS waterlevel");
+ selectMap.put("fishposition", "t.fishposition AS fishposition");
+ selectMap.put("dox", "t.dox AS dox");
+ selectMap.put("q", "t.q AS q");
+ selectMap.put("tu", "t.tu AS tu");
+ selectMap.put("remark", "t.remark AS remark");
+
+ selectMap.put("direction", "t.direction AS direction");
+ selectMap.put("directionName", "CASE t.direction WHEN '0' THEN '上行' WHEN '1' THEN '下行' WHEN '2' THEN '上行折返' WHEN '3' THEN '下行折返' END AS directionName");
+ selectMap.put("temperature", "t.temperature AS temperature");
+ selectMap.put("speed", "t.speed AS speed");
+ selectMap.put("channelno", "t.channelno AS channelno");
+ selectMap.put("fcnt", "t.fcnt AS fcnt");
+ selectMap.put("firstimgurl", "t.firstimgurl AS firstimgurl");
+ selectMap.put("videourl", "t.videourl AS videourl");
+ selectMap.put("mway", "t.mway AS mway");
+ selectMap.put("strdt", "t.strdt AS strdt");
+ selectMap.put("enddt", "t.enddt AS enddt");
+ selectMap.put("stCode", "t.stCode AS stCode");
+ selectMap.put("stName", "t.stName AS stName");
+
+ if (CollUtil.isEmpty(selectFields)) {
+ return String.join(", ", selectMap.values());
+ }
+ List actualColumns = new ArrayList<>();
+ for (String field : selectFields) {
+ String column = selectMap.get(field);
+ if (StrUtil.isNotBlank(column)) {
+ actualColumns.add(column);
+ }
+ }
+ if (actualColumns.isEmpty()) {
+ actualColumns.addAll(selectMap.values());
+ }
+ return String.join(", ", actualColumns);
+ }
+
private String buildQgcSdfpssrFilterCondition(DataSourceRequest.FilterDescriptor filter,
Map paramMap,
int[] indexHolder) {