作者 钟来

批量添加新增id返回

水产时价信息网的鱼价信息采集
1 package com.zhonglai.luhui.datasource.config; 1 package com.zhonglai.luhui.datasource.config;
2 2
3 import com.ruoyi.common.utils.StringUtils; 3 import com.ruoyi.common.utils.StringUtils;
  4 +import com.zhonglai.luhui.datasource.plugin.GeneratedKeyInterceptor;
4 import org.apache.ibatis.io.VFS; 5 import org.apache.ibatis.io.VFS;
  6 +import org.apache.ibatis.plugin.Interceptor;
5 import org.apache.ibatis.session.SqlSessionFactory; 7 import org.apache.ibatis.session.SqlSessionFactory;
6 import org.mybatis.spring.SqlSessionFactoryBean; 8 import org.mybatis.spring.SqlSessionFactoryBean;
  9 +import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
7 import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; 10 import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
8 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.beans.factory.annotation.Autowired;
  12 +import org.springframework.boot.ApplicationRunner;
9 import org.springframework.context.annotation.Bean; 13 import org.springframework.context.annotation.Bean;
10 import org.springframework.context.annotation.Configuration; 14 import org.springframework.context.annotation.Configuration;
11 import org.springframework.core.env.Environment; 15 import org.springframework.core.env.Environment;
@@ -131,6 +135,9 @@ public class MyBatisConfig @@ -131,6 +135,9 @@ public class MyBatisConfig
131 sessionFactory.setTypeAliasesPackage(typeAliasesPackage); 135 sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
132 sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); 136 sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
133 sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); 137 sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
  138 + sessionFactory.setPlugins(new Interceptor[]{
  139 + new GeneratedKeyInterceptor()
  140 + });
134 141
135 //创建一个MapperHelper 142 //创建一个MapperHelper
136 MapperHelper mapperHelper = new MapperHelper(); 143 MapperHelper mapperHelper = new MapperHelper();
@@ -160,4 +167,15 @@ public class MyBatisConfig @@ -160,4 +167,15 @@ public class MyBatisConfig
160 167
161 return sessionFactory.getObject(); 168 return sessionFactory.getObject();
162 } 169 }
  170 +
  171 + @Bean
  172 + public Interceptor generatedKeyInterceptor() {
  173 + return new GeneratedKeyInterceptor();
  174 + }
  175 +
  176 + @Bean
  177 + public ConfigurationCustomizer mybatisConfigurationCustomizer(Interceptor generatedKeyInterceptor) {
  178 + return configuration -> configuration.addInterceptor(generatedKeyInterceptor);
  179 + }
  180 +
