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) {