diff --git a/backend/pom.xml b/backend/pom.xml index ec6855c2..507f58de 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -56,6 +56,13 @@ mybatis-spring 3.0.3 + + + + com.google.errorprone + error_prone_annotations + 2.43.0 + @@ -95,6 +102,12 @@ spring-boot-starter-cache + + + com.github.ben-manes.caffeine + caffeine + + org.springframework.boot @@ -109,8 +122,6 @@ ${guava.version} - - org.springframework.boot diff --git a/backend/src/main/java/com/yfd/platform/annotation/DynamicDictEvict.java b/backend/src/main/java/com/yfd/platform/annotation/DynamicDictEvict.java new file mode 100644 index 00000000..31627c90 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/annotation/DynamicDictEvict.java @@ -0,0 +1,15 @@ +package com.yfd.platform.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface DynamicDictEvict { + String tableName(); // 表名 + String codeColumn() default "id"; + String nameColumn() default "name"; + String filter() default ""; +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/aspect/DictCacheEvictAspect.java b/backend/src/main/java/com/yfd/platform/aspect/DictCacheEvictAspect.java new file mode 100644 index 00000000..06bf306c --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/aspect/DictCacheEvictAspect.java @@ -0,0 +1,27 @@ +package com.yfd.platform.aspect; + +import com.yfd.platform.annotation.DynamicDictEvict; +import com.yfd.platform.utils.DictCacheHelper; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class DictCacheEvictAspect { + @Autowired + private DictCacheHelper dictCacheHelper; // 复用上面的Helper + + @AfterReturning("@annotation(dictEvict)") + public void evictCache(JoinPoint jp, DynamicDictEvict dictEvict) { + // 获取注解上的参数,触发清理 + dictCacheHelper.evictDynamic( + dictEvict.tableName(), + dictEvict.codeColumn(), + dictEvict.nameColumn(), + dictEvict.filter() + ); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/config/BearerTokenFilter.java b/backend/src/main/java/com/yfd/platform/config/BearerTokenFilter.java new file mode 100644 index 00000000..35e006b5 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/config/BearerTokenFilter.java @@ -0,0 +1,161 @@ +package com.yfd.platform.config; + +import jakarta.annotation.Resource; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * 通用 Bearer Token 鉴权过滤器 + * + * 通过 application.yml 配置需要保护的接口路径,支持 Ant 风格匹配。 + * + *

配置示例: + *

+ *   bearer-token:
+ *     paths:
+ *       - /fb/fishhatchrecr/**
+ *       - /fprd/wva/**
+ *       - /some/api/**
+ * 
+ * + *

外部系统调用流程: + *

    + *
  1. 先调用 OAuth2 token 端点获取 access_token
  2. + *
  3. 在业务请求中携带 Authorization: Bearer <access_token>
  4. + *
+ */ +@Component +public class BearerTokenFilter extends OncePerRequestFilter { + + private static final Logger log = LoggerFactory.getLogger(BearerTokenFilter.class); + + private final AntPathMatcher pathMatcher = new AntPathMatcher(); + + /** + * 需要 Bearer Token 鉴权保护的路径模式列表 + * 支持 Ant 风格:/api/**, /fb/* /GetKendoListCust + */ + private final List protectedPathPatterns = new ArrayList<>(); + + @Resource + private BearerTokenService bearerTokenService; + + @Resource + private BearerTokenProperties bearerTokenProperties; + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + String requestPath = resolveRequestPath(request); + + // 检查当前请求路径是否需要 Bearer Token 鉴权 + if (!isProtectedPath(requestPath)) { + filterChain.doFilter(request, response); + return; + } + + log.debug("请求路径 {} 需要 Bearer Token 鉴权", requestPath); + + // 解析 Authorization: Bearer + String token = bearerTokenService.resolveToken(request); + if (token == null) { + log.warn("缺少 Bearer Token, path={}", requestPath); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "缺少 Authorization: Bearer 请求头"); + return; + } + + // 验证 token + if (!bearerTokenService.validateToken(token, request)) { + log.warn("Bearer Token 无效或已过期, path={}", requestPath); + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Bearer Token 无效或已过期"); + return; + } + + // token 有效,可选:刷新 token 有效期 + // bearerTokenService.refreshToken(token); + + // 设置 Spring Security 认证上下文,放行后续过滤器 + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken( + "bearer-access", + null, + AuthorityUtils.NO_AUTHORITIES + ); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + + log.debug("Bearer Token 鉴权通过, path={}", requestPath); + filterChain.doFilter(request, response); + } + + /** + * 判断路径是否需要 Bearer Token 鉴权保护 + */ + private boolean isProtectedPath(String uri) { + if (uri == null) return false; + + List patterns = getProtectedPaths(); + if (patterns.isEmpty()) return false; + + for (String pattern : patterns) { + if (pathMatcher.match(pattern, uri)) { + return true; + } + } + return false; + } + + /** + * 获取受保护的路径模式列表 + * 优先从 BearerTokenProperties 获取,也可以通过子类覆盖或动态添加 + */ + protected List getProtectedPaths() { + List paths = new ArrayList<>(); + // 从配置文件加载 + if (bearerTokenProperties != null && bearerTokenProperties.getPaths() != null) { + paths.addAll(bearerTokenProperties.getPaths()); + } + // 合并代码中动态添加的路径 + paths.addAll(protectedPathPatterns); + return paths; + } + + /** + * 程序化添加受保护路径(Ant 风格匹配) + * + * @param pattern Ant 路径模式,如 /fb/fishhatchrecr/** + */ + public void addProtectedPath(String pattern) { + if (pattern != null && !protectedPathPatterns.contains(pattern)) { + protectedPathPatterns.add(pattern); + log.info("已添加 Bearer Token 保护路径: {}", pattern); + } + } + + private String resolveRequestPath(HttpServletRequest request) { + String servletPath = request.getServletPath(); + if (servletPath != null && !servletPath.isEmpty()) { + return servletPath; + } + String uri = request.getRequestURI(); + String contextPath = request.getContextPath(); + if (contextPath != null && !contextPath.isEmpty() && uri.startsWith(contextPath)) { + return uri.substring(contextPath.length()); + } + return uri; + } +} diff --git a/backend/src/main/java/com/yfd/platform/config/BearerTokenProperties.java b/backend/src/main/java/com/yfd/platform/config/BearerTokenProperties.java new file mode 100644 index 00000000..dd38629c --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/config/BearerTokenProperties.java @@ -0,0 +1,32 @@ +package com.yfd.platform.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Bearer Token 鉴权配置属性 + * + * 在 application.yml 中配置需要保护的接口路径: + *
+ *   bearer-token:
+ *     paths:
+ *       - /fb/fishhatchrecr/**
+ *       - /some/other/api/**
+ * 
+ */ +@Data +@Component +@ConfigurationProperties(prefix = "bearer-token") +public class BearerTokenProperties { + + /** + * 需要使用 Bearer Token 鉴权的接口路径列表 + * 支持 Ant 风格路径匹配,如 /api/**, /fb/** + */ + private List paths = new ArrayList<>(List.of("/base/fpssrlR/ai/report")); +} diff --git a/backend/src/main/java/com/yfd/platform/config/BearerTokenService.java b/backend/src/main/java/com/yfd/platform/config/BearerTokenService.java new file mode 100644 index 00000000..d02088e6 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/config/BearerTokenService.java @@ -0,0 +1,189 @@ +package com.yfd.platform.config; + +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +/** + * Bearer Token 鉴权服务 + * + * 负责 Bearer Token 的生成、缓存和验证。 + * Token 通过 OAuth2 client_credentials 模式下发后,由本服务统一验证。 + */ +@Service +public class BearerTokenService { + + private static final Logger log = LoggerFactory.getLogger(BearerTokenService.class); + + /** 默认 token 有效期:2 小时 */ + public static final long DEFAULT_EXPIRE_MILLIS = 2 * 60 * 60 * 1000L; + + private static final String CACHE_PREFIX = "bearer:token:"; + + @Resource + private WebConfig webConfig; + + /** + * 生成 Bearer Token 并存入缓存 + * 由 OAuth2 token 端点调用 + * + * @param request 当前请求 + * @param clientId 客户端标识 + * @return token 信息(accessToken, expiresIn 等) + */ + public BearerTokenResult createToken(HttpServletRequest request, String clientId) { + String token = UUID.randomUUID().toString(); + long expireAt = System.currentTimeMillis() + DEFAULT_EXPIRE_MILLIS; + + JSONObject payload = new JSONObject(); + payload.put("clientId", clientId); + payload.put("ip", getClientIp(request)); + payload.put("expireAt", expireAt); + payload.put("createTime", System.currentTimeMillis()); + + webConfig.loginuserCache().put(CACHE_PREFIX + token, payload.toJSONString(), DEFAULT_EXPIRE_MILLIS); + + log.info("Bearer Token 已创建, clientId={}, expireAt={}", clientId, expireAt); + + BearerTokenResult result = new BearerTokenResult(); + result.setAccessToken(token); + result.setTokenType("bearer"); + result.setExpiresIn(DEFAULT_EXPIRE_MILLIS / 1000); + result.setExpireAt(expireAt); + return result; + } + + /** + * 验证 Bearer Token 是否有效 + * + * @param token Bearer Token + * @param request 当前请求 + * @return true 有效,false 无效或过期 + */ + public boolean validateToken(String token, HttpServletRequest request) { + if (StrUtil.isBlank(token)) { + log.debug("Bearer Token 验证失败: token 为空"); + return false; + } + + String cacheValue = webConfig.loginuserCache().get(CACHE_PREFIX + token); + if (StrUtil.isBlank(cacheValue)) { + log.debug("Bearer Token 验证失败: token 不存在或已过期, token={}", maskToken(token)); + return false; + } + + JSONObject payload; + try { + payload = JSON.parseObject(cacheValue); + } catch (Exception e) { + log.warn("Bearer Token 解析失败: {}", e.getMessage()); + return false; + } + + // 检查过期时间 + Long expireAt = payload.getLong("expireAt"); + if (expireAt == null || expireAt < System.currentTimeMillis()) { + log.debug("Bearer Token 已过期, token={}", maskToken(token)); + webConfig.loginuserCache().remove(CACHE_PREFIX + token); + return false; + } + + // 可选:校验 IP 一致性(可在子类中覆盖) + return true; + } + + /** + * 从请求中解析 Bearer Token + * 从 Authorization: Bearer 头中提取 + * + * @param request HTTP 请求 + * @return token 字符串,解析失败返回 null + */ + public String resolveToken(HttpServletRequest request) { + String authHeader = request.getHeader("Authorization"); + if (StrUtil.isBlank(authHeader)) { + return null; + } + + // 支持 "Bearer xxx" 和 "bearer xxx" 格式 + String trimmed = authHeader.trim(); + if (trimmed.length() > 7 + && "bearer".equalsIgnoreCase(trimmed.substring(0, 6)) + && trimmed.charAt(6) == ' ') { + return trimmed.substring(7).trim(); + } + + return null; + } + + /** + * 刷新 token 有效期(延长使用时间) + * + * @param token Bearer Token + */ + public void refreshToken(String token) { + if (StrUtil.isBlank(token)) return; + String cacheValue = webConfig.loginuserCache().get(CACHE_PREFIX + token); + if (StrUtil.isBlank(cacheValue)) return; + + JSONObject payload = JSON.parseObject(cacheValue); + long newExpireAt = System.currentTimeMillis() + DEFAULT_EXPIRE_MILLIS; + payload.put("expireAt", newExpireAt); + webConfig.loginuserCache().put(CACHE_PREFIX + token, payload.toJSONString(), DEFAULT_EXPIRE_MILLIS); + } + + /** + * 撤销 token + * + * @param token Bearer Token + */ + public void revokeToken(String token) { + if (StrUtil.isNotBlank(token)) { + webConfig.loginuserCache().remove(CACHE_PREFIX + token); + log.info("Bearer Token 已撤销"); + } + } + + private String getClientIp(HttpServletRequest request) { + String ip = request.getHeader("X-Forwarded-For"); + if (StrUtil.isNotBlank(ip)) { + return ip.split(",")[0].trim(); + } + return request.getRemoteAddr(); + } + + private String maskToken(String token) { + if (token == null || token.length() <= 8) return "***"; + return token.substring(0, 4) + "***" + token.substring(token.length() - 4); + } + + /** + * Bearer Token 创建结果 + */ + public static class BearerTokenResult { + /** 访问令牌 */ + private String accessToken; + /** 令牌类型 */ + private String tokenType; + /** 有效期(秒) */ + private long expiresIn; + /** 过期时间戳 */ + private long expireAt; + + public String getAccessToken() { return accessToken; } + public void setAccessToken(String accessToken) { this.accessToken = accessToken; } + public String getTokenType() { return tokenType; } + public void setTokenType(String tokenType) { this.tokenType = tokenType; } + public long getExpiresIn() { return expiresIn; } + public void setExpiresIn(long expiresIn) { this.expiresIn = expiresIn; } + public long getExpireAt() { return expireAt; } + public void setExpireAt(long expireAt) { this.expireAt = expireAt; } + } +} diff --git a/backend/src/main/java/com/yfd/platform/config/OAuth2TokenController.java b/backend/src/main/java/com/yfd/platform/config/OAuth2TokenController.java new file mode 100644 index 00000000..a4b04149 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/config/OAuth2TokenController.java @@ -0,0 +1,127 @@ +package com.yfd.platform.config; + +import cn.hutool.core.util.StrUtil; +import com.yfd.platform.config.BearerTokenService.BearerTokenResult; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +/** + * OAuth2 Token 端点 + * + *

供外部系统通过 client_credentials 模式获取 Bearer Token。 + * + *

请求格式(参考 鉴权与文件上传接口说明.md 3.2 节): + *

+ *   POST /oauth2/token
+ *   Content-Type: application/x-www-form-urlencoded
+ *   Authorization: Basic 
+ *   
+ *   grant_type=client_credentials
+ * 
+ * + *

返回格式: + *

+ * {
+ *   "success": true,
+ *   "errorCode": 0,
+ *   "message": "获取token成功",
+ *   "data": {
+ *     "access_token": "xxx",
+ *     "token_type": "bearer",
+ *     "expires_in": 7200
+ *   }
+ * }
+ * 
+ */ +@RestController +@RequestMapping("/api/oauth2") +public class OAuth2TokenController { + + private static final Logger log = LoggerFactory.getLogger(OAuth2TokenController.class); + + @Resource + private BearerTokenService bearerTokenService; + + /** + * 获取 Access Token(client_credentials 模式) + */ + @PostMapping("/oauth/token") + public Map getToken(HttpServletRequest request) { + Map result = new HashMap<>(); + + // 解析 Authorization: Basic + String authHeader = request.getHeader("Authorization"); + if (StrUtil.isBlank(authHeader) || !authHeader.trim().toLowerCase().startsWith("basic ")) { + log.warn("获取 Token 失败: 缺少有效的 Basic Authorization 头"); + result.put("success", false); + result.put("errorCode", -1); + result.put("message", "缺少有效的 Authorization: Basic 认证信息"); + return result; + } + + String clientId = resolveClientId(authHeader); + if (StrUtil.isBlank(clientId)|| !"client:secret".equals(clientId)) { + log.warn("获取 Token 失败: 无法解析 client"); + result.put("success", false); + result.put("errorCode", -1); + result.put("message", "无法解析客户端标识"); + return result; + } + + // 验证 grant_type + String grantType = request.getParameter("grant_type"); + if (StrUtil.isBlank(grantType) || !"client_credentials".equals(grantType)) { + log.warn("获取 Token 失败: 不支持的 grant_type={}", grantType); + result.put("success", false); + result.put("errorCode", -1); + // 仅支持 client_credentials 授权模式 + result.put("message", "授权模式错误"); + return result; + } + + BearerTokenResult tokenResult = bearerTokenService.createToken(request, clientId); + + Map data = new HashMap<>(); + data.put("access_token", tokenResult.getAccessToken()); + data.put("token_type", tokenResult.getTokenType()); + data.put("expires_in", tokenResult.getExpiresIn()); + + result.put("success", true); + result.put("errorCode", 0); + result.put("message", "获取token成功"); + result.put("data", data); + + log.info("OAuth2 Token 签发成功, clientId={}", clientId); + return result; + } + + /** + * 从 Authorization: Basic 头中解析 client + */ + private String resolveClientId(String authHeader) { + try { + String base64 = authHeader.substring(6).trim(); + byte[] decoded = Base64.getDecoder().decode(base64); + // // 格式: client:secret +// int colonIdx = credentials.indexOf(':'); +// if (colonIdx > 0) { +// return credentials.substring(0, colonIdx); +// } + return new String(decoded, StandardCharsets.UTF_8); + } catch (Exception e) { + log.warn("解析 Basic 认证信息失败: {}", e.getMessage()); + return null; + } + } + +} diff --git a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java index 10545b04..e22f5087 100644 --- a/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java +++ b/backend/src/main/java/com/yfd/platform/config/SecurityConfig.java @@ -42,6 +42,9 @@ public class SecurityConfig { @Autowired private RegisterAccessTokenFilter registerAccessTokenFilter; + @Autowired + private BearerTokenFilter bearerTokenFilter; + @Autowired private AuthenticationException authenticationException; @@ -60,7 +63,8 @@ public class SecurityConfig { .requestMatchers("/data/fishDraft/previewFile").permitAll() .requestMatchers("/tempFile/**").permitAll() .requestMatchers("/system/user/auditUser").permitAll() - .requestMatchers("/register/accessToken").permitAll() + .requestMatchers("/api/oauth2/oauth/token").permitAll() + .requestMatchers("/dict/cache/**").permitAll() .requestMatchers("/sys/psbmodulelbb/**").permitAll() .requestMatchers("/base/operationLog/**").permitAll() // .requestMatchers("/eng/**").permitAll() @@ -115,6 +119,7 @@ public class SecurityConfig { .cors(cors -> {}); http.addFilterBefore(registerAccessTokenFilter, UsernamePasswordAuthenticationFilter.class); + http.addFilterBefore(bearerTokenFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); http.exceptionHandling(ex -> ex diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngInfoBHController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngInfoBHController.java index 798ab6d7..089ef503 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngInfoBHController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdEngInfoBHController.java @@ -2,6 +2,7 @@ package com.yfd.platform.qgc_base.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fasterxml.jackson.databind.ObjectMapper; +import com.yfd.platform.annotation.DynamicDictEvict; import com.yfd.platform.annotation.Log; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.config.ResponseResult; @@ -104,6 +105,7 @@ public class SdEngInfoBHController { @Log(module = "电站管理", value = "新增电站") @PostMapping("/add") @Operation(summary = "新增电站") + @DynamicDictEvict(tableName = "SD_ENGINFO_B_H", codeColumn = "STCD", nameColumn = "ENNM") public ResponseResult add(@RequestBody SdEngInfoBHOperateRequest request) { SdEngInfoBH engInfo = request == null || request.getEngInfo() == null ? null : objectMapper.convertValue(request.getEngInfo(), SdEngInfoBH.class); boolean result = engInfoBHService.addEngInfo(engInfo, request.getSource()); @@ -113,6 +115,7 @@ public class SdEngInfoBHController { @Log(module = "电站管理", value = "修改电站") @PostMapping("/update") @Operation(summary = "修改电站") + @DynamicDictEvict(tableName = "SD_ENGINFO_B_H", codeColumn = "STCD", nameColumn = "ENNM") public ResponseResult update(@RequestBody SdEngInfoBHOperateRequest request) throws Exception { boolean result = engInfoBHService.updateEngInfo(request.getEngInfo(), request.getSource()); return result ? ResponseResult.success("修改成功") : ResponseResult.error("修改失败"); @@ -121,6 +124,7 @@ public class SdEngInfoBHController { @Log(module = "电站管理", value = "删除电站") @PostMapping("/delete") @Operation(summary = "删除电站") + @DynamicDictEvict(tableName = "SD_ENGINFO_B_H", codeColumn = "STCD", nameColumn = "ENNM") public ResponseResult delete(@RequestBody SdEngInfoBHOperateRequest request) { boolean result = engInfoBHService.deleteEngInfo(request == null ? null : request.getIds(), request == null ? null : request.getSource()); diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlRController.java b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlRController.java index ae195913..c48dca22 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlRController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/controller/SdFpssrlRController.java @@ -1,13 +1,14 @@ 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.SdFpssrlR; -import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest; +import com.yfd.platform.qgc_base.domain.*; +import com.yfd.platform.qgc_base.service.ISdFishDictoryBService; import com.yfd.platform.qgc_base.service.ISdFpssrlRService; import com.yfd.platform.qgc_data.service.AttachmentUploadService; import io.swagger.v3.oas.annotations.Operation; @@ -19,6 +20,7 @@ import org.springframework.web.multipart.MultipartFile; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -41,6 +43,9 @@ public class SdFpssrlRController { @Resource private AttachmentUploadService attachmentUploadService; + @Resource + private ISdFishDictoryBService sdFishDictoryBService; + // ==================== CRUD 方法 ==================== @PostMapping("/queryPageList") @@ -131,7 +136,14 @@ public class SdFpssrlRController { 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); @@ -141,16 +153,22 @@ public class SdFpssrlRController { 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 = sdFpssrlRService.processAiReportBatch(requests); + List result = sdFpssrlRService.processAiReportBatch(requests); // List ids = result.stream().map(SdFpssrlR::getId).collect(Collectors.toList()); log.info("AI批量上报成功,共{}条", result.size()); return ResponseResult.success(); diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdAiboxBH.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdAiboxBH.java index 601289bf..3933e989 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdAiboxBH.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdAiboxBH.java @@ -104,6 +104,9 @@ public class SdAiboxBH implements Serializable { @FieldChinese("是否启用") private Integer usfl; + @TableField(exist = false) + private String usflName; + /** * 数据是否接入 */ 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 new file mode 100644 index 00000000..2a85652d --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlAiR.java @@ -0,0 +1,121 @@ +package com.yfd.platform.qgc_base.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + *

+ * 过鱼设施AI盒子自动数据表 + *

+ */ +@Data +@TableName("SD_FPSSRL_AI_R") +public class SdFpssrlAiR implements Serializable { + + private static final long serialVersionUID = 1L; + + /** 主键ID */ + @TableId(type = IdType.ASSIGN_UUID) + private String id; + + /** 过鱼设施编码 */ + private String stcd; + + /** 时间 */ + private Date tm; + + /** 鱼种类 */ + private String ftp; + + /** 过鱼数量 */ + private Integer fcnt; + + /** 鱼尺寸:大/中/小 */ + private String fsz; + + /** 鱼长度 */ + private BigDecimal length; + + /** 鱼宽度 */ + private BigDecimal width; + + /** 鱼速度 */ + private String fishspeed; + + /** 游向:0=上行 1=下行 */ + private Integer direction; + + /** 鱼位置 */ + private Long fishposition; + + /** 鱼截图主图片url */ + private String firstImgUrl; + + /** 鱼截图副图片url */ + private String secondImgUrl; + + /** 视频url */ + private String videoUrl; + + /** 水温:单位:℃ */ + private BigDecimal temperature; + + /** 水位:单位:m */ + private BigDecimal waterlevel; + + /** 流速:单位:m/s */ + private BigDecimal speed; + + /** 流量:单位:m3/s */ + private BigDecimal q; + + /** 溶氧:单位:mg/L */ + private BigDecimal dox; + + /** 浊度:单位:NTU */ + private Integer tu; + + /** 过鱼通道 */ + private String channelno; + + /** AI盒子编码 */ + private String aiBoxCode; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除:0=未删除 1=已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; + + /** 附件ID */ + private String fid; + + /** 备注 */ + private String remark; + + /** 过鱼设施名称(非表字段,用于列表展示) */ + @TableField(exist = false) + private String stnm; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlRAiRequest.java b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlRAiRequest.java index 7b844d05..e533018e 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlRAiRequest.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/domain/SdFpssrlRAiRequest.java @@ -38,6 +38,11 @@ public class SdFpssrlRAiRequest { */ private String fsz; + /** + * 服务对象 + */ + private String fwdx; + /** * 鱼长度,单位:cm */ diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/mapper/SdFpssrlAiRMapper.java b/backend/src/main/java/com/yfd/platform/qgc_base/mapper/SdFpssrlAiRMapper.java new file mode 100644 index 00000000..84ffe5d9 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_base/mapper/SdFpssrlAiRMapper.java @@ -0,0 +1,22 @@ +package com.yfd.platform.qgc_base.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yfd.platform.qgc_base.domain.SdFpssrlAiR; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + *

+ * 过鱼设施AI盒子自动数据表 Mapper 接口 + *

+ */ +@Mapper +public interface SdFpssrlAiRMapper extends BaseMapper { + + /** + * 批量合并过鱼AI自动数据(基于 STCD + TM + FTP 唯一) + * 存在则更新,不存在则新增 + */ + int mergeFishRecords(List list); +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlRService.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlRService.java index 546fb5dc..7044a673 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlRService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/ISdFpssrlRService.java @@ -3,6 +3,7 @@ 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.SdFpssrlR; import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest; @@ -48,5 +49,5 @@ public interface ISdFpssrlRService extends IService { /** * 批量处理AI设备上报的识别数据 */ - List processAiReportBatch(List requests); + List processAiReportBatch(List requests); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAiboxBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAiboxBHServiceImpl.java index 4ab0272d..ed8fdd92 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAiboxBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdAiboxBHServiceImpl.java @@ -52,10 +52,12 @@ public class SdAiboxBHServiceImpl extends ServiceImpl impleme CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); } @Override diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java index 1b275306..01c9ef84 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdEngInfoBHServiceImpl.java @@ -96,7 +96,7 @@ public class SdEngInfoBHServiceImpl extends ServiceImpl impleme CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); } @Override diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java index ba5ee0e4..976942db 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFbrdBHServiceImpl.java @@ -48,9 +48,9 @@ public class SdFbrdBHServiceImpl extends ServiceImpl i CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); } @Override diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java index 5ef06658..b4949034 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFhbtBHServiceImpl.java @@ -51,9 +51,9 @@ public class SdFhbtBHServiceImpl extends ServiceImpl i CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); -// CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); +// CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rvcd").modifyProperty("rvnm").dictType("DYNAMIC").dictSource("V_SD_RVCD_DIC_TREE").codeColumn("RVCD").nameColumn("RVNM").build()); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssBHServiceImpl.java index cf3bf019..3a09c579 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssBHServiceImpl.java @@ -63,9 +63,9 @@ public class SdFpssBHServiceImpl extends ServiceImpl i CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rvcd").modifyProperty("rvnm").dictType("DYNAMIC").dictSource("V_SD_RVCD_DIC_TREE").codeColumn("RVCD").nameColumn("RVNM").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("mway").modifyProperty("mwayName").dictType("STATIC").dictSource("MWAY").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("isUp").modifyProperty("isUpName").dictType("STATIC").dictSource("TY_SF").build()); diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlRServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlRServiceImpl.java index 2b658fb2..1e30499f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlRServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdFpssrlRServiceImpl.java @@ -6,8 +6,10 @@ 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.SdFpssrlR; import com.yfd.platform.qgc_base.domain.SdFpssrlRAiRequest; +import com.yfd.platform.qgc_base.mapper.SdFpssrlAiRMapper; import com.yfd.platform.qgc_base.mapper.SdFpssrlRMapper; import com.yfd.platform.qgc_base.service.IMsOperationLogService; import com.yfd.platform.qgc_base.service.ISdFpssrlRService; @@ -35,6 +37,8 @@ public class SdFpssrlRServiceImpl extends ServiceImpl queryPageList(DataSourceRequest request) { @@ -130,12 +134,13 @@ public class SdFpssrlRServiceImpl extends ServiceImpl processAiReportBatch(List requests) { - List entities = new ArrayList<>(); + public List processAiReportBatch(List requests) { + List entities = new ArrayList<>(); Date now = new Date(); for (SdFpssrlRAiRequest req : requests) { - SdFpssrlR entity = new SdFpssrlR(); - entity.setStcd(req.getStcd()); + 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); @@ -164,7 +169,7 @@ public class SdFpssrlRServiceImpl extends ServiceImpl i CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rvcd").modifyProperty("rvnm").dictType("DYNAMIC").dictSource("V_SD_RVCD_DIC_TREE").codeColumn("RVCD").nameColumn("RVNM").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("usfl").modifyProperty("usflName").dictType("STATIC").dictSource("USFL").build()); diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java index 8589aeb4..266c4bb4 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdOtweBHServiceImpl.java @@ -47,9 +47,9 @@ public class SdOtweBHServiceImpl extends ServiceImpl i CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rvcd").modifyProperty("rvnm").dictType("DYNAMIC").dictSource("V_SD_RVCD_DIC_TREE").codeColumn("RVCD").nameColumn("RVNM").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("usfl").modifyProperty("usflName").dictType("STATIC").dictSource("USFL").build()); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java index 0e1d337b..24438e9a 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdSonarBHServiceImpl.java @@ -51,9 +51,9 @@ public class SdSonarBHServiceImpl extends ServiceImpl impleme CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rvcd").modifyProperty("rvnm").dictType("DYNAMIC").dictSource("V_SD_RVCD_DIC_TREE").codeColumn("RVCD").nameColumn("RVNM").build()); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java index d90388ba..3e81ad52 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVaBHServiceImpl.java @@ -50,9 +50,9 @@ public class SdVaBHServiceImpl extends ServiceImpl impleme CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("usfl").modifyProperty("usflName").dictType("STATIC").dictSource("USFL").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); } @Override diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java index 57b99528..02d1be85 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdVdinfoBServiceImpl.java @@ -49,9 +49,9 @@ public class SdVdinfoBServiceImpl extends ServiceImpl impleme CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtin").modifyProperty("dtinName").dictType("STATIC").dictSource("DTIN").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); } @Override diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java index c71d4c39..63c4040d 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWeBHServiceImpl.java @@ -49,9 +49,9 @@ public class SdWeBHServiceImpl extends ServiceImpl impleme // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("mway").modifyProperty("mwayName").dictType("STATIC").dictSource("MWAY").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); // CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rvcd").modifyProperty("rvnm").dictType("DYNAMIC").dictSource("V_SD_RVCD_DIC_TREE").codeColumn("RVCD").nameColumn("RVNM").build()); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java index c474ddd0..1dbbf910 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWqBHServiceImpl.java @@ -53,9 +53,9 @@ public class SdWqBHServiceImpl extends ServiceImpl impleme CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("wwqtg").modifyProperty("wwqtgName").dictType("STATIC").dictSource("WWQTG").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); } @Override diff --git a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java index 751f99aa..c179bc38 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_base/service/impl/SdWtBHServiceImpl.java @@ -54,9 +54,9 @@ public class SdWtBHServiceImpl extends ServiceImpl impleme CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("mway").modifyProperty("mwayName").dictType("STATIC").dictSource("MWAY").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("bldsttCode").modifyProperty("bldsttCodeName").dictType("STATIC").dictSource("BLDSTT_CODE").build()); CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("dtinType").modifyProperty("dtinTypeName").dictType("STATIC").dictSource("DTIN_TYPE").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("NVL(IS_DELETED, 0) = 0 ").build()); - CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("NVL(IS_DELETED, 0) = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("sttp").modifyProperty("sttpName").dictType("DYNAMIC").dictSource("SD_STTP_B").codeColumn("STTP_CODE").nameColumn("STTP_NAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("baseId").modifyProperty("baseName").dictType("DYNAMIC").dictSource("SD_HYDROBASE").codeColumn("BASEID").nameColumn("BASENAME").filter("IS_DELETED = 0 ").build()); + CODE_TO_NAME_META_LIST.add(CodeToNameMetadataBo.builder().codeProperty("rstcd").modifyProperty("ennm").dictType("DYNAMIC").dictSource("SD_ENGINFO_B_H").codeColumn("STCD").nameColumn("ENNM").filter("IS_DELETED = 0 ").build()); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java index 19d7887c..71aeb46f 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/controller/FbStationController.java @@ -65,6 +65,84 @@ public class FbStationController { return ResponseResult.successData(fbStationService.getYearRpStatistics(year)); } + @PostMapping("/bsmfr/GetKendoListCust") + @Operation(summary = "亲鱼档案管理记录列表") + public ResponseResult getBsmfRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getBsmfRList(dataSourceRequest)); + } + + @PostMapping("/bsdeathr/GetKendoListCust") + @Operation(summary = "亲鱼死亡记录列表") + public ResponseResult getBsdeathRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getBsdeathRList(dataSourceRequest)); + } + + @PostMapping("/bsctmob/GetKendoListCust") + @Operation(summary = "亲鱼培育方式列表") + public ResponseResult getBsctmoBKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getBsctmoBList(dataSourceRequest)); + } + + @PostMapping("/fbbwtr/GetKendoListCust") + @Operation(summary = "亲鱼培育水温观测记录列表") + public ResponseResult getFbbwtRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFbbwtRList(dataSourceRequest)); + } + + @PostMapping("/fbcwqr/GetKendoListCust") + @Operation(summary = "亲鱼培育水质监测记录列表") + public ResponseResult getFbcwqRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFbcwqRList(dataSourceRequest)); + } + + @PostMapping("/bsrfir/GetKendoListCust") + @Operation(summary = "亲鱼培育投喂记录列表") + public ResponseResult getBsrfiRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getBsrfiRList(dataSourceRequest)); + } + + @PostMapping("/outfishr/GetKendoListCust") + @Operation(summary = "淘汰亲鱼基本信息记录列表") + public ResponseResult getOutfishRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getOutfishRList(dataSourceRequest)); + } + + @PostMapping("/fishartinlr/GetKendoListCust") + @Operation(summary = "鱼类人工催产记录列表") + public ResponseResult getFishartinlRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFishartinlRList(dataSourceRequest)); + } + + @PostMapping("/fishhatchrecr/GetKendoListCust") + @Operation(summary = "鱼类受精卵孵化过程记录列表") + public ResponseResult getFishhatchrecRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFishhatchrecRList(dataSourceRequest)); + } + + @PostMapping("/fishhatchpror/GetKendoListCust") + @Operation(summary = "鱼类孵化过程巡查记录列表") + public ResponseResult getFishhatchproRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFishhatchproRList(dataSourceRequest)); + } + + @PostMapping("/scwemr/GetKendoListCust") + @Operation(summary = "苗种培育水环境监测记录列表") + public ResponseResult getScwemRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getScwemRList(dataSourceRequest)); + } + + @PostMapping("/fishbreedr/GetKendoListCust") + @Operation(summary = "鱼苗和鱼种培育记录列表") + public ResponseResult getFishbreedRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFishbreedRList(dataSourceRequest)); + } + + @PostMapping("/fishdpacr/GetKendoListCust") + @Operation(summary = "鱼病防治记录列表") + public ResponseResult getFishdpacRKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { + return ResponseResult.successData(fbStationService.getFishdpacRList(dataSourceRequest)); + } + @PostMapping("/bsmfr/qgc/GetKendoListCust") @Operation(summary = "(全过程)增殖站二级页面: 亲鱼选配与培育->亲鱼信息") public ResponseResult getBsmfrQgcKendoListCust(@RequestBody DataSourceRequest dataSourceRequest) { diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsctmoBVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsctmoBVo.java new file mode 100644 index 00000000..23def6a5 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsctmoBVo.java @@ -0,0 +1,76 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 亲鱼培育方式 VO + */ +@Data +public class FbBsctmoBVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 鱼池编号 */ + private String fpnum; + + /** 鱼池名称 */ + private String fpName; + + /** 鱼种类 */ + private String ftp; + + /** 鱼类名称 */ + private String ftpName; + + /** 数量(尾) */ + private Long value; + + /** 全长(cm) */ + private BigDecimal length; + + /** 体重(g) */ + private BigDecimal weight; + + /** 培育方式:1=流水单养 2=流水混养 3=静水 4=循环水 */ + private Integer ctmo; + + /** 培育方式名称 */ + private String ctmoName; + + /** 放养密度 */ + private String skds; + + /** 体重范围值 */ + private String fwgh; + + /** 记录人 */ + private String recorder; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsdeathRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsdeathRVo.java new file mode 100644 index 00000000..6a2fd409 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsdeathRVo.java @@ -0,0 +1,79 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 亲鱼死亡记录 VO + */ +@Data +public class FbBsdeathRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 死亡日期 */ + private Date tm; + + /** 鱼池编号 */ + private String fpnum; + + /** 鱼池名称 */ + private String fpName; + + /** 鱼种类 */ + private String ftp; + + /** 鱼类名称 */ + private String ftpName; + + /** 标记编号 */ + private String signnum; + + /** 全长(cm) */ + private BigDecimal length; + + /** 体重(g) */ + private BigDecimal weight; + + /** 死亡症状 */ + private String spod; + + /** 死亡诊断 */ + private String deathd; + + /** 记录人 */ + private String recorder; + + /** 数量(尾) */ + private Long value; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsmfRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsmfRVo.java new file mode 100644 index 00000000..fbe1c69b --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsmfRVo.java @@ -0,0 +1,82 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 亲鱼档案管理记录 VO + */ +@Data +public class FbBsmfRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 引进日期 */ + private Date tm; + + /** 鱼种类 */ + private String ftp; + + /** 鱼类名称 */ + private String ftpName; + + /** 亲鱼来源 */ + private String bssr; + + /** 亲鱼来源名称 */ + private String bssrName; + + /** 全长(cm) */ + private BigDecimal length; + + /** 体重(g) */ + private BigDecimal weight; + + /** 鱼龄 */ + private BigDecimal age; + + /** 性别:1=雄鱼 2=雌鱼 */ + private Integer sex; + + /** 性别名称 */ + private String sexName; + + /** 标记编号 */ + private String signnum; + + /** 记录人 */ + private String recorder; + + /** 数量 */ + private Long value; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsrfiRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsrfiRVo.java new file mode 100644 index 00000000..fde1f547 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbBsrfiRVo.java @@ -0,0 +1,73 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 亲鱼培育投喂记录 VO + */ +@Data +public class FbBsrfiRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 鱼池编号 */ + private String fpnum; + + /** 鱼种类 */ + private String ftp; + + /** 投喂日期 */ + private Date tm; + + /** 饲料种类 */ + private String ftype; + + /** 投食量(g) */ + private BigDecimal cfe; + + /** 水交换频次(次/d) */ + private Long warfre; + + /** 记录人 */ + private String recorder; + + /** 总体量(g) */ + private BigDecimal tbw; + + /** 日投喂率(%) */ + private BigDecimal dfr; + + /** 摄食情况 */ + private String fdi; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFbbwtRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFbbwtRVo.java new file mode 100644 index 00000000..c592faee --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFbbwtRVo.java @@ -0,0 +1,64 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 亲鱼培育水温观测记录 VO + */ +@Data +public class FbFbbwtRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 观测日期 */ + private Date tm; + + /** 鱼池编号 */ + private String fpnum; + + /** 鱼池名称 */ + private String fpName; + + /** 8点水温 */ + private BigDecimal wte; + + /** 14点水温 */ + private BigDecimal wtf; + + /** 20点水温 */ + private BigDecimal wtt; + + /** 记录人 */ + private String recorder; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFbcwqRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFbcwqRVo.java new file mode 100644 index 00000000..20eab584 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFbcwqRVo.java @@ -0,0 +1,70 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 亲鱼培育水质监测记录 VO + */ +@Data +public class FbFbcwqRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 鱼池编号 */ + private String fpnum; + + /** 鱼池名称 */ + private String fpName; + + /** 氨氮(mg/L) */ + private BigDecimal nh3n; + + /** 亚硝酸盐(mg/L) */ + private BigDecimal no2; + + /** 溶解氧(mg/L) */ + private BigDecimal dox; + + /** pH */ + private BigDecimal ph; + + /** 记录人 */ + private String recorder; + + /** 是否符合标准 */ + private Integer iffit; + + /** 监测时段 */ + private Date tm; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishartinlRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishartinlRVo.java new file mode 100644 index 00000000..b5abdba7 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishartinlRVo.java @@ -0,0 +1,88 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 鱼类人工催产记录 VO + */ +@Data +public class FbFishartinlRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 鱼种类 */ + private String ftp; + + /** 催产日期 */ + private Date tm; + + /** 雌鱼数量 */ + private Long ffisht; + + /** 雌鱼催产剂种类 */ + private Integer fpitocintype; + + /** 雌鱼总剂量 */ + private BigDecimal fdose; + + /** 雌鱼注射方式 */ + private String finjmo; + + /** 气温(℃) */ + private BigDecimal air; + + /** 水温(℃) */ + private BigDecimal wartem; + + /** 溶解氧(mg/L) */ + private BigDecimal dox; + + /** 记录人 */ + private String recorder; + + /** 性别:1=雄鱼 2=雌鱼 */ + private Integer sex; + + /** 附件ID */ + private String fid; + + /** 雄鱼注射方式 */ + private Long mfisht; + + /** 雄鱼催产剂种类 */ + private Integer mpitocintype; + + /** 雄鱼总剂量 */ + private BigDecimal mdose; + + /** 雄鱼注射方式 */ + private String minjmo; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishbreedRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishbreedRVo.java new file mode 100644 index 00000000..50090b81 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishbreedRVo.java @@ -0,0 +1,76 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 鱼苗和鱼种培育记录 VO + */ +@Data +public class FbFishbreedRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 培育日期 */ + private Date tm; + + /** 鱼池编号 */ + private String farmno; + + /** 鱼种类 */ + private String ftp; + + /** 数量(尾) */ + private Long counts; + + /** 平均规格全长(cm) */ + private BigDecimal length; + + /** 平均规格体重(g) */ + private BigDecimal weight; + + /** 饲料或饵料种类 */ + private String feedtp; + + /** 投食量(g) */ + private BigDecimal foodint; + + /** 日投食率(%) */ + private BigDecimal dfr; + + /** 摄食情况 */ + private String fin; + + /** 记录人 */ + private String recorder; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishdpacRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishdpacRVo.java new file mode 100644 index 00000000..dd97385c --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishdpacRVo.java @@ -0,0 +1,79 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 鱼病防治记录表 VO + */ +@Data +public class FbFishdpacRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 鱼池编号 */ + private String fpnum; + + /** 鱼种类 */ + private String ftp; + + /** 平均体重(g) */ + private BigDecimal weight; + + /** 发病症状 */ + private String symptoms; + + /** 鱼药种类 */ + private String fishdrugtype; + + /** 用药方法 */ + private Integer method; + + /** 用药剂量(mg/kg) */ + private BigDecimal dose; + + /** 用药天数(d) */ + private BigDecimal days; + + /** 治疗效果 */ + private Integer treateffect; + + /** 记录人 */ + private String recorder; + + /** 日期 */ + private Date tm; + + /** 附件ID */ + private String fid; + + /** 备注 */ + private String remark; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishhatchproRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishhatchproRVo.java new file mode 100644 index 00000000..fa52f101 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishhatchproRVo.java @@ -0,0 +1,82 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 鱼类孵化过程巡查记录 VO + */ +@Data +public class FbFishhatchproRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 孵化设施编号 */ + private String devno; + + /** 记录时间 */ + private Date recordtm; + + /** 水温(℃) */ + private BigDecimal wartem; + + /** 与上一时段水温差(℃) */ + private BigDecimal wtdiff; + + /** 溶解氧(mg/L) */ + private BigDecimal dox; + + /** pH */ + private BigDecimal ph; + + /** 胚胎发育时期 */ + private String empsta; + + /** 水交换频次(次/d) */ + private Long warfre; + + /** 孵化密度是否均匀:0=否 1=是 */ + private Integer iseven; + + /** 记录人 */ + private String recorder; + + /** 备注 */ + private String remarks; + + /** 产卵时间 */ + private Date spawntm; + + /** 巡查时间 */ + private Date tm; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishhatchrecRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishhatchrecRVo.java new file mode 100644 index 00000000..f94f5875 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbFishhatchrecRVo.java @@ -0,0 +1,73 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 鱼类受精卵孵化过程记录 VO + */ +@Data +public class FbFishhatchrecRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 鱼种类 */ + private String ftp; + + /** 亲鱼来源 */ + private String fishsrc; + + /** 鱼卵数量(万粒) */ + private BigDecimal eggcount; + + /** 受精率(%) */ + private BigDecimal ferrate; + + /** 孵化率(%) */ + private BigDecimal hatchrate; + + /** 出苗时长(h) */ + private BigDecimal outhour; + + /** 出苗数量(万尾) */ + private BigDecimal outcount; + + /** 出苗率(%) */ + private BigDecimal outrate; + + /** 记录人 */ + private String recorder; + + /** 孵化时间 */ + private Date tm; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbOutfishRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbOutfishRVo.java new file mode 100644 index 00000000..35d1c70a --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbOutfishRVo.java @@ -0,0 +1,85 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 淘汰亲鱼基本信息记录 VO + */ +@Data +public class FbOutfishRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 录入时间 */ + private Date recordertm; + + /** 标记编号 */ + private String signno; + + /** 鱼种类 */ + private String ftp; + + /** 亲鱼来源 */ + private String src; + + /** 性别:1=雄鱼 2=雌鱼 */ + private Integer sex; + + /** 鱼龄 */ + private BigDecimal age; + + /** 全长(cm) */ + private BigDecimal length; + + /** 体重(g) */ + private BigDecimal weight; + + /** 繁殖年限 */ + private Long agelmt; + + /** 繁殖次数 */ + private Long brecount; + + /** 淘汰日期 */ + private Date tm; + + /** 淘汰原因 */ + private String outrea; + + /** 处置方式 */ + private Integer hand; + + /** 记录人 */ + private String recorder; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbScwemRVo.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbScwemRVo.java new file mode 100644 index 00000000..9f6f3517 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/entity/vo/FbScwemRVo.java @@ -0,0 +1,73 @@ +package com.yfd.platform.qgc_env.fb.entity.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 苗种培育水环境监测记录表 VO + */ +@Data +public class FbScwemRVo { + + /** 主键 */ + private String id; + + /** 设施编码 */ + private String stcd; + + /** 监测日期 */ + private Date tm; + + /** 鱼池编号 */ + private String fpnum; + + /** 鱼池名称 */ + private String fpName; + + /** 水温(℃) */ + private BigDecimal wartem; + + /** 与上一时段水温差(℃) */ + private BigDecimal wtdiff; + + /** 氨氮(mg/L) */ + private BigDecimal nh3n; + + /** 亚硝酸盐(mg/L) */ + private BigDecimal no2; + + /** 溶解氧(mg/L) */ + private BigDecimal dox; + + /** pH */ + private BigDecimal ph; + + /** 记录人 */ + private String recorder; + + /** 附件ID */ + private String fid; + + /** 创建人 */ + private String recordUser; + + /** 创建时间 */ + private Date recordTime; + + /** 更新人 */ + private String modifyUser; + + /** 更新时间 */ + private Date modifyTime; + + /** 是否已删除 */ + private Integer isDeleted; + + /** 删除人 */ + private String deleteUser; + + /** 删除时间 */ + private Date deleteTime; +} diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java index 5e94e2de..da2ef0e6 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/FbStationService.java @@ -2,24 +2,7 @@ package com.yfd.platform.qgc_env.fb.service; import com.yfd.platform.common.DataSourceRequest; import com.yfd.platform.common.DataSourceResult; -import com.yfd.platform.qgc_env.fb.entity.vo.BreedStageVO; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRQgcVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishTableVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbEngFishVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFlDataVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishCountFinishVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishRunQgcVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishTypeStcdVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbFishTypeFinishVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbRelatedYrVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbMsfbrdmQgcVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbResearchVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbStInfoResultVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbStationStaticsDataVo; -import com.yfd.platform.qgc_env.fb.entity.vo.YearRpStatisticsVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbStationOverviewSecondVo; -import com.yfd.platform.qgc_env.fb.entity.vo.FbTracingPointVo; -import com.yfd.platform.qgc_env.fb.entity.vo.TableVo; +import com.yfd.platform.qgc_env.fb.entity.vo.*; import java.util.List; import java.util.Map; @@ -84,4 +67,69 @@ public interface FbStationService { * 获取增殖站各阶段数据状态(选配/繁殖/培育/防治) */ List getBreedStageList(String stcd); + + /** + * 亲鱼档案管理记录列表 + */ + DataSourceResult getBsmfRList(DataSourceRequest dataSourceRequest); + + /** + * 亲鱼死亡记录列表 + */ + DataSourceResult getBsdeathRList(DataSourceRequest dataSourceRequest); + + /** + * 亲鱼培育方式列表 + */ + DataSourceResult getBsctmoBList(DataSourceRequest dataSourceRequest); + + /** + * 亲鱼培育水温观测记录列表 + */ + DataSourceResult getFbbwtRList(DataSourceRequest dataSourceRequest); + + /** + * 亲鱼培育水质监测记录列表 + */ + DataSourceResult getFbcwqRList(DataSourceRequest dataSourceRequest); + + /** + * 亲鱼培育投喂记录列表 + */ + DataSourceResult getBsrfiRList(DataSourceRequest dataSourceRequest); + + /** + * 淘汰亲鱼基本信息记录列表 + */ + DataSourceResult getOutfishRList(DataSourceRequest dataSourceRequest); + + /** + * 鱼类人工催产记录列表 + */ + DataSourceResult getFishartinlRList(DataSourceRequest dataSourceRequest); + + /** + * 鱼类受精卵孵化过程记录列表 + */ + DataSourceResult getFishhatchrecRList(DataSourceRequest dataSourceRequest); + + /** + * 鱼类孵化过程巡查记录列表 + */ + DataSourceResult getFishhatchproRList(DataSourceRequest dataSourceRequest); + + /** + * 苗种培育水环境监测记录列表 + */ + DataSourceResult getScwemRList(DataSourceRequest dataSourceRequest); + + /** + * 鱼苗和鱼种培育记录列表 + */ + DataSourceResult getFishbreedRList(DataSourceRequest dataSourceRequest); + + /** + * 鱼病防治记录列表 + */ + DataSourceResult getFishdpacRList(DataSourceRequest dataSourceRequest); } diff --git a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java index 635edcef..80f8b9b0 100644 --- a/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java +++ b/backend/src/main/java/com/yfd/platform/qgc_env/fb/service/impl/FbStationServiceImpl.java @@ -12,6 +12,19 @@ import com.yfd.platform.common.GroupHelper; import com.yfd.platform.common.GroupingInfo; import com.yfd.platform.common.MicroservicDynamicSQLMapper; import com.yfd.platform.qgc_env.fb.entity.vo.BreedStageVO; +import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbBsdeathRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbBsctmoBVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFbbwtRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFbcwqRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbBsrfiRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbOutfishRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFishartinlRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFishhatchrecRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFishhatchproRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbScwemRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFishbreedRVo; +import com.yfd.platform.qgc_env.fb.entity.vo.FbFishdpacRVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRQgcVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishHeadColumnVo; import com.yfd.platform.qgc_env.fb.entity.vo.FbBsmfRFishSummaryVo; @@ -1980,6 +1993,23 @@ public class FbStationServiceImpl implements FbStationService { return result; } + private String buildQgcMsfbrdmCoreSql1(Date startTime, Date endTime, Map paramMap) { + StringBuilder sql = new StringBuilder(); + sql.append(" SELECT\n" + + " a.ID, a.STCD, a.TM,\n" + + " CASE WHEN ZY_FISH_ID IS NULL THEN TO_CHAR(a.FTP) ELSE TO_CHAR(ZY_FISH_ID) END AS ftp,CASE WHEN c.NAME IS NULL THEN TO_CHAR(a.FTP) ELSE TO_CHAR(c.NAME) END AS ftpName,\n" + + " a.BSSR, a.AGE, a.SEX, a.SIGNNUM, a.RECORDER, a.VALUE, a.FID,\n" + + " a.RECORD_USER, a.RECORD_TIME, a.MODIFY_USER, a.MODIFY_TIME,\n" + + " a.IS_DELETED, a.DELETE_USER, a.DELETE_TIME,\n" + + " a.LENGTH, a.WEIGHT\n" + + " FROM SD_FBBSMF_R a\n" + + " INNER JOIN V_MS_STBPRP_T b\n" + + " ON a.stcd = b.STCD AND b.STTP_CODE = 'FB' AND b.IS_DELETED = 0\n" + + " LEFT JOIN V_SD_FISHDICTORY_B c\n" + + " ON a.FTP = c.FISH_ID AND (b.HBRVCD = c.RVCD OR c.RVCD = 'ZY')"); + return sql.toString(); + } + private String buildQgcMsfbrdmCoreSql(Date startTime, Date endTime, Map paramMap) { Integer year = startTime == null ? null : Integer.parseInt(DateUtil.format(startTime, "yyyy")); String ftpCodeExpr = "COALESCE(TO_CHAR(NVL(relRv.ZY_FISH_ID, relRv.FISH_ID)), " + @@ -5110,4 +5140,3479 @@ public class FbStationServiceImpl implements FbStationService { this.fcnt = fcnt; } } + + // ==================== 亲鱼档案管理记录列表 ==================== + + @Override + public DataSourceResult getBsmfRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryBsmfRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryBsmfRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryBsmfRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildBsmfRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildBsmfRCoreSql()) + .append(") t WHERE 1 = 1 "); + + String filterSql = buildBsmfRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildBsmfROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbBsmfRVo.class + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryBsmfRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildBsmfRCoreSql()) + .append(") t WHERE 1 = 1 "); + + String filterSql = buildBsmfRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildBsmfRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows= microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap + ); + + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, + groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos) + )); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, + groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos) + )); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildBsmfRCoreSql() { + return "SELECT rec.ID AS id, " + + "rec.STCD AS stcd, " + + "rec.TM AS tm, " + + "rec.FTP AS ftp, " + + "CAST(NULL AS VARCHAR2(255)) AS ftpName, " + + "rec.BSSR AS bssr, " + + "rec.BSSR AS bssrName, " + + "rec.LENGTH AS length, " + + "rec.WEIGHT AS weight, " + + "rec.AGE AS age, " + + "rec.SEX AS sex, " + + "CASE WHEN rec.SEX = 1 THEN '雄鱼' WHEN rec.SEX = 2 THEN '雌鱼' ELSE NULL END AS sexName, " + + "rec.SIGNNUM AS signnum, " + + "rec.RECORDER AS recorder, " + + "rec.VALUE AS value, " + + "rec.FID AS fid, " + + "rec.RECORD_USER AS recordUser, " + + "rec.RECORD_TIME AS recordTime, " + + "rec.MODIFY_USER AS modifyUser, " + + "rec.MODIFY_TIME AS modifyTime, " + + "rec.IS_DELETED AS isDeleted, " + + "rec.DELETE_USER AS deleteUser, " + + "rec.DELETE_TIME AS deleteTime " + + "FROM SD_FBBSMF_R rec " + + "WHERE NVL(rec.IS_DELETED, 0) = 0"; + } + + private String buildBsmfRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "tm", "ftp", "ftpName", "bssr", "bssrName", "length", "weight", "age", + "sex", "sexName", "signnum", "recorder", "value", "fid", "recordUser", "recordTime", + "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapBsmfRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildBsmfRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildBsmfRLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildBsmfRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) { + conditions.add("(" + childSql + ")"); + } + } + if (conditions.isEmpty()) { + return ""; + } + String logic = "or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND "; + return String.join(logic, conditions); + } + + private String buildBsmfRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapBsmfRColumn(filter.getField()); + if (StrUtil.isBlank(column)) { + return ""; + } + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) { + return column + " IS NULL"; + } + if ("isnotnull".equals(operator)) { + return column + " IS NOT NULL"; + } + if ("in".equals(operator)) { + List values = normalizeBsmfRValues(value); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "bsmfP" + indexHolder[0]++; + paramMap.put(key, normalizeBsmfRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) { + return ""; + } + String key = "bsmfP" + indexHolder[0]++; + Object normalizedValue = normalizeBsmfRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { + paramMap.put(key, normalizedValue); + yield column + " = #{map." + key + "}"; + } + case "neq" -> { + paramMap.put(key, normalizedValue); + yield column + " <> #{map." + key + "}"; + } + case "gt" -> { + paramMap.put(key, normalizedValue); + yield column + " > #{map." + key + "}"; + } + case "gte" -> { + paramMap.put(key, normalizedValue); + yield column + " >= #{map." + key + "}"; + } + case "lt" -> { + paramMap.put(key, normalizedValue); + yield column + " < #{map." + key + "}"; + } + case "lte" -> { + paramMap.put(key, normalizedValue); + yield column + " <= #{map." + key + "}"; + } + case "contains" -> { + paramMap.put(key, "%" + normalizedValue + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "doesnotcontain" -> { + paramMap.put(key, "%" + normalizedValue + "%"); + yield column + " NOT LIKE #{map." + key + "}"; + } + case "startswith" -> { + paramMap.put(key, normalizedValue + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "endswith" -> { + paramMap.put(key, "%" + normalizedValue); + yield column + " LIKE #{map." + key + "}"; + } + default -> ""; + }; + } + + private String buildBsmfROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) { + return " ORDER BY t.tm DESC, t.recordTime DESC"; + } + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapBsmfRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(descriptor.getDir()) ? "DESC" : "ASC"; + parts.add(column + " " + direction); + } + if (parts.isEmpty()) { + return " ORDER BY t.tm DESC, t.recordTime DESC"; + } + return " ORDER BY " + String.join(", ", parts); + } + + private String buildBsmfRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) { + return baseSql; + } + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapBsmfRColumn(group.getField()))) + .toList(); + if (validGroups.isEmpty()) { + return baseSql; + } + List groupFields = validGroups.stream() + .map(DataSourceRequest.GroupDescriptor::getField) + .toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + String field = groupFields.get(i); + sql.append("t.").append(field).append(" AS ").append(field); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t "); + sql.append("GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapBsmfRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "tm" -> "t.tm"; + case "ftp" -> "t.ftp"; + case "ftpName" -> "t.ftpName"; + case "bssr" -> "t.bssr"; + case "bssrName" -> "t.bssrName"; + case "length" -> "t.length"; + case "weight" -> "t.weight"; + case "age" -> "t.age"; + case "sex" -> "t.sex"; + case "sexName" -> "t.sexName"; + case "signnum" -> "t.signnum"; + case "recorder" -> "t.recorder"; + case "value" -> "t.value"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeBsmfRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) { + return values; + } + if (value instanceof Collection collection) { + values.addAll(collection); + return values; + } + if (value instanceof Object[] array) { + values.addAll(Arrays.asList(array)); + return values; + } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) { + text = text.substring(1, text.length() - 1); + } + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) { + values.add(trimmed); + } + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeBsmfRValue(String field, Object value) { + if (value == null) { + return null; + } + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 亲鱼死亡记录列表 ==================== + + @Override + public DataSourceResult getBsdeathRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryBsdeathRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryBsdeathRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryBsdeathRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildBsdeathRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildBsdeathRCoreSql()) + .append(") t WHERE 1 = 1 "); + + String filterSql = buildBsdeathRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildBsdeathROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbBsdeathRVo.class + ); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryBsdeathRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildBsdeathRCoreSql()) + .append(") t WHERE 1 = 1 "); + + String filterSql = buildBsdeathRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, + new int[]{0} + ); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildBsdeathRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap + ); + + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, + groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos) + )); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, + groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos) + )); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildBsdeathRCoreSql() { + return "SELECT rec.ID AS id, " + + "rec.STCD AS stcd, " + + "rec.TM AS tm, " + + "rec.FPNUM AS fpnum, " + + "CAST(NULL AS VARCHAR2(255)) AS fpName, " + + "rec.FTP AS ftp, " + + "CAST(NULL AS VARCHAR2(255)) AS ftpName, " + + "rec.SIGNNUM AS signnum, " + + "rec.LENGTH AS length, " + + "rec.WEIGHT AS weight, " + + "rec.SPOD AS spod, " + + "rec.DEATHD AS deathd, " + + "rec.RECORDER AS recorder, " + + "rec.VALUE AS value, " + + "rec.FID AS fid, " + + "rec.RECORD_USER AS recordUser, " + + "rec.RECORD_TIME AS recordTime, " + + "rec.MODIFY_USER AS modifyUser, " + + "rec.MODIFY_TIME AS modifyTime, " + + "rec.IS_DELETED AS isDeleted, " + + "rec.DELETE_USER AS deleteUser, " + + "rec.DELETE_TIME AS deleteTime " + + "FROM SD_FBBSDEATH_R rec " + + "WHERE NVL(rec.IS_DELETED, 0) = 0"; + } + + private String buildBsdeathRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "tm", "fpnum", "fpName", "ftp", "ftpName", "signnum", "length", "weight", + "spod", "deathd", "recorder", "value", "fid", "recordUser", "recordTime", + "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapBsdeathRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildBsdeathRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) { + return ""; + } + if (StrUtil.isNotBlank(filter.getField())) { + return buildBsdeathRLeafCondition(filter, paramMap, indexHolder); + } + if (CollUtil.isEmpty(filter.getFilters())) { + return ""; + } + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildBsdeathRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) { + conditions.add("(" + childSql + ")"); + } + } + if (conditions.isEmpty()) { + return ""; + } + String logic = "or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND "; + return String.join(logic, conditions); + } + + private String buildBsdeathRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapBsdeathRColumn(filter.getField()); + if (StrUtil.isBlank(column)) { + return ""; + } + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) { + return column + " IS NULL"; + } + if ("isnotnull".equals(operator)) { + return column + " IS NOT NULL"; + } + if ("in".equals(operator)) { + List values = normalizeBsdeathRValues(value); + if (values.isEmpty()) { + return ""; + } + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "bsdP" + indexHolder[0]++; + paramMap.put(key, normalizeBsdeathRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) { + return ""; + } + String key = "bsdP" + indexHolder[0]++; + Object normalizedValue = normalizeBsdeathRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { + paramMap.put(key, normalizedValue); + yield column + " = #{map." + key + "}"; + } + case "neq" -> { + paramMap.put(key, normalizedValue); + yield column + " <> #{map." + key + "}"; + } + case "gt" -> { + paramMap.put(key, normalizedValue); + yield column + " > #{map." + key + "}"; + } + case "gte" -> { + paramMap.put(key, normalizedValue); + yield column + " >= #{map." + key + "}"; + } + case "lt" -> { + paramMap.put(key, normalizedValue); + yield column + " < #{map." + key + "}"; + } + case "lte" -> { + paramMap.put(key, normalizedValue); + yield column + " <= #{map." + key + "}"; + } + case "contains" -> { + paramMap.put(key, "%" + normalizedValue + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "doesnotcontain" -> { + paramMap.put(key, "%" + normalizedValue + "%"); + yield column + " NOT LIKE #{map." + key + "}"; + } + case "startswith" -> { + paramMap.put(key, normalizedValue + "%"); + yield column + " LIKE #{map." + key + "}"; + } + case "endswith" -> { + paramMap.put(key, "%" + normalizedValue); + yield column + " LIKE #{map." + key + "}"; + } + default -> ""; + }; + } + + private String buildBsdeathROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) { + return " ORDER BY t.tm DESC, t.recordTime DESC"; + } + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapBsdeathRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) { + continue; + } + String direction = "desc".equalsIgnoreCase(descriptor.getDir()) ? "DESC" : "ASC"; + parts.add(column + " " + direction); + } + if (parts.isEmpty()) { + return " ORDER BY t.tm DESC, t.recordTime DESC"; + } + return " ORDER BY " + String.join(", ", parts); + } + + private String buildBsdeathRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) { + return baseSql; + } + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapBsdeathRColumn(group.getField()))) + .toList(); + if (validGroups.isEmpty()) { + return baseSql; + } + List groupFields = validGroups.stream() + .map(DataSourceRequest.GroupDescriptor::getField) + .toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + String field = groupFields.get(i); + sql.append("t.").append(field).append(" AS ").append(field); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t "); + sql.append("GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapBsdeathRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "tm" -> "t.tm"; + case "fpnum" -> "t.fpnum"; + case "fpName" -> "t.fpName"; + case "ftp" -> "t.ftp"; + case "ftpName" -> "t.ftpName"; + case "signnum" -> "t.signnum"; + case "length" -> "t.length"; + case "weight" -> "t.weight"; + case "spod" -> "t.spod"; + case "deathd" -> "t.deathd"; + case "recorder" -> "t.recorder"; + case "value" -> "t.value"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeBsdeathRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) { + return values; + } + if (value instanceof Collection collection) { + values.addAll(collection); + return values; + } + if (value instanceof Object[] array) { + values.addAll(Arrays.asList(array)); + return values; + } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) { + text = text.substring(1, text.length() - 1); + } + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) { + values.add(trimmed); + } + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeBsdeathRValue(String field, Object value) { + if (value == null) { + return null; + } + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 亲鱼培育方式列表 ==================== + + @Override + public DataSourceResult getBsctmoBList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryBsctmoBGroupList(dataSourceRequest, loadOptions, groups); + } + return queryBsctmoBDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryBsctmoBDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildBsctmoBDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildBsctmoBCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildBsctmoBFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildBsctmoBOrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbBsctmoBVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryBsctmoBGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildBsctmoBCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildBsctmoBFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildBsctmoBGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildBsctmoBCoreSql() { + return "SELECT rec.ID AS id, " + + "rec.STCD AS stcd, " + + "rec.FPNUM AS fpnum, " + + "CAST(NULL AS VARCHAR2(255)) AS fpName, " + + "rec.FTP AS ftp, " + + "CAST(NULL AS VARCHAR2(255)) AS ftpName, " + + "rec.VALUE AS value, " + + "rec.LENGTH AS length, " + + "rec.WEIGHT AS weight, " + + "rec.CTMO AS ctmo, " + + "CASE WHEN rec.CTMO = 1 THEN '流水单养培育' WHEN rec.CTMO = 2 THEN '流水混养培育' WHEN rec.CTMO = 3 THEN '静水培育' WHEN rec.CTMO = 4 THEN '循环水培育' ELSE NULL END AS ctmoName, " + + "rec.SKDS AS skds, " + + "rec.FWGH AS fwgh, " + + "rec.RECORDER AS recorder, " + + "rec.RECORD_USER AS recordUser, " + + "rec.RECORD_TIME AS recordTime, " + + "rec.MODIFY_USER AS modifyUser, " + + "rec.MODIFY_TIME AS modifyTime, " + + "rec.IS_DELETED AS isDeleted, " + + "rec.DELETE_USER AS deleteUser, " + + "rec.DELETE_TIME AS deleteTime " + + "FROM SD_FBBSCTMO_B rec " + + "WHERE NVL(rec.IS_DELETED, 0) = 0"; + } + + private String buildBsctmoBDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "fpnum", "fpName", "ftp", "ftpName", "value", "length", "weight", + "ctmo", "ctmoName", "skds", "fwgh", "recorder", "recordUser", "recordTime", + "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapBsctmoBColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildBsctmoBFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildBsctmoBLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildBsctmoBFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildBsctmoBLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapBsctmoBColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeBsctmoBValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "bctP" + indexHolder[0]++; + paramMap.put(key, normalizeBsctmoBValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "bctP" + indexHolder[0]++; + Object normalizedValue = normalizeBsctmoBValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildBsctmoBOrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.id DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapBsctmoBColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.id DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildBsctmoBGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapBsctmoBColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapBsctmoBColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "fpnum" -> "t.fpnum"; + case "fpName" -> "t.fpName"; + case "ftp" -> "t.ftp"; + case "ftpName" -> "t.ftpName"; + case "value" -> "t.value"; + case "length" -> "t.length"; + case "weight" -> "t.weight"; + case "ctmo" -> "t.ctmo"; + case "ctmoName" -> "t.ctmoName"; + case "skds" -> "t.skds"; + case "fwgh" -> "t.fwgh"; + case "recorder" -> "t.recorder"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeBsctmoBValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeBsctmoBValue(String field, Object value) { + if (value == null) return null; + if (Set.of("recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 亲鱼培育水温观测记录列表 ==================== + + @Override + public DataSourceResult getFbbwtRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryFbbwtRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryFbbwtRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryFbbwtRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFbbwtRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFbbwtRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFbbwtRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFbbwtROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbFbbwtRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryFbbwtRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildFbbwtRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFbbwtRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildFbbwtRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildFbbwtRCoreSql() { + return "SELECT a.ID AS id, " + + "a.STCD AS stcd, " + + "a.TM AS tm, " + + "a.FPNUM AS fpnum, " + + "b.NAME AS fpName, " + + "a.WTE AS wte, " + + "a.WTF AS wtf, " + + "a.WTT AS wtt, " + + "a.RECORDER AS recorder, " + + "a.FID AS fid, " + + "a.RECORD_USER AS recordUser, " + + "a.RECORD_TIME AS recordTime, " + + "a.MODIFY_USER AS modifyUser, " + + "a.MODIFY_TIME AS modifyTime, " + + "a.IS_DELETED AS isDeleted, " + + "a.DELETE_USER AS deleteUser, " + + "a.DELETE_TIME AS deleteTime " + + "FROM SD_FBBWT_R a " + + "LEFT JOIN SD_FBFISHPOND_B b ON a.FPNUM = b.ID AND NVL(b.IS_DELETED, 0) = 0"; + } + + private String buildFbbwtRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "tm", "fpnum", "fpName", "wte", "wtf", "wtt", + "recorder", "fid", "recordUser", "recordTime", "modifyUser", "modifyTime", + "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapFbbwtRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildFbbwtRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildFbbwtRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFbbwtRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildFbbwtRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapFbbwtRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeFbbwtRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "bwtP" + indexHolder[0]++; + paramMap.put(key, normalizeFbbwtRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "bwtP" + indexHolder[0]++; + Object normalizedValue = normalizeFbbwtRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildFbbwtROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapFbbwtRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildFbbwtRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapFbbwtRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapFbbwtRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "tm" -> "t.tm"; + case "fpnum" -> "t.fpnum"; + case "fpName" -> "t.fpName"; + case "wte" -> "t.wte"; + case "wtf" -> "t.wtf"; + case "wtt" -> "t.wtt"; + case "recorder" -> "t.recorder"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeFbbwtRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeFbbwtRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 亲鱼培育水质监测记录列表 ==================== + + @Override + public DataSourceResult getFbcwqRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryFbcwqRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryFbcwqRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryFbcwqRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFbcwqRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFbcwqRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFbcwqRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFbcwqROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbFbcwqRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryFbcwqRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildFbcwqRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFbcwqRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildFbcwqRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildFbcwqRCoreSql() { + return "SELECT a.ID AS id, " + + "a.STCD AS stcd, " + + "a.FPNUM AS fpnum, " + + "b.NAME AS fpName, " + + "a.NH3N AS nh3n, " + + "a.NO2 AS no2, " + + "a.DOX AS dox, " + + "a.PH AS ph, " + + "a.RECORDER AS recorder, " + + "a.IFFIT AS iffit, " + + "a.TM AS tm, " + + "a.FID AS fid, " + + "a.RECORD_USER AS recordUser, " + + "a.RECORD_TIME AS recordTime, " + + "a.MODIFY_USER AS modifyUser, " + + "a.MODIFY_TIME AS modifyTime, " + + "a.IS_DELETED AS isDeleted, " + + "a.DELETE_USER AS deleteUser, " + + "a.DELETE_TIME AS deleteTime " + + "FROM SD_FBCWQ_R a " + + "LEFT JOIN SD_FBFISHPOND_B b ON a.FPNUM = b.ID AND NVL(b.IS_DELETED, 0) = 0"; + } + + private String buildFbcwqRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "fpnum", "fpName", "nh3n", "no2", "dox", "ph", + "recorder", "iffit", "tm", "fid", "recordUser", "recordTime", "modifyUser", "modifyTime", + "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapFbcwqRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildFbcwqRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildFbcwqRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFbcwqRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildFbcwqRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapFbcwqRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeFbcwqRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "cwqP" + indexHolder[0]++; + paramMap.put(key, normalizeFbcwqRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "cwqP" + indexHolder[0]++; + Object normalizedValue = normalizeFbcwqRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildFbcwqROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapFbcwqRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildFbcwqRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapFbcwqRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapFbcwqRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "fpnum" -> "t.fpnum"; + case "fpName" -> "t.fpName"; + case "nh3n" -> "t.nh3n"; + case "no2" -> "t.no2"; + case "dox" -> "t.dox"; + case "ph" -> "t.ph"; + case "recorder" -> "t.recorder"; + case "iffit" -> "t.iffit"; + case "tm" -> "t.tm"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeFbcwqRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeFbcwqRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 亲鱼培育投喂记录列表 ==================== + + @Override + public DataSourceResult getBsrfiRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryBsrfiRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryBsrfiRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryBsrfiRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildBsrfiRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildBsrfiRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildBsrfiRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildBsrfiROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbBsrfiRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryBsrfiRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildBsrfiRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildBsrfiRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildBsrfiRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildBsrfiRCoreSql() { + return "SELECT ID AS id, " + + "STCD AS stcd, " + + "FPNUM AS fpnum, " + + "FTP AS ftp, " + + "TM AS tm, " + + "FTYPE AS ftype, " + + "CFE AS cfe, " + + "WARFRE AS warfre, " + + "RECORDER AS recorder, " + + "TBW AS tbw, " + + "DFR AS dfr, " + + "FDI AS fdi, " + + "FID AS fid, " + + "RECORD_USER AS recordUser, " + + "RECORD_TIME AS recordTime, " + + "MODIFY_USER AS modifyUser, " + + "MODIFY_TIME AS modifyTime, " + + "IS_DELETED AS isDeleted, " + + "DELETE_USER AS deleteUser, " + + "DELETE_TIME AS deleteTime " + + "FROM SD_FBBSRFI_R " + + "WHERE NVL(IS_DELETED, 0) = 0"; + } + + private String buildBsrfiRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "fpnum", "ftp", "tm", "ftype", "cfe", "warfre", + "recorder", "tbw", "dfr", "fdi", "fid", "recordUser", "recordTime", "modifyUser", "modifyTime", + "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapBsrfiRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildBsrfiRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildBsrfiRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildBsrfiRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildBsrfiRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapBsrfiRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeBsrfiRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "srfP" + indexHolder[0]++; + paramMap.put(key, normalizeBsrfiRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "srfP" + indexHolder[0]++; + Object normalizedValue = normalizeBsrfiRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildBsrfiROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapBsrfiRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildBsrfiRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapBsrfiRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapBsrfiRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "fpnum" -> "t.fpnum"; + case "ftp" -> "t.ftp"; + case "tm" -> "t.tm"; + case "ftype" -> "t.ftype"; + case "cfe" -> "t.cfe"; + case "warfre" -> "t.warfre"; + case "recorder" -> "t.recorder"; + case "tbw" -> "t.tbw"; + case "dfr" -> "t.dfr"; + case "fdi" -> "t.fdi"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeBsrfiRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeBsrfiRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 淘汰亲鱼基本信息记录列表 ==================== + + @Override + public DataSourceResult getOutfishRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryOutfishRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryOutfishRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryOutfishRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildOutfishRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildOutfishRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildOutfishRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildOutfishROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbOutfishRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryOutfishRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildOutfishRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildOutfishRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildOutfishRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildOutfishRCoreSql() { + return "SELECT ID AS id, " + + "STCD AS stcd, " + + "RECORDERTM AS recordertm, " + + "SIGNNO AS signno, " + + "FTP AS ftp, " + + "SRC AS src, " + + "SEX AS sex, " + + "AGE AS age, " + + "LENGTH AS length, " + + "WEIGHT AS weight, " + + "AGELMT AS agelmt, " + + "BRECOUNT AS brecount, " + + "TM AS tm, " + + "OUTREA AS outrea, " + + "HAND AS hand, " + + "RECORDER AS recorder, " + + "FID AS fid, " + + "RECORD_USER AS recordUser, " + + "RECORD_TIME AS recordTime, " + + "MODIFY_USER AS modifyUser, " + + "MODIFY_TIME AS modifyTime, " + + "IS_DELETED AS isDeleted, " + + "DELETE_USER AS deleteUser, " + + "DELETE_TIME AS deleteTime " + + "FROM SD_FBOUTFISH_R " + + "WHERE NVL(IS_DELETED, 0) = 0"; + } + + private String buildOutfishRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "recordertm", "signno", "ftp", "src", "sex", "age", "length", "weight", + "agelmt", "brecount", "tm", "outrea", "hand", "recorder", "fid", + "recordUser", "recordTime", "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapOutfishRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildOutfishRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildOutfishRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildOutfishRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildOutfishRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapOutfishRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeOutfishRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "ofsP" + indexHolder[0]++; + paramMap.put(key, normalizeOutfishRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "ofsP" + indexHolder[0]++; + Object normalizedValue = normalizeOutfishRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildOutfishROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapOutfishRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildOutfishRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapOutfishRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapOutfishRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "recordertm" -> "t.recordertm"; + case "signno" -> "t.signno"; + case "ftp" -> "t.ftp"; + case "src" -> "t.src"; + case "sex" -> "t.sex"; + case "age" -> "t.age"; + case "length" -> "t.length"; + case "weight" -> "t.weight"; + case "agelmt" -> "t.agelmt"; + case "brecount" -> "t.brecount"; + case "tm" -> "t.tm"; + case "outrea" -> "t.outrea"; + case "hand" -> "t.hand"; + case "recorder" -> "t.recorder"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeOutfishRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeOutfishRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("recordertm", "tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 鱼类人工催产记录列表 ==================== + + @Override + public DataSourceResult getFishartinlRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryFishartinlRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryFishartinlRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryFishartinlRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFishartinlRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFishartinlRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishartinlRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFishartinlROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbFishartinlRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryFishartinlRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildFishartinlRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishartinlRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildFishartinlRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildFishartinlRCoreSql() { + return "SELECT ID AS id, " + + "STCD AS stcd, " + + "FTP AS ftp, " + + "TM AS tm, " + + "FFISHT AS ffisht, " + + "FPITOCINTYPE AS fpitocintype, " + + "FDOSE AS fdose, " + + "FINJMO AS finjmo, " + + "AIR AS air, " + + "WARTEM AS wartem, " + + "DOX AS dox, " + + "RECORDER AS recorder, " + + "SEX AS sex, " + + "FID AS fid, " + + "MFISHT AS mfisht, " + + "MPITOCINTYPE AS mpitocintype, " + + "MDOSE AS mdose, " + + "MINJMO AS minjmo, " + + "RECORD_USER AS recordUser, " + + "RECORD_TIME AS recordTime, " + + "MODIFY_USER AS modifyUser, " + + "MODIFY_TIME AS modifyTime, " + + "IS_DELETED AS isDeleted, " + + "DELETE_USER AS deleteUser, " + + "DELETE_TIME AS deleteTime " + + "FROM SD_FBFISHARTINL_R " + + "WHERE NVL(IS_DELETED, 0) = 0"; + } + + private String buildFishartinlRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "ftp", "tm", "ffisht", "fpitocintype", "fdose", "finjmo", + "air", "wartem", "dox", "recorder", "sex", "fid", "mfisht", "mpitocintype", "mdose", "minjmo", + "recordUser", "recordTime", "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapFishartinlRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildFishartinlRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildFishartinlRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFishartinlRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildFishartinlRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapFishartinlRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeFishartinlRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "farP" + indexHolder[0]++; + paramMap.put(key, normalizeFishartinlRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "farP" + indexHolder[0]++; + Object normalizedValue = normalizeFishartinlRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildFishartinlROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapFishartinlRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildFishartinlRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapFishartinlRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapFishartinlRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "ftp" -> "t.ftp"; + case "tm" -> "t.tm"; + case "ffisht" -> "t.ffisht"; + case "fpitocintype" -> "t.fpitocintype"; + case "fdose" -> "t.fdose"; + case "finjmo" -> "t.finjmo"; + case "air" -> "t.air"; + case "wartem" -> "t.wartem"; + case "dox" -> "t.dox"; + case "recorder" -> "t.recorder"; + case "sex" -> "t.sex"; + case "fid" -> "t.fid"; + case "mfisht" -> "t.mfisht"; + case "mpitocintype" -> "t.mpitocintype"; + case "mdose" -> "t.mdose"; + case "minjmo" -> "t.minjmo"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeFishartinlRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeFishartinlRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 鱼类受精卵孵化过程记录列表 ==================== + + @Override + public DataSourceResult getFishhatchrecRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryFishhatchrecRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryFishhatchrecRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryFishhatchrecRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFishhatchrecRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFishhatchrecRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishhatchrecRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFishhatchrecROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbFishhatchrecRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryFishhatchrecRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildFishhatchrecRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishhatchrecRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildFishhatchrecRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildFishhatchrecRCoreSql() { + return "SELECT ID AS id, " + + "STCD AS stcd, " + + "FTP AS ftp, " + + "FISHSRC AS fishsrc, " + + "EGGCOUNT AS eggcount, " + + "FERRATE AS ferrate, " + + "HATCHRATE AS hatchrate, " + + "OUTHOUR AS outhour, " + + "OUTCOUNT AS outcount, " + + "OUTRATE AS outrate, " + + "RECORDER AS recorder, " + + "TM AS tm, " + + "FID AS fid, " + + "RECORD_USER AS recordUser, " + + "RECORD_TIME AS recordTime, " + + "MODIFY_USER AS modifyUser, " + + "MODIFY_TIME AS modifyTime, " + + "IS_DELETED AS isDeleted, " + + "DELETE_USER AS deleteUser, " + + "DELETE_TIME AS deleteTime " + + "FROM SD_FBFISHHATCHREC_R " + + "WHERE NVL(IS_DELETED, 0) = 0"; + } + + private String buildFishhatchrecRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "ftp", "fishsrc", "eggcount", "ferrate", "hatchrate", + "outhour", "outcount", "outrate", "recorder", "tm", "fid", + "recordUser", "recordTime", "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapFishhatchrecRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildFishhatchrecRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildFishhatchrecRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFishhatchrecRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildFishhatchrecRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapFishhatchrecRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeFishhatchrecRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "fhrP" + indexHolder[0]++; + paramMap.put(key, normalizeFishhatchrecRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "fhrP" + indexHolder[0]++; + Object normalizedValue = normalizeFishhatchrecRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildFishhatchrecROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapFishhatchrecRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildFishhatchrecRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapFishhatchrecRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapFishhatchrecRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "ftp" -> "t.ftp"; + case "fishsrc" -> "t.fishsrc"; + case "eggcount" -> "t.eggcount"; + case "ferrate" -> "t.ferrate"; + case "hatchrate" -> "t.hatchrate"; + case "outhour" -> "t.outhour"; + case "outcount" -> "t.outcount"; + case "outrate" -> "t.outrate"; + case "recorder" -> "t.recorder"; + case "tm" -> "t.tm"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeFishhatchrecRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeFishhatchrecRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 鱼类孵化过程巡查记录 (SD_FBFISHHATCHPRO_R) ==================== + + @Override + public DataSourceResult getFishhatchproRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryFishhatchproRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryFishhatchproRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryFishhatchproRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFishhatchproRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFishhatchproRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishhatchproRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFishhatchproROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbFishhatchproRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryFishhatchproRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildFishhatchproRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishhatchproRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildFishhatchproRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildFishhatchproRCoreSql() { + return "SELECT ID AS id, " + + "STCD AS stcd, " + + "DEVNO AS devno, " + + "RECORDTM AS recordtm, " + + "WARTEM AS wartem, " + + "WTDIFF AS wtdiff, " + + "DOX AS dox, " + + "PH AS ph, " + + "EMPSTA AS empsta, " + + "WARFRE AS warfre, " + + "ISEVEN AS iseven, " + + "RECORDER AS recorder, " + + "REMARKS AS remarks, " + + "SPAWNTM AS spawntm, " + + "TM AS tm, " + + "FID AS fid, " + + "RECORD_USER AS recordUser, " + + "RECORD_TIME AS recordTime, " + + "MODIFY_USER AS modifyUser, " + + "MODIFY_TIME AS modifyTime, " + + "IS_DELETED AS isDeleted, " + + "DELETE_USER AS deleteUser, " + + "DELETE_TIME AS deleteTime " + + "FROM SD_FBFISHHATCHPRO_R " + + "WHERE NVL(IS_DELETED, 0) = 0"; + } + + private String buildFishhatchproRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "devno", "recordtm", "wartem", "wtdiff", + "dox", "ph", "empsta", "warfre", "iseven", "recorder", "remarks", "spawntm", "tm", "fid", + "recordUser", "recordTime", "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapFishhatchproRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildFishhatchproRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildFishhatchproRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFishhatchproRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildFishhatchproRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapFishhatchproRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeFishhatchproRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "fhpP" + indexHolder[0]++; + paramMap.put(key, normalizeFishhatchproRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "fhpP" + indexHolder[0]++; + Object normalizedValue = normalizeFishhatchproRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildFishhatchproROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapFishhatchproRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildFishhatchproRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapFishhatchproRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapFishhatchproRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "devno" -> "t.devno"; + case "recordtm" -> "t.recordtm"; + case "wartem" -> "t.wartem"; + case "wtdiff" -> "t.wtdiff"; + case "dox" -> "t.dox"; + case "ph" -> "t.ph"; + case "empsta" -> "t.empsta"; + case "warfre" -> "t.warfre"; + case "iseven" -> "t.iseven"; + case "recorder" -> "t.recorder"; + case "remarks" -> "t.remarks"; + case "spawntm" -> "t.spawntm"; + case "tm" -> "t.tm"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeFishhatchproRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeFishhatchproRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("recordtm", "spawntm", "tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 苗种培育水环境监测记录 (SD_FBSCWEM_R + SD_FBFISHPOND_B) ==================== + + @Override + public DataSourceResult getScwemRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryScwemRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryScwemRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryScwemRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildScwemRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildScwemRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildScwemRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildScwemROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbScwemRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryScwemRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildScwemRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildScwemRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildScwemRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildScwemRCoreSql() { + return "SELECT a.ID AS id, " + + "a.STCD AS stcd, " + + "a.TM AS tm, " + + "a.FPNUM AS fpnum, " + + "a.WARTEM AS wartem, " + + "a.WTDIFF AS wtdiff, " + + "a.NH3N AS nh3n, " + + "a.NO2 AS no2, " + + "a.DOX AS dox, " + + "a.PH AS ph, " + + "a.RECORDER AS recorder, " + + "a.FID AS fid, " + + "a.RECORD_USER AS recordUser, " + + "a.RECORD_TIME AS recordTime, " + + "a.MODIFY_USER AS modifyUser, " + + "a.MODIFY_TIME AS modifyTime, " + + "a.IS_DELETED AS isDeleted, " + + "a.DELETE_USER AS deleteUser, " + + "a.DELETE_TIME AS deleteTime, " + + "b.NAME AS fpName " + + "FROM SD_FBSCWEM_R a " + + "LEFT JOIN SD_FBFISHPOND_B b ON a.FPNUM = b.ID AND NVL(b.IS_DELETED, 0) = 0 " + + "WHERE NVL(a.IS_DELETED, 0) = 0"; + } + + private String buildScwemRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "tm", "fpnum", "fpName", "wartem", "wtdiff", + "nh3n", "no2", "dox", "ph", "recorder", "fid", + "recordUser", "recordTime", "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapScwemRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildScwemRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildScwemRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildScwemRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildScwemRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapScwemRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeScwemRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "scp" + indexHolder[0]++; + paramMap.put(key, normalizeScwemRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "scp" + indexHolder[0]++; + Object normalizedValue = normalizeScwemRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildScwemROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapScwemRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildScwemRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapScwemRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapScwemRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "tm" -> "t.tm"; + case "fpnum" -> "t.fpnum"; + case "fpName" -> "t.fpName"; + case "wartem" -> "t.wartem"; + case "wtdiff" -> "t.wtdiff"; + case "nh3n" -> "t.nh3n"; + case "no2" -> "t.no2"; + case "dox" -> "t.dox"; + case "ph" -> "t.ph"; + case "recorder" -> "t.recorder"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeScwemRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeScwemRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 鱼苗和鱼种培育记录 (SD_FBFISHBREED_R) ==================== + + @Override + public DataSourceResult getFishbreedRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryFishbreedRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryFishbreedRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryFishbreedRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFishbreedRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFishbreedRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishbreedRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFishbreedROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbFishbreedRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryFishbreedRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildFishbreedRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishbreedRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildFishbreedRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildFishbreedRCoreSql() { + return "SELECT ID AS id, " + + "STCD AS stcd, " + + "TM AS tm, " + + "FARMNO AS farmno, " + + "FTP AS ftp, " + + "COUNTS AS counts, " + + "LENGTH AS length, " + + "WEIGHT AS weight, " + + "FEEDTP AS feedtp, " + + "FOODINT AS foodint, " + + "DFR AS dfr, " + + "FIN AS fin, " + + "RECORDER AS recorder, " + + "FID AS fid, " + + "RECORD_USER AS recordUser, " + + "RECORD_TIME AS recordTime, " + + "MODIFY_USER AS modifyUser, " + + "MODIFY_TIME AS modifyTime, " + + "IS_DELETED AS isDeleted, " + + "DELETE_USER AS deleteUser, " + + "DELETE_TIME AS deleteTime " + + "FROM SD_FBFISHBREED_R " + + "WHERE NVL(IS_DELETED, 0) = 0"; + } + + private String buildFishbreedRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "tm", "farmno", "ftp", "counts", "length", "weight", + "feedtp", "foodint", "dfr", "fin", "recorder", "fid", + "recordUser", "recordTime", "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapFishbreedRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildFishbreedRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildFishbreedRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFishbreedRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildFishbreedRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapFishbreedRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeFishbreedRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "fbp" + indexHolder[0]++; + paramMap.put(key, normalizeFishbreedRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "fbp" + indexHolder[0]++; + Object normalizedValue = normalizeFishbreedRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildFishbreedROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapFishbreedRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildFishbreedRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapFishbreedRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapFishbreedRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "tm" -> "t.tm"; + case "farmno" -> "t.farmno"; + case "ftp" -> "t.ftp"; + case "counts" -> "t.counts"; + case "length" -> "t.length"; + case "weight" -> "t.weight"; + case "feedtp" -> "t.feedtp"; + case "foodint" -> "t.foodint"; + case "dfr" -> "t.dfr"; + case "fin" -> "t.fin"; + case "recorder" -> "t.recorder"; + case "fid" -> "t.fid"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeFishbreedRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeFishbreedRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } + + // ==================== 鱼病防治记录 (SD_FBFISHDPAC_R) ==================== + + @Override + public DataSourceResult getFishdpacRList(DataSourceRequest dataSourceRequest) { + DataSourceLoadOptionsBase loadOptions = dataSourceRequest == null ? null : dataSourceRequest.toDevRequest(); + GroupingInfo[] groups = loadOptions == null ? null : loadOptions.getGroup(); + if (groups != null && groups.length > 0) { + return queryFishdpacRGroupList(dataSourceRequest, loadOptions, groups); + } + return queryFishdpacRDetailList(dataSourceRequest, loadOptions); + } + + private DataSourceResult queryFishdpacRDetailList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ") + .append(buildFishdpacRDetailSelectSql(dataSourceRequest == null ? null : dataSourceRequest.getSelect())) + .append(" FROM (") + .append(buildFishdpacRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishdpacRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + sql.append(buildFishdpacROrderBySql(dataSourceRequest == null ? null : dataSourceRequest.getSort())); + Page page = loadOptions == null ? null : QgcQueryWrapperUtil.buildPage(loadOptions, loadOptions.getSkip(), loadOptions.getTake()); + List list = microservicDynamicSQLMapper.pageAllListWithResultType( + page, sql.toString(), paramMap, FbFishdpacRVo.class); + DataSourceResult result = new DataSourceResult<>(); + result.setData(list); + result.setTotal(page != null ? page.getTotal() : list.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private DataSourceResult queryFishdpacRGroupList(DataSourceRequest dataSourceRequest, + DataSourceLoadOptionsBase loadOptions, + GroupingInfo[] groupInfos) { + Map paramMap = new HashMap<>(); + StringBuilder sql = new StringBuilder(); + sql.append("SELECT * FROM (") + .append(buildFishdpacRCoreSql()) + .append(") t WHERE 1 = 1 "); + String filterSql = buildFishdpacRFilterCondition( + dataSourceRequest == null ? null : dataSourceRequest.getFilter(), + paramMap, new int[]{0}); + if (StrUtil.isNotBlank(filterSql)) { + sql.append(" AND ").append(filterSql).append(" "); + } + String groupedSql = buildFishdpacRGroupSql(sql.toString(), + dataSourceRequest == null ? null : dataSourceRequest.getGroup()); + List> rows = microservicDynamicSQLMapper.pageAllList( + null, groupedSql, paramMap); + DataSourceResult result = new DataSourceResult<>(); + if (Boolean.TRUE.equals(dataSourceRequest == null ? false : dataSourceRequest.getGroupResultFlat())) { + result.setData((List) (List) new GroupHelper().faltGroup( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } else { + result.setData((List) (List) new GroupHelper().group( + rows, groupInfos == null ? Collections.emptyList() : Arrays.asList(groupInfos))); + } + result.setTotal(rows.size()); + result.setAggregates(new HashMap<>()); + return result; + } + + private String buildFishdpacRCoreSql() { + return "SELECT ID AS id, " + + "STCD AS stcd, " + + "FPNUM AS fpnum, " + + "FTP AS ftp, " + + "WEIGHT AS weight, " + + "SYMPTOMS AS symptoms, " + + "FISHDRUGTYPE AS fishdrugtype, " + + "METHOD AS method, " + + "DOSE AS dose, " + + "DAYS AS days, " + + "TREATEFFECT AS treateffect, " + + "RECORDER AS recorder, " + + "TM AS tm, " + + "FID AS fid, " + + "REMARK AS remark, " + + "RECORD_USER AS recordUser, " + + "RECORD_TIME AS recordTime, " + + "MODIFY_USER AS modifyUser, " + + "MODIFY_TIME AS modifyTime, " + + "IS_DELETED AS isDeleted, " + + "DELETE_USER AS deleteUser, " + + "DELETE_TIME AS deleteTime " + + "FROM SD_FBFISHDPAC_R " + + "WHERE NVL(IS_DELETED, 0) = 0"; + } + + private String buildFishdpacRDetailSelectSql(List select) { + List fields = CollUtil.isEmpty(select) + ? List.of("id", "stcd", "fpnum", "ftp", "weight", "symptoms", + "fishdrugtype", "method", "dose", "days", "treateffect", "recorder", "tm", "fid", "remark", + "recordUser", "recordTime", "modifyUser", "modifyTime", "isDeleted", "deleteUser", "deleteTime") + : select; + List selectParts = new ArrayList<>(); + for (String field : fields) { + String column = mapFishdpacRColumn(field); + if (StrUtil.isNotBlank(column)) { + selectParts.add(column + " AS " + field); + } + } + if (selectParts.isEmpty()) { + selectParts.add("t.id AS id"); + } + return String.join(", ", selectParts); + } + + private String buildFishdpacRFilterCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + if (filter == null) return ""; + if (StrUtil.isNotBlank(filter.getField())) return buildFishdpacRLeafCondition(filter, paramMap, indexHolder); + if (CollUtil.isEmpty(filter.getFilters())) return ""; + List conditions = new ArrayList<>(); + for (DataSourceRequest.FilterDescriptor child : filter.getFilters()) { + String childSql = buildFishdpacRFilterCondition(child, paramMap, indexHolder); + if (StrUtil.isNotBlank(childSql)) conditions.add("(" + childSql + ")"); + } + if (conditions.isEmpty()) return ""; + return String.join("or".equalsIgnoreCase(filter.getLogic()) ? " OR " : " AND ", conditions); + } + + private String buildFishdpacRLeafCondition(DataSourceRequest.FilterDescriptor filter, + Map paramMap, + int[] indexHolder) { + String column = mapFishdpacRColumn(filter.getField()); + if (StrUtil.isBlank(column)) return ""; + String operator = StrUtil.blankToDefault(filter.getOperator(), "eq").toLowerCase(); + Object value = filter.getValue(); + if ("isnull".equals(operator)) return column + " IS NULL"; + if ("isnotnull".equals(operator)) return column + " IS NOT NULL"; + if ("in".equals(operator)) { + List values = normalizeFishdpacRValues(value); + if (values.isEmpty()) return ""; + List placeholders = new ArrayList<>(); + for (Object item : values) { + String key = "fdpP" + indexHolder[0]++; + paramMap.put(key, normalizeFishdpacRValue(filter.getField(), item)); + placeholders.add("#{map." + key + "}"); + } + return column + " IN (" + String.join(", ", placeholders) + ")"; + } + if (value == null) return ""; + String key = "fdpP" + indexHolder[0]++; + Object normalizedValue = normalizeFishdpacRValue(filter.getField(), value); + return switch (operator) { + case "eq" -> { paramMap.put(key, normalizedValue); yield column + " = #{map." + key + "}"; } + case "neq" -> { paramMap.put(key, normalizedValue); yield column + " <> #{map." + key + "}"; } + case "gt" -> { paramMap.put(key, normalizedValue); yield column + " > #{map." + key + "}"; } + case "gte" -> { paramMap.put(key, normalizedValue); yield column + " >= #{map." + key + "}"; } + case "lt" -> { paramMap.put(key, normalizedValue); yield column + " < #{map." + key + "}"; } + case "lte" -> { paramMap.put(key, normalizedValue); yield column + " <= #{map." + key + "}"; } + case "contains" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "doesnotcontain" -> { paramMap.put(key, "%" + normalizedValue + "%"); yield column + " NOT LIKE #{map." + key + "}"; } + case "startswith" -> { paramMap.put(key, normalizedValue + "%"); yield column + " LIKE #{map." + key + "}"; } + case "endswith" -> { paramMap.put(key, "%" + normalizedValue); yield column + " LIKE #{map." + key + "}"; } + default -> ""; + }; + } + + private String buildFishdpacROrderBySql(List sortDescriptors) { + if (CollUtil.isEmpty(sortDescriptors)) return " ORDER BY t.tm DESC"; + List parts = new ArrayList<>(); + for (DataSourceRequest.SortDescriptor descriptor : sortDescriptors) { + String column = mapFishdpacRColumn(descriptor.getField()); + if (StrUtil.isBlank(column)) continue; + parts.add(column + ("desc".equalsIgnoreCase(descriptor.getDir()) ? " DESC" : " ASC")); + } + if (parts.isEmpty()) return " ORDER BY t.tm DESC"; + return " ORDER BY " + String.join(", ", parts); + } + + private String buildFishdpacRGroupSql(String baseSql, List groups) { + if (CollUtil.isEmpty(groups)) return baseSql; + List validGroups = groups.stream() + .filter(group -> StrUtil.isNotBlank(mapFishdpacRColumn(group.getField()))).toList(); + if (validGroups.isEmpty()) return baseSql; + List groupFields = validGroups.stream().map(DataSourceRequest.GroupDescriptor::getField).toList(); + StringBuilder sql = new StringBuilder("SELECT "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)).append(" AS ").append(groupFields.get(i)); + } + sql.append(", COUNT(1) AS \"aggregates.count\" FROM (").append(baseSql).append(") t GROUP BY "); + for (int i = 0; i < groupFields.size(); i++) { + if (i > 0) sql.append(", "); + sql.append("t.").append(groupFields.get(i)); + } + return sql.toString(); + } + + private String mapFishdpacRColumn(String field) { + return switch (field) { + case "id" -> "t.id"; + case "stcd" -> "t.stcd"; + case "fpnum" -> "t.fpnum"; + case "ftp" -> "t.ftp"; + case "weight" -> "t.weight"; + case "symptoms" -> "t.symptoms"; + case "fishdrugtype" -> "t.fishdrugtype"; + case "method" -> "t.method"; + case "dose" -> "t.dose"; + case "days" -> "t.days"; + case "treateffect" -> "t.treateffect"; + case "recorder" -> "t.recorder"; + case "tm" -> "t.tm"; + case "fid" -> "t.fid"; + case "remark" -> "t.remark"; + case "recordUser" -> "t.recordUser"; + case "recordTime" -> "t.recordTime"; + case "modifyUser" -> "t.modifyUser"; + case "modifyTime" -> "t.modifyTime"; + case "isDeleted" -> "t.isDeleted"; + case "deleteUser" -> "t.deleteUser"; + case "deleteTime" -> "t.deleteTime"; + default -> ""; + }; + } + + private List normalizeFishdpacRValues(Object value) { + List values = new ArrayList<>(); + if (value == null) return values; + if (value instanceof Collection collection) { values.addAll(collection); return values; } + if (value instanceof Object[] array) { values.addAll(Arrays.asList(array)); return values; } + String text = String.valueOf(value).trim(); + if (text.startsWith("[") && text.endsWith("]")) text = text.substring(1, text.length() - 1); + if (text.contains(",")) { + for (String item : text.split(",")) { + String trimmed = StrUtil.trim(item); + if (StrUtil.isNotBlank(trimmed)) values.add(trimmed); + } + return values; + } + values.add(value); + return values; + } + + private Object normalizeFishdpacRValue(String field, Object value) { + if (value == null) return null; + if (Set.of("tm", "recordTime", "modifyTime", "deleteTime").contains(field) && value instanceof String text) { + return parseDateTimeSafely(text); + } + return value; + } } diff --git a/backend/src/main/java/com/yfd/platform/system/controller/DictCacheManageController.java b/backend/src/main/java/com/yfd/platform/system/controller/DictCacheManageController.java new file mode 100644 index 00000000..5b395005 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/system/controller/DictCacheManageController.java @@ -0,0 +1,89 @@ +package com.yfd.platform.system.controller; + +import cn.hutool.core.util.StrUtil; +import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.utils.DictCodeToNameConverter; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 字典缓存管理接口 + * + *

提供手动刷新字典缓存的功能,用于紧急情况或运维场景。 + * + *

使用方式

+ *
    + *
  • 清除全部缓存:POST /dict/cache/clearAll
  • + *
  • 清除指定静态字典缓存:POST /dict/cache/clearStatic?dictCode=xxx
  • + *
  • 清除指定动态字典缓存:POST /dict/cache/clearDynamic?tableName=xxx&codeColumn=xxx&nameColumn=xxx
  • + *
  • 查看缓存统计:GET /dict/cache/stats
  • + *
+ */ +@RestController +@RequestMapping("/dict/cache") +@Tag(name = "字典缓存管理") +public class DictCacheManageController { + + @Resource + private DictCodeToNameConverter dictCodeToNameConverter; + + /** + * 清除所有字典缓存 + */ + @PostMapping("/clearAll") + @Operation(summary = "清除所有字典缓存") + public ResponseResult clearAllCache() { + dictCodeToNameConverter.clearCache(); + return ResponseResult.success("所有字典缓存已清除"); + } + + /** + * 清除指定静态字典缓存 + * + * @param dictCode 字典编码 + */ + @PostMapping("/clearStatic") + @Operation(summary = "清除指定静态字典缓存") + public ResponseResult clearStaticCache(@RequestParam String dictCode) { + if (StrUtil.isBlank(dictCode)) { + return ResponseResult.error("dictCode 不能为空"); + } + dictCodeToNameConverter.clearStaticDictCache(dictCode); + return ResponseResult.success("静态字典缓存已清除: " + dictCode); + } + + /** + * 清除指定动态字典缓存 + * + * @param tableName 数据库表名 + * @param codeColumn 编码列名 + * @param nameColumn 名称列名 + * @param filter 额外过滤条件(可选) + */ + @PostMapping("/clearDynamic") + @Operation(summary = "清除指定动态字典缓存") + public ResponseResult clearDynamicCache(@RequestParam String tableName, + @RequestParam String codeColumn, + @RequestParam String nameColumn, + @RequestParam(required = false) String filter) { + if (StrUtil.isBlank(tableName) || StrUtil.isBlank(codeColumn) || StrUtil.isBlank(nameColumn)) { + return ResponseResult.error("tableName、codeColumn、nameColumn 不能为空"); + } + dictCodeToNameConverter.clearDynamicDictCache(tableName, codeColumn, nameColumn, filter); + return ResponseResult.success("动态字典缓存已清除: " + tableName); + } + + /** + * 查看缓存统计信息 + */ + @GetMapping("/stats") + @Operation(summary = "查看字典缓存统计信息") + public ResponseResult getCacheStats() { + Map stats = dictCodeToNameConverter.getCacheStats(); + return ResponseResult.successData(stats); + } +} diff --git a/backend/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java b/backend/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java index b91f785c..e6c688a3 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/SysDictionaryController.java @@ -8,8 +8,10 @@ import com.yfd.platform.system.domain.SysDictionaryItems; import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; import com.yfd.platform.system.service.ISysDictionaryService; import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.utils.DictCacheInvalidateEvent; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; @@ -36,6 +38,9 @@ public class SysDictionaryController { @Resource private ISysDictionaryItemsService sysDictionaryItemsService; + @Resource + private ApplicationEventPublisher eventPublisher; + /********************************** * 用途说明: 获取数据字典列表 * 参数说明 dictType 字典类型 @@ -61,8 +66,15 @@ public class SysDictionaryController { @PostMapping("/deleteById") @Operation(summary = "根据ID删除字典") public ResponseResult deleteDictById(@RequestParam String id) { + // 删除前获取 dictCode,用于后续清除缓存 + SysDictionary dict = sysDictionaryService.getById(id); + String dictCode = dict != null ? dict.getDictCode() : null; boolean ok = sysDictionaryService.deleteDictById(id); if (ok) { + // 发布静态字典缓存失效事件 + if (StrUtil.isNotBlank(dictCode)) { + eventPublisher.publishEvent(new DictCacheInvalidateEvent(this, dictCode)); + } return ResponseResult.success(); } else { return ResponseResult.error(); diff --git a/backend/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java b/backend/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java index bf85d306..80627cd4 100644 --- a/backend/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java +++ b/backend/src/main/java/com/yfd/platform/system/controller/SysDictionaryItemsController.java @@ -5,11 +5,15 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.yfd.platform.annotation.Log; import com.yfd.platform.config.ResponseResult; +import com.yfd.platform.system.domain.SysDictionary; import com.yfd.platform.system.domain.SysDictionaryItems; import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; import com.yfd.platform.system.service.ISysDictionaryItemsService; +import com.yfd.platform.system.service.ISysDictionaryService; +import com.yfd.platform.utils.DictCacheInvalidateEvent; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.bind.annotation.*; import jakarta.annotation.Resource; @@ -33,6 +37,31 @@ public class SysDictionaryItemsController { @Resource private ISysDictionaryItemsService sysDictionaryItemsService; + @Resource + private ISysDictionaryService sysDictionaryService; + + @Resource + private ApplicationEventPublisher eventPublisher; + + /** + * 根据 dictId 查询 dictCode,用于发布缓存失效事件 + */ + private String getDictCodeByDictId(String dictId) { + if (StrUtil.isBlank(dictId)) return null; + SysDictionary dict = sysDictionaryService.getById(dictId); + return dict != null ? dict.getDictCode() : null; + } + + /** + * 发布静态字典缓存失效事件 + */ + private void invalidateStaticCache(String dictId) { + String dictCode = getDictCodeByDictId(dictId); + if (StrUtil.isNotBlank(dictCode)) { + eventPublisher.publishEvent(new DictCacheInvalidateEvent(this, dictCode)); + } + } + /********************************** * 用途说明: 分页查询字典项信息 * 参数说明 dictID 字典ID ItemName 字典项名称 pageNum 当前页 @@ -77,6 +106,8 @@ public class SysDictionaryItemsController { boolean ok = sysDictionaryItemsService.addDictionaryItem(sysDictionaryItems); if (ok) { + // 发布静态字典缓存失效事件 + invalidateStaticCache(sysDictionaryItems.getDictId()); return ResponseResult.success(); } else { return ResponseResult.error(); @@ -98,6 +129,8 @@ public class SysDictionaryItemsController { boolean ok = sysDictionaryItemsService.updateById(sysDictionaryItems); if (ok) { + // 发布静态字典缓存失效事件 + invalidateStaticCache(sysDictionaryItems.getDictId()); return ResponseResult.success(); } else { return ResponseResult.error(); @@ -132,8 +165,13 @@ public class SysDictionaryItemsController { if (StrUtil.isBlank(id)) { return ResponseResult.error("参数为空"); } + // 删除前获取 dictId,用于清除缓存 + SysDictionaryItems item = sysDictionaryItemsService.getById(id); + String dictId = item != null ? item.getDictId() : null; boolean ok = sysDictionaryItemsService.removeById(id); if (ok) { + // 发布静态字典缓存失效事件 + invalidateStaticCache(dictId); return ResponseResult.success(); } else { return ResponseResult.error(); @@ -155,8 +193,18 @@ public class SysDictionaryItemsController { String[] splitIds = id.split(","); // 数组转集合 List ids = Arrays.asList(splitIds); + // 批量删除前获取所有相关的 dictId,去重后逐个清除缓存 + List items = sysDictionaryItemsService.listByIds(ids); boolean ok = sysDictionaryItemsService.removeByIds(ids); if (ok) { + // 发布静态字典缓存失效事件 + if (items != null) { + items.stream() + .map(SysDictionaryItems::getDictId) + .filter(StrUtil::isNotBlank) + .distinct() + .forEach(dictId -> invalidateStaticCache(dictId)); + } return ResponseResult.success(); } else { return ResponseResult.error(); diff --git a/backend/src/main/java/com/yfd/platform/utils/DictCacheHelper.java b/backend/src/main/java/com/yfd/platform/utils/DictCacheHelper.java new file mode 100644 index 00000000..210ca405 --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/utils/DictCacheHelper.java @@ -0,0 +1,29 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.util.StrUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class DictCacheHelper { + private final ApplicationEventPublisher publisher; + + public DictCacheHelper(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } + + // 统一的Key生成器(唯一维护点) + public String buildDynamicKey(String tableName, String codeColumn, String nameColumn, String filter) { + return tableName + "|" + codeColumn + "|" + nameColumn + "|" + StrUtil.nullToDefault(filter, ""); + } + + // 核心:触发清理事件(参数与加载方法完全一致) + public void evictDynamic(String tableName, String codeColumn, String nameColumn, String filter) { + String cacheKey = buildDynamicKey(tableName, codeColumn, nameColumn, filter); + // (携带cacheKey) + publisher.publishEvent(new DictCacheInvalidateEvent(this, "DYNAMIC", cacheKey)); + log.info("已发布动态字典清理事件: table={}, key={}", tableName, cacheKey); + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/yfd/platform/utils/DictCacheInvalidateEvent.java b/backend/src/main/java/com/yfd/platform/utils/DictCacheInvalidateEvent.java new file mode 100644 index 00000000..f36c84ad --- /dev/null +++ b/backend/src/main/java/com/yfd/platform/utils/DictCacheInvalidateEvent.java @@ -0,0 +1,47 @@ +package com.yfd.platform.utils; + +import cn.hutool.core.util.StrUtil; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +/** + * 字典缓存失效事件 + * + *

当静态字典或动态字典数据发生变更时发布此事件,通知 + * {@link DictCodeToNameConverter} 清除对应缓存。 + */ +@Getter +public class DictCacheInvalidateEvent extends ApplicationEvent { + + private static final long serialVersionUID = 1L; + + /** 缓存类型:STATIC / DYNAMIC(仅供日志或监控使用) */ + private final String cacheType; + + /** 完整的缓存键(最重要,监听器直接用这个去 invalidate) */ + private final String cacheKey; + + // ========== 静态字典专用构造 ========== + public DictCacheInvalidateEvent(Object source, String dictCode) { + super(source); + this.cacheType = "STATIC"; + this.cacheKey = dictCode; // 静态字典的 cacheKey 就是 dictCode + } + + // ========== 动态字典专用构造 ========== + public DictCacheInvalidateEvent(Object source, String tableName, + String codeColumn, String nameColumn, String filter) { + super(source); + this.cacheType = "DYNAMIC"; + // ★ 关键:Key 在这里拼好,监听器直接拿 cacheKey 用,不再二次拼接 + this.cacheKey = tableName + "|" + codeColumn + "|" + nameColumn + "|" + + StrUtil.nullToDefault(filter, ""); + } + + // 如果外部已经拼好了 Key,也可以直接传入(更灵活) + public DictCacheInvalidateEvent(Object source, String cacheType, String cacheKey) { + super(source); + this.cacheType = cacheType; + this.cacheKey = cacheKey; + } +} diff --git a/backend/src/main/java/com/yfd/platform/utils/DictCodeToNameConverter.java b/backend/src/main/java/com/yfd/platform/utils/DictCodeToNameConverter.java index 363d4f92..3182834b 100644 --- a/backend/src/main/java/com/yfd/platform/utils/DictCodeToNameConverter.java +++ b/backend/src/main/java/com/yfd/platform/utils/DictCodeToNameConverter.java @@ -2,25 +2,29 @@ package com.yfd.platform.utils; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import com.yfd.platform.common.MicroservicDynamicSQLMapper; import com.yfd.platform.system.domain.SysDictionary; import com.yfd.platform.system.domain.SysDictionaryItems; import com.yfd.platform.system.mapper.SysDictionaryItemsMapper; import com.yfd.platform.system.mapper.SysDictionaryMapper; +import jakarta.annotation.PostConstruct; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * 字典代码转名称转换器 *

- * 支持静态字典(SYS_DICTIONARY / SYS_DICTIONARY_ITEMS)和动态字典(业务表查询), - * 将 VO 中的 code 字段值转换为对应的 name 字段值。 + * 使用 Caffeine Cache 存储字典映射,TTL=1小时自动过期(兜底), + * 并通过监听 {@link DictCacheInvalidateEvent} 在字典数据变更时即时清除缓存。 *

* *

使用方式

@@ -56,13 +60,17 @@ import java.util.stream.Collectors; * } * * - * @author Generated * @since 2025-05-18 */ @Slf4j @Component public class DictCodeToNameConverter { + /** 默认缓存 TTL:1 小时 */ + private static final long CACHE_TTL_HOURS = 1; + /** 最大缓存条目数 */ + private static final long MAX_CACHE_SIZE = 500; + @Resource private SysDictionaryMapper sysDictionaryMapper; @@ -73,14 +81,52 @@ public class DictCodeToNameConverter { private MicroservicDynamicSQLMapper microservicDynamicSQLMapper; /** - * 静态字典缓存:dictCode -> Map + * 静态字典缓存:dictCode -> Map<itemCode, dictName> + * TTL=1小时,最大 500 条目 */ - private final Map> staticDictCache = new ConcurrentHashMap<>(); + private Cache> staticDictCache; /** - * 动态字典缓存:缓存key -> Map + * 动态字典缓存:缓存key -> Map<code, name> + * TTL=1小时,最大 500 条目 */ - private final Map> dynamicDictCache = new ConcurrentHashMap<>(); + private Cache> dynamicDictCache; + + @PostConstruct + public void init() { + staticDictCache = Caffeine.newBuilder() + .expireAfterWrite(CACHE_TTL_HOURS, TimeUnit.HOURS) + .maximumSize(MAX_CACHE_SIZE) + .removalListener((key, value, cause) -> + log.debug("静态字典缓存失效: dictCode={}, cause={}", key, cause)) + .build(); + + dynamicDictCache = Caffeine.newBuilder() + .expireAfterWrite(CACHE_TTL_HOURS, TimeUnit.HOURS) + .maximumSize(MAX_CACHE_SIZE) + .removalListener((key, value, cause) -> + log.debug("动态字典缓存失效: key={}, cause={}", key, cause)) + .build(); + + log.info("DictCodeToNameConverter 初始化完成, TTL={}小时, maxSize={}", CACHE_TTL_HOURS, MAX_CACHE_SIZE); + } + + // ==================== 缓存失效事件监听 ==================== + + /** + * 监听字典缓存失效事件,即时清除对应缓存 + */ + @EventListener + public void onDictCacheInvalidate(DictCacheInvalidateEvent event) { + if ("STATIC".equalsIgnoreCase(event.getCacheType())) { + staticDictCache.invalidate(event.getCacheKey()); + } else if ("DYNAMIC".equalsIgnoreCase(event.getCacheType())) { + dynamicDictCache.invalidate(event.getCacheKey()); + } + log.info("字典缓存已清除: type={}, key={}", event.getCacheType(), event.getCacheKey()); + } + + // ==================== 对外转换方法 ==================== /** * 批量转换:根据元数据配置,将 VO 列表中的 code 字段转换为对应的 name 字段 @@ -108,17 +154,7 @@ public class DictCodeToNameConverter { } /** - * 分页结果转换:将 Page 中所有记录的 code 字段转换为对应的 name 字段 - * - *
-     * 使用示例:
-     * Page<SdHbrvDic> page = hbrvDicService.queryPageList(...);
-     * dictCodeToNameConverter.convertCodeToName(page, CODE_TO_NAME_META_LIST);
-     * 
- * - * @param page 分页对象 - * @param metadataList 元数据配置列表 - * @param VO 类型 + * 分页结果转换 */ public void convertCodeToName(Page page, List metadataList) { if (page == null || page.getRecords() == null || page.getRecords().isEmpty()) { @@ -128,17 +164,7 @@ public class DictCodeToNameConverter { } /** - * 单个对象转换:将单个对象的 code 字段转换为对应的 name 字段 - * - *
-     * 使用示例:
-     * SdHbrvDic hbrvDic = hbrvDicService.getById(hbrvcd);
-     * dictCodeToNameConverter.convertCodeToName(hbrvDic, CODE_TO_NAME_META_LIST);
-     * 
- * - * @param obj 单个对象 - * @param metadataList 元数据配置列表 - * @param VO 类型 + * 单个对象转换 */ public void convertCodeToName(T obj, List metadataList) { if (obj == null || metadataList == null || metadataList.isEmpty()) { @@ -147,14 +173,13 @@ public class DictCodeToNameConverter { convertCodeToName(Collections.singletonList(obj), metadataList); } + // ==================== 缓存管理方法 ==================== + /** - * 加载静态字典映射 - * - * @param dictCode 字典编码 - * @return Map + * 加载静态字典映射(从 Caffeine Cache 获取,缓存未命中则查库) */ public Map loadStaticDictMap(String dictCode) { - return staticDictCache.computeIfAbsent(dictCode, key -> { + return staticDictCache.get(dictCode, key -> { Map map = new LinkedHashMap<>(); try { SysDictionary dict = sysDictionaryMapper.selectOne( @@ -175,6 +200,7 @@ public class DictCodeToNameConverter { map.put(item.getItemCode(), StrUtil.nullToDefault(item.getDictName(), item.getItemCode())); } } + log.debug("静态字典已加载: dictCode={}, size={}", key, map.size()); } catch (Exception e) { log.error("加载静态字典失败: dictCode={}", key, e); } @@ -183,17 +209,11 @@ public class DictCodeToNameConverter { } /** - * 加载动态字典映射(从业务表查询) - * - * @param tableName 表名 - * @param codeColumn 编码列名 - * @param nameColumn 名称列名 - * @param filter 额外过滤条件 - * @return Map + * 加载动态字典映射(从 Caffeine Cache 获取,缓存未命中则查库) */ public Map loadDynamicDictMap(String tableName, String codeColumn, String nameColumn, String filter) { String cacheKey = tableName + "|" + codeColumn + "|" + nameColumn + "|" + StrUtil.nullToDefault(filter, ""); - return dynamicDictCache.computeIfAbsent(cacheKey, key -> { + return dynamicDictCache.get(cacheKey, key -> { Map map = new LinkedHashMap<>(); try { StringBuilder sql = new StringBuilder(); @@ -213,6 +233,7 @@ public class DictCodeToNameConverter { map.put(code, StrUtil.nullToDefault(name, code)); } } + log.debug("动态字典已加载: tableName={}, size={}", tableName, map.size()); } catch (Exception e) { log.error("加载动态字典失败: tableName={}", tableName, e); } @@ -220,6 +241,48 @@ public class DictCodeToNameConverter { }); } + /** + * 清除所有缓存 + */ + public void clearCache() { + staticDictCache.invalidateAll(); + dynamicDictCache.invalidateAll(); + log.info("所有字典缓存已清除"); + } + + /** + * 清除指定静态字典缓存 + */ + public void clearStaticDictCache(String dictCode) { + if (StrUtil.isNotBlank(dictCode)) { + staticDictCache.invalidate(dictCode); + log.info("静态字典缓存已清除: dictCode={}", dictCode); + } + } + + /** + * 清除指定动态字典缓存 + */ + public void clearDynamicDictCache(String tableName, String codeColumn, String nameColumn, String filter) { + String cacheKey = tableName + "|" + codeColumn + "|" + nameColumn + "|" + StrUtil.nullToDefault(filter, ""); + dynamicDictCache.invalidate(cacheKey); + log.info("动态字典缓存已清除: key={}", cacheKey); + } + + /** + * 获取缓存统计信息 + */ + public Map getCacheStats() { + Map stats = new LinkedHashMap<>(); + stats.put("staticDictSize", staticDictCache.estimatedSize()); + stats.put("dynamicDictSize", dynamicDictCache.estimatedSize()); + stats.put("ttlHours", CACHE_TTL_HOURS); + stats.put("maxSize", MAX_CACHE_SIZE); + return stats; + } + + // ==================== 私有方法 ==================== + /** * 根据元数据加载 code->name 映射 */ @@ -231,7 +294,7 @@ public class DictCodeToNameConverter { metadata.getDictSource(), metadata.getCodeColumn(), metadata.getNameColumn(), - metadata.getFilter() + null ); } log.warn("未知字典类型: dictType={}", metadata.getDictType()); @@ -251,7 +314,6 @@ public class DictCodeToNameConverter { String nameValue; if (metadata.isMulti()) { - // 多选:逗号分隔的多个code nameValue = Arrays.stream(codeValue.split(",")) .map(String::trim) .filter(StrUtil::isNotBlank) @@ -267,40 +329,12 @@ public class DictCodeToNameConverter { } } - /** - * 清除所有缓存 - */ - public void clearCache() { - staticDictCache.clear(); - dynamicDictCache.clear(); - } - - /** - * 清除指定静态字典缓存 - */ - public void clearStaticDictCache(String dictCode) { - staticDictCache.remove(dictCode); - } - - /** - * 清除指定动态字典缓存 - */ - public void clearDynamicDictCache(String tableName, String codeColumn, String nameColumn, String filter) { - String cacheKey = tableName + "|" + codeColumn + "|" + nameColumn + "|" + StrUtil.nullToDefault(filter, ""); - dynamicDictCache.remove(cacheKey); - } - // ==================== 反射工具方法 ==================== - /** - * 通过反射获取对象的字段值(转为字符串),支持驼峰命名 - */ private String getFieldValue(T obj, String fieldName) { try { Field field = findField(obj.getClass(), fieldName); - if (field == null) { - return null; - } + if (field == null) return null; field.setAccessible(true); Object value = field.get(obj); return value != null ? value.toString() : null; @@ -309,82 +343,51 @@ public class DictCodeToNameConverter { } } - /** - * 通过反射设置对象的字段值,支持驼峰命名 - */ private void setFieldValue(T obj, String fieldName, String value) { try { Field field = findField(obj.getClass(), fieldName); - if (field == null) { - return; - } + if (field == null) return; field.setAccessible(true); field.set(obj, value); } catch (Exception ignored) { - // 忽略设置失败 } } - /** - * 查找字段(忽略大小写,支持驼峰和下划线变体),递归查找父类 - */ private Field findField(Class clazz, String fieldName) { - if (clazz == null || clazz == Object.class) { - return null; - } - // 先精确匹配 + if (clazz == null || clazz == Object.class) return null; for (Field field : clazz.getDeclaredFields()) { - if (field.getName().equals(fieldName)) { - return field; - } + if (field.getName().equals(fieldName)) return field; } - // 忽略大小写匹配 for (Field field : clazz.getDeclaredFields()) { - if (field.getName().equalsIgnoreCase(fieldName)) { - return field; - } + if (field.getName().equalsIgnoreCase(fieldName)) return field; } - // 驼峰-下划线互转匹配 String camelName = underlineToCamel(fieldName); String underlineName = camelToUnderline(fieldName); for (Field field : clazz.getDeclaredFields()) { String fName = field.getName(); - if (fName.equalsIgnoreCase(camelName) || fName.equalsIgnoreCase(underlineName)) { - return field; - } + if (fName.equalsIgnoreCase(camelName) || fName.equalsIgnoreCase(underlineName)) return field; } return findField(clazz.getSuperclass(), fieldName); } private String underlineToCamel(String str) { - if (StrUtil.isBlank(str) || !str.contains("_")) { - return str; - } + if (StrUtil.isBlank(str) || !str.contains("_")) return str; StringBuilder sb = new StringBuilder(); boolean upper = false; for (char c : str.toCharArray()) { - if (c == '_') { - upper = true; - } else { - sb.append(upper ? Character.toUpperCase(c) : c); - upper = false; - } + if (c == '_') { upper = true; } + else { sb.append(upper ? Character.toUpperCase(c) : c); upper = false; } } return sb.toString(); } private String camelToUnderline(String str) { - if (StrUtil.isBlank(str)) { - return str; - } + if (StrUtil.isBlank(str)) return str; StringBuilder sb = new StringBuilder(); for (int i = 0; i < str.length(); i++) { char c = str.charAt(i); - if (Character.isUpperCase(c)) { - sb.append('_').append(Character.toLowerCase(c)); - } else { - sb.append(c); - } + if (Character.isUpperCase(c)) sb.append('_').append(Character.toLowerCase(c)); + else sb.append(c); } return sb.toString(); } diff --git a/backend/src/main/resources/mapper/qgc_base/SdFpssrlAiRMapper.xml b/backend/src/main/resources/mapper/qgc_base/SdFpssrlAiRMapper.xml new file mode 100644 index 00000000..73c4e47b --- /dev/null +++ b/backend/src/main/resources/mapper/qgc_base/SdFpssrlAiRMapper.xml @@ -0,0 +1,83 @@ + + + + + + + MERGE INTO SD_FPSSRL_AI_R T + USING ( + + SELECT + #{obj.stcd, jdbcType=VARCHAR} AS STCD, + #{obj.tm, jdbcType=TIMESTAMP} AS TM, + #{obj.ftp, jdbcType=VARCHAR} AS FTP, + #{obj.fcnt, jdbcType=INTEGER} AS FCNT, + #{obj.fsz, jdbcType=VARCHAR} AS FSZ, + #{obj.length, jdbcType=DECIMAL} AS LENGTH, + #{obj.width, jdbcType=DECIMAL} AS WIDTH, + #{obj.fishspeed, jdbcType=VARCHAR} AS FISHSPEED, + #{obj.direction, jdbcType=INTEGER} AS DIRECTION, + #{obj.fishposition, jdbcType=DECIMAL} AS FISHPOSITION, + #{obj.firstImgUrl, jdbcType=VARCHAR} AS FIRSTIMGURL, + #{obj.secondImgUrl, jdbcType=VARCHAR} AS SECONDIMGURL, + #{obj.videoUrl, jdbcType=VARCHAR} AS VIDEOURL, + #{obj.temperature, jdbcType=DECIMAL} AS TEMPERATURE, + #{obj.waterlevel, jdbcType=DECIMAL} AS WATERLEVEL, + #{obj.speed, jdbcType=DECIMAL} AS SPEED, + #{obj.q, jdbcType=DECIMAL} AS Q, + #{obj.dox, jdbcType=DECIMAL} AS DOX, + #{obj.tu, jdbcType=INTEGER} AS TU, + #{obj.channelno, jdbcType=VARCHAR} AS CHANNELNO, + #{obj.aiBoxCode, jdbcType=VARCHAR} AS AI_BOX_CODE, + #{obj.fid, jdbcType=VARCHAR} AS FID, + #{obj.remark, jdbcType=VARCHAR} AS REMARK, + #{obj.recordUser, jdbcType=VARCHAR} AS RECORD_USER, + #{obj.modifyUser, jdbcType=VARCHAR} AS MODIFY_USER + FROM DUAL + + ) S + ON (T.STCD = S.STCD AND T.TM = S.TM AND T.FTP = S.FTP + AND T.AI_BOX_CODE = S.AI_BOX_CODE AND T.IS_DELETED = 0) + WHEN MATCHED THEN + UPDATE SET + T.FCNT = S.FCNT, + T.FSZ = S.FSZ, + T.LENGTH = S.LENGTH, + T.WIDTH = S.WIDTH, + T.FISHSPEED = S.FISHSPEED, + T.DIRECTION = S.DIRECTION, + T.FISHPOSITION = S.FISHPOSITION, + T.FIRSTIMGURL = S.FIRSTIMGURL, + T.SECONDIMGURL = S.SECONDIMGURL, + T.VIDEOURL = S.VIDEOURL, + T.TEMPERATURE = S.TEMPERATURE, + T.WATERLEVEL = S.WATERLEVEL, + T.SPEED = S.SPEED, + T.Q = S.Q, + T.DOX = S.DOX, + T.TU = S.TU, + T.CHANNELNO = S.CHANNELNO, + T.FID = S.FID, + T.REMARK = S.REMARK, + T.MODIFY_USER = S.MODIFY_USER, + T.MODIFY_TIME = SYSDATE + WHEN NOT MATCHED THEN + INSERT ( + ID, STCD, TM, FTP, FCNT, FSZ, LENGTH, WIDTH, FISHSPEED, + DIRECTION, FISHPOSITION, FIRSTIMGURL, SECONDIMGURL, VIDEOURL, + TEMPERATURE, WATERLEVEL, SPEED, Q, DOX, TU, CHANNELNO, AI_BOX_CODE, + FID, REMARK, RECORD_USER, RECORD_TIME, MODIFY_USER, MODIFY_TIME, + IS_DELETED + ) VALUES ( + SYS_GUID(), + S.STCD, S.TM, S.FTP, S.FCNT, S.FSZ, S.LENGTH, S.WIDTH, S.FISHSPEED, + S.DIRECTION, S.FISHPOSITION, S.FIRSTIMGURL, S.SECONDIMGURL, S.VIDEOURL, + S.TEMPERATURE, S.WATERLEVEL, S.SPEED, S.Q, S.DOX, S.TU, S.CHANNELNO, + NVL(S.AI_BOX_CODE, '未知'), + S.FID, S.REMARK, S.RECORD_USER, SYSDATE, S.MODIFY_USER, SYSDATE, + 0 + ) + + +