163 } 181 }
  1 +package com.zhonglai.luhui.datasource.plugin;
  2 +
  3 +import org.apache.ibatis.executor.Executor;
  4 +import org.apache.ibatis.executor.statement.StatementHandler;
  5 +import org.apache.ibatis.mapping.MappedStatement;
  6 +import org.apache.ibatis.mapping.SqlCommandType;
  7 +import org.apache.ibatis.plugin.*;
  8 +import org.apache.ibatis.reflection.MetaObject;
  9 +import org.apache.ibatis.reflection.SystemMetaObject;
  10 +
  11 +import java.lang.annotation.Annotation;
  12 +import java.lang.reflect.Field;
  13 +import java.sql.ResultSet;
  14 +import java.sql.Statement;
  15 +import java.util.*;
  16 +
  17 +@Intercepts({
  18 + @Signature(
  19 + type = StatementHandler.class,
  20 + method = "update",
  21 + args = {Statement.class}
  22 + )
  23 +})
  24 +public class GeneratedKeyInterceptor implements Interceptor {
  25 +
  26 + @Override
  27 + public Object intercept(Invocation invocation) throws Throwable {
  28 + StatementHandler handler = (StatementHandler) invocation.getTarget();
  29 + MetaObject meta = SystemMetaObject.forObject(handler);
  30 + MappedStatement ms = (MappedStatement) meta.getValue("delegate.mappedStatement");
  31 + if (ms == null || ms.getSqlCommandType() != SqlCommandType.INSERT) {
  32 + return invocation.proceed();
  33 + }
  34 +
  35 + Object paramObj = meta.getValue("delegate.boundSql.parameterObject");
  36 + List<?> list = extractList(paramObj);
  37 + if (list == null || list.isEmpty()) {
  38 + return invocation.proceed();
  39 + }
  40 +
  41 + // 执行插入
  42 + Object result = invocation.proceed();
  43 +
  44 + // 从 Statement 里读取生成的 keys,并回写到集合对象
  45 + Statement stmt = (Statement) invocation.getArgs()[0];
  46 + try (ResultSet rs = stmt.getGeneratedKeys()) {
  47 + String pkName = resolvePkName(list.get(0));
  48 + if (pkName == null) {
  49 + return result; // 无法解析主键名,跳过
  50 + }
  51 + int idx = 0;
  52 + while (rs != null && rs.next() && idx < list.size()) {
  53 + Object keyVal = rs.getObject(1);
  54 + setFieldValue(list.get(idx), pkName, keyVal);
  55 + idx++;
  56 + }
  57 + } catch (Exception ignore) {
  58 + // 不应该抛出,保证插入不受影响;若需要可记录日志
  59 + }
  60 + return result;
  61 + }
  62 +
  63 + @SuppressWarnings("rawtypes")
  64 + private List extractList(Object param) {
  65 + if (param == null) return null;
  66 + if (param instanceof List) return (List) param;
  67 + if (param instanceof Map) {
  68 + Map map = (Map) param;
  69 + // MyBatis 对未指定 name 的 collection 参数可能为 "list" 或 "collection"
  70 + if (map.get("list") instanceof List) return (List) map.get("list");
  71 + if (map.get("collection") instanceof List) return (List) map.get("collection");
  72 + // 支持 @Param("list")
  73 + for (Object v : map.values()) {
  74 + if (v instanceof List) return (List) v;
  75 + }
  76 + }
  77 + return null;
  78 + }
  79 +
  80 + private String resolvePkName(Object obj) {
  81 + Class<?> cls = obj.getClass();
  82 + // 查找带 @Id 注解的字段(javax.persistence 或其它同名注解)
  83 + List<Field> list = getAllFields(cls);
  84 + for (Field f : list) {
  85 + for (Annotation a : f.getAnnotations()) {
  86 + String an = a.annotationType().getSimpleName();
  87 + if ("Id".equals(an) || "TableId".equals(an)) {
  88 + return f.getName();
  89 + }
  90 + }
  91 + }
  92 + // 回退到常见命名
  93 + for (Field f : list) {
  94 + if ("id".equalsIgnoreCase(f.getName())) return f.getName();
  95 + }
  96 + return null;
  97 + }
  98 +
  99 + private List<Field> getAllFields(Class<?> cls) {
  100 + List<Field> fields = new ArrayList<>();
  101 + Class<?> cur = cls;
  102 + while (cur != null && cur != Object.class) {
  103 + Collections.addAll(fields, cur.getDeclaredFields());
  104 + cur = cur.getSuperclass();
  105 + }
  106 + return fields;
  107 + }
  108 +
  109 + private void setFieldValue(Object target, String fieldName, Object value) {
  110 + try {
  111 + Field f = findField(target.getClass(), fieldName);
  112 + if (f == null) return;
  113 + f.setAccessible(true);
  114 + Class<?> type = f.getType();
  115 + Object converted = convertValue(value, type);
  116 + f.set(target, converted);
  117 + } catch (Exception ignored) {
  118 + }
  119 + }
  120 +
  121 + private Field findField(Class<?> cls, String name) {
  122 + Class<?> cur = cls;
  123 + while (cur != null && cur != Object.class) {
  124 + try {
  125 + return cur.getDeclaredField(name);
  126 + } catch (NoSuchFieldException e) {
  127 + cur = cur.getSuperclass();
  128 + }
  129 + }
  130 + return null;
  131 + }
  132 +
  133 + private Object convertValue(Object val, Class<?> targetType) {
  134 + if (val == null) return null;
  135 + if (targetType.isAssignableFrom(val.getClass())) return val;
  136 + if (Number.class.isAssignableFrom(val.getClass()) || val instanceof Number) {
  137 + Number n = (Number) val;
  138 + if (targetType == Long.class || targetType == long.class) return n.longValue();
  139 + if (targetType == Integer.class || targetType == int.class) return n.intValue();
  140 + if (targetType == Short.class || targetType == short.class) return n.shortValue();
  141 + if (targetType == Byte.class || targetType == byte.class) return n.byteValue();
  142 + }
  143 + // 最后回退到字符串再尝试解析简单类型
  144 + String s = val.toString();
  145 + if (targetType == String.class) return s;
  146 + try {
  147 + if (targetType == Long.class || targetType == long.class) return Long.parseLong(s);
  148 + if (targetType == Integer.class || targetType == int.class) return Integer.parseInt(s);
  149 + if (targetType == Short.class || targetType == short.class) return Short.parseShort(s);
  150 + if (targetType == Byte.class || targetType == byte.class) return Byte.parseByte(s);
  151 + } catch (Exception ignored) {
  152 + }
  153 + return val;
  154 + }
  155 +
  156 + @Override
  157 + public Object plugin(Object target) {
  158 + return Plugin.wrap(target, this);
  159 + }
  160 +
  161 + @Override
  162 + public void setProperties(Properties properties) {
  163 + // no-op
  164 + }
  165 +}
