作者 钟来

修改日志输出内容imei号为空的异常

正在显示 73 个修改的文件 包含 2717 行增加307 行删除
... ... @@ -8,8 +8,10 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.support.ResourceEditorRegistrar;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
/**
... ... @@ -174,4 +176,17 @@ public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationC
{
return null != beanFactory;
}
/**
* 获取当前的AnnotationConfigApplicationContext
*
* @return AnnotationConfigApplicationContext
*/
public static AnnotationConfigServletWebServerApplicationContext getAnnotationConfigApplicationContext() {
if (applicationContext instanceof AnnotationConfigServletWebServerApplicationContext) {
return (AnnotationConfigServletWebServerApplicationContext) applicationContext;
} else {
throw new IllegalStateException("Current ApplicationContext is not an instance of AnnotationConfigApplicationContext");
}
}
}
... ...
... ... @@ -6,16 +6,21 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StreamUtils;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.nio.file.FileSystems;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* 实现 Filter
... ... @@ -28,7 +33,6 @@ import java.util.Map;
@WebFilter(filterName="httpServletRequestReplacedFilter",urlPatterns="/*")
public class HttpServletRequestReplacedFilter implements Filter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void init(FilterConfig arg0) throws ServletException {
}
... ... @@ -39,8 +43,22 @@ public class HttpServletRequestReplacedFilter implements Filter {
RequestReaderHttpServletRequestWrapper requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new RequestReaderHttpServletRequestWrapper((HttpServletRequest) request);
printLog((HttpServletRequest)request,requestWrapper.getBody());
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
//解决Invalid character found in method name的问题
String method = httpServletRequest.getMethod();
if (!isValidHttpMethod(method)) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.getWriter().write("Invalid HTTP method");
httpServletResponse.setStatus(400); // Bad Request
return;
}
if (!match(httpServletRequest.getRequestURI().toString()))
{
requestWrapper = new RequestReaderHttpServletRequestWrapper(httpServletRequest);
printLog(httpServletRequest,requestWrapper.getBody());
}
}
//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。
// 在chain.doFiler方法中传递新的request对象
... ... @@ -51,7 +69,6 @@ public class HttpServletRequestReplacedFilter implements Filter {
}
}
@Override
public void destroy() {
}
... ... @@ -76,4 +93,38 @@ public class HttpServletRequestReplacedFilter implements Filter {
map.put("body",body);
logger.info("---------------------参数:"+ GsonConstructor.get().toJson(map)+"----------------------------");
}
// 设置排除路径
private final static List<String> excludeUrls = Arrays.asList("/","/v2/api-docs","/swagger-resources/**","/resources/**","/**/*.html","/**/*.js","/**/*.css","/**/*.gif","/**/*.jpg","/**/*.png","/**/*.ico","/**/*.woff","/**/*.ttf","/**/*.svg");
public static boolean match( String path) {
path = path.replace("\\", "/").trim();
// 创建一个 Ant 风格的路径匹配器
AntPathMatcher matcher = new AntPathMatcher();
for (String excludeUrl:excludeUrls)
{
// 使用 matcher 的 match 方法来判断给定的路径是否匹配模式
if (matcher.match(excludeUrl, path))
{
return true;
}
}
return false;
}
private static final String[] VALID_METHODS = {"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "TRACE", "CONNECT"};
private boolean isValidHttpMethod(String method) {
if (method == null || method.isEmpty()) {
return false;
}
for (String validMethod : VALID_METHODS) {
if (validMethod.equalsIgnoreCase(method)) {
return true;
}
}
return false;
}
}
... ...
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-jar</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>lh-jar-plugins-init</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>ruoyi-common</artifactId>
</dependency>
<dependency>
<groupId>net.jodah</groupId>
<artifactId>expiringmap</artifactId>
</dependency>
<!-- 文档 -->
<dependency >
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
\ No newline at end of file
... ...
package com.zhonglai.luhui.plugins;
import com.zhonglai.luhui.plugins.config.PluginsClassLoader;
import net.jodah.expiringmap.ExpirationListener;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import java.util.concurrent.TimeUnit;
@Component
public class FileChangeListener {
private static Logger log = LoggerFactory.getLogger(FileChangeListener.class);
private WatchService watchService;
private Thread watcherThread;
private volatile boolean running = true;
private static Integer time = 5;
private static ExpiringMap<String, Boolean> upFileMap = ExpiringMap.builder().maxSize(100).expiration(time, TimeUnit.SECONDS)
.variableExpiration()
.asyncExpirationListener((ExpirationListener<String, Boolean>) (s, b) -> log.info("超时清除>>>>>>>:{} ",s))
.expirationPolicy(ExpirationPolicy.CREATED).build();
public void startFileWatcher() throws IOException {
//初始化插件
InitPlugins.init();
//注册文件监听
registerListener();
//启动监听线程
startThread();
}
private void registerListener() throws IOException {
// 创建WatchService,它是对操作系统的文件监视器的封装,相对之前,不需要遍历文件目录,效率要高很多
watchService = FileSystems.getDefault().newWatchService();
// 注册指定目录使用的监听器,监视目录下文件的变化;
// PS:Path必须是目录,不能是文件;
// StandardWatchEventKinds.ENTRY_MODIFY,表示监视文件的修改事件
Path path = Paths.get(InitPlugins.getPluginsPath());
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
}
private void startThread()
{
// 创建并启动监听线程
watcherThread = new Thread(this::watchFileChanges);
watcherThread.start();
}
private void watchFileChanges() {
while (running) {
try {
//记录变化的文件
// 获取目录的变化:
// take()是一个阻塞方法,会等待监视器发出的信号才返回。
// 还可以使用watcher.poll()方法,非阻塞方法,会立即返回当时监视器中是否有信号。
// 返回结果WatchKey,是一个单例对象,与前面的register方法返回的实例是同一个;
WatchKey key = watchService.take();
//记录变化的文件
// 处理文件变化事件:
// key.pollEvents()用于获取文件变化事件,只能获取一次,不能重复获取,类似队列的形式。
for (WatchEvent<?> event : key.pollEvents()) {
System.out.println(event.kind()+"事件触发"+": " + event.context());
// event.kind():事件类型
if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
//事件可能lost or discarded
continue;
}
// 返回触发事件的文件或目录的路径(相对路径)
Path fileName = (Path) event.context();
Path dir = (Path) key.watchable();
Path fullPath = dir.resolve(fileName);
File file = fullPath.toFile();
if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY)
{
if(file.exists() && file.length()>0)
{
if(!upFileMap.containsKey(fullPath.toString()))
{
PluginsClassLoader.uploadJar(file);
upFileMap.put(fullPath.toString(),true);
}
}
}
if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE)
{
PluginsClassLoader.unloadJar(file.getAbsolutePath());
}
}
// 每次调用WatchService的take()或poll()方法时需要通过本方法重置
if (!key.reset()) {
break;
}
} catch (Exception e) {
e.printStackTrace();
// 如果有必要,可以在这里优雅地处理异常,比如重新注册WatchKey
break;
}
}
}
@PreDestroy
public void stopFileWatcher() {
running = false; // 设置标志以停止线程
// 等待线程完成(可选,但可能不总是需要的,取决于你的需求)
try {
watcherThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 关闭WatchService
try {
if (watchService != null) {
watchService.
close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Bean
public CommandLineRunner onApplicationEvent() {
return args -> {
// 在这里编写启动后需要执行的逻辑
System.out.println("Spring Boot 应用已启动完成,执行额外的加载操作...");
// 执行加载数据库,连接外部服务等操作
startFileWatcher();
};
}
public static void main(String[] args) throws IOException {
FileChangeListener fileChangeListener = new FileChangeListener();
fileChangeListener.startFileWatcher();
System.out.println("启动成功");
}
}
... ...
package com.zhonglai.luhui.plugins;
import com.zhonglai.luhui.plugins.config.PluginsClassLoader;
import java.io.File;
import java.net.MalformedURLException;
public class InitPlugins {
private static final String pluginsPath = "plugins";
private static final String pluginsJarPath = "pluginsjar";
public static void init()
{
File[] files = getJarFiles(getPluginsPath());
PluginsClassLoader.uploadJar(files);
}
public static String getPluginsPath()
{
String jarFilePath = System.getProperty("jarFilePath");
String path = null != jarFilePath?jarFilePath:System.getProperty("user.dir")+"/"+pluginsPath;
return path;
}
public static String getPluginsJarPath()
{
String path = System.getProperty("user.dir")+"/"+pluginsJarPath;
return path;
}
public static File[] getJarFiles(String jarFilePath)
{
File file = new File(jarFilePath);
if(file.exists() && file.isDirectory())
{
File[] files = file.listFiles((dir, name) -> {
if(name.endsWith(".jar"))
{
return true;
}
return false;
});
return files;
}
return null;
}
public static void main(String[] args) throws MalformedURLException {
File[] files = getJarFiles(getPluginsPath());
PluginsClassLoader.uploadJar(files);
}
public static String toJarPath(String filePath)
{
return filePath.replace(pluginsPath,pluginsJarPath);
}
}
... ...
package com.zhonglai.luhui.plugins.config;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.zhonglai.luhui.plugins.InitPlugins;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import springfox.documentation.service.Operation;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.readers.operation.CachingOperationNameGenerator;
import org.springframework.web.method.HandlerMethod;
import java.lang.reflect.Method;
import java.util.*;
/**
* 自定义的类加载器
*/
public class PluginsClassLoader extends URLClassLoader {
private static final Logger log = LoggerFactory.getLogger(PluginsClassLoader.class);
/**
* 存放类
*/
private static ConcurrentHashMap<String,PluginsClassLoader> jarMap = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String,Class<?>> classMap = new ConcurrentHashMap<>();
private PluginsClassLoader(URL[] urls) {
super(urls,PluginsClassLoader.class.getClassLoader());
}
/**
* 卸载jar
* @throws IOException
*/
public static void unloadJar(String... filePaths) throws IOException {
for (String filePath:filePaths)
{
String key = InitPlugins.toJarPath(filePath);
// 移除并关闭 ClassLoader
PluginsClassLoader pluginsClassLoader = jarMap.remove(key);
if (pluginsClassLoader != null) {
try {
pluginsClassLoader.close();
} catch (IOException e) {
log.error("Failed to close ClassLoader for JAR: " + key, e);
}
}
// 移除 classMap 中的类
Iterator<Map.Entry<String, Class<?>>> it = classMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Class<?>> entry = it.next();
if (entry.getValue().getClassLoader() == pluginsClassLoader) {
it.remove();
}
}
File file = new File(key);
if(file.exists())
{
file.delete();
}
}
}
/**
* 更新jar
* @throws IOException
*/
public static void uploadJar(File... filePaths) {
try {
for (File file:filePaths)
{
String filePath = file.getAbsolutePath();
System.out.println("绝对路径:"+filePath);
unloadJar(filePath);
String key = InitPlugins.toJarPath(filePath);
copyFile(filePath,key);
PluginsClassLoader pluginsClassLoader = new PluginsClassLoader(new URL[]{new URL("file:"+key)});
jarMap.put(key,pluginsClassLoader);
laodJar(new File(key),pluginsClassLoader);
}
} catch (IOException e) {
log.error("更新jar异常",e);
}
}
private static void laodJar(File jarfile, ClassLoader classLoader) {
try (JarFile jarFile = new JarFile(jarfile)) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String entryName = jarEntry.getName();
if (entryName != null && entryName.endsWith(".class")) {
entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
Class<?> clas = classLoader.loadClass(entryName);
if (SpringUtils.isSpringBean()) {
Object bean = loadBean(clas);
}
classMap.put(entryName, clas);
}else if(entryName.endsWith("jar.config"))
{
//加载配置文件jar.config
InputStream is = classLoader.getResourceAsStream(entryName);
String scanPath = loadconfig(is).getProperty("scan-path");
SpringUtils.getAnnotationConfigApplicationContext().scan(scanPath);
}
}
} catch (IOException | ClassNotFoundException e) {
log.error("加载jar:"+jarfile.getAbsolutePath()+" 失败",e);
}
}
private static Properties loadconfig() throws IOException {
return loadconfig(null);
}
private static Properties loadconfig(InputStream is) throws IOException {
Properties prop = new Properties();
// 加载配置文件
prop.load(is);
return prop;
}
private static <T> T loadBean(Class<T> clas)
{
if(isInstantiable(clas))
{
try {
Object object = clas.newInstance();
if(clas.isAnnotationPresent(Component.class) || clas.isAnnotationPresent(Service.class))
{
SpringUtils.registerBean(null,object);
return SpringUtils.getBean(clas);
}else {
return (T) object;
}
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return null;
}
private static boolean isInstantiable(Class<?> clazz) {
// 检查类是否是抽象类或接口
if (Modifier.isAbstract(clazz.getModifiers()) || clazz.isInterface()) {
return false;
}
// 尝试查找并访问默认的无参构造器
try {
clazz.getDeclaredConstructor((Class[]) null);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
public static <T> T getJarClass(Class<T> tClass,String classname) {
Class clazz = classMap.get(classname);
if (null != clazz)
{
try {
if (tClass.isAssignableFrom(clazz) && !clazz.equals(tClass)) {
return tClass.cast(clazz.getDeclaredConstructor().newInstance());
}else if(tClass.isAssignableFrom(clazz) && clazz.equals(tClass))
{
return tClass.newInstance();
}
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
return null;
}
public static void copyFile(String sourcePathStr, String targetPathStr) {
Path sourcePath = Paths.get(sourcePathStr);
Path targetPath = Paths.get(targetPathStr);
// 检查源文件是否存在
if (!Files.exists(sourcePath)) {
log.error("Source file does not exist: " + sourcePath.toString());
return;
}
// 检查目标文件所在目录是否存在,如果不存在则创建
Path targetDir = targetPath.getParent();
if (!Files.exists(targetDir)) {
try {
Files.createDirectories(targetDir);
} catch (IOException e) {
log.error("Failed to create target directory: " + targetDir.toString(),e);
return;
}
}
// 执行文件复制
try {
Files.copy(sourcePath, targetPath);
System.out.println("File copied successfully from " + sourcePath.toString() + " to " + targetPath.toString());
} catch (IOException e) {
throw new RuntimeException("Failed to copy file from " + sourcePath.toString() + " to " + targetPath.toString(), e);
}
}
}
... ...
... ... @@ -18,6 +18,7 @@
<module>lh-jar-rocketmq</module>
<module>lh-jar-sys-service</module>
<module>lh-jar-order-service</module>
<module>lh-jar-plugins-init</module>
</modules>
<properties>
... ...
... ... @@ -7,11 +7,15 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.Message;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.GsonConstructor;
import com.ruoyi.framework.config.ThreadPoolConfig;
import com.zhonglai.luhui.action.BaseController;
import com.zhonglai.luhui.device.domain.IotDevice;
import com.zhonglai.luhui.device.domain.IotProductTranslate;
import com.zhonglai.luhui.device.dto.CommandType;
import com.zhonglai.luhui.device.dto.DeviceCommand;
import com.zhonglai.luhui.device.service.IIotDeviceService;
import com.zhonglai.luhui.device.service.IIotProductTranslateService;
import com.zhonglai.luhui.device.service.impl.IotProductTranslateServiceImpl;
import com.zhonglai.luhui.rocketmq.service.RocketMqService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
... ... @@ -27,6 +31,9 @@ import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Api(tags = "控制器")
@RestController
... ... @@ -37,6 +44,12 @@ public class ControlDeviceConreoller extends BaseController {
@Autowired
private IIotDeviceService iotDeviceService;
@Autowired
private IotProductTranslateServiceImpl iotProductTranslateService;
@Autowired
private ScheduledExecutorService scheduledExecutorService;
@ApiOperation(value = "写指令",notes = "body参数描述:\r\n" +
"{\n" +
" \"0\":{\n" +
... ... @@ -138,6 +151,17 @@ public class ControlDeviceConreoller extends BaseController {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("product_id",product_id);
deviceCommand.setData(jsonObject);
scheduledExecutorService.schedule(() -> {
IotProductTranslate iotProductTranslate = new IotProductTranslate();
iotProductTranslate.setProduct_id(product_id);
List<IotProductTranslate> list = iotProductTranslateService.selectIotProductTranslateList(iotProductTranslate);
for (IotProductTranslate iotProductTranslate1:list)
{
iotProductTranslateService.upCache(iotProductTranslate1);
}
}, 0, TimeUnit.SECONDS);
return sysControl(deviceCommand);
}
... ...
# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: D:/ruoyi/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: 47.112.163.61 # 端口,默认为6379 port: 9527 # 数据库索引 database: 1 # 密码 password: Luhui586 # 连接超时时间 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 # NameServer地址 rocketmq: name-server: 47.115.144.179:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-mqtt-service-deviceCommand-test send-tags: 1
\ No newline at end of file
# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: D:/ruoyi/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 # 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
\ No newline at end of file
... ...
... ... @@ -166,4 +166,38 @@ public class IotTerminalController extends BaseController
iotTerminalService.batchUpName(ids,names,userid);
return toAjax( userTerminalGroupRelationService.groupTerminal(iot_terminal_group_id,ids,userid));
}
@ApiOperation("统计用户终端")
@GetMapping("/countUserDevices")
public AjaxResult countUserDevices()
{
String ids = SecurityUtils.getUserId()+"";
String sql = "SELECT t.mqtt_username,COUNT(t.mqtt_username) ct FROM \n" +
"(\n" +
"SELECT b.`device_id`,a.`iot_terminal_id`,b.`mqtt_username` FROM `mqtt_broker`.`user_terminal_group_relation` a LEFT JOIN `mqtt_broker`.`iot_terminal` b ON b.`id`=a.`iot_terminal_id` WHERE a.user_info_id IN("+ids+")\n" +
"UNION \n" +
"SELECT device_id,id iot_terminal_id,device_model mqtt_username FROM `liu_yu_le`.`device_info` WHERE device_type=0 and user_id IN("+ids+")\n" +
"UNION\n" +
"SELECT base_station_id device_id,id iot_terminal_id,'WDB' mqtt_username FROM `liu_yu_le`.`wdb_terminal` WHERE user_id IN("+ids+")\n" +
") t GROUP BY t.mqtt_username HAVING COUNT(t.mqtt_username) > 0";
List list = publicService.getObjectListBySQL(sql);
return AjaxResult.success(list);
}
@ApiOperation("统计用户设备")
@GetMapping("/countUserHostDevices")
public AjaxResult countUserHostDevices()
{
String ids = SecurityUtils.getUserId()+"";
String sql = "SELECT t.mqtt_username,COUNT(t.mqtt_username) ct FROM (\n" +
"SELECT * FROM \n" +
"(\n" +
"SELECT b.`device_id`,b.`mqtt_username` FROM `mqtt_broker`.`user_terminal_group_relation` a LEFT JOIN `mqtt_broker`.`iot_terminal` b ON b.`id`=a.`iot_terminal_id` WHERE a.user_info_id IN("+ids+") GROUP BY b.`device_id`\n" +
"UNION \n" +
"SELECT id device_id,device_model mqtt_username FROM `liu_yu_le`.`device_host` WHERE user_id IN("+ids+")\n" +
") mt GROUP BY device_id \n" +
") t GROUP BY t.mqtt_username HAVING COUNT(t.mqtt_username) > 0";
List list = publicService.getObjectListBySQL(sql);
return AjaxResult.success(list);
}
}
... ...
# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: D:/ruoyi/uploadPath # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 captchaType: math # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 18080 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: 47.112.163.61 # 端口,默认为6379 port: 9527 # 数据库索引 database: 1 # 密码 password: Luhui586 # 连接超时时间 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-api # 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/ApiLogin/*,/content/**,/test/**,/fish/FishPriceCollection/*,/iot/IotTerminal/listByDeviceId/* # NameServer地址 rocketmq: name-server: 47.115.144.179:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-mqtt-service-deviceCommand-test send-tags: 1
\ No newline at end of file
# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: D:/ruoyi/uploadPath # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 captchaType: math # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 18080 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: 8.129.224.117 # 端口,默认为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-api # 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/ApiLogin/*,/content/**,/test/**,/fish/FishPriceCollection/*,/iot/IotTerminal/listByDeviceId/* # NameServer地址 rocketmq: name-server: 47.115.144.179:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-mqtt-service-deviceCommand-test send-tags: 1
\ No newline at end of file
... ...
package com.zhonglai.luhui.device.protocol.http.analysis.analysis;
import com.google.gson.JsonObject;
import com.ruoyi.common.utils.GsonConstructor;
import com.zhonglai.luhui.device.analysis.comm.factory.Topic;
import com.zhonglai.luhui.device.analysis.util.TopicUtil;
import com.zhonglai.luhui.device.protocol.factory.analysis.ProtocolParserFactory;
... ... @@ -8,7 +9,7 @@ import com.zhonglai.luhui.device.protocol.factory.dto.AnalysisResult;
import com.zhonglai.luhui.device.protocol.http.analysis.analysis.topic.*;
import org.springframework.stereotype.Service;
public class ProtocolParserServiceImpl implements ProtocolParserFactory<JsonObject> {
public class ProtocolParserServiceImpl implements ProtocolParserFactory {
private static final String topicModel = "/{{roleid}}/{{username}}/{{clientid}}/{{payloadtype}}/{{topicType}}/{{messageid}}";
@Override
... ... @@ -19,7 +20,8 @@ public class ProtocolParserServiceImpl implements ProtocolParserFactory<JsonObje
}
@Override
public AnalysisResult analysisPayload(Topic topic, JsonObject payload) {
public AnalysisResult analysisPayload(Topic topic, byte[] payloadby) {
JsonObject payload = GsonConstructor.get().fromJson(new String(payloadby), JsonObject.class);
switch (topic.getTopicType())
{
case "online":
... ...
... ... @@ -8,7 +8,7 @@ import com.zhonglai.luhui.device.protocol.factory.dto.AnalysisResult;
import com.zhonglai.luhui.device.protocol.modbus.TopicConfig;
import com.zhonglai.luhui.device.protocol.modbus.dto.ModbusDto;
public class ModbusProtocolParserFactoryImpl implements ProtocolParserFactory<byte[]> {
public class ModbusProtocolParserFactoryImpl implements ProtocolParserFactory {
@Override
public Topic analysisTopic(String topicStr) {
... ...
... ... @@ -20,7 +20,7 @@ import com.zhonglai.luhui.device.protocol.factory.dto.AnalysisResult;
import com.zhonglai.luhui.device.protocol.factory.dto.ParserDeviceHostDto;
public class PLC004ProtocolParserFactoryImpl implements ProtocolParserFactory<byte[]> {
public class PLC004ProtocolParserFactoryImpl implements ProtocolParserFactory {
private static final String topicModel = "/{{roleid}}/{{username}}/{{clientid}}/{{payloadtype}}/{{topicType}}";
@Override
public Topic analysisTopic(String topicStr) {
... ...
... ... @@ -36,7 +36,7 @@ public class ProtocolParserAndPurificationFactory<T> {
* @param payload 数据信息
* @return
*/
public ProtocolPurificationModel analysisAndPurification(String imei,IotProduct iotProduct, String topicStr, T payload ) throws InstantiationException, IllegalAccessException {
public ProtocolPurificationModel analysisAndPurification(String imei,IotProduct iotProduct, String topicStr, byte[] payload ) throws InstantiationException, IllegalAccessException {
Topic baseTopic = TopicUtil.initTopicFromModelStr(topicStr,"/{{roleid}}/{{username}}"); //我们定义的topic
//根据产品类型找到对应的解析服务
... ...
... ... @@ -8,7 +8,7 @@ import com.zhonglai.luhui.device.protocol.factory.analysis.topic.*;
import com.zhonglai.luhui.device.protocol.factory.dto.AnalysisResult;
import org.springframework.stereotype.Service;
public class DefaultProtocolParserFactoryImpl implements ProtocolParserFactory<byte[]>{
public class DefaultProtocolParserFactoryImpl implements ProtocolParserFactory{
private static final String topicModel = "/{{roleid}}/{{username}}/{{clientid}}/{{payloadtype}}/{{topicType}}";
@Override
... ...
... ... @@ -7,9 +7,8 @@ import com.zhonglai.luhui.device.protocol.factory.dto.AnalysisResult;
/**
* 协议解析工厂
* @param <T>
*/
public interface ProtocolParserFactory<T> {
public interface ProtocolParserFactory {
Topic analysisTopic(String topicStr);
AnalysisResult analysisPayload(Topic topic, T payload);
AnalysisResult analysisPayload(Topic topic, byte[] payload);
}
... ...
... ... @@ -48,7 +48,7 @@ public abstract class BaseCallback<T> {
/**
* 数据处理的工作流
*/
protected ProtocolPurificationModel messageArrived(String imei,String s,T payload) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
protected ProtocolPurificationModel messageArrived(String imei,String s,byte[] payload) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//判断网关是否存在
ParserDeviceHostDto oldparserDeviceHostDto = persistenceDBService.getOldParserDeviceHostDto(imei);
if(null == oldparserDeviceHostDto)
... ...
... ... @@ -282,4 +282,8 @@ public class DefaultDbService {
return baseDao.findBysql("SELECT * FROM `iot_product_payload_model_number` WHERE product_id=?", IotProductPayloadModelNumber.class,product_id);
}
public String getProductUsernames()
{
return baseDao.findBysql("SELECT GROUP_CONCAT(mqtt_username) users FROM `iot_product`",String.class);
}
}
... ...
... ... @@ -11,7 +11,7 @@ import com.zhonglai.luhui.device.protocol.factory.dto.AnalysisResult;
import com.zhonglai.luhui.device.protocol.uyu.analysis.topic.CkaDtSndR;
import com.zhonglai.luhui.device.protocol.uyu.analysis.topic.CkaDtSndS;
public class UyuProtocolParserFactoryImpl implements ProtocolParserFactory<byte[]> {
public class UyuProtocolParserFactoryImpl implements ProtocolParserFactory {
private static final String topicModel = "/{{roleid}}/{{username}}/{{clientid}}/{{payloadtype}}/{{topicType}}";
@Override
public Topic analysisTopic(String topicStr) {
... ...
... ... @@ -23,7 +23,7 @@ import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
public class ProtocolParserServiceImpl implements ProtocolParserFactory<byte[]> {
public class ProtocolParserServiceImpl implements ProtocolParserFactory{
private static final String topicModel = "/{{roleid}}/{{username}}/{{password}}/{{payloadtype}}/{{clientid}}/{{topicType}}";
@Override
... ...
... ... @@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
... ... @@ -60,11 +61,10 @@ public class HttpDataProxyController {
public AjaxResult post(@PathVariable String role, @PathVariable String username, @PathVariable String imei, @PathVariable Integer time, @PathVariable String topicType, HttpServletRequest request) throws IOException {
byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());
String body = new String(bodyBytes, request.getCharacterEncoding());
JsonObject jsonObject = GsonConstructor.get().fromJson(body,JsonObject.class);
String topic = "/"+role+"/"+username+"/"+imei+"/Json/"+topicType+"/"+time;
DeviceDataLog.info(imei, DataLogType.发来数据,topic,jsonObject);
DeviceDataLog.info(imei, DataLogType.发来数据,topic,body);
ProtocolPurificationModel protocolPurificationModel = httpCallback.messageArrived(imei,topic,jsonObject);
ProtocolPurificationModel protocolPurificationModel = httpCallback.messageArrived(imei,topic,body.getBytes(StandardCharsets.UTF_8));
Map<String,Object> map = HttpNoticeCach.consumer(imei);
if (null == map)
... ...
... ... @@ -19,9 +19,9 @@ import java.util.TimerTask;
@Service
public class HttpCallback extends BaseCallback<JsonObject> {
public class HttpCallback extends BaseCallback<byte[]> {
public ProtocolPurificationModel messageArrived(String imei, String topic, JsonObject data)
public ProtocolPurificationModel messageArrived(String imei, String topic, byte[] data)
{
try {
return HttpCallback.super.messageArrived(imei,topic,data);
... ...
... ... @@ -18,30 +18,102 @@
</properties>
<dependencies>
<!-- 核心模块-->
<!-- spring-boot-devtools -->
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>ruoyi-framework</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-common-datasource</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring框架基本的核心工具 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- yml解析器 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<!-- servlet包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<!-- 文档 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--https://mvnrepository.com/artifact/io.swagger/swagger-models-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger-models.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--&lt;!&ndash; https://mvnrepository.com/artifact/com.github.xiaoymin/swagger-bootstrap-ui &ndash;&gt;-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>${swagger-ui.version}</version>
</dependency>
<!-- 支持data -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--常用工具类 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-jar-device-analysis</artifactId>
<artifactId>lh-domain</artifactId>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-jar-device-service</artifactId>
<artifactId>lh-device-protocol-factory</artifactId>
</dependency>
</dependencies>
... ...
package com.zhonglai.luhui.http.service;
import com.ruoyi.framework.config.ResourcesConfig;
import com.zhonglai.luhui.device.service.DeviceControlService;
import com.zhonglai.luhui.device.analysis.comm.config.CacheConfig;
import com.zhonglai.luhui.device.analysis.comm.config.FastJson2JsonRedisSerializer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
... ... @@ -9,17 +9,15 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
@ComponentScan(basePackages = {
"com.ruoyi.common",
"com.ruoyi.framework",
"com.zhonglai.luhui.datasource",
"com.zhonglai.luhui.dao",
"com.zhonglai.luhui.device",
"com.zhonglai.luhui.redis",
"com.zhonglai.luhui.rocketmq",
"com.ruoyi.common.utils.spring",
"com.zhonglai.luhui.device.protocol",
"com.zhonglai.luhui.device.analysis.comm.config",
"com.zhonglai.luhui.http.service",
}
)
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
},excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = {CacheConfig.class, FastJson2JsonRedisSerializer.class}
) )
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class LhHttpServiceApplication {
public static void main(String[] args) {
SpringApplication.run(LhHttpServiceApplication.class,args);
... ...
package com.zhonglai.luhui.http.service.config;
import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
... ... @@ -18,9 +16,6 @@ import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
... ... @@ -39,13 +34,13 @@ public class SwaggerConfig {
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题:登陆服务")
.title("标题:http协议服务监听")
// 描述
.description("描述:各种角色的登陆接口")
.description("描述:用于通过http发送指令控制终端端操作")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
.contact(new Contact("", null, null))
// 版本
.version("版本号:" + ruoyiConfig.getVersion())
.version("版本号:1.1.1" )
.build();
}
... ...
package com.zhonglai.luhui.http.service.controller;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.zhonglai.luhui.device.analysis.comm.dto.ServerDto;
import com.zhonglai.luhui.device.analysis.comm.dto.business.BusinessDto;
import com.zhonglai.luhui.device.analysis.comm.dto.business.BusinessDtoClassNew;
import com.zhonglai.luhui.device.analysis.comm.factory.BusinessAgreement;
import com.zhonglai.luhui.device.analysis.comm.factory.BusinessAgreementFactory;
import com.zhonglai.luhui.device.analysis.comm.factory.Topic;
import com.zhonglai.luhui.device.analysis.comm.service.CacheService;
import com.zhonglai.luhui.device.analysis.comm.service.DataPersistenceService;
import com.zhonglai.luhui.device.domain.IotDevice;
import com.zhonglai.luhui.device.service.IIotDeviceService;
import com.zhonglai.luhui.http.service.util.HttpServletRequestUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Api(tags = "设备操作")
@RestController
@RequestMapping("/device")
public class DeviceServiceController {
private static final Logger log = LoggerFactory.getLogger(DeviceServiceController.class);
private static String authKey = "key";
@Autowired
private IIotDeviceService deviceService ;
@Autowired
private BusinessAgreementFactory businessAgreementFactory;
@Autowired
private CacheService cacheService; //数据缓存
@Autowired
private DataPersistenceService dataPersistenceService; //数据持久化
/**
* 添加校验
* @return
*/
@ModelAttribute
public void preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
{
String key = request.getParameter(authKey);
if(StringUtils.isNoneEmpty(key))
{
response.setStatus(403);
throw new ServiceException("验证失败");
}
}
@ApiOperation("更新指定设备的全部数据")
@RequestMapping(value = "putAllData/{deviceid}/{messageid}")
public AjaxResult putDeviceAllData(@PathVariable String deviceid,@PathVariable String messageid,HttpServletRequest request) throws Exception {
String str = HttpServletRequestUtil.getAllParametersAsJSON(request);
if(StringUtils.isEmpty(str))
{
return AjaxResult.error("数据为空");
}
String imei = deviceid.split("_")[0];
IotDevice iotDevice = deviceService.selectIotDeviceByClient_id(imei);
Topic topic = new Topic();
topic.setRoleid(iotDevice.getProduct_id()+"");
topic.setUsername(iotDevice.getMqtt_username());
topic.setClientid(iotDevice.getClient_id());
topic.setTopicType("PUT");
topic.setMessageid(messageid);
topic.setPayloadtype("json");
//转化为协议对象
BusinessDto businessDto = BusinessDtoClassNew.newBean(topic.getPayloadtype(),str.getBytes()).analyticalModel(iotDevice.getThings_model_value());
BusinessAgreement businessAgreement = businessAgreementFactory.createBusinessAgreement(topic);
//解析为业务对象
ServerDto dto = businessAgreement.analysis(topic,businessAgreement.toData(businessDto));
if(null == dto)
{
return AjaxResult.error("没有业务解析方法");
}
log.info("{} 解析到的dto【{}】",dto);
//缓存数据
cacheService.updateCache(topic,dto);
//数据持久化
dataPersistenceService.persistence(topic,dto);
return AjaxResult.success();
}
@ApiOperation("更新指定设备的部分数据")
@RequestMapping(value = "putPartialData/{deviceid}")
public AjaxResult putDevicePartialData(@PathVariable String deviceid)
{
return AjaxResult.success();
}
}
//package com.zhonglai.luhui.http.service.controller;
//
//import com.ruoyi.common.core.domain.AjaxResult;
//import com.ruoyi.common.exception.ServiceException;
//import com.ruoyi.common.utils.StringUtils;
//import com.zhonglai.luhui.device.analysis.comm.dto.ServerDto;
//import com.zhonglai.luhui.device.analysis.comm.dto.business.BusinessDto;
//import com.zhonglai.luhui.device.analysis.comm.dto.business.BusinessDtoClassNew;
//import com.zhonglai.luhui.device.analysis.comm.factory.BusinessAgreement;
//import com.zhonglai.luhui.device.analysis.comm.factory.BusinessAgreementFactory;
//import com.zhonglai.luhui.device.analysis.comm.factory.Topic;
//import com.zhonglai.luhui.device.analysis.comm.service.CacheService;
//import com.zhonglai.luhui.device.analysis.comm.service.DataPersistenceService;
//import com.zhonglai.luhui.device.domain.IotDevice;
//import com.zhonglai.luhui.device.service.IIotDeviceService;
//import com.zhonglai.luhui.http.service.util.HttpServletRequestUtil;
//import io.swagger.annotations.Api;
//import io.swagger.annotations.ApiOperation;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.web.bind.annotation.ModelAttribute;
//import org.springframework.web.bind.annotation.PathVariable;
//import org.springframework.web.bind.annotation.RequestMapping;
//import org.springframework.web.bind.annotation.RestController;
//
//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//
//@Api(tags = "设备操作")
//@RestController
//@RequestMapping("/device")
//public class DeviceServiceController {
//
// private static final Logger log = LoggerFactory.getLogger(DeviceServiceController.class);
//
// private static String authKey = "key";
//
// @Autowired
// private IIotDeviceService deviceService ;
//
// @Autowired
// private BusinessAgreementFactory businessAgreementFactory;
//
// @Autowired
// private CacheService cacheService; //数据缓存
//
// @Autowired
// private DataPersistenceService dataPersistenceService; //数据持久化
//
// /**
// * 添加校验
// * @return
// */
// @ModelAttribute
// public void preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
// {
// String key = request.getParameter(authKey);
// if(StringUtils.isNoneEmpty(key))
// {
// response.setStatus(403);
// throw new ServiceException("验证失败");
// }
//
// }
//
// @ApiOperation("更新指定设备的全部数据")
// @RequestMapping(value = "putAllData/{deviceid}/{messageid}")
// public AjaxResult putDeviceAllData(@PathVariable String deviceid,@PathVariable String messageid,HttpServletRequest request) throws Exception {
// String str = HttpServletRequestUtil.getAllParametersAsJSON(request);
//
// if(StringUtils.isEmpty(str))
// {
// return AjaxResult.error("数据为空");
// }
//
// String imei = deviceid.split("_")[0];
// IotDevice iotDevice = deviceService.selectIotDeviceByClient_id(imei);
// Topic topic = new Topic();
// topic.setRoleid(iotDevice.getProduct_id()+"");
// topic.setUsername(iotDevice.getMqtt_username());
// topic.setClientid(iotDevice.getClient_id());
// topic.setTopicType("PUT");
// topic.setMessageid(messageid);
// topic.setPayloadtype("json");
//
// //转化为协议对象
// BusinessDto businessDto = BusinessDtoClassNew.newBean(topic.getPayloadtype(),str.getBytes()).analyticalModel(iotDevice.getThings_model_value());
//
// BusinessAgreement businessAgreement = businessAgreementFactory.createBusinessAgreement(topic);
// //解析为业务对象
// ServerDto dto = businessAgreement.analysis(topic,businessAgreement.toData(businessDto));
// if(null == dto)
// {
// return AjaxResult.error("没有业务解析方法");
// }
// log.info("{} 解析到的dto【{}】",dto);
//
// //缓存数据
// cacheService.updateCache(topic,dto);
//
// //数据持久化
// dataPersistenceService.persistence(topic,dto);
// return AjaxResult.success();
// }
//
// @ApiOperation("更新指定设备的部分数据")
// @RequestMapping(value = "putPartialData/{deviceid}")
// public AjaxResult putDevicePartialData(@PathVariable String deviceid)
// {
// return AjaxResult.success();
// }
//}
... ...
package com.zhonglai.luhui.http.service.controller;
import com.zhonglai.luhui.device.service.IIotDeviceService;
import com.zhonglai.luhui.http.service.model.LogMessageDto;
import com.zhonglai.luhui.http.service.service.HttpCallback;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.nio.charset.StandardCharsets;
@Api(tags = "首页")
@RestController
@RequestMapping("/")
public class IndexController {
@Autowired
private HttpCallback httpCallback ;
@ApiOperation("获取一天的日志")
@ApiImplicitParams({
@ApiImplicitParam(value = "时间",name = "time"),
@ApiImplicitParam(value = "设备imei",name = "clientId"),
@ApiImplicitParam(value = "产品账号",name = "username"),
@ApiImplicitParam(value = "topic",name = "topic"),
@ApiImplicitParam(value = "数据消息",name = "message"),
})
@PostMapping(value = "logpost")
public void logpost(@RequestBody LogMessageDto logMessageDto) {
httpCallback.messageArrived(logMessageDto.getClientId(),logMessageDto.getTopic(),logMessageDto.getMessage().getBytes(StandardCharsets.UTF_8));
}
}
... ...
package com.zhonglai.luhui.http.service.model;
public class LogMessageDto {
private String time;
private String username;
private String clientId;
private String topic;
private String message;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
... ...
package com.zhonglai.luhui.http.service.service;
import com.ruoyi.common.utils.StringUtils;
import com.zhonglai.luhui.device.analysis.comm.config.SysParameter;
import com.zhonglai.luhui.device.protocol.factory.dto.ProtocolPurificationModel;
import com.zhonglai.luhui.device.protocol.factory.service.BaseCallback;
import com.zhonglai.luhui.device.protocol.factory.service.impl.DefaultDbService;
import org.aspectj.lang.annotation.AfterReturning;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@Service
public class HttpCallback extends BaseCallback<byte[]> {
@Value("${sys.register-product}")
private String registerProduct; //客户端操作时间
@Autowired
private DefaultDbService defaultDbService;
@Value("${server.port}")
private long port;
public ProtocolPurificationModel messageArrived(String imei, String topic, byte[] data)
{
try {
return HttpCallback.super.messageArrived(imei,topic,data);
} catch (Exception e) {
log.error(imei+"解析数据失败",e);
}
return null;
}
@EventListener(ApplicationReadyEvent.class)
public void registerService()
{
if(StringUtils.isBlank(registerProduct))
{
registerProduct =defaultDbService.getProductUsernames();
}
for (String product:registerProduct.split(","))
{
JedisService.registerLogListenService(product, SysParameter.service_ip+":"+port);
}
}
}
... ...
package com.zhonglai.luhui.http.service.service;
import com.zhonglai.luhui.device.protocol.factory.control.ClienNoticeServiceFactory;
import com.zhonglai.luhui.device.protocol.factory.dto.NoticeMessageDto;
import org.springframework.stereotype.Service;
@Service
public class HttpClienNoticeServiceImpl implements ClienNoticeServiceFactory {
@Override
public boolean sendMessage(NoticeMessageDto noticeMessageDomain) {
return false;
}
}
... ...
package com.zhonglai.luhui.http.service.service;
import cn.hutool.db.nosql.redis.RedisDS;
import java.util.Map;
public class JedisService {
private static RedisDS jedis = RedisDS.create();
public static Map<String,String> getLogListenServiceRegister()
{
return jedis.getJedis().hgetAll("log-listen-service-register");
}
public static Long registerLogListenService(String username,String host)
{
return jedis.getJedis().hset("log-listen-service-register",username,host);
}
public static void main(String[] args) {
jedis.getJedis().hset("log-listen-service-register", "6_WP","localhost:8443");
jedis.getJedis().hset("log-listen-service-register", "6_WP","localhost:84431");
System.out.println(jedis.getJedis().hget("log-listen-service-register","6_WP"));
}
}
... ...
restart.include.json=/com.alibaba.fastjson.*.jar
\ No newline at end of file
# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: D:/ruoyi/uploadPath # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 captchaType: math # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8065 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 # Spring配置 spring: # 资源信息 messages: # 国际化资源文件路径 basename: i18n/messages profiles: active: druid # 文件上传 servlet: multipart: # 单个文件大小 max-file-size: 10MB # 设置总上传的文件大小 max-request-size: 20MB # 服务模块 devtools: restart: # 热部署开关 enabled: true ##redic配置 redis: database: 1 # Redis服务器地址 写你的ip host: 47.112.163.61 # Redis服务器连接端口 port: 9527 # Redis服务器连接密码(默认为空) password: Luhui586 # 连接池最大连接数(使用负值表示没有限制 类似于mysql的连接池 jedis: pool: max-active: 200 # 连接池最大阻塞等待时间(使用负值表示没有限制) 表示连接池的链接拿完了 现在去申请需要等待的时间 max-wait: -1 # 连接池中的最大空闲连接 max-idle: 10 # 连接池中的最小空闲连接 min-idle: 0 # 连接超时时间(毫秒) 去链接redis服务端 timeout: 6000 # MyBatis配置 mybatis: # 搜索指定包别名 typeAliasesPackage: com.ruoyi.**.domain,com.zhonglai.luhui.**.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/* sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /** redis: field: "lh:mqttservice:" isText: false # NameServer地址 rocketmq: name-server: 47.115.144.179:9876 # 默认的消息组 producer: group: deviceCommand send-message-timeout: 30000 send-topic: lh-http-service-deviceCommand-test send-tags: 1
\ No newline at end of file
# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 实例演示开关 demoEnabled: true # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) profile: D:/ruoyi/uploadPath # 获取ip地址开关 addressEnabled: false # 验证码类型 math 数组计算 char 字符验证 captchaType: math # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8065 servlet: # 应用的访问路径 context-path: / tomcat: # tomcat的URI编码 uri-encoding: UTF-8 # 连接数满后的排队数,默认为100 accept-count: 1000 threads: # tomcat最大线程数,默认为200 max: 800 # Tomcat启动初始化的线程数,默认值10 min-spare: 100 mqtt: client: #客户端操作时间 operationTime: 10 sys: register-product: 6_WP #rocketmq配置信息 rocketmq: #nameservice服务器地址(多个以英文逗号隔开) name-server: 47.115.144.179:9876 consumerGroup: lh-http-service topic: deviceCommandListen operationToken: ${random.uuid}
\ No newline at end of file
... ...
#-------------------------------------------------------------------------------
# Redis客户端配置样例
# 每一个分组代表一个Redis实例
# 无分组的Pool配置为所有分组的共用配置,如果分组自己定义Pool配置,则覆盖共用配置
# 池配置来自于:https://www.cnblogs.com/jklk/p/7095067.html
#-------------------------------------------------------------------------------
#----- 默认(公有)配置
# 地址,默认localhost
host = 47.112.163.61
# 端口,默认6379
port = 9527
# 超时,默认2000
timeout = 2000
# 连接超时,默认timeout
connectionTimeout = 2000
# 读取超时,默认timeout
soTimeout = 2000
# 密码,默认无
password = Luhui586
# 数据库序号,默认0
database = 0
# 客户端名,默认"Hutool"
clientName = Hutool
# SSL连接,默认false
ssl = false;
#----- 自定义分组的连接
[custom]
# 地址,默认localhost
host = 47.112.163.61
# 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
BlockWhenExhausted = true;
# 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
evictionPolicyClassName = org.apache.commons.pool2.impl.DefaultEvictionPolicy
# 是否启用pool的jmx管理功能, 默认true
jmxEnabled = true;
# 是否启用后进先出, 默认true
lifo = true;
# 最大空闲连接数, 默认8个
maxIdle = 8
# 最小空闲连接数, 默认0
minIdle = 0
# 最大连接数, 默认8个
maxTotal = 8
# 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
maxWaitMillis = -1
# 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
minEvictableIdleTimeMillis = 1800000
# 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
numTestsPerEvictionRun = 3;
# 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
SoftMinEvictableIdleTimeMillis = 1800000
# 在获取连接的时候检查有效性, 默认false
testOnBorrow = false
# 在空闲时检查有效性, 默认false
testWhileIdle = false
# 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
timeBetweenEvictionRunsMillis = -1
... ...
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟
user.password.delete=对不起,您的账号已被删除
user.blocked=用户已封禁,请联系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志存放路径 -->
<property name="log.path" value="logs" />
<!-- 日志输出格式 -->
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 用户访问日志输出 -->
<appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-user.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
</appender>
<!-- 系统模块日志级别控制 -->
<logger name="com.ruoyi" level="info" />
<!-- Spring日志级别控制 -->
<logger name="org.springframework" level="warn" />
<root level="info">
<appender-ref ref="console" />
</root>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="file_info" />
<appender-ref ref="file_error" />
</root>
<!--系统用户操作日志-->
<logger name="sys-user" level="info">
<appender-ref ref="sys-user"/>
</logger>
</configuration>
\ No newline at end of file
... ... @@ -29,7 +29,7 @@ mqtt:
client:
#客户端操作时间
operationTime: 10
productids: 30
productids: 17
#rocketmq配置信息
rocketmq:
... ...
package com.zhonglai.luhui.mqtt;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
@ChannelHandler.Sharable
public class LogServerHandler extends SimpleChannelInboundHandler<String> {
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("Connection established: " + ctx.channel().remoteAddress());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("Connection closed: " + ctx.channel().remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close(); // 关闭有异常的连接
}
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
// 处理接收到的日志信息,这里直接输出到控制台
System.out.println("Received log: " + s);
}
}
\ No newline at end of file
... ...
package com.zhonglai.luhui.mqtt;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class LogServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new StringDecoder()); // 将字节解码为字符串
ch.pipeline().addLast(new StringEncoder()); // 将字符串编码为字节
ch.pipeline().addLast(new LogServerHandler()); // 自定义日志处理器
}
}
\ No newline at end of file
... ...
package com.zhonglai.luhui.mqtt;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class TestMain {
private final int port;
public TestMain(int port) {
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new LogServerInitializer())
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 启动服务器
ChannelFuture future = bootstrap.bind(port).sync();
System.out.println("Log Server started on port: " + port);
// 等待服务器关闭
future.channel().closeFuture().sync();
} finally {
// 关闭线程组
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new TestMain(8080).start(); // 监听端口 5000
}
}
... ...
... ... @@ -89,7 +89,7 @@ public class TerminalService {
public void subscribe() throws MqttException {
List<String> ts = getCompletionTopics();
mqttclient.subscribe(ts.toArray(new String[ts.size()]));
// mqttclient.subscribe(ts.toArray(new String[ts.size()]));
}
public List<String> getCompletionTopics()
... ...
... ... @@ -41,7 +41,7 @@ spring:
mqtt:
#链接地址
broker: tcp://175.24.61.68:1883
broker: tcp://iot.yu2le.com:1883
#唯一标识
clientId: ${random.uuid}
#公司id
... ... @@ -67,6 +67,6 @@ sys:
#rocketmq配置信息
rocketmq:
#nameservice服务器地址(多个以英文逗号隔开)
name-server: 47.115.144.179:9876
name-server: 8.129.224.117:9876
send-topic: lh-mqtt-service-deviceCommand-test
send-tag: 1
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>lh-ssh-service-lesten</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
... ...
package com.zhonglai.luhui.http.service.ssh;
import com.zhonglai.luhui.http.service.ssh.service.SSHTailService;
public class SshServiceLestenMain {
public static void main(String[] args) {
SSHTailService sshTailService = new SSHTailService();
sshTailService.startListening();
}
}
... ...
package com.zhonglai.luhui.http.service.ssh.config;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
public class ThreadPool {
//业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。
private final static ExecutorService executorService = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors() * 2, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(2000));
//定时任务线程池
private final static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
public static ExecutorService getExecutorService() {
return executorService;
}
public static ScheduledExecutorService getScheduledExecutorService() {
return scheduledExecutorService;
}
}
... ...
package com.zhonglai.luhui.http.service.ssh.config;
import com.zhonglai.luhui.http.service.ssh.service.JedisService;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* topic订阅服务器配置
*/
public class TopicSubscribeService {
private static Map<String,String> lisenServiceRegister = new HashMap<>();
public static void init()
{
//一分钟更新一次,监听服务器的注册信息
ThreadPool.getScheduledExecutorService().scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Map<String,String> map = JedisService.getLogListenServiceRegister();
if(null != map && map.size() != 0)
{
for (String key : map.keySet())
{
lisenServiceRegister.put(key,map.get(key));
}
}
}
},0,60, TimeUnit.SECONDS);
}
public static String getTopicSubscribeService(String username)
{
return lisenServiceRegister.get(username);
}
}
... ...
package com.zhonglai.luhui.http.service.ssh.dto;
public class LogMessageDto {
private String time;
private String username;
private String clientId;
private String topic;
private String message;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
... ...
package com.zhonglai.luhui.http.service.ssh.service;
import cn.hutool.db.nosql.redis.RedisDS;
import java.util.Map;
public class JedisService {
private static RedisDS jedis = RedisDS.create();
public static Map<String,String> getLogListenServiceRegister()
{
return jedis.getJedis().hgetAll("log-listen-service-register");
}
public static void main(String[] args) {
jedis.getJedis().hset("log-listen-service-register", "6_WP","localhost:8443");
jedis.getJedis().hset("log-listen-service-register", "6_WP","localhost:84431");
System.out.println(jedis.getJedis().hget("log-listen-service-register","6_WP"));
}
}
... ...
package com.zhonglai.luhui.http.service.ssh.service;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.zhonglai.luhui.http.service.ssh.config.ThreadPool;
import com.zhonglai.luhui.http.service.ssh.config.TopicSubscribeService;
import com.zhonglai.luhui.http.service.ssh.dto.LogMessageDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SSHTailService {
private static Logger log = LoggerFactory.getLogger(SSHTailService.class);
private static final Pattern LOG_PATTERN = Pattern.compile("【(.*?)】【(.*?)】【(.*?)】【(.*?)】(.*)");
private String host;
private String user;
private String password;
private String logFilePath;
private Session session = null; // SSH 会话
private Channel channel = null; // SSH 通道
public void init() {
this.host = "175.24.61.68";
this.user = "root";
this.password = "Luhui586";
this.logFilePath = "/root/hivemq-ce-2021.2/log/mylogs/message.log";
}
public BufferedReader open()
{
try {
// 建立 SSH 连接
JSch jsch = new JSch();
session = jsch.getSession(user, host, 22);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
session.setConfig("ServerAliveInterval", "60"); // 保持连接
session.connect();
System.out.println("SSH连接成功");
// 打开 Shell 通道
channel = session.openChannel("shell");
InputStream in = channel.getInputStream();
OutputStream out = channel.getOutputStream();
channel.connect();
// 发送 tail 命令
out.write(("tail -f " + logFilePath + "\n").getBytes());
out.flush();
// 读取并解析日志行
return new BufferedReader(new InputStreamReader(in));
} catch (Exception e) {
log.error("ssh服务启动失败",e);
}
return null;
}
public void startListening() {
try {
init();
BufferedReader bufferedReader = open();
if(null != bufferedReader)
{
read(bufferedReader);
}
}catch (Exception e)
{
log.error("执行错误",e);
}finally {
close();
startListening();
}
}
public void read(BufferedReader bufferedReader)
{
try {
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println("读取到一行: " + line);
LogMessageDto logMessageDto = parseLogLine(line);
if(null !=logMessageDto && null != logMessageDto.getMessage() && !"".equals(logMessageDto.getMessage()))
{
String host = TopicSubscribeService.getTopicSubscribeService(logMessageDto.getUsername());
if(null != host && host.length()!=0)
{
ThreadPool.getExecutorService().execute(() -> {
HttpUtil.post("http://"+host+"/logpost", JSONUtil.toJsonStr(logMessageDto));
});
}
}
}
}catch (Exception e)
{
log.error("读取日志信息异常",e);
}finally {
close();
}
}
public void close()
{
// 关闭资源
if (channel != null && channel.isConnected()) {
channel.disconnect();
}
if (session != null && session.isConnected()) {
session.disconnect();
}
}
private LogMessageDto parseLogLine(String line) {
// 根据日志格式解析内容
Matcher matcher = LOG_PATTERN.matcher(line);
if (matcher.matches()) {
LogMessageDto logMessageDto = new LogMessageDto();
logMessageDto.setTime(matcher.group(1));
logMessageDto.setUsername(matcher.group(2));
logMessageDto.setClientId(matcher.group(3));
logMessageDto.setTopic(matcher.group(4));
logMessageDto.setMessage(matcher.group(5));
if(null != logMessageDto.getMessage() )
{
logMessageDto.setMessage(logMessageDto.getMessage().replace("|||","\n"));
}
return logMessageDto;
} else {
log.error("日志行格式不正确: " + line);
}
return null;
}
}
... ...
#-------------------------------------------------------------------------------
# Redis客户端配置样例
# 每一个分组代表一个Redis实例
# 无分组的Pool配置为所有分组的共用配置,如果分组自己定义Pool配置,则覆盖共用配置
# 池配置来自于:https://www.cnblogs.com/jklk/p/7095067.html
#-------------------------------------------------------------------------------
#----- 默认(公有)配置
# 地址,默认localhost
host = 47.112.163.61
# 端口,默认6379
port = 9527
# 超时,默认2000
timeout = 2000
# 连接超时,默认timeout
connectionTimeout = 2000
# 读取超时,默认timeout
soTimeout = 2000
# 密码,默认无
password = Luhui586
# 数据库序号,默认0
database = 0
# 客户端名,默认"Hutool"
clientName = Hutool
# SSL连接,默认false
ssl = false;
#----- 自定义分组的连接
[custom]
# 地址,默认localhost
host = 47.112.163.61
# 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
BlockWhenExhausted = true;
# 设置的逐出策略类名, 默认DefaultEvictionPolicy(当连接超过最大空闲时间,或连接数超过最大空闲连接数)
evictionPolicyClassName = org.apache.commons.pool2.impl.DefaultEvictionPolicy
# 是否启用pool的jmx管理功能, 默认true
jmxEnabled = true;
# 是否启用后进先出, 默认true
lifo = true;
# 最大空闲连接数, 默认8个
maxIdle = 8
# 最小空闲连接数, 默认0
minIdle = 0
# 最大连接数, 默认8个
maxTotal = 8
# 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
maxWaitMillis = -1
# 逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
minEvictableIdleTimeMillis = 1800000
# 每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
numTestsPerEvictionRun = 3;
# 对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
SoftMinEvictableIdleTimeMillis = 1800000
# 在获取连接的时候检查有效性, 默认false
testOnBorrow = false
# 在空闲时检查有效性, 默认false
testWhileIdle = false
# 逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
timeBetweenEvictionRunsMillis = -1
... ...
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-superweb-jar</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>lh-web-douyin</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 通用工具-->
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-domain</artifactId>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-public-dao</artifactId>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-common-datasource</artifactId>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-jar-action</artifactId>
</dependency>
<!-- 文档 -->
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-common-swagger</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
... ...
package com.zhonglai.luhui.web.douyin.config;
import com.ruoyi.common.config.RuoYiConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class DouyinSwaggerConfig {
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
@Bean
public Docket createDouyinRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("抖音")
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.zhonglai.luhui.web.douyin.controller"))
.paths(PathSelectors.any())
.build();
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo()
{
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题:抖音业务")
// 描述
.description("描述:抖音相关业务")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
// 版本
.version("版本号:" + ruoyiConfig.getVersion())
.build();
}
}
\ No newline at end of file
... ...
package com.zhonglai.luhui.web.douyin.config;
public class DouyingConfig {
public static String client_key = "aw1lv7cqpap1nm2x";
public static String client_secret = "c006743430919ad918c0951acece4fa4";
}
... ...
package com.zhonglai.luhui.web.douyin.controller;
import cn.hutool.http.HttpUtil;
import com.ruoyi.common.core.domain.Message;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.GsonConstructor;
import com.zhonglai.luhui.action.BaseController;
import com.zhonglai.luhui.dao.service.PublicService;
import com.zhonglai.luhui.web.douyin.config.DouyingConfig;
import com.zhonglai.luhui.web.douyin.util.dto.DyAccessTokenResponsData;
import com.zhonglai.luhui.web.douyin.util.DouyinFunctionUtil;
import com.zhonglai.luhui.web.douyin.util.dto.DyRespons;
import com.zhonglai.luhui.web.douyin.util.dto.DyUserinfoResponsData;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
@Api(tags = "抖音用户业务")
@Controller
@RequestMapping("/douyin/douyinUser")
public class DouyinUserController extends BaseController {
@Autowired
private PublicService publicService;
@ApiOperation("授权")
@GetMapping(value = "/oauthUrl")
public void oauthUrl(String okRetunUrl, HttpServletRequest httpServletRequest, HttpServletResponse respons) throws IOException {
String scopes = "user_info";
// String scopes = "user_info,open.account.data,im.direct_message,item.comment";
String redirect_uri = "https://"+httpServletRequest.getServerName()+httpServletRequest.getContextPath()+"/douyin/douyinUser/callback";
String url = DouyinFunctionUtil.oauthUrl(scopes,okRetunUrl,redirect_uri);
logger.info("抖音授权链接【{}】",url);
// 重定向到生成的URL
respons.sendRedirect(url);
}
@ApiOperation("获取用户信息")
@Transactional
@RequestMapping(value = "/callback")
public void callback(String code,String state,HttpServletResponse respons) throws IOException {
String okRetunUrl = state;
DyRespons<DyAccessTokenResponsData> dyRespons = DouyinFunctionUtil.accessToken(code);
switch (dyRespons.getMessage())
{
case "success":
DyAccessTokenResponsData oauthAccessTokenOkData = dyRespons.getData();
Long lg = publicService.selectCountBySql("select count(*) ct from `liu_yu_le`.`dy_oauth_access_token` where open_id='"+oauthAccessTokenOkData.getOpen_id()+"'");
if(lg>0)
{
publicService.updateBySql("UPDATE `liu_yu_le`.`dy_oauth_access_token` SET access_token='"+oauthAccessTokenOkData.getAccess_token()+"',expires_in="+(oauthAccessTokenOkData.getExpires_in()+DateUtils.getNowTimeMilly())+",refresh_token='"+oauthAccessTokenOkData.getRefresh_token()+"',refresh_expires_in="+(oauthAccessTokenOkData.getRefresh_expires_in()+DateUtils.getNowTimeMilly())+",scope='"+oauthAccessTokenOkData.getScope()+"' WHERE open_id='"+oauthAccessTokenOkData.getOpen_id()+"'");
}else{
publicService.updateBySql("INSERT INTO `liu_yu_le`.`dy_oauth_access_token`(`open_id`,`access_token`,`expires_in`,`refresh_token`,`refresh_expires_in`,`scope`,`create_time`) VALUES('"+oauthAccessTokenOkData.getOpen_id()+"','"+oauthAccessTokenOkData.getAccess_token()+"',"+(oauthAccessTokenOkData.getExpires_in()+DateUtils.getNowTimeMilly())+",'"+oauthAccessTokenOkData.getRefresh_token()+"',"+(oauthAccessTokenOkData.getRefresh_expires_in()+DateUtils.getNowTimeMilly())+",'"+oauthAccessTokenOkData.getScope()+"',"+ DateUtils.getNowTimeMilly() +")");
}
lg = publicService.selectCountBySql("select count(*) ct from `liu_yu_le`.`dy_userinfo` where open_id='"+oauthAccessTokenOkData.getOpen_id()+"'");
DyRespons<DyUserinfoResponsData> dyUserinfoResponsDataDyRespons = DouyinFunctionUtil.userinfo(oauthAccessTokenOkData.getAccess_token(),oauthAccessTokenOkData.getOpen_id());
DyUserinfoResponsData dyUserinfoResponsData = dyUserinfoResponsDataDyRespons.getData();
if(lg>0)
{
publicService.updateBySql("UPDATE `liu_yu_le`.`dy_userinfo` SET union_id='"+dyUserinfoResponsData.getUnion_id()+"',nickname='"+dyUserinfoResponsData.getNickname()+"',avatar='"+dyUserinfoResponsData.getAvatar()+"' WHERE open_id='"+dyUserinfoResponsData.getOpen_id()+"'");
}else{
publicService.updateBySql("INSERT INTO `liu_yu_le`.`dy_userinfo`(`open_id`,`union_id`,`nickname`,`avatar`,`create_time`) VALUES('"+dyUserinfoResponsData.getOpen_id()+"','"+dyUserinfoResponsData.getUnion_id()+"','"+dyUserinfoResponsData.getNickname()+"','"+dyUserinfoResponsData.getAvatar()+"',"+ DateUtils.getNowTimeMilly() +")");
}
okRetunUrl = URLDecoder.decode(okRetunUrl, "UTF-8");
// 重定向到生成的URL
String reUrl = okRetunUrl+"?open_id="+oauthAccessTokenOkData.getOpen_id();
logger.info("获取用户成功重定向到指定页面:{}",reUrl);
respons.sendRedirect(reUrl);
break;
default:
// 构建错误信息的HTML
String errorMessageHtml = "<html><body><h1>错误信息</h1><p>" + GsonConstructor.get().toJson(dyRespons) + "</p></body></html>";
// 设置响应的内容类型和字符编码
respons.setContentType("text/html; charset=UTF-8");
// 输出HTML内容
try (PrintWriter out = respons.getWriter()) {
out.print(errorMessageHtml);
}
break;
}
}
}
... ...
package com.zhonglai.luhui.web.douyin.controller;
import com.google.gson.JsonObject;
import com.ruoyi.common.utils.GsonConstructor;
import com.zhonglai.luhui.action.BaseController;
import com.zhonglai.luhui.web.douyin.config.DouyingConfig;
import com.zhonglai.luhui.web.douyin.dto.DouyinCommentReply;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;
@Api(tags = "webhooks")
@RestController
@RequestMapping("/douyin/webhooks")
public class WebhooksController extends BaseController {
@ApiOperation("webhooks")
@PostMapping(value = "")
@ResponseBody
public String webhooks(HttpServletRequest httpServletRequest)
{
try {
String sReqData = new String(StreamUtils.copyToByteArray(httpServletRequest.getInputStream()));
String signature = httpServletRequest.getHeader("X-Douyin-Signature");
if(validateSignature(signature,sReqData))
{
JsonObject jsonObject = GsonConstructor.get().fromJson(sReqData, JsonObject.class);
if(jsonObject.has("event"))
{
switch (jsonObject.get("event").getAsString())
{
case "verify_webhook":
JsonObject challenge = jsonObject.get("content").getAsJsonObject();
return challenge.toString();
case "item_comment_reply":
DouyinCommentReply douyinCommentReply = GsonConstructor.get().fromJson(sReqData, DouyinCommentReply.class);
return "";
default:
logger.info("无法识别的操作:{}",jsonObject);
return "";
}
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return "";
}
public boolean validateSignature(String signature,String sReqData) {
if (signature == null || signature.isEmpty()) {
return false;
}
try {
String calculatedSignature = calculateSignature(DouyingConfig.client_secret, sReqData);
return Objects.equals(signature, calculatedSignature);
} catch (Exception e) {
return false;
}
}
private String calculateSignature(String clientSecret, String body) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] hash = digest.digest((clientSecret + body).getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
}
... ...
package com.zhonglai.luhui.web.douyin.dto;
/**
* 评论消息内容
* "comment_id":"", //评论id
* "comment_user_id", //发评论的用户openid
* "content":, //评论内容
* "create_time":123123, //评论创建时间(秒级时间戳)
* "digg_count":0, //该评论的点赞量
* "reply_comment_total":1, //该评论下的回复评论量
* "reply_to_comment_id":"", //该评论回复的上一级评论的评论id
* "reply_to_item_id":"@9VxT0uuFWsE7M3Koc4olFM791WbsNPGKOp1wrgiiJ1ERbfD060zdRmYqig357zEB1tSgbExoci7R0e1yFxAIMw==",//该评论回复的视频id
* "at_user_id": "", //评论@的用户uid
* "avatar": "https://uuue/ehdne", //评论发送方的头像
* "nick_name": "xiaoming" //评论发送方的昵称
* "parent_id":"" //二级评论和三级评论的parentId是所属的一级评论id,一级评论parentId为视频ID
*/
public class DouyinCommentContent {
/** 评论id */
private String comment_id;
/** 发评论的用户openid */
private String comment_user_id;
/** 评论内容 */
private String content;
/** 评论创建时间(秒级时间戳) */
private Long create_time;
/** 该评论的点赞量 */
private Integer digg_count;
/** 该评论下的回复评论量 */
private Integer reply_comment_total;
/** 该评论回复的上一级评论的评论id */
private String reply_to_comment_id;
/** 该评论回复的视频id */
private String reply_to_item_id;
/** 评论@的用户uid */
private String at_user_id;
/** 评论发送方的头像 */
private String avatar;
/** 评论发送方的昵称 */
private String nick_name;
/** 所属的一级评论id */
private String parent_id;
public String getComment_id() {
return comment_id;
}
public void setComment_id(String comment_id) {
this.comment_id = comment_id;
}
public String getComment_user_id() {
return comment_user_id;
}
public void setComment_user_id(String comment_user_id) {
this.comment_user_id = comment_user_id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Long getCreate_time() {
return create_time;
}
public void setCreate_time(Long create_time) {
this.create_time = create_time;
}
public Integer getDigg_count() {
return digg_count;
}
public void setDigg_count(Integer digg_count) {
this.digg_count = digg_count;
}
public Integer getReply_comment_total() {
return reply_comment_total;
}
public void setReply_comment_total(Integer reply_comment_total) {
this.reply_comment_total = reply_comment_total;
}
public String getReply_to_comment_id() {
return reply_to_comment_id;
}
public void setReply_to_comment_id(String reply_to_comment_id) {
this.reply_to_comment_id = reply_to_comment_id;
}
public String getReply_to_item_id() {
return reply_to_item_id;
}
public void setReply_to_item_id(String reply_to_item_id) {
this.reply_to_item_id = reply_to_item_id;
}
public String getAt_user_id() {
return at_user_id;
}
public void setAt_user_id(String at_user_id) {
this.at_user_id = at_user_id;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getNick_name() {
return nick_name;
}
public void setNick_name(String nick_name) {
this.nick_name = nick_name;
}
public String getParent_id() {
return parent_id;
}
public void setParent_id(String parent_id) {
this.parent_id = parent_id;
}
}
... ...
package com.zhonglai.luhui.web.douyin.dto;
/**
* 评论消息
* "event": "item_comment_reply",
* "from_user_id": "", //发送评论的用户openid
* "to_user_id": "", //创建视频的用户openId
* "client_key": "", //创建视频用户授权的应用id
* "content": {} //评论内容
*/
public class DouyinCommentReply {
/** 发送评论的用户openid */
private String from_user_id;
/** 创建视频的用户openId */
private String to_user_id;
/** 创建视频用户授权的应用id */
private String client_key;
/** 评论内容 */
private DouyinCommentContent content;
public String getFrom_user_id() {
return from_user_id;
}
public void setFrom_user_id(String from_user_id) {
this.from_user_id = from_user_id;
}
public String getTo_user_id() {
return to_user_id;
}
public void setTo_user_id(String to_user_id) {
this.to_user_id = to_user_id;
}
public String getClient_key() {
return client_key;
}
public void setClient_key(String client_key) {
this.client_key = client_key;
}
public DouyinCommentContent getContent() {
return content;
}
public void setContent(DouyinCommentContent content) {
this.content = content;
}
}
... ...
package com.zhonglai.luhui.web.douyin.util;
import cn.hutool.http.HttpUtil;
import com.google.gson.reflect.TypeToken;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.GsonConstructor;
import com.zhonglai.luhui.web.douyin.config.DouyingConfig;
import com.zhonglai.luhui.web.douyin.util.dto.DyAccessTokenResponsData;
import com.zhonglai.luhui.web.douyin.util.dto.DyRespons;
import com.zhonglai.luhui.web.douyin.util.dto.DyUserinfoResponsData;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
public class DouyinFunctionUtil {
/**
* 抖音获取授权码
* @param scopes
* @param okRetunUrl
* @param redirect_uri
* @return
*/
public static String oauthUrl(String scopes,String okRetunUrl,String redirect_uri)
{
StringBuffer url = new StringBuffer("https://open.douyin.com/platform/oauth/connect?");
url.append("client_key=").append(DouyingConfig.client_key);
url.append("&response_type=code");
url.append("&scope=");
url.append(scopes);
String encodedOkReturnUrl = null;
try {
encodedOkReturnUrl = URLEncoder.encode(okRetunUrl, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
url.append("&redirect_uri=").append(redirect_uri);
url.append("&state="+encodedOkReturnUrl);
return url.toString();
}
/**
* 获取 access_token
* @param code
* @return
*/
public static DyRespons<DyAccessTokenResponsData> accessToken(String code)
{
String url = "https://open.douyin.com/oauth/access_token/";
Map<String,Object> map = new HashMap<>();
map.put("client_key",DouyingConfig.client_key);
map.put("client_secret",DouyingConfig.client_secret);
map.put("code",code);
map.put("grant_type","authorization_code");
String result = HttpUtil.post(url,map);
return DyRespons.newDyRspons(result,DyAccessTokenResponsData.class);
}
/**
* 获取用户公开信息
* @return
*/
public static DyRespons<DyUserinfoResponsData> userinfo(String access_token, String open_id)
{
String url = "https://open.douyin.com/oauth/userinfo/";
Map<String,Object> map = new HashMap<>();
map.put("access_token",access_token);
map.put("open_id",open_id);
String result = HttpUtil.post(url,map);
return DyRespons.newDyRspons(result,DyUserinfoResponsData.class);
}
/**
* 刷新tokoen
* @return
*/
public static DyRespons<DyAccessTokenResponsData> refreshToken(String access_token, String open_id)
{
String url = "https://open.douyin.com/oauth/renew_refresh_token/";
Map<String,Object> map = new HashMap<>();
map.put("access_token",access_token);
map.put("refresh_token",open_id);
String result = HttpUtil.post(url,map);
return DyRespons.newDyRspons(result,DyAccessTokenResponsData.class);
}
}
... ...
package com.zhonglai.luhui.web.douyin.util.dto;
/**
* 授权成功数据对象
* "access_token": "act.f7094fbffab2ecbfc45e9af9c32bc241oYdckvBKe82BPx8T******",
* "captcha": "",
* "desc_url": "",
* "description": "",
* "error_code": 0,
* "expires_in": 1296000,
* "log_id": "20230525105733ED3ED7AC56A******",
* "open_id": "b9b71865-7fea-44cc-******",
* "refresh_expires_in": 2592000,
* "refresh_token": "rft.713900b74edde9f30ec4e246b706da30t******",
* "scope": "user_info"
*/
public class DyAccessTokenResponsData extends DyResponsBaseData {
private String access_token; //接口调用凭证
private Integer expires_in; //access_token接口调用凭证超时时间,单位(秒)
private String log_id; //请求唯一标识
private String open_id; //授权用户唯一标识
private Integer refresh_expires_in; //refresh_token凭证超时时间,单位(秒)
private String refresh_token; //用户刷新access_token
private String scope; //用户授权的作用域(Scope),使用逗号(,)分隔,开放平台几乎每个接口都需要特定的Scope。
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getAccess_token() {
return access_token;
}
public Integer getExpires_in() {
return expires_in;
}
public void setExpires_in(Integer expires_in) {
this.expires_in = expires_in;
}
public String getLog_id() {
return log_id;
}
public void setLog_id(String log_id) {
this.log_id = log_id;
}
public String getOpen_id() {
return open_id;
}
public void setOpen_id(String open_id) {
this.open_id = open_id;
}
public Integer getRefresh_expires_in() {
return refresh_expires_in;
}
public void setRefresh_expires_in(Integer refresh_expires_in) {
this.refresh_expires_in = refresh_expires_in;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
... ...
package com.zhonglai.luhui.web.douyin.util.dto;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.ruoyi.common.utils.GsonConstructor;
import java.lang.reflect.Type;
public class DyRespons<T> {
private String message;
private T data;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public static <T> DyRespons<T> newDyRspons(String jsonString, Class<T> dataClass) {
// 先解析message
JsonObject jsonObject = JsonParser.parseString(jsonString).getAsJsonObject();
String message = jsonObject.get("message").getAsString();
// 解析data字段
T dataObject = GsonConstructor.get().fromJson(jsonObject.get("data"), dataClass);
// 构建DyRespons对象
DyRespons<T> response = new DyRespons<>();
response.setMessage(message);
response.setData(dataObject);
return response;
}
}
... ...
package com.zhonglai.luhui.web.douyin.util.dto;
public class DyResponsBaseData {
private String error_code; //错误码
private String description; //描述
private String desc_url; //描述地址
private String captcha; //验证码
public String getError_code() {
return error_code;
}
public void setError_code(String error_code) {
this.error_code = error_code;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getDesc_url() {
return desc_url;
}
public void setDesc_url(String desc_url) {
this.desc_url = desc_url;
}
public String getCaptcha() {
return captcha;
}
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
}
... ...
package com.zhonglai.luhui.web.douyin.util.dto;
/**
* 用户公开信息接口返回数据
* avatar String 否 https://example.com/x.jpeg
* description String 否
* 错误码描述
*
* error_code Int64 否 0
* 错误码
*
* nickname String 否 张伟
* open_id String 否 0da22181-d833-447f-995f-1beefe******
* 用户在当前应用的唯一标识
*
* union_id String 否 1ad4e099-4a0c-47d1-a410-bffb4f******
* 用户在当前开发者账号下的唯一标识(未绑定开发者账号没有该字段),即同一企业主体下的入接入多个应用,unionid可用于识别同一个抖音用户
*/
public class DyUserinfoResponsData extends DyResponsBaseData{
private String avatar;
private String nickname;
private String open_id;
private String union_id;
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getOpen_id() {
return open_id;
}
public void setOpen_id(String open_id) {
this.open_id = open_id;
}
public String getUnion_id() {
return union_id;
}
public void setUnion_id(String union_id) {
this.union_id = union_id;
}
}
... ...
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>lh-superweb-jar</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<packaging>pom</packaging>
<modules>
<module>lh-web-douyin</module>
</modules>
<dependencies>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-web-douyin</artifactId>
<version>${ruoyi.version}</version>
</dependency>
</dependencies>
<description>
web服务
</description>
</project>
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>lh-superweb</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 核心模块-->
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>ruoyi-framework</artifactId>
</dependency>
<!-- 文档 -->
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-common-swagger</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-public-dao</artifactId>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-common-datasource</artifactId>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-jar-action</artifactId>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-superweb-jar</artifactId>
</dependency>
</dependencies>
</project>
\ No newline at end of file
... ...
package com.zhonglai.luhui.superweb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackages = {
"com.ruoyi.common",
"com.zhonglai.luhui.config",
"com.zhonglai.luhui.datasource",
"com.zhonglai.luhui.dao",
"com.zhonglai.luhui.superweb",
"com.zhonglai.luhui.web",
})
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class SuperwebApplication {
public static void main(String[] args) {
SpringApplication.run(SuperwebApplication.class,args);
System.out.println("启动成功");
}
}
... ...
package com.zhonglai.luhui.superweb.config;
import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("默认")
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.zhonglai.luhui.superweb.controller"))
.paths(PathSelectors.any())
.build();
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo()
{
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题:web接口")
// 描述
.description("描述:总tomcat代理")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
// 版本
.version("版本号:" + ruoyiConfig.getVersion())
.build();
}
}
\ No newline at end of file
... ...
package com.zhonglai.luhui.superweb.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
@Api(tags = "主页")
@RestController
@RequestMapping("/")
public class RootController {
@ApiOperation("默认主页")
@RequestMapping(value = "")
public String index(HttpServletRequest httpServletRequest) throws IOException {
StringBuffer bodyBytes = new StringBuffer("<html>");
bodyBytes.append("<head><meta charset=\"UTF-8\"></head>");
bodyBytes.append("<body>");
Map<String, String[]> map = httpServletRequest.getParameterMap();
bodyBytes.append("<b>parameter:</b></br>");
for (Map.Entry<String, String[]> entry : map.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
if (values.length == 1) {
bodyBytes.append(key).append(":").append(values[0]).append("</br>");
} else {
bodyBytes.append(key).append(":").append(values).append("</br>");
}
}
// 打印请求头信息
bodyBytes.append("<b>header:</b></br>");
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = httpServletRequest.getHeader(headerName);
bodyBytes.append(headerName).append(":").append(headerValue).append("</br>");
}
//获取请求body
byte[] rbody = StreamUtils.copyToByteArray(httpServletRequest.getInputStream());
String body = new String(rbody, httpServletRequest.getCharacterEncoding());
bodyBytes.append("<b>body:</b></br>");
bodyBytes.append(body);
bodyBytes.append("</body>");
bodyBytes.append("</html>");
return bodyBytes.toString();
}
}
... ...
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://rm-wz9740un21f09iokuao.mysql.rds.aliyuncs.com:3306/mqtt_broker?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: luhui
password: Luhui586
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: false
url:
username:
password:
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: ruoyi
login-password: 123456
filter:
stat:
enabled: true
# 慢SQL记录
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: true
wall:
config:
multi-statement-allow: true
\ No newline at end of file
... ...
# 项目相关配置 jhlt: # 名称 name: zhonglai # 版本 version: 3.8.2 # 版权年份 copyrightYear: 2022 # 开发环境配置 server: # 服务器的HTTP端口,默认为8080 port: 8080 servlet: # 应用的访问路径 context-path: /superweb 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 # 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/* sys: ## // 对于登录login 注册register 验证码captchaImage 允许匿名访问 antMatchers: /v2/api-docs
\ No newline at end of file
... ...
... ... @@ -33,6 +33,9 @@
<module>lh-device-protocol-parser</module>
<module>lh-device-operation-service</module>
<module>lh-alarm-timer</module>
<module>lh-superweb</module>
<module>lh-superweb-jar</module>
<module>lh-ssh-service-lesten</module>
</modules>
<properties>
... ...
... ... @@ -371,11 +371,21 @@
<version>${ruoyi.version}</version>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-superweb-jar</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<dependency>
<groupId>com.zhonglai</groupId>
<artifactId>ServiceDao</artifactId>
<version>1.4.3</version>
</dependency>
<dependency>
<groupId>com.zhonglai.luhui</groupId>
<artifactId>lh-jar-plugins-init</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<!-- 支持data -->
<dependency>
<groupId>org.projectlombok</groupId>
... ...