@@ -81,9 +81,8 @@ public abstract class AquaticPublicOpinionBase implements AquaticPublicOpinionS @@ -81,9 +81,8 @@ public abstract class AquaticPublicOpinionBase implements AquaticPublicOpinionS
81 //保存文件 81 //保存文件
82 for (FishAquaticPublicOpinion aquaticPublicOpinion:list) 82 for (FishAquaticPublicOpinion aquaticPublicOpinion:list)
83 { 83 {
84 - Map<String,Object> map = publicService.getObjectList(aquaticPublicOpinion,"id,info_url",null,null,0,0).get(0);  
85 - String text = getInfo((String) map.get("info_url"));  
86 - updateSaveInfo(text, (Integer) map.get("id")); 84 + String text = getInfo(aquaticPublicOpinion.getInfoUrl());
  85 + updateSaveInfo(text, aquaticPublicOpinion.getId());
87 } 86 }
88 } 87 }
89 } 88 }
@@ -2,9 +2,11 @@ package com.ruoyi.quartz.task.aquatic; @@ -2,9 +2,11 @@ package com.ruoyi.quartz.task.aquatic;
2 2
3 import cn.hutool.http.HttpUtil; 3 import cn.hutool.http.HttpUtil;
4 import com.ruoyi.system.domain.fish.FishAquaticPublicOpinion; 4 import com.ruoyi.system.domain.fish.FishAquaticPublicOpinion;
  5 +import org.springframework.stereotype.Service;
5 6
6 import java.util.*; 7 import java.util.*;
7 8
  9 +@Service
8 public class ZnyjNftecAgriCn extends AquaticPublicOpinionBase{ 10 public class ZnyjNftecAgriCn extends AquaticPublicOpinionBase{
9 @Override 11 @Override
10 List<FishAquaticPublicOpinion> collect(String day, Set<String> dedupMap) { 12 List<FishAquaticPublicOpinion> collect(String day, Set<String> dedupMap) {
@@ -17,7 +19,7 @@ public class ZnyjNftecAgriCn extends AquaticPublicOpinionBase{ @@ -17,7 +19,7 @@ public class ZnyjNftecAgriCn extends AquaticPublicOpinionBase{
17 aquaticPublicOpinion.setCreateTime(new Date()); 19 aquaticPublicOpinion.setCreateTime(new Date());
18 aquaticPublicOpinion.setAquaticType(4); 20 aquaticPublicOpinion.setAquaticType(4);
19 list.add(aquaticPublicOpinion); 21 list.add(aquaticPublicOpinion);
20 - return Collections.emptyList(); 22 + return list;
21 } 23 }
22 24
23 @Override 25 @Override
@@ -37,7 +37,7 @@ public interface IIotTerminalService @@ -37,7 +37,7 @@ public interface IIotTerminalService
37 * @return 结果 37 * @return 结果
38 */ 38 */
39 public int insertIotTerminal(IotTerminal iotTerminal); 39 public int insertIotTerminal(IotTerminal iotTerminal);
40 - 40 + public int updateIotTerminalOrderBy(String id,Integer order_by);
41 int addIotTerminals(Integer userId, List<String> ids); 41 int addIotTerminals(Integer userId, List<String> ids);
42 42
43 /** 43 /**
@@ -102,6 +102,15 @@ public class IotTerminalServiceImpl implements IIotTerminalService @@ -102,6 +102,15 @@ public class IotTerminalServiceImpl implements IIotTerminalService
102 * @return 结果 102 * @return 结果
103 */ 103 */
104 @Override 104 @Override
  105 + public int updateIotTerminalOrderBy(String id,Integer order_by)
  106 + {
  107 + IotTerminal iotTerminal = new IotTerminal();
  108 + iotTerminal.setId(id);
  109 + iotTerminal.setOrder_by(order_by);
  110 + return iotTerminalMapper.updateIotTerminal(iotTerminal);
  111 + }
  112 +
  113 + @Override
105 public int updateIotTerminal(IotTerminal iotTerminal) 114 public int updateIotTerminal(IotTerminal iotTerminal)
106 { 115 {
107 DeviceCommandApi deviceCommandApi = new DeviceCommandApi(); 116 DeviceCommandApi deviceCommandApi = new DeviceCommandApi();
@@ -97,6 +97,13 @@ @@ -97,6 +97,13 @@
97 <groupId>com.zhonglai.luhui</groupId> 97 <groupId>com.zhonglai.luhui</groupId>
98 <artifactId>lh-user</artifactId> 98 <artifactId>lh-user</artifactId>
99 </dependency> 99 </dependency>
  100 +
  101 + <dependency>
  102 + <groupId>org.springframework.boot</groupId>
  103 + <artifactId>spring-boot-starter-test</artifactId>
  104 + <scope>test</scope>
  105 + </dependency>
  106 +
100 </dependencies> 107 </dependencies>
101 108
102 <build> 109 <build>
@@ -115,4 +115,12 @@ public class IotTerminalController extends BaseController @@ -115,4 +115,12 @@ public class IotTerminalController extends BaseController
115 { 115 {
116 return toAjax(iotTerminalService.deleteIotTerminalByIds(ids)); 116 return toAjax(iotTerminalService.deleteIotTerminalByIds(ids));
117 } 117 }
  118 +
  119 + @ApiOperation("修改终端排序")
  120 + @Log(title = "终端", businessType = BusinessType.UPDATE)
  121 + @PostMapping("editOrderBy")
  122 + public AjaxResult editOrderBy(@RequestBody IotTerminal iotTerminal)
  123 + {
  124 + return toAjax(iotTerminalService.updateIotTerminalOrderBy(iotTerminal.getId(),iotTerminal.getOrder_by()));
  125 + }
118 } 126 }
1 -# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: E:/work/idea/Luhui/uploadPath # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 captchaType: math # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8080 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn com.zhonglai.luhui: debug # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # redis 配置 redis: # 地址 host: 119.23.218.181 # 端口,默认为6379 port: 6379 # 数据库索引 database: 1 # 密码 password: # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # token配置 token: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟) expireTime: 1440 rediskey: lh-admin # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain,com.zhonglai.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /dev-api # 防止XSS攻击 xss: # 过滤开关 enabled: true # 排除链接(多个用逗号分隔) excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* mqtt: client: device_life: 180 sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /login,/register,/captchaImage,/getCacheObject,/v2/api-docs,/tool/gen/generatorCodeFromDb,/data/**,/monitor/server/upload,/monitor/server/getSysMonitorServerList,/monitor/server/getSysMonitorServerLogList,/monitor/server/getNowAquaticPublicOpinion # NameServer地址 rocketmq: name-server: 8.129.224.117:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-mqtt-service-deviceCommand-test send-tags: 1 task: tesseract-path: E:/work/idea/Luhui/tessdata  
  1 +# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: E:/work/idea/Luhui/uploadPath # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 captchaType: math # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8080 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 # 日志配置 logging: level: com.ruoyi: debug org.springframework: warn com.zhonglai.luhui: debug # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true # redis 配置 redis: # 地址 host: 127.0.0.1 # 端口,默认为6379 port: 6379 # 数据库索引 database: 1 # 密码 password: # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms # token配置 token: # 令牌自定义标识 header: Authorization # 令牌密钥 secret: abcdefghijklmnopqrstuvwxyz # 令牌有效期(默认30分钟) expireTime: 1440 rediskey: lh-admin # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain,com.zhonglai.**.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 加载全局的配置文件 configLocation: classpath:mybatis/mybatis-config.xml # PageHelper分页插件 pagehelper: helperDialect: mysql supportMethodsArguments: true params: count=countSql # Swagger配置 swagger: # 是否开启swagger enabled: true # 请求前缀 pathMapping: /dev-api # 防止XSS攻击 xss: # 过滤开关 enabled: true # 排除链接(多个用逗号分隔) excludes: /system/notice # 匹配链接 urlPatterns: /system/*,/monitor/*,/tool/* mqtt: client: device_life: 180 sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /login,/register,/captchaImage,/getCacheObject,/v2/api-docs,/tool/gen/generatorCodeFromDb,/data/**,/monitor/server/upload,/monitor/server/getSysMonitorServerList,/monitor/server/getSysMonitorServerLogList,/monitor/server/getNowAquaticPublicOpinion # NameServer地址 rocketmq: name-server: 8.129.224.117:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-mqtt-service-deviceCommand-test send-tags: 1 task: tesseract-path: E:/work/idea/Luhui/tessdata
  1 +package com.zhonglai.luhui.admin;
  2 +
  3 +import com.ruoyi.quartz.task.AquaticPublicOpinionTask;
  4 +import com.ruoyi.quartz.task.aquatic.AquaticPublicOpinionService;
  5 +import com.ruoyi.quartz.task.aquatic.ZnyjNftecAgriCn;
  6 +import com.ruoyi.system.domain.sys.SysConfig;
  7 +import com.zhonglai.luhui.dao.service.PublicService;
  8 +import org.junit.jupiter.api.Test;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.boot.test.context.SpringBootTest;
  11 +
  12 +import java.util.List;
  13 +
  14 +@SpringBootTest(classes = AdminApplication.class)
  15 +class AquaticPublicOpinionTaskTestTest {
  16 + @Autowired
  17 + private ZnyjNftecAgriCn znyjNftecAgriCn;
  18 +
  19 + @Test
  20 + public void testRun() {
  21 + //初始微信采集服务
  22 + znyjNftecAgriCn.run("20260105");
  23 + }
  24 +}
@@ -686,25 +686,12 @@ @@ -686,25 +686,12 @@
686 <repositories> 686 <repositories>
687 <repository> 687 <repository>
688 <id>public</id> 688 <id>public</id>
689 - <name>aliyun nexus</name>  
690 - <url>https://maven.aliyun.com/repository/public</url> 689 + <name>luhui nexus</name>
  690 + <url>http://120.25.223.227:4781/repository/maven-public/</url>
691 <releases> 691 <releases>
692 <enabled>true</enabled> 692 <enabled>true</enabled>
693 </releases> 693 </releases>
694 </repository> 694 </repository>
695 </repositories> 695 </repositories>
696 696
697 - <pluginRepositories>  
698 - <pluginRepository>  
699 - <id>public</id>  
700 - <name>aliyun nexus</name>  
701 - <url>https://maven.aliyun.com/repository/public</url>  
702 - <releases>  
703 - <enabled>true</enabled>  
704 - </releases>  
705 - <snapshots>  
706 - <enabled>false</enabled>  
707 - </snapshots>  
708 - </pluginRepository>  
709 - </pluginRepositories>  
710 </project> 697 </project>