mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-21 22:53:56 +08:00
Merge branch 'main' into vosk_asr
This commit is contained in:
@@ -157,3 +157,4 @@ main/xiaozhi-server/models/SenseVoiceSmall/model.pt
|
||||
main/xiaozhi-server/models/sherpa-onnx*
|
||||
main/xiaozhi-server/models/vosk-model*
|
||||
my_wakeup_words.mp3
|
||||
main/manager-api/.vscode
|
||||
|
||||
+2
-2
@@ -241,7 +241,7 @@ pip install -r requirements.txt
|
||||
|
||||
## 4.配置项目文件
|
||||
|
||||
接下里,程序还不能直接运行,你需要配置一下,你到底使用的是什么模型。你可以看这个教程:
|
||||
接下来,程序还不能直接运行,你需要配置一下,你到底使用的是什么模型。你可以看这个教程:
|
||||
[跳转到配置项目文件](#配置项目)
|
||||
|
||||
## 5.运行项目
|
||||
@@ -338,4 +338,4 @@ LLM:
|
||||
|
||||
[5、我说话很慢,停顿时小智老是抢话](../README.md#1%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%AF%B4%E7%9A%84%E8%AF%9D%E5%B0%8F%E6%99%BA%E8%AF%86%E5%88%AB%E5%87%BA%E6%9D%A5%E5%BE%88%E5%A4%9A%E9%9F%A9%E6%96%87%E6%97%A5%E6%96%87%E8%8B%B1%E6%96%87)
|
||||
|
||||
[6、我想通过小智控制电灯、空调、远程开关机等操作](../README.md#1%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%AF%B4%E7%9A%84%E8%AF%9D%E5%B0%8F%E6%99%BA%E8%AF%86%E5%88%AB%E5%87%BA%E6%9D%A5%E5%BE%88%E5%A4%9A%E9%9F%A9%E6%96%87%E6%97%A5%E6%96%87%E8%8B%B1%E6%96%87)
|
||||
[6、我想通过小智控制电灯、空调、远程开关机等操作](../README.md#1%E4%B8%BA%E4%BB%80%E4%B9%88%E6%88%91%E8%AF%B4%E7%9A%84%E8%AF%9D%E5%B0%8F%E6%99%BA%E8%AF%86%E5%88%AB%E5%87%BA%E6%9D%A5%E5%BE%88%E5%A4%9A%E9%9F%A9%E6%96%87%E6%97%A5%E6%96%87%E8%8B%B1%E6%96%87)
|
||||
|
||||
@@ -11,6 +11,7 @@ manager-api 该项目基于SpringBoot框架开发。
|
||||
JDK 21
|
||||
Maven 3.8+
|
||||
MySQL 8.0+
|
||||
Redis 5.0+
|
||||
Vue 3.x
|
||||
|
||||
# 创建数据库
|
||||
@@ -43,6 +44,30 @@ spring:
|
||||
password: 123456
|
||||
```
|
||||
|
||||
|
||||
# 连接Redis
|
||||
|
||||
如果还没有Redis,你可以通过docker安装redis
|
||||
|
||||
```
|
||||
docker run --name xiaozhi-esp32-server-redis -d -p 6379:6379 redis
|
||||
```
|
||||
|
||||
# 确认项目Redis连接信息
|
||||
|
||||
在`src/main/resources/application-dev.yml`中配置Redis连接信息
|
||||
|
||||
```
|
||||
spring:
|
||||
data:
|
||||
redis:
|
||||
host: localhost
|
||||
port: 6379
|
||||
password:
|
||||
database: 0
|
||||
```
|
||||
|
||||
|
||||
# 测试启动
|
||||
|
||||
本项目为SpringBoot项目,启动方式为:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<java.version>21</java.version>
|
||||
<junit.version>5.12.0</junit.version>
|
||||
<junit.version>5.10.1</junit.version>
|
||||
<druid.version>1.2.20</druid.version>
|
||||
<mybatisplus.version>3.5.5</mybatisplus.version>
|
||||
<hutool.version>5.8.24</hutool.version>
|
||||
@@ -27,7 +27,6 @@
|
||||
<shiro.version>2.0.2</shiro.version>
|
||||
<captcha.version>1.6.2</captcha.version>
|
||||
<guava.version>33.0.0-jre</guava.version>
|
||||
<easyexcel.version>3.3.2</easyexcel.version>
|
||||
<liquibase-core.version>4.20.0</liquibase-core.version>
|
||||
</properties>
|
||||
|
||||
@@ -88,6 +87,36 @@
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-params</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-launcher</artifactId>
|
||||
<version>1.10.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-commons</artifactId>
|
||||
<version>1.10.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.platform</groupId>
|
||||
<artifactId>junit-platform-engine</artifactId>
|
||||
<version>1.10.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package xiaozhi.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 数据过滤注解
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package xiaozhi.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 操作日志注解
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package xiaozhi.common.aspect;
|
||||
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
|
||||
/**
|
||||
* Redis切面处理类
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -19,7 +20,7 @@ import org.springframework.stereotype.Component;
|
||||
@Component
|
||||
public class RedisAspect {
|
||||
/**
|
||||
* 是否开启redis缓存 true开启 false关闭
|
||||
* 是否开启redis缓存 true开启 false关闭
|
||||
*/
|
||||
@Value("${renren.redis.open}")
|
||||
private boolean open;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package xiaozhi.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
|
||||
import xiaozhi.common.interceptor.DataFilterInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* mybatis-plus配置
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package xiaozhi.common.config;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import io.swagger.v3.oas.models.OpenAPI;
|
||||
import io.swagger.v3.oas.models.info.Info;
|
||||
|
||||
/**
|
||||
* Swagger配置
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -14,11 +15,60 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration
|
||||
public class SwaggerConfig {
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi deviceApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("device")
|
||||
.pathsToMatch("/device/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi agentApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("agent")
|
||||
.pathsToMatch("/agent/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi modelApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("models")
|
||||
.pathsToMatch("/models/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi oatApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("ota")
|
||||
.pathsToMatch("/ota/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi timbreApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("timbre")
|
||||
.pathsToMatch("/timbre/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi sysApi() {
|
||||
return GroupedOpenApi.builder()
|
||||
.group("admin")
|
||||
.pathsToMatch("/admin/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi userApi() {
|
||||
String[] paths = {"/**"};
|
||||
return GroupedOpenApi.builder().group("xiaozhi")
|
||||
.pathsToMatch(paths).build();
|
||||
return GroupedOpenApi.builder()
|
||||
.group("user")
|
||||
.pathsToMatch("/user/**")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package xiaozhi.common.convert;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 日期转换
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package xiaozhi.common.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 基础实体类,所有实体都需要继承
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package xiaozhi.common.exception;
|
||||
|
||||
|
||||
import xiaozhi.common.utils.MessageUtils;
|
||||
|
||||
/**
|
||||
@@ -10,7 +9,6 @@ import xiaozhi.common.utils.MessageUtils;
|
||||
*/
|
||||
public class RenException extends RuntimeException {
|
||||
|
||||
|
||||
private int code;
|
||||
private String msg;
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package xiaozhi.common.exception;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.UnauthorizedException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import xiaozhi.common.utils.Result;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import xiaozhi.common.utils.Result;
|
||||
|
||||
/**
|
||||
* 异常处理器
|
||||
@@ -23,35 +23,34 @@ public class RenExceptionHandler {
|
||||
* 处理自定义异常
|
||||
*/
|
||||
@ExceptionHandler(RenException.class)
|
||||
public Result handleRenException(RenException ex) {
|
||||
Result result = new Result();
|
||||
public Result<Void> handleRenException(RenException ex) {
|
||||
Result<Void> result = new Result<>();
|
||||
result.error(ex.getCode(), ex.getMsg());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ExceptionHandler(DuplicateKeyException.class)
|
||||
public Result handleDuplicateKeyException(DuplicateKeyException ex) {
|
||||
Result result = new Result();
|
||||
public Result<Void> handleDuplicateKeyException(DuplicateKeyException ex) {
|
||||
Result<Void> result = new Result<>();
|
||||
result.error(ErrorCode.DB_RECORD_EXISTS);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ExceptionHandler(UnauthorizedException.class)
|
||||
public Result handleUnauthorizedException(UnauthorizedException ex) {
|
||||
Result result = new Result();
|
||||
public Result<Void> handleUnauthorizedException(UnauthorizedException ex) {
|
||||
Result<Void> result = new Result<>();
|
||||
result.error(ErrorCode.FORBIDDEN);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Result handleException(Exception ex) {
|
||||
public Result<Void> handleException(Exception ex) {
|
||||
log.error(ex.getMessage(), ex);
|
||||
|
||||
return new Result().error();
|
||||
return new Result<Void>().error();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
package xiaozhi.common.handler;
|
||||
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
|
||||
/**
|
||||
* 公共字段,自动填充值
|
||||
@@ -22,35 +24,34 @@ public class FieldMetaObjectHandler implements MetaObjectHandler {
|
||||
private final static String UPDATER = "updater";
|
||||
|
||||
private final static String DATA_OPERATION = "dataOperation";
|
||||
private final static String DEPT_ID = "deptId";
|
||||
|
||||
@Override
|
||||
public void insertFill(MetaObject metaObject) {
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
Date date = new Date();
|
||||
|
||||
//创建者
|
||||
// 创建者
|
||||
strictInsertFill(metaObject, CREATOR, Long.class, user.getId());
|
||||
//创建时间
|
||||
// 创建时间
|
||||
strictInsertFill(metaObject, CREATE_DATE, Date.class, date);
|
||||
|
||||
//更新者
|
||||
// 更新者
|
||||
strictInsertFill(metaObject, UPDATER, Long.class, user.getId());
|
||||
//更新时间
|
||||
// 更新时间
|
||||
strictInsertFill(metaObject, UPDATE_DATE, Date.class, date);
|
||||
|
||||
//数据标识
|
||||
// 数据标识
|
||||
strictInsertFill(metaObject, DATA_OPERATION, String.class, Constant.DataOperation.INSERT.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(MetaObject metaObject) {
|
||||
//更新者
|
||||
// 更新者
|
||||
strictUpdateFill(metaObject, UPDATER, Long.class, SecurityUser.getUserId());
|
||||
//更新时间
|
||||
// 更新时间
|
||||
strictUpdateFill(metaObject, UPDATE_DATE, Date.class, new Date());
|
||||
|
||||
//数据标识
|
||||
// 数据标识
|
||||
strictInsertFill(metaObject, DATA_OPERATION, String.class, Constant.DataOperation.UPDATE.getValue());
|
||||
}
|
||||
}
|
||||
+14
-10
@@ -1,8 +1,17 @@
|
||||
package xiaozhi.common.interceptor;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import net.sf.jsqlparser.JSQLParserException;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.StringValue;
|
||||
@@ -10,13 +19,6 @@ import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
||||
import net.sf.jsqlparser.statement.select.PlainSelect;
|
||||
import net.sf.jsqlparser.statement.select.Select;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.apache.ibatis.session.RowBounds;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 数据过滤
|
||||
@@ -25,8 +27,10 @@ import java.util.Map;
|
||||
*/
|
||||
public class DataFilterInterceptor implements InnerInterceptor {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
|
||||
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
|
||||
ResultHandler resultHandler, BoundSql boundSql) {
|
||||
DataScope scope = getDataScope(parameter);
|
||||
// 不进行数据过滤
|
||||
if (scope == null || StrUtil.isBlank(scope.getSqlFilter())) {
|
||||
@@ -48,7 +52,7 @@ public class DataFilterInterceptor implements InnerInterceptor {
|
||||
// 判断参数里是否有DataScope对象
|
||||
if (parameter instanceof Map) {
|
||||
Map<?, ?> parameterMap = (Map<?, ?>) parameter;
|
||||
for (Map.Entry entry : parameterMap.entrySet()) {
|
||||
for (Map.Entry<?, ?> entry : parameterMap.entrySet()) {
|
||||
if (entry.getValue() != null && entry.getValue() instanceof DataScope) {
|
||||
return (DataScope) entry.getValue();
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package xiaozhi.common.page;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 分页工具类
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package xiaozhi.common.page;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 令牌信息
|
||||
*
|
||||
@@ -14,7 +14,6 @@ import java.io.Serializable;
|
||||
@Schema(description = "令牌信息")
|
||||
public class TokenDTO implements Serializable {
|
||||
|
||||
|
||||
@Schema(description = "密码")
|
||||
private String token;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package xiaozhi.common.redis;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
@@ -8,6 +7,8 @@ import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
/**
|
||||
* Redis配置
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package xiaozhi.common.redis;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
/**
|
||||
* Redis工具类
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package xiaozhi.common.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
|
||||
/**
|
||||
* 基础服务接口,所有Service接口都要继承
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -56,7 +56,8 @@ public interface BaseService<T> {
|
||||
* </p>
|
||||
*
|
||||
* @param entity 实体对象
|
||||
* @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
|
||||
* @param updateWrapper 实体对象封装操作类
|
||||
* {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper}
|
||||
*/
|
||||
boolean update(T entity, Wrapper<T> updateWrapper);
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package xiaozhi.common.service;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
|
||||
/**
|
||||
* CRUD基础服务接口
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
package xiaozhi.common.service.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.apache.ibatis.binding.MapperMethod;
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import com.baomidou.mybatisplus.core.enums.SqlMethod;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
@@ -10,22 +23,11 @@ import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
|
||||
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.BaseService;
|
||||
import xiaozhi.common.utils.ConvertUtils;
|
||||
import org.apache.ibatis.binding.MapperMethod;
|
||||
import org.apache.ibatis.logging.Log;
|
||||
import org.apache.ibatis.logging.LogFactory;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* 基础服务类,所有Service都要继承
|
||||
@@ -45,7 +47,7 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
|
||||
* @param isAsc 排序方式
|
||||
*/
|
||||
protected IPage<T> getPage(Map<String, Object> params, String defaultOrderField, boolean isAsc) {
|
||||
//分页参数
|
||||
// 分页参数
|
||||
long curPage = 1;
|
||||
long limit = 10;
|
||||
|
||||
@@ -56,17 +58,17 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
|
||||
limit = Long.parseLong((String) params.get(Constant.LIMIT));
|
||||
}
|
||||
|
||||
//分页对象
|
||||
// 分页对象
|
||||
Page<T> page = new Page<>(curPage, limit);
|
||||
|
||||
//分页参数
|
||||
// 分页参数
|
||||
params.put(Constant.PAGE, page);
|
||||
|
||||
//排序字段
|
||||
// 排序字段
|
||||
String orderField = (String) params.get(Constant.ORDER_FIELD);
|
||||
String order = (String) params.get(Constant.ORDER);
|
||||
|
||||
//前端字段排序
|
||||
// 前端字段排序
|
||||
if (StringUtils.isNotBlank(orderField) && StringUtils.isNotBlank(order)) {
|
||||
if (Constant.ASC.equalsIgnoreCase(order)) {
|
||||
return page.addOrder(OrderItem.asc(orderField));
|
||||
@@ -75,12 +77,12 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
|
||||
}
|
||||
}
|
||||
|
||||
//没有排序字段,则不排序
|
||||
// 没有排序字段,则不排序
|
||||
if (StringUtils.isBlank(defaultOrderField)) {
|
||||
return page;
|
||||
}
|
||||
|
||||
//默认排序
|
||||
// 默认排序
|
||||
if (isAsc) {
|
||||
page.addOrder(OrderItem.asc(defaultOrderField));
|
||||
} else {
|
||||
@@ -90,13 +92,13 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
|
||||
return page;
|
||||
}
|
||||
|
||||
protected <T> PageData<T> getPageData(List<?> list, long total, Class<T> target) {
|
||||
List<T> targetList = ConvertUtils.sourceToTarget(list, target);
|
||||
protected <D> PageData<D> getPageData(List<?> list, long total, Class<D> target) {
|
||||
List<D> targetList = ConvertUtils.sourceToTarget(list, target);
|
||||
|
||||
return new PageData<>(targetList, total);
|
||||
}
|
||||
|
||||
protected <T> PageData<T> getPageData(IPage page, Class<T> target) {
|
||||
protected <D> PageData<D> getPageData(IPage<?> page, Class<D> target) {
|
||||
return getPageData(page.getRecords(), page.getTotal(), target);
|
||||
}
|
||||
|
||||
@@ -126,10 +128,12 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
|
||||
return SqlHelper.retBool(result);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Class<M> currentMapperClass() {
|
||||
return (Class<M>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Class<T> currentModelClass() {
|
||||
return (Class<T>) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 1);
|
||||
@@ -164,11 +168,11 @@ public abstract class BaseServiceImpl<M extends BaseMapper<T>, T> implements Bas
|
||||
/**
|
||||
* 执行批量操作
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
|
||||
return SqlHelper.executeBatch(this.currentModelClass(), this.log, list, batchSize, consumer);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateById(T entity) {
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
package xiaozhi.common.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.CrudService;
|
||||
import xiaozhi.common.utils.ConvertUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.CrudService;
|
||||
import xiaozhi.common.utils.ConvertUtils;
|
||||
|
||||
/**
|
||||
* CRUD基础服务类
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
* Website: https://www.renren.io
|
||||
*/
|
||||
public abstract class CrudServiceImpl<M extends BaseMapper<T>, T, D> extends BaseServiceImpl<M, T> implements CrudService<T, D> {
|
||||
public abstract class CrudServiceImpl<M extends BaseMapper<T>, T, D> extends BaseServiceImpl<M, T>
|
||||
implements CrudService<T, D> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Class<D> currentDtoClass() {
|
||||
return (Class<D>) ReflectionKit.getSuperClassGenericType(getClass(), CrudServiceImpl.class, 2);
|
||||
}
|
||||
@@ -29,8 +33,7 @@ public abstract class CrudServiceImpl<M extends BaseMapper<T>, T, D> extends Bas
|
||||
public PageData<D> page(Map<String, Object> params) {
|
||||
IPage<T> page = baseDao.selectPage(
|
||||
getPage(params, null, false),
|
||||
getWrapper(params)
|
||||
);
|
||||
getWrapper(params));
|
||||
|
||||
return getPageData(page, currentDtoClass());
|
||||
}
|
||||
@@ -56,7 +59,7 @@ public abstract class CrudServiceImpl<M extends BaseMapper<T>, T, D> extends Bas
|
||||
T entity = ConvertUtils.sourceToTarget(dto, currentModelClass());
|
||||
insert(entity);
|
||||
|
||||
//copy主键值到dto
|
||||
// copy主键值到dto
|
||||
BeanUtils.copyProperties(entity, dto);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package xiaozhi.common.user;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 登录用户信息
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package xiaozhi.common.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 转换工具类
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -20,7 +21,7 @@ public class ConvertUtils {
|
||||
}
|
||||
T targetObject = null;
|
||||
try {
|
||||
targetObject = target.newInstance();
|
||||
targetObject = target.getDeclaredConstructor().newInstance();
|
||||
BeanUtils.copyProperties(source, targetObject);
|
||||
} catch (Exception e) {
|
||||
log.error("convert error ", e);
|
||||
@@ -34,10 +35,10 @@ public class ConvertUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
List targetList = new ArrayList<>(sourceList.size());
|
||||
List<T> targetList = new ArrayList<>(sourceList.size());
|
||||
try {
|
||||
for (Object source : sourceList) {
|
||||
T targetObject = target.newInstance();
|
||||
T targetObject = target.getDeclaredConstructor().newInstance();
|
||||
BeanUtils.copyProperties(source, targetObject);
|
||||
targetList.add(targetObject);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
package xiaozhi.common.utils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Http
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -70,15 +71,15 @@ public class HttpContextUtils {
|
||||
}
|
||||
|
||||
public static String getLanguage() {
|
||||
//默认语言
|
||||
// 默认语言
|
||||
String defaultLanguage = "zh-CN";
|
||||
//request
|
||||
// request
|
||||
HttpServletRequest request = getHttpServletRequest();
|
||||
if (request == null) {
|
||||
return defaultLanguage;
|
||||
}
|
||||
|
||||
//请求语言
|
||||
// 请求语言
|
||||
defaultLanguage = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
|
||||
|
||||
return defaultLanguage;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package xiaozhi.common.utils;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* IP地址
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package xiaozhi.common.utils;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* JSON 工具类
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -59,7 +60,8 @@ public class JsonUtils {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||
return objectMapper.readValue(text,
|
||||
objectMapper.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package xiaozhi.common.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -36,7 +36,6 @@ public class Result<T> implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Result<T> error() {
|
||||
this.code = ErrorCode.INTERNAL_SERVER_ERROR;
|
||||
this.msg = MessageUtils.getMessage(this.code);
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package xiaozhi.common.utils;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 树节点,所有需要实现树节点的,都需要继承该类
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package xiaozhi.common.utils;
|
||||
|
||||
import xiaozhi.common.validator.AssertUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import xiaozhi.common.validator.AssertUtils;
|
||||
|
||||
/**
|
||||
* 树形结构工具类,如:菜单、部门等
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -17,8 +17,8 @@ public class TreeUtils {
|
||||
/**
|
||||
* 根据pid,构建树节点
|
||||
*/
|
||||
public static <T extends TreeNode> List<T> build(List<T> treeNodes, Long pid) {
|
||||
//pid不能为空
|
||||
public static <T extends TreeNode<T>> List<T> build(List<T> treeNodes, Long pid) {
|
||||
// pid不能为空
|
||||
AssertUtils.isNull(pid, "pid");
|
||||
|
||||
List<T> treeList = new ArrayList<>();
|
||||
@@ -34,7 +34,7 @@ public class TreeUtils {
|
||||
/**
|
||||
* 查找子节点
|
||||
*/
|
||||
private static <T extends TreeNode> T findChildren(List<T> treeNodes, T rootNode) {
|
||||
private static <T extends TreeNode<T>> T findChildren(List<T> treeNodes, T rootNode) {
|
||||
for (T treeNode : treeNodes) {
|
||||
if (rootNode.getId().equals(treeNode.getPid())) {
|
||||
rootNode.getChildren().add(findChildren(treeNodes, treeNode));
|
||||
@@ -46,10 +46,10 @@ public class TreeUtils {
|
||||
/**
|
||||
* 构建树节点
|
||||
*/
|
||||
public static <T extends TreeNode> List<T> build(List<T> treeNodes) {
|
||||
public static <T extends TreeNode<T>> List<T> build(List<T> treeNodes) {
|
||||
List<T> result = new ArrayList<>();
|
||||
|
||||
//list转map
|
||||
// list转map
|
||||
Map<Long, T> nodeMap = new LinkedHashMap<>(treeNodes.size());
|
||||
for (T treeNode : treeNodes) {
|
||||
nodeMap.put(treeNode.getId(), treeNode);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package xiaozhi.common.validator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 校验工具类
|
||||
@@ -73,11 +74,11 @@ public class AssertUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void isMapEmpty(Map map, String... params) {
|
||||
public static void isMapEmpty(Map<?, ?> map, String... params) {
|
||||
isMapEmpty(map, ErrorCode.NOT_NULL, params);
|
||||
}
|
||||
|
||||
public static void isMapEmpty(Map map, Integer code, String... params) {
|
||||
public static void isMapEmpty(Map<?, ?> map, Integer code, String... params) {
|
||||
if (code == null) {
|
||||
throw new RenException(ErrorCode.NOT_NULL, "code");
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package xiaozhi.common.validator;
|
||||
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.context.support.ResourceBundleMessageSource;
|
||||
import org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.Validation;
|
||||
import jakarta.validation.Validator;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
|
||||
/**
|
||||
* hibernate-validator校验工具类
|
||||
@@ -35,7 +36,7 @@ public class ValidatorUtils {
|
||||
throws RenException {
|
||||
Locale.setDefault(LocaleContextHolder.getLocale());
|
||||
Validator validator = Validation.byDefaultProvider().configure().messageInterpolator(
|
||||
new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(getMessageSource())))
|
||||
new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(getMessageSource())))
|
||||
.buildValidatorFactory().getValidator();
|
||||
|
||||
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package xiaozhi.common.xss;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* SQL过滤
|
||||
@@ -20,19 +21,20 @@ public class SqlFilter {
|
||||
if (StringUtils.isBlank(str)) {
|
||||
return null;
|
||||
}
|
||||
//去掉'|"|;|\字符
|
||||
// 去掉'|"|;|\字符
|
||||
str = StringUtils.replace(str, "'", "");
|
||||
str = StringUtils.replace(str, "\"", "");
|
||||
str = StringUtils.replace(str, ";", "");
|
||||
str = StringUtils.replace(str, "\\", "");
|
||||
|
||||
//转换成小写
|
||||
// 转换成小写
|
||||
str = str.toLowerCase();
|
||||
|
||||
//非法字符
|
||||
String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"};
|
||||
// 非法字符
|
||||
String[] keywords = { "master", "truncate", "insert", "select", "delete", "update", "declare", "alter",
|
||||
"drop" };
|
||||
|
||||
//判断是否包含非法字符
|
||||
// 判断是否包含非法字符
|
||||
for (String keyword : keywords) {
|
||||
if (str.contains(keyword)) {
|
||||
throw new RenException(ErrorCode.INVALID_SYMBOL);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package xiaozhi.common.xss;
|
||||
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
@@ -8,6 +7,8 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import jakarta.servlet.DispatcherType;
|
||||
|
||||
/**
|
||||
* XSS 配置文件
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package xiaozhi.common.xss;
|
||||
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.AllArgsConstructor;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import java.io.IOException;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
/**
|
||||
* XSS过滤
|
||||
@@ -38,7 +44,8 @@ public class XssFilter implements Filter {
|
||||
|
||||
private boolean shouldNotFilter(HttpServletRequest request) {
|
||||
// 放行不过滤的URL
|
||||
return properties.getExcludeUrls().stream().anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, request.getServletPath()));
|
||||
return properties.getExcludeUrls().stream()
|
||||
.anyMatch(excludeUrl -> pathMatcher.match(excludeUrl, request.getServletPath()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+12
-12
@@ -1,20 +1,20 @@
|
||||
package xiaozhi.common.xss;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import jakarta.servlet.ReadListener;
|
||||
import jakarta.servlet.ServletInputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
/**
|
||||
* XSS过滤处理
|
||||
@@ -29,18 +29,18 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
//非json类型,直接返回
|
||||
// 非json类型,直接返回
|
||||
if (!MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE))) {
|
||||
return super.getInputStream();
|
||||
}
|
||||
|
||||
//为空,直接返回
|
||||
// 为空,直接返回
|
||||
String json = IoUtil.readUtf8(super.getInputStream());
|
||||
if (StringUtils.isBlank(json)) {
|
||||
return super.getInputStream();
|
||||
}
|
||||
|
||||
//xss过滤
|
||||
// xss过滤
|
||||
json = xssEncode(json);
|
||||
final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
|
||||
return new ServletInputStream() {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package xiaozhi.common.xss;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* XSS 配置项
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -22,13 +22,15 @@ public class XssUtils extends Safelist {
|
||||
*/
|
||||
private static Safelist xssWhitelist() {
|
||||
return new Safelist()
|
||||
//支持的标签
|
||||
// 支持的标签
|
||||
.addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl",
|
||||
"dt", "em", "h1", "h2", "h3", "h4", "h5", "h6", "i", "img", "li", "ol", "p", "pre", "q", "small",
|
||||
"strike", "strong", "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u", "ul",
|
||||
"dt", "em", "h1", "h2", "h3", "h4", "h5", "h6", "i", "img", "li", "ol", "p", "pre", "q",
|
||||
"small",
|
||||
"strike", "strong", "sub", "sup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", "u",
|
||||
"ul",
|
||||
"embed", "object", "param", "span")
|
||||
|
||||
//支持的标签属性
|
||||
// 支持的标签属性
|
||||
.addAttributes("a", "href", "class", "style", "target", "rel", "nofollow")
|
||||
.addAttributes("blockquote", "cite")
|
||||
.addAttributes("code", "class", "style")
|
||||
@@ -44,13 +46,16 @@ public class XssUtils extends Safelist {
|
||||
.addAttributes("ul", "type", "style")
|
||||
.addAttributes("pre", "class", "style")
|
||||
.addAttributes("div", "class", "id", "style")
|
||||
.addAttributes("embed", "src", "wmode", "flashvars", "pluginspage", "allowFullScreen", "allowfullscreen",
|
||||
"quality", "width", "height", "align", "allowScriptAccess", "allowscriptaccess", "allownetworking", "type")
|
||||
.addAttributes("object", "type", "id", "name", "data", "width", "height", "style", "classid", "codebase")
|
||||
.addAttributes("embed", "src", "wmode", "flashvars", "pluginspage", "allowFullScreen",
|
||||
"allowfullscreen",
|
||||
"quality", "width", "height", "align", "allowScriptAccess", "allowscriptaccess",
|
||||
"allownetworking", "type")
|
||||
.addAttributes("object", "type", "id", "name", "data", "width", "height", "style", "classid",
|
||||
"codebase")
|
||||
.addAttributes("param", "name", "value")
|
||||
.addAttributes("span", "class", "style")
|
||||
|
||||
//标签属性对应的协议
|
||||
// 标签属性对应的协议
|
||||
.addProtocols("a", "href", "ftp", "http", "https", "mailto")
|
||||
.addProtocols("img", "src", "http", "https")
|
||||
.addProtocols("blockquote", "cite", "http", "https")
|
||||
|
||||
+46
-23
@@ -1,13 +1,28 @@
|
||||
package xiaozhi.modules.agent.controller;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
@@ -16,20 +31,19 @@ import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.modules.agent.dto.AgentCreateDTO;
|
||||
import xiaozhi.modules.agent.dto.AgentUpdateDTO;
|
||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
|
||||
import xiaozhi.modules.agent.service.AgentService;
|
||||
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "智能体管理")
|
||||
@AllArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/agent")
|
||||
public class AgentController {
|
||||
private final AgentService agentService;
|
||||
|
||||
private final AgentTemplateService agentTemplateService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取用户智能体列表")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
@@ -38,7 +52,7 @@ public class AgentController {
|
||||
List<AgentEntity> agents = agentService.getUserAgents(user.getId());
|
||||
return new Result<List<AgentEntity>>().ok(agents);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/all")
|
||||
@Operation(summary = "智能体列表(管理员)")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
@@ -51,7 +65,7 @@ public class AgentController {
|
||||
PageData<AgentEntity> page = agentService.adminAgentList(params);
|
||||
return new Result<PageData<AgentEntity>>().ok(page);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取智能体详情")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
@@ -59,35 +73,35 @@ public class AgentController {
|
||||
AgentEntity agent = agentService.getAgentById(id);
|
||||
return new Result<AgentEntity>().ok(agent);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "创建智能体")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<Void> save(@RequestBody @Valid AgentCreateDTO dto) {
|
||||
AgentEntity entity = ConvertUtils.sourceToTarget(dto, AgentEntity.class);
|
||||
|
||||
|
||||
// 设置用户ID和创建者信息
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
entity.setUserId(user.getId());
|
||||
entity.setCreator(user.getId());
|
||||
entity.setCreatedAt(new Date());
|
||||
|
||||
|
||||
// ID、智能体编码和排序会在Service层自动生成
|
||||
agentService.insert(entity);
|
||||
|
||||
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "更新智能体")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<Void> update(@RequestBody @Valid AgentUpdateDTO dto) {
|
||||
public Result<Void> update(@PathVariable String id, @RequestBody @Valid AgentUpdateDTO dto) {
|
||||
// 先查询现有实体
|
||||
AgentEntity existingEntity = agentService.getAgentById(dto.getId());
|
||||
AgentEntity existingEntity = agentService.getAgentById(id);
|
||||
if (existingEntity == null) {
|
||||
return new Result<Void>().error("智能体不存在");
|
||||
}
|
||||
|
||||
|
||||
// 只更新提供的非空字段
|
||||
if (dto.getAgentName() != null) {
|
||||
existingEntity.setAgentName(dto.getAgentName());
|
||||
@@ -128,17 +142,17 @@ public class AgentController {
|
||||
if (dto.getSort() != null) {
|
||||
existingEntity.setSort(dto.getSort());
|
||||
}
|
||||
|
||||
|
||||
// 设置更新者信息
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
existingEntity.setUpdater(user.getId());
|
||||
existingEntity.setUpdatedAt(new Date());
|
||||
|
||||
|
||||
agentService.updateById(existingEntity);
|
||||
|
||||
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除智能体")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
@@ -146,4 +160,13 @@ public class AgentController {
|
||||
agentService.deleteById(id);
|
||||
return new Result<>();
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/template")
|
||||
@Operation(summary = "智能体模板模板列表")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<List<AgentTemplateEntity>> templateList() {
|
||||
List<AgentTemplateEntity> list = agentTemplateService
|
||||
.list(new QueryWrapper<AgentTemplateEntity>().orderByAsc("sort"));
|
||||
return new Result<List<AgentTemplateEntity>>().ok(list);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
package xiaozhi.modules.agent.dao;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import xiaozhi.common.dao.BaseDao;
|
||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||
|
||||
@Mapper
|
||||
public interface AgentDao extends BaseDao<AgentEntity> {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package xiaozhi.modules.agent.dao;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
|
||||
|
||||
/**
|
||||
* @author chenerlei
|
||||
* @description 针对表【ai_agent_template(智能体配置模板表)】的数据库操作Mapper
|
||||
* @createDate 2025-03-22 11:48:18
|
||||
*/
|
||||
@Mapper
|
||||
public interface AgentTemplateDao extends BaseMapper<AgentTemplateEntity> {
|
||||
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package xiaozhi.modules.agent.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 智能体创建DTO
|
||||
* 专用于新增智能体,不包含id、agentCode和sort字段,这些字段由系统自动生成/设置默认值
|
||||
@@ -14,38 +14,8 @@ import java.io.Serializable;
|
||||
@Schema(description = "智能体创建对象")
|
||||
public class AgentCreateDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@Schema(description = "智能体名称", example = "客服助手")
|
||||
@NotBlank(message = "智能体名称不能为空")
|
||||
private String agentName;
|
||||
|
||||
@Schema(description = "语音识别模型标识", example = "asr_model_01")
|
||||
private String asrModelId;
|
||||
|
||||
@Schema(description = "语音活动检测标识", example = "vad_model_01")
|
||||
private String vadModelId;
|
||||
|
||||
@Schema(description = "大语言模型标识", example = "llm_model_01")
|
||||
private String llmModelId;
|
||||
|
||||
@Schema(description = "语音合成模型标识", example = "tts_model_01")
|
||||
private String ttsModelId;
|
||||
|
||||
@Schema(description = "音色标识", example = "voice_01")
|
||||
private String ttsVoiceId;
|
||||
|
||||
@Schema(description = "记忆模型标识", example = "mem_model_01")
|
||||
private String memModelId;
|
||||
|
||||
@Schema(description = "意图模型标识", example = "intent_model_01")
|
||||
private String intentModelId;
|
||||
|
||||
@Schema(description = "角色设定参数", example = "你是一个专业的客服助手,负责回答用户问题")
|
||||
private String systemPrompt;
|
||||
|
||||
@Schema(description = "语言编码", example = "zh_CN")
|
||||
private String langCode;
|
||||
|
||||
@Schema(description = "交互语种", example = "中文")
|
||||
private String language;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
package xiaozhi.modules.agent.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 智能体更新DTO
|
||||
* 专用于更新智能体,id字段是必需的,用于标识要更新的智能体
|
||||
@@ -16,46 +15,42 @@ import java.io.Serializable;
|
||||
public class AgentUpdateDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "智能体唯一标识", example = "a1b2c3d4e5f6", required = true)
|
||||
@NotBlank(message = "智能体ID不能为空")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "智能体编码", example = "AGT_1234567890", required = false)
|
||||
private String agentCode;
|
||||
|
||||
|
||||
@Schema(description = "智能体名称", example = "客服助手", required = false)
|
||||
private String agentName;
|
||||
|
||||
|
||||
@Schema(description = "语音识别模型标识", example = "asr_model_02", required = false)
|
||||
private String asrModelId;
|
||||
|
||||
|
||||
@Schema(description = "语音活动检测标识", example = "vad_model_02", required = false)
|
||||
private String vadModelId;
|
||||
|
||||
|
||||
@Schema(description = "大语言模型标识", example = "llm_model_02", required = false)
|
||||
private String llmModelId;
|
||||
|
||||
|
||||
@Schema(description = "语音合成模型标识", example = "tts_model_02", required = false)
|
||||
private String ttsModelId;
|
||||
|
||||
|
||||
@Schema(description = "音色标识", example = "voice_02", required = false)
|
||||
private String ttsVoiceId;
|
||||
|
||||
|
||||
@Schema(description = "记忆模型标识", example = "mem_model_02", required = false)
|
||||
private String memModelId;
|
||||
|
||||
|
||||
@Schema(description = "意图模型标识", example = "intent_model_02", required = false)
|
||||
private String intentModelId;
|
||||
|
||||
|
||||
@Schema(description = "角色设定参数", example = "你是一个专业的客服助手,负责回答用户问题并提供帮助", required = false)
|
||||
private String systemPrompt;
|
||||
|
||||
|
||||
@Schema(description = "语言编码", example = "zh_CN", required = false)
|
||||
private String langCode;
|
||||
|
||||
|
||||
@Schema(description = "交互语种", example = "中文", required = false)
|
||||
private String language;
|
||||
|
||||
|
||||
@Schema(description = "排序", example = "1", required = false)
|
||||
private Integer sort;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +1,74 @@
|
||||
package xiaozhi.modules.agent.entity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("ai_agent")
|
||||
@Schema(description = "智能体信息")
|
||||
public class AgentEntity {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
@Schema(description = "智能体唯一标识")
|
||||
private String id;
|
||||
|
||||
|
||||
@Schema(description = "所属用户ID")
|
||||
private Long userId;
|
||||
|
||||
|
||||
@Schema(description = "智能体编码")
|
||||
private String agentCode;
|
||||
|
||||
|
||||
@Schema(description = "智能体名称")
|
||||
private String agentName;
|
||||
|
||||
|
||||
@Schema(description = "语音识别模型标识")
|
||||
private String asrModelId;
|
||||
|
||||
|
||||
@Schema(description = "语音活动检测标识")
|
||||
private String vadModelId;
|
||||
|
||||
|
||||
@Schema(description = "大语言模型标识")
|
||||
private String llmModelId;
|
||||
|
||||
|
||||
@Schema(description = "语音合成模型标识")
|
||||
private String ttsModelId;
|
||||
|
||||
|
||||
@Schema(description = "音色标识")
|
||||
private String ttsVoiceId;
|
||||
|
||||
|
||||
@Schema(description = "记忆模型标识")
|
||||
private String memModelId;
|
||||
|
||||
|
||||
@Schema(description = "意图模型标识")
|
||||
private String intentModelId;
|
||||
|
||||
|
||||
@Schema(description = "角色设定参数")
|
||||
private String systemPrompt;
|
||||
|
||||
|
||||
@Schema(description = "语言编码")
|
||||
private String langCode;
|
||||
|
||||
|
||||
@Schema(description = "交互语种")
|
||||
private String language;
|
||||
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sort;
|
||||
|
||||
|
||||
@Schema(description = "创建者")
|
||||
private Long creator;
|
||||
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private Date createdAt;
|
||||
|
||||
|
||||
@Schema(description = "更新者")
|
||||
private Long updater;
|
||||
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
private Date updatedAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package xiaozhi.modules.agent.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 智能体配置模板表
|
||||
*
|
||||
* @TableName ai_agent_template
|
||||
*/
|
||||
@TableName(value = "ai_agent_template")
|
||||
@Data
|
||||
public class AgentTemplateEntity implements Serializable {
|
||||
/**
|
||||
* 智能体唯一标识
|
||||
*/
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 智能体编码
|
||||
*/
|
||||
private String agentCode;
|
||||
|
||||
/**
|
||||
* 智能体名称
|
||||
*/
|
||||
private String agentName;
|
||||
|
||||
/**
|
||||
* 语音识别模型标识
|
||||
*/
|
||||
private String asrModelId;
|
||||
|
||||
/**
|
||||
* 语音活动检测标识
|
||||
*/
|
||||
private String vadModelId;
|
||||
|
||||
/**
|
||||
* 大语言模型标识
|
||||
*/
|
||||
private String llmModelId;
|
||||
|
||||
/**
|
||||
* 语音合成模型标识
|
||||
*/
|
||||
private String ttsModelId;
|
||||
|
||||
/**
|
||||
* 音色标识
|
||||
*/
|
||||
private String ttsVoiceId;
|
||||
|
||||
/**
|
||||
* 记忆模型标识
|
||||
*/
|
||||
private String memModelId;
|
||||
|
||||
/**
|
||||
* 意图模型标识
|
||||
*/
|
||||
private String intentModelId;
|
||||
|
||||
/**
|
||||
* 角色设定参数
|
||||
*/
|
||||
private String systemPrompt;
|
||||
|
||||
/**
|
||||
* 语言编码
|
||||
*/
|
||||
private String langCode;
|
||||
|
||||
/**
|
||||
* 交互语种
|
||||
*/
|
||||
private String language;
|
||||
|
||||
/**
|
||||
* 排序权重
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 创建者 ID
|
||||
*/
|
||||
private Long creator;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createdAt;
|
||||
|
||||
/**
|
||||
* 更新者 ID
|
||||
*/
|
||||
private Long updater;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updatedAt;
|
||||
|
||||
@TableField(exist = false)
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
package xiaozhi.modules.agent.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.BaseService;
|
||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface AgentService extends BaseService<AgentEntity> {
|
||||
/**
|
||||
* 根据用户ID获取智能体列表
|
||||
*/
|
||||
List<AgentEntity> getUserAgents(Long userId);
|
||||
|
||||
|
||||
/**
|
||||
* 管理员获取所有智能体列表(分页)
|
||||
*/
|
||||
PageData<AgentEntity> adminAgentList(Map<String, Object> params);
|
||||
|
||||
|
||||
/**
|
||||
* 获取智能体详情
|
||||
*/
|
||||
AgentEntity getAgentById(String id);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package xiaozhi.modules.agent.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
|
||||
|
||||
/**
|
||||
* @author chenerlei
|
||||
* @description 针对表【ai_agent_template(智能体配置模板表)】的数据库操作Service
|
||||
* @createDate 2025-03-22 11:48:18
|
||||
*/
|
||||
public interface AgentTemplateService extends IService<AgentTemplateEntity> {
|
||||
|
||||
}
|
||||
+65
-64
@@ -1,64 +1,65 @@
|
||||
package xiaozhi.modules.agent.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springframework.stereotype.Service;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||
import xiaozhi.modules.agent.dao.AgentDao;
|
||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||
import xiaozhi.modules.agent.service.AgentService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> implements AgentService {
|
||||
private final AgentDao agentDao;
|
||||
|
||||
public AgentServiceImpl(AgentDao agentDao) {
|
||||
this.agentDao = agentDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AgentEntity> getUserAgents(Long userId) {
|
||||
QueryWrapper<AgentEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("user_id", userId);
|
||||
return agentDao.selectList(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
|
||||
IPage<AgentEntity> page = agentDao.selectPage(
|
||||
getPage(params, "sort", true),
|
||||
new QueryWrapper<>()
|
||||
);
|
||||
return new PageData<>(page.getRecords(), page.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentEntity getAgentById(String id) {
|
||||
return agentDao.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insert(AgentEntity entity) {
|
||||
// 如果ID为空,自动生成一个UUID作为ID
|
||||
if (entity.getId() == null || entity.getId().trim().isEmpty()) {
|
||||
entity.setId(UUID.randomUUID().toString().replace("-", ""));
|
||||
}
|
||||
|
||||
// 如果智能体编码为空,自动生成一个带前缀的编码
|
||||
if (entity.getAgentCode() == null || entity.getAgentCode().trim().isEmpty()) {
|
||||
entity.setAgentCode("AGT_" + System.currentTimeMillis());
|
||||
}
|
||||
|
||||
// 如果排序字段为空,设置默认值0
|
||||
if (entity.getSort() == null) {
|
||||
entity.setSort(0);
|
||||
}
|
||||
|
||||
return super.insert(entity);
|
||||
}
|
||||
}
|
||||
package xiaozhi.modules.agent.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||
import xiaozhi.modules.agent.dao.AgentDao;
|
||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||
import xiaozhi.modules.agent.service.AgentService;
|
||||
|
||||
@Service
|
||||
public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> implements AgentService {
|
||||
private final AgentDao agentDao;
|
||||
|
||||
public AgentServiceImpl(AgentDao agentDao) {
|
||||
this.agentDao = agentDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AgentEntity> getUserAgents(Long userId) {
|
||||
QueryWrapper<AgentEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("user_id", userId);
|
||||
return agentDao.selectList(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
|
||||
IPage<AgentEntity> page = agentDao.selectPage(
|
||||
getPage(params, "sort", true),
|
||||
new QueryWrapper<>());
|
||||
return new PageData<>(page.getRecords(), page.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentEntity getAgentById(String id) {
|
||||
return agentDao.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insert(AgentEntity entity) {
|
||||
// 如果ID为空,自动生成一个UUID作为ID
|
||||
if (entity.getId() == null || entity.getId().trim().isEmpty()) {
|
||||
entity.setId(UUID.randomUUID().toString().replace("-", ""));
|
||||
}
|
||||
|
||||
// 如果智能体编码为空,自动生成一个带前缀的编码
|
||||
if (entity.getAgentCode() == null || entity.getAgentCode().trim().isEmpty()) {
|
||||
entity.setAgentCode("AGT_" + System.currentTimeMillis());
|
||||
}
|
||||
|
||||
// 如果排序字段为空,设置默认值0
|
||||
if (entity.getSort() == null) {
|
||||
entity.setSort(0);
|
||||
}
|
||||
|
||||
return super.insert(entity);
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package xiaozhi.modules.agent.service.impl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import xiaozhi.modules.agent.dao.AgentTemplateDao;
|
||||
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
|
||||
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||
|
||||
/**
|
||||
* @author chenerlei
|
||||
* @description 针对表【ai_agent_template(智能体配置模板表)】的数据库操作Service实现
|
||||
* @createDate 2025-03-22 11:48:18
|
||||
*/
|
||||
@Service
|
||||
public class AgentTemplateServiceImpl extends ServiceImpl<AgentTemplateDao, AgentTemplateEntity>
|
||||
implements AgentTemplateService {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package xiaozhi.modules.agent.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AgentTemplateVO extends AgentTemplateEntity {
|
||||
// 角色音色
|
||||
private String ttsModelName;
|
||||
|
||||
// 角色模型
|
||||
private String llmModelName;
|
||||
}
|
||||
+28
-41
@@ -1,82 +1,69 @@
|
||||
package xiaozhi.modules.device.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.redis.RedisKeys;
|
||||
import xiaozhi.common.redis.RedisUtils;
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
import xiaozhi.common.utils.JsonUtils;
|
||||
import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.modules.device.dto.DeviceHeaderDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceBindDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceUnBindDTO;
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
import xiaozhi.modules.device.service.DeviceService;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Tag(name = "设备管理")
|
||||
@AllArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/device")
|
||||
public class DeviceController {
|
||||
private final DeviceService deviceService;
|
||||
|
||||
private final RedisUtils redisUtils;
|
||||
|
||||
|
||||
@PostMapping("/bind/{deviceCode}")
|
||||
@PostMapping("/bind/{agentId}/{deviceCode}")
|
||||
@Operation(summary = "绑定设备")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<DeviceEntity> bindDevice(@PathVariable String deviceCode) {
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
|
||||
String deviceHeaders = (String) redisUtils.get(RedisKeys.getDeviceCaptchaKey(deviceCode));
|
||||
if (StringUtils.isBlank(deviceHeaders)) {
|
||||
return new Result<DeviceEntity>().error(ErrorCode.DEVICE_CAPTCHA_ERROR);
|
||||
public Result<Void> bindDevice(@PathVariable String agentId, @PathVariable String deviceCode) {
|
||||
String macAddress = (String) redisUtils.get(RedisKeys.getDeviceCaptchaKey(deviceCode));
|
||||
if (StringUtils.isBlank(macAddress)) {
|
||||
return new Result<Void>().error(ErrorCode.DEVICE_CAPTCHA_ERROR);
|
||||
}
|
||||
DeviceHeaderDTO deviceHeader = JsonUtils.parseObject(deviceHeaders.getBytes(), DeviceHeaderDTO.class);
|
||||
DeviceEntity device = deviceService.bindDevice(user.getId(), deviceHeader);
|
||||
return new Result<DeviceEntity>().ok(device);
|
||||
Long user = SecurityUser.getUser().getId();
|
||||
DeviceBindDTO deviceBindDTO = new DeviceBindDTO(macAddress, user, agentId);
|
||||
deviceService.bindDevice(deviceBindDTO);
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
@GetMapping("/bind")
|
||||
@GetMapping("/bind/{agentId}")
|
||||
@Operation(summary = "获取已绑定设备")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<List<DeviceEntity>> getUserDevices() {
|
||||
public Result<List<DeviceEntity>> getUserDevices(@PathVariable String agentId) {
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
List<DeviceEntity> devices = deviceService.getUserDevices(user.getId());
|
||||
List<DeviceEntity> devices = deviceService.getUserDevices(user.getId(), agentId);
|
||||
return new Result<List<DeviceEntity>>().ok(devices);
|
||||
}
|
||||
|
||||
@PostMapping("/unbind")
|
||||
@Operation(summary = "解绑设备")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result unbindDevice(@RequestBody DeviceUnBindDTO unDeviveBind) {
|
||||
public Result<Void> unbindDevice(@RequestBody DeviceUnBindDTO unDeviveBind) {
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
deviceService.unbindDevice(user.getId(), unDeviveBind.getDeviceId());
|
||||
return new Result();
|
||||
return new Result<Void>();
|
||||
}
|
||||
|
||||
@GetMapping("/all")
|
||||
@Operation(summary = "设备列表(管理员)")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
@Parameters({
|
||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true),
|
||||
})
|
||||
public Result<PageData<DeviceEntity>> adminDeviceList(
|
||||
@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||
PageData<DeviceEntity> page = deviceService.adminDeviceList(params);
|
||||
return new Result<PageData<DeviceEntity>>().ok(page);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package xiaozhi.modules.device.controller;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.modules.device.dto.DeviceReportReqDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceReportRespDTO;
|
||||
import xiaozhi.modules.device.service.DeviceService;
|
||||
import xiaozhi.modules.device.utils.NetworkUtil;
|
||||
|
||||
@Tag(name = "设备管理", description = "OTA 相关接口")
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/ota")
|
||||
public class OTAController {
|
||||
private final DeviceService deviceService;
|
||||
|
||||
@Operation(summary = "检查 OTA 版本和设备激活状态")
|
||||
@PostMapping
|
||||
public ResponseEntity<String> checkOTAVersion(
|
||||
@RequestBody DeviceReportReqDTO deviceReportReqDTO,
|
||||
|
||||
@Parameter(name = "Device-Id", description = "设备唯一标识", required = true, in = ParameterIn.HEADER) @RequestHeader("Device-Id") String deviceId,
|
||||
|
||||
@Parameter(name = "Client-Id", description = "客户端标识", required = true, in = ParameterIn.HEADER) @RequestHeader("Client-Id") String clientId) {
|
||||
if (StringUtils.isAnyBlank(deviceId, clientId)) {
|
||||
return createResponse(DeviceReportRespDTO.createError("Device ID is required"));
|
||||
}
|
||||
String macAddress = deviceReportReqDTO.getMacAddress();
|
||||
boolean macAddressValid = NetworkUtil.isMacAddressValid(macAddress);
|
||||
// 设备Id和Mac地址应是一致的, 并且必须需要application字段
|
||||
if (!deviceId.equals(macAddress) || !macAddressValid || deviceReportReqDTO.getApplication() == null) {
|
||||
return createResponse(DeviceReportRespDTO.createError("Invalid OTA request"));
|
||||
}
|
||||
return createResponse(deviceService.checkDeviceActive(macAddress, deviceId, clientId, deviceReportReqDTO));
|
||||
}
|
||||
|
||||
@Operation(summary = "设备激活")
|
||||
@GetMapping("/activation")
|
||||
public Result<Boolean> deviceActivation(@RequestParam String code) {
|
||||
return new Result<Boolean>().ok(deviceService.deviceActivation(code));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private ResponseEntity<String> createResponse(DeviceReportRespDTO deviceReportRespDTO) {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
|
||||
String json = objectMapper.writeValueAsString(deviceReportRespDTO);
|
||||
byte[] jsonBytes = json.getBytes(StandardCharsets.UTF_8);
|
||||
return ResponseEntity
|
||||
.ok()
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.contentLength(jsonBytes.length)
|
||||
.body(json);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
package xiaozhi.modules.device.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
|
||||
@Mapper
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package xiaozhi.modules.device.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 设备绑定的DTO
|
||||
*
|
||||
* @author zjy
|
||||
* @since 2025-3-28
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "设备连接头信息")
|
||||
public class DeviceBindDTO {
|
||||
|
||||
@Schema(description = "mac地址")
|
||||
private String macAddress;
|
||||
|
||||
@Schema(description = "所属用户id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "智能体id")
|
||||
private String agentId;
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package xiaozhi.modules.device.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "设备连接头信息")
|
||||
public class DeviceHeaderDTO {
|
||||
|
||||
@Schema(description = "设备ID")
|
||||
private String deviceId;
|
||||
|
||||
@Schema(description = "协议版本号")
|
||||
private Long protocolVersion;
|
||||
|
||||
@Schema(description = "认证信息")
|
||||
private String authorization;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package xiaozhi.modules.device.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
@Schema(description = "设备固件信息上报求请求体")
|
||||
public class DeviceReportReqDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
// region 实体属性
|
||||
@Schema(description = "板子固件版本号")
|
||||
private Integer version;
|
||||
|
||||
@Schema(description = "闪存大小(单位:字节)")
|
||||
@JsonProperty("flash_size")
|
||||
private Integer flashSize;
|
||||
|
||||
@Schema(description = "最小空闲堆内存(字节)")
|
||||
@JsonProperty("minimum_free_heap_size")
|
||||
private Integer minimumFreeHeapSize;
|
||||
|
||||
@Schema(description = "设备 MAC 地址")
|
||||
@JsonProperty("mac_address")
|
||||
private String macAddress;
|
||||
|
||||
@Schema(description = "设备唯一标识 UUID")
|
||||
private String uuid;
|
||||
|
||||
@Schema(description = "芯片型号名称")
|
||||
@JsonProperty("chip_model_name")
|
||||
private String chipModelName;
|
||||
|
||||
@Schema(description = "芯片详细信息")
|
||||
@JsonProperty("chip_info")
|
||||
private ChipInfo chipInfo;
|
||||
|
||||
@Schema(description = "应用程序信息")
|
||||
private Application application;
|
||||
|
||||
@Schema(description = "分区表列表")
|
||||
@JsonProperty("partition_table")
|
||||
private List<Partition> partitionTable;
|
||||
|
||||
@Schema(description = "当前运行的 OTA 分区信息")
|
||||
private OtaInfo ota;
|
||||
|
||||
@Schema(description = "板子配置信息")
|
||||
private BoardInfo board;
|
||||
|
||||
// endregion
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "芯片信息")
|
||||
public static class ChipInfo {
|
||||
@Schema(description = "芯片模型代码")
|
||||
private Integer model;
|
||||
|
||||
@Schema(description = "核心数")
|
||||
private Integer cores;
|
||||
|
||||
@Schema(description = "硬件修订版本")
|
||||
private Integer revision;
|
||||
|
||||
@Schema(description = "芯片功能标志位")
|
||||
private Integer features;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "板子编译信息")
|
||||
public static class Application {
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "应用版本号")
|
||||
private String version;
|
||||
|
||||
@Schema(description = "编译时间(UTC ISO格式)")
|
||||
@JsonProperty("compile_time")
|
||||
private String compileTime;
|
||||
|
||||
@Schema(description = "ESP-IDF 版本号")
|
||||
@JsonProperty("idf_version")
|
||||
private String idfVersion;
|
||||
|
||||
@Schema(description = "ELF 文件 SHA256 校验")
|
||||
@JsonProperty("elf_sha256")
|
||||
private String elfSha256;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "分区信息")
|
||||
public static class Partition {
|
||||
@Schema(description = "分区标签名")
|
||||
private String label;
|
||||
|
||||
@Schema(description = "分区类型")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "子类型")
|
||||
private Integer subtype;
|
||||
|
||||
@Schema(description = "起始地址")
|
||||
private Integer address;
|
||||
|
||||
@Schema(description = "分区大小")
|
||||
private Integer size;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "OTA信息")
|
||||
public static class OtaInfo {
|
||||
@Schema(description = "当前OTA标签")
|
||||
private String label;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Schema(description = "板子连接和网络信息")
|
||||
public static class BoardInfo {
|
||||
@Schema(description = "板子类型")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "连接的 Wi-Fi SSID")
|
||||
private String ssid;
|
||||
|
||||
@Schema(description = "Wi-Fi 信号强度(RSSI)")
|
||||
private Integer rssi;
|
||||
|
||||
@Schema(description = "Wi-Fi 信道")
|
||||
private Integer channel;
|
||||
|
||||
@Schema(description = "IP 地址")
|
||||
private String ip;
|
||||
|
||||
@Schema(description = "MAC 地址")
|
||||
private String mac;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package xiaozhi.modules.device.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Data
|
||||
@Schema(description = "设备OTA检测版本返回体,包含激活码要求")
|
||||
public class DeviceReportRespDTO {
|
||||
@Schema(description = "服务器时间")
|
||||
private ServerTime serverTime;
|
||||
|
||||
@Schema(description = "激活码")
|
||||
private Activation activation;
|
||||
|
||||
@Schema(description = "错误信息")
|
||||
private String error;
|
||||
|
||||
@Schema(description = "固件版本信息")
|
||||
private Firmware firmware;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Firmware {
|
||||
@Schema(description = "版本号")
|
||||
private String version;
|
||||
@Schema(description = "下载地址")
|
||||
private String url;
|
||||
}
|
||||
|
||||
public static DeviceReportRespDTO createError(String message) {
|
||||
DeviceReportRespDTO resp = new DeviceReportRespDTO();
|
||||
resp.setError(message);
|
||||
return resp;
|
||||
}
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public static class Activation {
|
||||
@Schema(description = "激活码")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "激活码信息: 激活地址")
|
||||
private String message;
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class ServerTime {
|
||||
@Schema(description = "时间戳")
|
||||
private Long timestamp;
|
||||
|
||||
@Schema(description = "时区")
|
||||
private String timeZone;
|
||||
|
||||
@Schema(description = "时区偏移量,单位为分钟")
|
||||
private Integer timezoneOffset;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
package xiaozhi.modules.device.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 设备解绑表单
|
||||
*/
|
||||
@@ -14,6 +15,6 @@ public class DeviceUnBindDTO implements Serializable {
|
||||
|
||||
@Schema(description = "设备ID")
|
||||
@NotBlank(message = "设备ID不能为空")
|
||||
private Long deviceId;
|
||||
private String deviceId;
|
||||
|
||||
}
|
||||
@@ -1,17 +1,26 @@
|
||||
package xiaozhi.modules.device.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@TableName("ai_device")
|
||||
@Schema(description = "设备信息")
|
||||
public class DeviceEntity {
|
||||
@Schema(description = "设备ID")
|
||||
private Long id;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
@Schema(description = "ID")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "关联用户ID")
|
||||
private Long userId;
|
||||
@@ -40,15 +49,19 @@ public class DeviceEntity {
|
||||
@Schema(description = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "创建者")
|
||||
private Long creator;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private Date createDate;
|
||||
|
||||
@Schema(description = "更新者")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private Long updater;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private Date updateDate;
|
||||
|
||||
@Schema(description = "创建者")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Long creator;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date createDate;
|
||||
}
|
||||
@@ -1,18 +1,42 @@
|
||||
package xiaozhi.modules.device.service;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.modules.device.dto.DeviceHeaderDTO;
|
||||
import java.util.List;
|
||||
|
||||
import xiaozhi.modules.device.dto.DeviceBindDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceReportReqDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceReportRespDTO;
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface DeviceService {
|
||||
DeviceEntity bindDevice(Long userId, DeviceHeaderDTO deviceHeader);
|
||||
|
||||
List<DeviceEntity> getUserDevices(Long userId);
|
||||
|
||||
void unbindDevice(Long userId, Long deviceId);
|
||||
|
||||
PageData<DeviceEntity> adminDeviceList(Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 根据Mac地址获取设备信息
|
||||
*/
|
||||
DeviceEntity getDeviceById(String macAddress);
|
||||
|
||||
/**
|
||||
* 检查设备是否激活
|
||||
*/
|
||||
DeviceReportRespDTO checkDeviceActive(String macAddress, String deviceId, String clientId,
|
||||
DeviceReportReqDTO deviceReport);
|
||||
|
||||
/**
|
||||
* 绑定设备
|
||||
*/
|
||||
DeviceEntity bindDevice(DeviceBindDTO deviceHeader);
|
||||
|
||||
/**
|
||||
* 获取用户设备列表
|
||||
*/
|
||||
List<DeviceEntity> getUserDevices(Long userId, String agentId);
|
||||
|
||||
/**
|
||||
* 解绑设备
|
||||
*/
|
||||
void unbindDevice(Long userId, String deviceId);
|
||||
|
||||
/**
|
||||
* 设备激活
|
||||
*/
|
||||
Boolean deviceActivation(String activationCode);
|
||||
}
|
||||
+181
-30
@@ -1,57 +1,208 @@
|
||||
package xiaozhi.modules.device.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springframework.stereotype.Service;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||
import xiaozhi.modules.device.dao.DeviceDao;
|
||||
import xiaozhi.modules.device.dto.DeviceHeaderDTO;
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
import xiaozhi.modules.device.service.DeviceService;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
import xiaozhi.common.utils.ConvertUtils;
|
||||
import xiaozhi.modules.device.dao.DeviceDao;
|
||||
import xiaozhi.modules.device.dto.DeviceBindDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceReportReqDTO;
|
||||
import xiaozhi.modules.device.dto.DeviceReportRespDTO;
|
||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||
import xiaozhi.modules.device.service.DeviceService;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
|
||||
@Service
|
||||
public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity> implements DeviceService {
|
||||
|
||||
private final DeviceDao deviceDao;
|
||||
|
||||
private final String frontedUrl;
|
||||
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
// 添加构造函数来初始化 deviceMapper
|
||||
public DeviceServiceImpl(DeviceDao deviceDao) {
|
||||
public DeviceServiceImpl(DeviceDao deviceDao,
|
||||
@Value("${app.fronted-url:http://localhost:8001}") String frontedUrl,
|
||||
RedisTemplate<String, Object> redisTemplate) {
|
||||
this.deviceDao = deviceDao;
|
||||
this.frontedUrl = frontedUrl;
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceEntity bindDevice(Long userId, DeviceHeaderDTO deviceHeader) {
|
||||
DeviceEntity device = new DeviceEntity();
|
||||
device.setUserId(userId);
|
||||
device.setMacAddress(deviceHeader.getDeviceId());
|
||||
device.setCreateDate(new Date());
|
||||
deviceDao.insert(device);
|
||||
return device;
|
||||
public DeviceEntity getDeviceById(String deviceId) {
|
||||
LambdaQueryWrapper<DeviceEntity> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(DeviceEntity::getId, deviceId);
|
||||
return deviceDao.selectOne(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceEntity> getUserDevices(Long userId) {
|
||||
public Boolean deviceActivation(String activationCode) {
|
||||
if (StringUtils.isBlank(activationCode)) {
|
||||
throw new RenException("激活码不能为空");
|
||||
}
|
||||
String deviceKey = "ota:activation:code:" + activationCode;
|
||||
Object cacheDeviceId = redisTemplate.opsForValue().get(deviceKey);
|
||||
if (cacheDeviceId == null) {
|
||||
throw new RenException("激活码错误");
|
||||
}
|
||||
String deviceId = (String) cacheDeviceId;
|
||||
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
||||
String cacheDeviceKey = String.format("ota:activation:data:%s", safeDeviceId);
|
||||
Map<Object, Object> cacheMap = redisTemplate.opsForHash().entries(cacheDeviceKey);
|
||||
if (cacheMap == null) {
|
||||
throw new RenException("激活码错误");
|
||||
}
|
||||
String cachedCode = (String) cacheMap.get("activation_code");
|
||||
if (!activationCode.equals(cachedCode)) {
|
||||
throw new RenException("激活码错误");
|
||||
}
|
||||
// 检查设备有没有被激活
|
||||
if (selectById(deviceId) != null) {
|
||||
throw new RenException("设备已激活");
|
||||
}
|
||||
|
||||
String macAddress = (String) cacheMap.get("mac_address");
|
||||
String board = (String) cacheMap.get("board");
|
||||
String appVersion = (String) cacheMap.get("app_version");
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
if (user.getId() == null) {
|
||||
throw new RenException("用户未登录");
|
||||
}
|
||||
|
||||
Date currentTime = new Date();
|
||||
DeviceEntity deviceEntity = new DeviceEntity();
|
||||
deviceEntity.setId(deviceId);
|
||||
deviceEntity.setBoard(board);
|
||||
deviceEntity.setAppVersion(appVersion);
|
||||
deviceEntity.setMacAddress(macAddress);
|
||||
deviceEntity.setUserId(user.getId());
|
||||
deviceEntity.setCreator(user.getId());
|
||||
deviceEntity.setCreateDate(currentTime);
|
||||
deviceEntity.setUpdater(user.getId());
|
||||
deviceEntity.setUpdateDate(currentTime);
|
||||
deviceEntity.setLastConnectedAt(currentTime);
|
||||
deviceDao.insert(deviceEntity);
|
||||
|
||||
// 清理redis缓存
|
||||
redisTemplate.delete(cacheDeviceKey);
|
||||
redisTemplate.delete(deviceKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceReportRespDTO checkDeviceActive(String macAddress, String deviceId, String clientId,
|
||||
DeviceReportReqDTO deviceReport) {
|
||||
DeviceReportRespDTO response = new DeviceReportRespDTO();
|
||||
response.setServerTime(buildServerTime());
|
||||
// todo: 此处是固件信息,目前是针对固件上传上来的版本号再返回回去
|
||||
// 在未来开发了固件更新功能,需要更换此处代码,
|
||||
// 或写定时任务定期请求虾哥的OTA,获取最新的版本讯息保存到服务内
|
||||
DeviceReportRespDTO.Firmware firmware = new DeviceReportRespDTO.Firmware();
|
||||
firmware.setVersion(deviceReport.getApplication().getVersion());
|
||||
firmware.setUrl("http://localhost:8002/xiaozhi-esp32-api/api/v1/ota/download");
|
||||
response.setFirmware(firmware);
|
||||
|
||||
DeviceEntity deviceById = getDeviceById(deviceId);
|
||||
if (deviceById != null) { // 如果设备存在,则更新上次连接时间
|
||||
deviceById.setLastConnectedAt(new Date());
|
||||
deviceDao.updateById(deviceById);
|
||||
} else { // 如果设备不存在,则生成激活码
|
||||
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
||||
String dataKey = String.format("ota:activation:data:%s", safeDeviceId);
|
||||
|
||||
Map<Object, Object> cacheMap = redisTemplate.opsForHash().entries(dataKey);
|
||||
DeviceReportRespDTO.Activation code = new DeviceReportRespDTO.Activation();
|
||||
|
||||
if (cacheMap != null && cacheMap.containsKey("activation_code")) {
|
||||
String cachedCode = (String) cacheMap.get("activation_code");
|
||||
code.setCode(cachedCode);
|
||||
code.setMessage(frontedUrl + "\n" + cachedCode);
|
||||
} else {
|
||||
String newCode = RandomUtil.randomNumbers(6);
|
||||
code.setCode(newCode);
|
||||
code.setMessage(frontedUrl + "\n" + newCode);
|
||||
|
||||
Map<String, Object> dataMap = new HashMap<>();
|
||||
dataMap.put("id", deviceId);
|
||||
dataMap.put("mac_address", macAddress);
|
||||
dataMap.put("board", (deviceReport.getChipModelName() != null) ? deviceReport.getChipModelName()
|
||||
: (deviceReport.getBoard() != null ? deviceReport.getBoard().getType() : "unknown"));
|
||||
dataMap.put("app_version", (deviceReport.getApplication() != null)
|
||||
? deviceReport.getApplication().getVersion()
|
||||
: null);
|
||||
dataMap.put("deviceId", deviceId);
|
||||
dataMap.put("activation_code", newCode);
|
||||
|
||||
// 写入主数据 key
|
||||
redisTemplate.opsForHash().putAll(dataKey, dataMap);
|
||||
redisTemplate.expire(dataKey, 24, TimeUnit.HOURS);
|
||||
|
||||
// 写入反查激活码 key
|
||||
String codeKey = "ota:activation:code:" + newCode;
|
||||
redisTemplate.opsForValue().set(codeKey, deviceId, 24, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
response.setActivation(code);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceEntity bindDevice(DeviceBindDTO dto) {
|
||||
// 查看是否已经被绑定
|
||||
DeviceEntity deviceEntity = baseDao
|
||||
.selectOne(new LambdaQueryWrapper<DeviceEntity>().eq(DeviceEntity::getMacAddress, dto.getMacAddress()));
|
||||
if (deviceEntity != null) {
|
||||
throw new RenException("设备已绑定");
|
||||
}
|
||||
deviceEntity = ConvertUtils.sourceToTarget(dto, DeviceEntity.class);
|
||||
baseDao.insert(deviceEntity);
|
||||
return deviceEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DeviceEntity> getUserDevices(Long userId, String agentId) {
|
||||
QueryWrapper<DeviceEntity> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("user_id", userId);
|
||||
return deviceDao.selectList(wrapper);
|
||||
wrapper.eq("agent_id", agentId);
|
||||
return baseDao.selectList(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unbindDevice(Long userId, Long deviceId) {
|
||||
deviceDao.deleteById(deviceId);
|
||||
public void unbindDevice(Long userId, String deviceId) {
|
||||
UpdateWrapper<DeviceEntity> wrapper = new UpdateWrapper<>();
|
||||
wrapper.eq("user_id", userId);
|
||||
wrapper.eq("id", deviceId);
|
||||
baseDao.delete(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<DeviceEntity> adminDeviceList(Map<String, Object> params) {
|
||||
IPage<DeviceEntity> page = deviceDao.selectPage(
|
||||
getPage(params, "sort", true),
|
||||
new QueryWrapper<>()
|
||||
);
|
||||
return new PageData<>(page.getRecords(), page.getTotal());
|
||||
private DeviceReportRespDTO.ServerTime buildServerTime() {
|
||||
DeviceReportRespDTO.ServerTime serverTime = new DeviceReportRespDTO.ServerTime();
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
serverTime.setTimestamp(Instant.now().toEpochMilli());
|
||||
serverTime.setTimeZone(tz.getID());
|
||||
serverTime.setTimezoneOffset(tz.getOffset(System.currentTimeMillis()) / (60 * 1000));
|
||||
return serverTime;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package xiaozhi.modules.device.utils;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* 网络工具类
|
||||
*/
|
||||
public class NetworkUtil {
|
||||
/**
|
||||
* MAC地址正则表达式
|
||||
*/
|
||||
private static final Pattern macPattern = Pattern.compile("^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$");
|
||||
|
||||
/**
|
||||
* 判断MAC地址是否合法
|
||||
*/
|
||||
public static boolean isMacAddressValid(String mac) {
|
||||
if (StringUtils.isBlank(mac)) {
|
||||
return false;
|
||||
}
|
||||
// 正则校验格式
|
||||
if (!macPattern.matcher(mac).matches()) {
|
||||
return false;
|
||||
}
|
||||
// 校验MAC地址是否为单播地址
|
||||
String normalized = mac.toLowerCase();
|
||||
String[] parts = normalized.split("[:-]");
|
||||
int firstByte = Integer.parseInt(parts[0], 16);
|
||||
return (firstByte & 1) == 0; // 最低位为0表示单播地址,合法
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package xiaozhi.modules.device.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class DeviceOtaVO {
|
||||
private Activation activation;
|
||||
private Mqtt mqtt;
|
||||
private ServerTime server_time;
|
||||
private Firmware firmware;
|
||||
private String error;
|
||||
|
||||
@Data
|
||||
public static class Activation {
|
||||
private String code;
|
||||
private String message;
|
||||
|
||||
public Activation() {
|
||||
}
|
||||
|
||||
public Activation(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public class Mqtt {
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ServerTime {
|
||||
private Long timestamp;
|
||||
private Integer timezone_offset;
|
||||
|
||||
public ServerTime() {
|
||||
}
|
||||
|
||||
public ServerTime(Long timestamp, Integer timezone_offset) {
|
||||
this.timestamp = timestamp;
|
||||
this.timezone_offset = timezone_offset;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Firmware {
|
||||
private String version;
|
||||
private String url;
|
||||
|
||||
public Firmware() {
|
||||
}
|
||||
|
||||
public Firmware(String version, String url) {
|
||||
this.version = version;
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
package xiaozhi.modules.model.controller;public class ModelConfigController {
|
||||
}
|
||||
+35
-26
@@ -1,20 +1,30 @@
|
||||
package xiaozhi.modules.model.controller;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
||||
import xiaozhi.modules.model.dto.ModelConfigDTO;
|
||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||
import xiaozhi.modules.model.dto.ModelProviderFieldDTO;
|
||||
import xiaozhi.modules.model.service.ModelConfigService;
|
||||
import xiaozhi.modules.model.service.ModelProviderService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/models")
|
||||
@@ -29,7 +39,7 @@ public class ModelController {
|
||||
@Operation(summary = "获取所有模型名称")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
public Result<List<String>> getModelNames(@RequestParam String modelType,
|
||||
@RequestParam(required = false) String modelName) {
|
||||
@RequestParam(required = false) String modelName) {
|
||||
List<String> modelNameList = modelConfigService.getModelCodeList(modelType, modelName);
|
||||
return new Result<List<String>>().ok(modelNameList);
|
||||
}
|
||||
@@ -45,62 +55,61 @@ public class ModelController {
|
||||
@GetMapping("/{modelType}/{provideCode}/fields")
|
||||
@Operation(summary = "获取模型供应器字段")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
public Result<List<String>> getModelProviderFields(@PathVariable String modelType, @PathVariable String provideCode) {
|
||||
List<String> fieldList = modelProviderService.getFieldList(modelType, provideCode);
|
||||
return new Result<List<String>>().ok(fieldList);
|
||||
public Result<List<ModelProviderFieldDTO>> getModelProviderFields(@PathVariable String modelType,
|
||||
@PathVariable String provideCode) {
|
||||
List<ModelProviderFieldDTO> fieldList = modelProviderService.getFieldList(modelType, provideCode);
|
||||
return new Result<List<ModelProviderFieldDTO>>().ok(fieldList);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/models/list")
|
||||
@Operation(summary = "获取模型配置列表")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
public Result<PageData<ModelConfigDTO>> getModelConfigList(@RequestParam String modelType,
|
||||
@RequestParam(required = false) String modelName,
|
||||
@RequestParam(required = false, defaultValue = "0") Integer page,
|
||||
@RequestParam(required = false,defaultValue = "10") Integer limit) {
|
||||
public Result<PageData<ModelConfigDTO>> getModelConfigList(
|
||||
@RequestParam(required = true) String modelType,
|
||||
@RequestParam(required = false) String modelName,
|
||||
@RequestParam(required = true, defaultValue = "0") String page,
|
||||
@RequestParam(required = true, defaultValue = "10") String limit) {
|
||||
PageData<ModelConfigDTO> pageList = modelConfigService.getPageList(modelType, modelName, page, limit);
|
||||
return new Result<PageData<ModelConfigDTO>>().ok(pageList);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/models/{modelType}/{provideCode}")
|
||||
@Operation(summary = "新增模型配置")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
public Result<ModelConfigDTO> addModelConfig(@PathVariable String modelType,
|
||||
@PathVariable String provideCode,
|
||||
@RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||
@PathVariable String provideCode,
|
||||
@RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||
ModelConfigDTO modelConfigDTO = modelConfigService.add(modelType, provideCode, modelConfigBodyDTO);
|
||||
return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
||||
}
|
||||
|
||||
|
||||
@PutMapping("/models/{modelType}/{provideCode}/{id}")
|
||||
@Operation(summary = "编辑模型配置")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
public Result<ModelConfigDTO> editModelConfig(@PathVariable String modelType,
|
||||
@PathVariable String provideCode,
|
||||
@PathVariable String id,
|
||||
@RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||
@PathVariable String provideCode,
|
||||
@PathVariable String id,
|
||||
@RequestBody ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||
ModelConfigDTO modelConfigDTO = modelConfigService.edit(modelType, provideCode, id, modelConfigBodyDTO);
|
||||
return new Result<ModelConfigDTO>().ok(modelConfigDTO);
|
||||
}
|
||||
|
||||
|
||||
@DeleteMapping("/models/{modelType}/{provideCode}/{id}")
|
||||
@Operation(summary = "删除模型配置")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
public Result<Void> deleteModelConfig(@PathVariable String modelType, @PathVariable String provideCode, @PathVariable String id) {
|
||||
public Result<Void> deleteModelConfig(@PathVariable String modelType, @PathVariable String provideCode,
|
||||
@PathVariable String id) {
|
||||
modelConfigService.delete(modelType, provideCode, id);
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
@GetMapping("/models/{modelName}/voices")
|
||||
@GetMapping("/models/{modelId}/voices")
|
||||
@Operation(summary = "获取模型音色")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<List<String>> getVoiceList(@PathVariable String modelName,
|
||||
@RequestParam(required = false) String voiceName) {
|
||||
public Result<List<String>> getVoiceList(@PathVariable String modelId,
|
||||
@RequestParam(required = false) String voiceName) {
|
||||
|
||||
List<String> voiceList = modelConfigService.getVoiceList(modelName, voiceName);
|
||||
List<String> voiceList = modelConfigService.getVoiceList(modelId, voiceName);
|
||||
return new Result<List<String>>().ok(voiceList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package xiaozhi.modules.model.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import xiaozhi.common.dao.BaseDao;
|
||||
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ModelConfigDao extends BaseDao<ModelConfigEntity> {
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package xiaozhi.modules.model.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import xiaozhi.common.dao.BaseDao;
|
||||
import xiaozhi.modules.model.entity.ModelProviderEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ModelProviderDao extends BaseDao<ModelProviderEntity> {
|
||||
|
||||
List<String> getFieldList(@Param("modelType") String modelType, @Param("provideCode") String provideCode);
|
||||
List<String> getFieldList(@Param("modelType") String modelType, @Param("providerCode") String providerCode);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package xiaozhi.modules.model.dto;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
@Data
|
||||
@Schema(description = "模型供应器/商")
|
||||
public class ModelConfigBodyDTO {
|
||||
@@ -12,9 +12,9 @@ public class ModelConfigBodyDTO {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// @Schema(description = "模型类型(Memory/ASR/VAD/LLM/TTS)")
|
||||
// private String modelType;
|
||||
//
|
||||
// @Schema(description = "模型类型(Memory/ASR/VAD/LLM/TTS)")
|
||||
// private String modelType;
|
||||
//
|
||||
@Schema(description = "模型编码(如AliLLM、DoubaoTTS)")
|
||||
private String modelCode;
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package xiaozhi.modules.model.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "模型供应器/商")
|
||||
public class ModelConfigDTO implements Serializable {
|
||||
@@ -14,7 +14,7 @@ public class ModelConfigDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主键")
|
||||
private Long id;
|
||||
private String id;
|
||||
|
||||
@Schema(description = "模型类型(Memory/ASR/VAD/LLM/TTS)")
|
||||
private String modelType;
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
package xiaozhi.modules.model.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "模型供应器/商")
|
||||
public class ModelProviderDTO implements Serializable {
|
||||
//
|
||||
// @Schema(description = "主键")
|
||||
// private Long id;
|
||||
//
|
||||
// @Schema(description = "主键")
|
||||
// private Long id;
|
||||
|
||||
@Schema(description = "模型类型(Memory/ASR/VAD/LLM/TTS)")
|
||||
private String modelType;
|
||||
@@ -31,15 +33,19 @@ public class ModelProviderDTO implements Serializable {
|
||||
@Schema(description = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "创建者")
|
||||
private Long creator;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private Date createDate;
|
||||
|
||||
@Schema(description = "更新者")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private Long updater;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private Date updateDate;
|
||||
|
||||
@Schema(description = "创建者")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Long creator;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date createDate;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package xiaozhi.modules.model.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "模型供应器字段")
|
||||
public class ModelProviderFieldDTO implements Serializable {
|
||||
@Schema(description = "字段名")
|
||||
private String key;
|
||||
|
||||
@Schema(description = "字段标签")
|
||||
private String label;
|
||||
|
||||
@Schema(description = "字段类型")
|
||||
private String type;
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
package xiaozhi.modules.model.entity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
@Data
|
||||
@TableName("ai_model_config")
|
||||
@Schema(description = "模型配置表")
|
||||
@@ -48,15 +49,19 @@ public class ModelConfigEntity {
|
||||
@Schema(description = "排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "创建者")
|
||||
private Long creator;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private Date createDate;
|
||||
|
||||
@Schema(description = "更新者")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private Long updater;
|
||||
|
||||
@Schema(description = "更新时间")
|
||||
@TableField(fill = FieldFill.UPDATE)
|
||||
private Date updateDate;
|
||||
|
||||
@Schema(description = "创建者")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Long creator;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private Date createDate;
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,15 +1,16 @@
|
||||
package xiaozhi.modules.model.entity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@TableName("ai_model_provider")
|
||||
@Schema(description = "模型供应器表")
|
||||
|
||||
+3
-3
@@ -1,16 +1,16 @@
|
||||
package xiaozhi.modules.model.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.modules.model.dto.ModelConfigBodyDTO;
|
||||
import xiaozhi.modules.model.dto.ModelConfigDTO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ModelConfigService {
|
||||
|
||||
List<String> getModelCodeList(String modelType, String modelName);
|
||||
|
||||
PageData<ModelConfigDTO> getPageList(String modelType, String modelName, Integer page, Integer limit);
|
||||
PageData<ModelConfigDTO> getPageList(String modelType, String modelName, String page, String limit);
|
||||
|
||||
ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO);
|
||||
|
||||
|
||||
+6
-5
@@ -1,13 +1,14 @@
|
||||
package xiaozhi.modules.model.service;
|
||||
|
||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||
import xiaozhi.modules.model.entity.ModelProviderEntity;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||
import xiaozhi.modules.model.dto.ModelProviderFieldDTO;
|
||||
import xiaozhi.modules.model.entity.ModelProviderEntity;
|
||||
|
||||
public interface ModelProviderService {
|
||||
|
||||
// List<String> getModelNames(String modelType, String modelName);
|
||||
// List<String> getModelNames(String modelType, String modelName);
|
||||
|
||||
List<ModelProviderDTO> getListByModelType(String modelType);
|
||||
|
||||
@@ -19,5 +20,5 @@ public interface ModelProviderService {
|
||||
|
||||
List<ModelProviderDTO> getList(String modelType, String provideCode);
|
||||
|
||||
List<String> getFieldList(String modelType, String provideCode);
|
||||
List<ModelProviderFieldDTO> getFieldList(String modelType, String provideCode);
|
||||
}
|
||||
|
||||
+17
-26
@@ -1,13 +1,19 @@
|
||||
package xiaozhi.modules.model.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import lombok.AllArgsConstructor;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import xiaozhi.common.page.PageData;
|
||||
@@ -22,13 +28,10 @@ import xiaozhi.modules.model.service.ModelConfigService;
|
||||
import xiaozhi.modules.model.service.ModelProviderService;
|
||||
import xiaozhi.modules.timbre.service.TimbreService;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, ModelConfigEntity> implements ModelConfigService {
|
||||
public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, ModelConfigEntity>
|
||||
implements ModelConfigService {
|
||||
|
||||
private final ModelConfigDao modelConfigDao;
|
||||
private final ModelProviderService modelProviderService;
|
||||
@@ -42,7 +45,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<ModelConfigDTO> getPageList(String modelType, String modelName, Integer page, Integer limit) {
|
||||
public PageData<ModelConfigDTO> getPageList(String modelType, String modelName, String page, String limit) {
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
params.put(Constant.PAGE, page);
|
||||
params.put(Constant.LIMIT, limit);
|
||||
@@ -50,8 +53,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
||||
getPage(params, "sort", true),
|
||||
new QueryWrapper<ModelConfigEntity>()
|
||||
.eq("model_type", modelType)
|
||||
.like(StringUtils.isNotBlank(modelName), "model_name", modelName)
|
||||
);
|
||||
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%"));
|
||||
return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
|
||||
}
|
||||
|
||||
@@ -103,22 +105,11 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
||||
throw new RenException("供应器不存在");
|
||||
}
|
||||
|
||||
modelConfigDao.deleteById(Long.getLong(id));
|
||||
modelConfigDao.deleteById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getVoiceList(String modelName, String voiceName) {
|
||||
QueryWrapper<ModelConfigEntity> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("model_name", StringUtils.isBlank(modelName) ? "" : modelName);
|
||||
queryWrapper.eq("model_type", "TTS");
|
||||
List<ModelConfigEntity> modelConfigEntities = modelConfigDao.selectList(queryWrapper);
|
||||
if (CollectionUtil.isEmpty(modelConfigEntities)) {
|
||||
logger.warn("没有找到模型配置信息");
|
||||
return null;
|
||||
}
|
||||
ModelConfigEntity modelConfigEntity = modelConfigEntities.get(0);
|
||||
String id = modelConfigEntity.getId();
|
||||
|
||||
return timbreService.getVoiceNames(id, voiceName);
|
||||
public List<String> getVoiceList(String modelId, String voiceName) {
|
||||
return timbreService.getVoiceNames(modelId, voiceName);
|
||||
}
|
||||
}
|
||||
|
||||
+27
-16
@@ -1,21 +1,26 @@
|
||||
package xiaozhi.modules.model.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||
import xiaozhi.common.utils.ConvertUtils;
|
||||
import xiaozhi.modules.model.dao.ModelProviderDao;
|
||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||
import xiaozhi.modules.model.entity.ModelProviderEntity;
|
||||
import xiaozhi.modules.model.service.ModelProviderService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||
import xiaozhi.common.utils.ConvertUtils;
|
||||
import xiaozhi.common.utils.JsonUtils;
|
||||
import xiaozhi.modules.model.dao.ModelProviderDao;
|
||||
import xiaozhi.modules.model.dto.ModelProviderDTO;
|
||||
import xiaozhi.modules.model.dto.ModelProviderFieldDTO;
|
||||
import xiaozhi.modules.model.entity.ModelProviderEntity;
|
||||
import xiaozhi.modules.model.service.ModelProviderService;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao, ModelProviderEntity> implements ModelProviderService {
|
||||
public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao, ModelProviderEntity>
|
||||
implements ModelProviderService {
|
||||
|
||||
private final ModelProviderDao modelProviderDao;
|
||||
|
||||
@@ -44,16 +49,22 @@ public class ModelProviderServiceImpl extends BaseServiceImpl<ModelProviderDao,
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ModelProviderDTO> getList(String modelType, String provideCode) {
|
||||
public List<ModelProviderDTO> getList(String modelType, String providerCode) {
|
||||
QueryWrapper<ModelProviderEntity> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("model_type", StringUtils.isBlank(modelType) ? "" : modelType);
|
||||
queryWrapper.eq("provide_code", StringUtils.isBlank(provideCode) ? "" : provideCode);
|
||||
queryWrapper.eq("provider_code", StringUtils.isBlank(providerCode) ? "" : providerCode);
|
||||
List<ModelProviderEntity> providerEntities = modelProviderDao.selectList(queryWrapper);
|
||||
return ConvertUtils.sourceToTarget(providerEntities, ModelProviderDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getFieldList(String modelType, String provideCode) {
|
||||
return modelProviderDao.getFieldList(modelType, provideCode);
|
||||
public List<ModelProviderFieldDTO> getFieldList(String modelType, String providerCode) {
|
||||
List<String> modelProviderEntities = modelProviderDao.getFieldList(modelType, providerCode);
|
||||
if (modelProviderEntities == null || modelProviderEntities.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String fields = modelProviderEntities.getFirst();
|
||||
List<ModelProviderFieldDTO> fieldList = JsonUtils.parseArray(fields, ModelProviderFieldDTO.class);
|
||||
return fieldList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
|
||||
|
||||
/**
|
||||
* Filter配置
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -18,7 +17,7 @@ public class FilterConfig {
|
||||
public FilterRegistrationBean<DelegatingFilterProxy> shiroFilterRegistration() {
|
||||
FilterRegistrationBean<DelegatingFilterProxy> registration = new FilterRegistrationBean<>();
|
||||
registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
|
||||
//该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
|
||||
// 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
|
||||
registration.addInitParameter("targetFilterLifecycle", "true");
|
||||
registration.setEnabled(true);
|
||||
registration.setOrder(Integer.MAX_VALUE - 1);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package xiaozhi.modules.security.config;
|
||||
|
||||
import xiaozhi.modules.security.oauth2.Oauth2Filter;
|
||||
import xiaozhi.modules.security.oauth2.Oauth2Realm;
|
||||
import jakarta.servlet.Filter;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.session.mgt.SessionManager;
|
||||
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||
@@ -14,9 +15,9 @@ import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import jakarta.servlet.Filter;
|
||||
import xiaozhi.modules.security.oauth2.Oauth2Filter;
|
||||
import xiaozhi.modules.security.oauth2.Oauth2Realm;
|
||||
|
||||
/**
|
||||
* Shiro的配置文件
|
||||
@@ -53,18 +54,21 @@ public class ShiroConfig {
|
||||
shiroFilter.setSecurityManager(securityManager);
|
||||
shiroFilter.setShiroFilterConfiguration(config);
|
||||
|
||||
//oauth过滤
|
||||
// oauth过滤
|
||||
Map<String, Filter> filters = new HashMap<>();
|
||||
filters.put("oauth2", new Oauth2Filter());
|
||||
shiroFilter.setFilters(filters);
|
||||
|
||||
//添加Shiro的内置过滤器
|
||||
/*anon:无需认证就可以访问
|
||||
authc:必须认证了才能让问
|
||||
user:必须拥有,记住我功能,才能访问
|
||||
perms:拥有对某个资源的权限才能访问
|
||||
role:拥有某个角色权限才能访问*/
|
||||
// 添加Shiro的内置过滤器
|
||||
/*
|
||||
* anon:无需认证就可以访问
|
||||
* authc:必须认证了才能让问
|
||||
* user:必须拥有,记住我功能,才能访问
|
||||
* perms:拥有对某个资源的权限才能访问
|
||||
* role:拥有某个角色权限才能访问
|
||||
*/
|
||||
Map<String, String> filterMap = new LinkedHashMap<>();
|
||||
filterMap.put("/ota/**", "anon");
|
||||
filterMap.put("/webjars/**", "anon");
|
||||
filterMap.put("/druid/**", "anon");
|
||||
filterMap.put("/v3/api-docs/**", "anon");
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package xiaozhi.modules.security.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
|
||||
@@ -15,8 +14,10 @@ import org.springframework.http.converter.support.AllEncompassingFormHttpMessage
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
@@ -45,14 +46,14 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
//忽略未知属性
|
||||
// 忽略未知属性
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
|
||||
//日期格式转换
|
||||
//mapper.setDateFormat(new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN));
|
||||
// 日期格式转换
|
||||
// mapper.setDateFormat(new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN));
|
||||
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
|
||||
|
||||
//Long类型转String类型
|
||||
// Long类型转String类型
|
||||
SimpleModule simpleModule = new SimpleModule();
|
||||
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
|
||||
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
|
||||
|
||||
+11
-8
@@ -1,12 +1,18 @@
|
||||
package xiaozhi.modules.security.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import xiaozhi.common.page.TokenDTO;
|
||||
@@ -22,8 +28,6 @@ import xiaozhi.modules.sys.dto.PasswordDTO;
|
||||
import xiaozhi.modules.sys.dto.SysUserDTO;
|
||||
import xiaozhi.modules.sys.service.SysUserService;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 登录控制层
|
||||
*/
|
||||
@@ -36,14 +40,13 @@ public class LoginController {
|
||||
private final SysUserTokenService sysUserTokenService;
|
||||
private final CaptchaService captchaService;
|
||||
|
||||
|
||||
@GetMapping("/captcha")
|
||||
@Operation(summary = "验证码")
|
||||
public void captcha(HttpServletResponse response, String uuid) throws IOException {
|
||||
//uuid不能为空
|
||||
// uuid不能为空
|
||||
AssertUtils.isBlank(uuid, ErrorCode.IDENTIFIER_NOT_NULL);
|
||||
|
||||
//生成验证码
|
||||
// 生成验证码
|
||||
captchaService.create(response, uuid);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package xiaozhi.modules.security.dao;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import xiaozhi.common.dao.BaseDao;
|
||||
import xiaozhi.modules.security.entity.SysUserTokenEntity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 系统用户Token
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package xiaozhi.modules.security.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 登录表单
|
||||
*/
|
||||
|
||||
+4
-3
@@ -1,13 +1,14 @@
|
||||
package xiaozhi.modules.security.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 系统用户Token
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package xiaozhi.modules.security.oauth2;
|
||||
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
@@ -11,14 +9,17 @@ import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.utils.HttpContextUtils;
|
||||
import xiaozhi.common.utils.JsonUtils;
|
||||
import xiaozhi.common.utils.Result;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* oauth2过滤器
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -30,7 +31,7 @@ public class Oauth2Filter extends AuthenticatingFilter {
|
||||
|
||||
@Override
|
||||
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
|
||||
//获取请求token
|
||||
// 获取请求token
|
||||
String token = getRequestToken((HttpServletRequest) request);
|
||||
|
||||
if (StringUtils.isBlank(token)) {
|
||||
@@ -52,14 +53,9 @@ public class Oauth2Filter extends AuthenticatingFilter {
|
||||
|
||||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
|
||||
//获取请求token,如果token不存在,直接返回401
|
||||
// 获取请求token,如果token不存在,直接返回401
|
||||
String token = getRequestToken((HttpServletRequest) request);
|
||||
|
||||
// TODO 调试接口,临时取消登录限制,需要 token 参数的除外
|
||||
if (true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(token)) {
|
||||
logger.warn("onAccessDenied:token is empty");
|
||||
|
||||
@@ -68,7 +64,7 @@ public class Oauth2Filter extends AuthenticatingFilter {
|
||||
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
|
||||
|
||||
String json = JsonUtils.toJsonString(new Result().error(ErrorCode.UNAUTHORIZED));
|
||||
String json = JsonUtils.toJsonString(new Result<Void>().error(ErrorCode.UNAUTHORIZED));
|
||||
|
||||
httpResponse.getWriter().print(json);
|
||||
|
||||
@@ -79,16 +75,17 @@ public class Oauth2Filter extends AuthenticatingFilter {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
|
||||
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request,
|
||||
ServletResponse response) {
|
||||
HttpServletResponse httpResponse = (HttpServletResponse) response;
|
||||
httpResponse.setContentType("application/json;charset=utf-8");
|
||||
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
|
||||
try {
|
||||
//处理登录失败的异常
|
||||
// 处理登录失败的异常
|
||||
logger.error("onLoginFailure:登录失败!", e);
|
||||
Throwable throwable = e.getCause() == null ? e : e.getCause();
|
||||
Result r = new Result().error(ErrorCode.UNAUTHORIZED, throwable.getMessage());
|
||||
Result<Void> r = new Result<Void>().error(ErrorCode.UNAUTHORIZED, throwable.getMessage());
|
||||
|
||||
String json = JsonUtils.toJsonString(r);
|
||||
httpResponse.getWriter().print(json);
|
||||
@@ -104,7 +101,7 @@ public class Oauth2Filter extends AuthenticatingFilter {
|
||||
*/
|
||||
private String getRequestToken(HttpServletRequest httpRequest) {
|
||||
String token = null;
|
||||
//从header中获取token
|
||||
// 从header中获取token
|
||||
String authorization = httpRequest.getHeader(Constant.AUTHORIZATION);
|
||||
if (StringUtils.isNotBlank(authorization) && authorization.startsWith("Bearer ")) {
|
||||
token = authorization.replace("Bearer ", "");
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
package xiaozhi.modules.security.oauth2;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.shiro.authc.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.DisabledAccountException;
|
||||
import org.apache.shiro.authc.IncorrectCredentialsException;
|
||||
import org.apache.shiro.authc.LockedAccountException;
|
||||
import org.apache.shiro.authc.SimpleAuthenticationInfo;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
@@ -10,6 +18,8 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
import xiaozhi.common.utils.ConvertUtils;
|
||||
@@ -19,9 +29,6 @@ import xiaozhi.modules.security.service.ShiroService;
|
||||
import xiaozhi.modules.sys.entity.SysUserEntity;
|
||||
import xiaozhi.modules.sys.enums.SuperAdminEnum;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 认证
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -47,7 +54,7 @@ public class Oauth2Realm extends AuthorizingRealm {
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
|
||||
UserDetail user = (UserDetail) principals.getPrimaryPrincipal();
|
||||
|
||||
//用户权限列表
|
||||
// 用户权限列表
|
||||
Set<String> permsSet = new HashSet<>();
|
||||
|
||||
if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) {
|
||||
@@ -69,22 +76,22 @@ public class Oauth2Realm extends AuthorizingRealm {
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
|
||||
String accessToken = (String) token.getPrincipal();
|
||||
|
||||
//根据accessToken,查询用户信息
|
||||
// 根据accessToken,查询用户信息
|
||||
SysUserTokenEntity tokenEntity = shiroService.getByToken(accessToken);
|
||||
//token失效
|
||||
// token失效
|
||||
if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) {
|
||||
throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID));
|
||||
}
|
||||
|
||||
//查询用户信息
|
||||
// 查询用户信息
|
||||
SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId());
|
||||
|
||||
//转换成UserDetail对象
|
||||
// 转换成UserDetail对象
|
||||
UserDetail userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class);
|
||||
|
||||
userDetail.setToken(accessToken);
|
||||
|
||||
//账号锁定
|
||||
// 账号锁定
|
||||
if (userDetail.getStatus() == null) {
|
||||
logger.error("账号状态异常,status 不能为空");
|
||||
throw new DisabledAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_DISABLE));
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package xiaozhi.modules.security.oauth2;
|
||||
|
||||
import xiaozhi.common.exception.RenException;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.util.UUID;
|
||||
|
||||
import xiaozhi.common.exception.RenException;
|
||||
|
||||
/**
|
||||
* 生成token
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
@@ -5,22 +5,28 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* BCrypt implements OpenBSD-style Blowfish password hashing using the scheme described in
|
||||
* BCrypt implements OpenBSD-style Blowfish password hashing using the scheme
|
||||
* described in
|
||||
* "A Future-Adaptable Password Scheme" by Niels Provos and David Mazieres.
|
||||
* <p>
|
||||
* This password hashing system tries to thwart off-line password cracking using a
|
||||
* computationally-intensive hashing algorithm, based on Bruce Schneier's Blowfish cipher.
|
||||
* The work factor of the algorithm is parameterised, so it can be increased as computers
|
||||
* This password hashing system tries to thwart off-line password cracking using
|
||||
* a
|
||||
* computationally-intensive hashing algorithm, based on Bruce Schneier's
|
||||
* Blowfish cipher.
|
||||
* The work factor of the algorithm is parameterised, so it can be increased as
|
||||
* computers
|
||||
* get faster.
|
||||
* <p>
|
||||
* Usage is really simple. To hash a password for the first time, call the hashpw method
|
||||
* Usage is really simple. To hash a password for the first time, call the
|
||||
* hashpw method
|
||||
* with a random salt, like this:
|
||||
* <p>
|
||||
* <code>
|
||||
* String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); <br>
|
||||
* </code>
|
||||
* <p>
|
||||
* To check whether a plaintext password matches one that has been hashed previously, use
|
||||
* To check whether a plaintext password matches one that has been hashed
|
||||
* previously, use
|
||||
* the checkpw method:
|
||||
* <p>
|
||||
* <code>
|
||||
@@ -30,7 +36,8 @@ import java.security.SecureRandom;
|
||||
* System.out.println("It does not match");<br>
|
||||
* </code>
|
||||
* <p>
|
||||
* The gensalt() method takes an optional parameter (log_rounds) that determines the
|
||||
* The gensalt() method takes an optional parameter (log_rounds) that determines
|
||||
* the
|
||||
* computational complexity of the hashing:
|
||||
* <p>
|
||||
* <code>
|
||||
@@ -38,7 +45,8 @@ import java.security.SecureRandom;
|
||||
* String stronger_salt = BCrypt.gensalt(12)<br>
|
||||
* </code>
|
||||
* <p>
|
||||
* The amount of work increases exponentially (2**log_rounds), so each increment is twice
|
||||
* The amount of work increases exponentially (2**log_rounds), so each increment
|
||||
* is twice
|
||||
* as much work. The default log_rounds is 10, and the valid range is 4 to 31.
|
||||
*
|
||||
* @author Damien Miller
|
||||
@@ -51,11 +59,11 @@ public class BCrypt {
|
||||
// Blowfish parameters
|
||||
private static final int BLOWFISH_NUM_ROUNDS = 16;
|
||||
// Initial contents of key schedule
|
||||
private static final int P_orig[] = {0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||
private static final int P_orig[] = { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377,
|
||||
0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||
0x9216d5d9, 0x8979fb1b};
|
||||
private static final int S_orig[] = {0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
|
||||
0x9216d5d9, 0x8979fb1b };
|
||||
private static final int S_orig[] = { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
|
||||
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7,
|
||||
0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
|
||||
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5,
|
||||
@@ -225,24 +233,24 @@ public class BCrypt {
|
||||
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76,
|
||||
0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
|
||||
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0,
|
||||
0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6};
|
||||
0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 };
|
||||
// bcrypt IV: "OrpheanBeholderScryDoubt"
|
||||
static private final int bf_crypt_ciphertext[] = {0x4f727068, 0x65616e42,
|
||||
0x65686f6c, 0x64657253, 0x63727944, 0x6f756274};
|
||||
static private final int bf_crypt_ciphertext[] = { 0x4f727068, 0x65616e42,
|
||||
0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 };
|
||||
// Table for Base64 encoding
|
||||
static private final char base64_code[] = {'.', '/', 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
static private final char base64_code[] = { '.', '/', 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
|
||||
'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
|
||||
'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
|
||||
'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
|
||||
// Table for Base64 decoding
|
||||
static private final byte index_64[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
static private final byte index_64[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55,
|
||||
56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7,
|
||||
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
|
||||
-1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1};
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1 };
|
||||
static final int MIN_LOG_ROUNDS = 4;
|
||||
static final int MAX_LOG_ROUNDS = 31;
|
||||
// Expanded Blowfish key
|
||||
@@ -250,7 +258,8 @@ public class BCrypt {
|
||||
private int S[];
|
||||
|
||||
/**
|
||||
* Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note
|
||||
* Encode a byte array using bcrypt's slightly-modified base64 encoding scheme.
|
||||
* Note
|
||||
* that this is <strong>not</strong> compatible with the standard MIME-base64
|
||||
* encoding.
|
||||
*
|
||||
@@ -306,7 +315,8 @@ public class BCrypt {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that
|
||||
* Decode a string encoded using bcrypt's base64 scheme to a byte array. Note
|
||||
* that
|
||||
* this is *not* compatible with the standard MIME-base64 encoding.
|
||||
*
|
||||
* @param s the string to decode
|
||||
@@ -365,7 +375,7 @@ public class BCrypt {
|
||||
int i, n, l = lr[off], r = lr[off + 1];
|
||||
|
||||
l ^= P[0];
|
||||
for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2; ) {
|
||||
for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) {
|
||||
// Feistel substitution on left word
|
||||
n = S[(l >> 24) & 0xff];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
@@ -388,7 +398,8 @@ public class BCrypt {
|
||||
* Cycically extract a word of key material
|
||||
*
|
||||
* @param data the string to extract the data from
|
||||
* @param offp a "pointer" (as a one-entry array) to the current offset into data
|
||||
* @param offp a "pointer" (as a one-entry array) to the current offset into
|
||||
* data
|
||||
* @return the next word of material from data
|
||||
*/
|
||||
private static int streamtoword(byte data[], int offp[]) {
|
||||
@@ -420,8 +431,8 @@ public class BCrypt {
|
||||
*/
|
||||
private void key(byte key[]) {
|
||||
int i;
|
||||
int koffp[] = {0};
|
||||
int lr[] = {0, 0};
|
||||
int koffp[] = { 0 };
|
||||
int lr[] = { 0, 0 };
|
||||
int plen = P.length, slen = S.length;
|
||||
|
||||
for (i = 0; i < plen; i++) {
|
||||
@@ -443,15 +454,16 @@ public class BCrypt {
|
||||
|
||||
/**
|
||||
* Perform the "enhanced key schedule" step described by Provos and Mazieres in
|
||||
* "A Future-Adaptable Password Scheme" http://www.openbsd.org/papers/bcrypt-paper.ps
|
||||
* "A Future-Adaptable Password Scheme"
|
||||
* http://www.openbsd.org/papers/bcrypt-paper.ps
|
||||
*
|
||||
* @param data salt information
|
||||
* @param key password information
|
||||
*/
|
||||
private void ekskey(byte data[], byte key[]) {
|
||||
int i;
|
||||
int koffp[] = {0}, doffp[] = {0};
|
||||
int lr[] = {0, 0};
|
||||
int koffp[] = { 0 }, doffp[] = { 0 };
|
||||
int lr[] = { 0, 0 };
|
||||
int plen = P.length, slen = S.length;
|
||||
|
||||
for (i = 0; i < plen; i++) {
|
||||
@@ -487,7 +499,8 @@ public class BCrypt {
|
||||
*
|
||||
* @param password the password to hash
|
||||
* @param salt the binary salt to hash with the password
|
||||
* @param log_rounds the binary logarithm of the number of rounds of hashing to apply
|
||||
* @param log_rounds the binary logarithm of the number of rounds of hashing to
|
||||
* apply
|
||||
* @return an array containing the binary hashed password
|
||||
*/
|
||||
private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) {
|
||||
@@ -524,7 +537,8 @@ public class BCrypt {
|
||||
* Hash a password using the OpenBSD bcrypt scheme
|
||||
*
|
||||
* @param password the password to hash
|
||||
* @param salt the salt to hash with (perhaps generated using BCrypt.gensalt)
|
||||
* @param salt the salt to hash with (perhaps generated using
|
||||
* BCrypt.gensalt)
|
||||
* @return the hashed password
|
||||
* @throws IllegalArgumentException if invalid salt is passed
|
||||
*/
|
||||
@@ -599,8 +613,10 @@ public class BCrypt {
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method
|
||||
*
|
||||
* @param log_rounds the log2 of the number of rounds of hashing to apply - the work
|
||||
* factor therefore increases as 2**log_rounds. Minimum 4, maximum 31.
|
||||
* @param log_rounds the log2 of the number of rounds of hashing to apply - the
|
||||
* work
|
||||
* factor therefore increases as 2**log_rounds. Minimum 4,
|
||||
* maximum 31.
|
||||
* @param random an instance of SecureRandom to use
|
||||
* @return an encoded salt value
|
||||
*/
|
||||
@@ -626,8 +642,10 @@ public class BCrypt {
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method
|
||||
*
|
||||
* @param log_rounds the log2 of the number of rounds of hashing to apply - the work
|
||||
* factor therefore increases as 2**log_rounds. Minimum 4, maximum 31.
|
||||
* @param log_rounds the log2 of the number of rounds of hashing to apply - the
|
||||
* work
|
||||
* factor therefore increases as 2**log_rounds. Minimum 4,
|
||||
* maximum 31.
|
||||
* @return an encoded salt value
|
||||
*/
|
||||
public static String gensalt(int log_rounds) {
|
||||
@@ -635,7 +653,8 @@ public class BCrypt {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method, selecting a reasonable
|
||||
* Generate a salt for use with the BCrypt.hashpw() method, selecting a
|
||||
* reasonable
|
||||
* default for the number of hashing rounds to apply
|
||||
*
|
||||
* @return an encoded salt value
|
||||
|
||||
+9
-6
@@ -1,15 +1,18 @@
|
||||
package xiaozhi.modules.security.password;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Implementation of PasswordEncoder that uses the BCrypt strong hashing function. Clients
|
||||
* can optionally supply a "strength" (a.k.a. log rounds in BCrypt) and a SecureRandom
|
||||
* instance. The larger the strength parameter the more work will have to be done
|
||||
* Implementation of PasswordEncoder that uses the BCrypt strong hashing
|
||||
* function. Clients
|
||||
* can optionally supply a "strength" (a.k.a. log rounds in BCrypt) and a
|
||||
* SecureRandom
|
||||
* instance. The larger the strength parameter the more work will have to be
|
||||
* done
|
||||
* (exponentially) to hash the passwords. The default value is 10.
|
||||
*
|
||||
* @author Dave Syer
|
||||
|
||||
+7
-4
@@ -10,20 +10,23 @@ package xiaozhi.modules.security.password;
|
||||
public interface PasswordEncoder {
|
||||
|
||||
/**
|
||||
* Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or
|
||||
* Encode the raw password. Generally, a good encoding algorithm applies a SHA-1
|
||||
* or
|
||||
* greater hash combined with an 8-byte or greater randomly generated salt.
|
||||
*/
|
||||
String encode(CharSequence rawPassword);
|
||||
|
||||
/**
|
||||
* Verify the encoded password obtained from storage matches the submitted raw
|
||||
* password after it too is encoded. Returns true if the passwords match, false if
|
||||
* password after it too is encoded. Returns true if the passwords match, false
|
||||
* if
|
||||
* they do not. The stored password itself is never decoded.
|
||||
*
|
||||
* @param rawPassword the raw password to encode and match
|
||||
* @param encodedPassword the encoded password from storage to compare with
|
||||
* @return true if the raw password, after encoding, matches the encoded password from
|
||||
* storage
|
||||
* @return true if the raw password, after encoding, matches the encoded
|
||||
* password from
|
||||
* storage
|
||||
*/
|
||||
boolean matches(CharSequence rawPassword, String encodedPassword);
|
||||
|
||||
|
||||
@@ -18,19 +18,17 @@ public class PasswordUtils {
|
||||
return passwordEncoder.encode(str);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 比较密码是否相等
|
||||
*
|
||||
* @param str 明文密码
|
||||
* @param password 加密后密码
|
||||
* @return true:成功 false:失败
|
||||
* @return true:成功 false:失败
|
||||
*/
|
||||
public static boolean matches(String str, String password) {
|
||||
return passwordEncoder.matches(str, password);
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
String str = "admin";
|
||||
String password = encode(str);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package xiaozhi.modules.security.service;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
@@ -21,7 +21,7 @@ public interface CaptchaService {
|
||||
*
|
||||
* @param uuid uuid
|
||||
* @param code 验证码
|
||||
* @return true:成功 false:失败
|
||||
* @return true:成功 false:失败
|
||||
*/
|
||||
boolean validate(String uuid, String code);
|
||||
}
|
||||
|
||||
+20
-21
@@ -1,23 +1,22 @@
|
||||
package xiaozhi.modules.security.service.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.wf.captcha.SpecCaptcha;
|
||||
import com.wf.captcha.base.Captcha;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import xiaozhi.common.redis.RedisKeys;
|
||||
import xiaozhi.common.redis.RedisUtils;
|
||||
import xiaozhi.modules.security.oauth2.Oauth2Realm;
|
||||
import xiaozhi.modules.security.service.CaptchaService;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import xiaozhi.common.redis.RedisKeys;
|
||||
import xiaozhi.common.redis.RedisUtils;
|
||||
import xiaozhi.modules.security.service.CaptchaService;
|
||||
|
||||
/**
|
||||
* 验证码
|
||||
@@ -29,10 +28,10 @@ public class CaptchaServiceImpl implements CaptchaService {
|
||||
@Value("${renren.redis.open}")
|
||||
private boolean open;
|
||||
/**
|
||||
* Local Cache 5分钟过期
|
||||
* Local Cache 5分钟过期
|
||||
*/
|
||||
Cache<String, String> localCache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterAccess(5, TimeUnit.MINUTES).build();
|
||||
private static final Logger logger = LoggerFactory.getLogger(Oauth2Realm.class);
|
||||
Cache<String, String> localCache = CacheBuilder.newBuilder().maximumSize(1000)
|
||||
.expireAfterAccess(5, TimeUnit.MINUTES).build();
|
||||
|
||||
@Override
|
||||
public void create(HttpServletResponse response, String uuid) throws IOException {
|
||||
@@ -41,13 +40,13 @@ public class CaptchaServiceImpl implements CaptchaService {
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setDateHeader("Expires", 0);
|
||||
|
||||
//生成验证码
|
||||
// 生成验证码
|
||||
SpecCaptcha captcha = new SpecCaptcha(150, 40);
|
||||
captcha.setLen(5);
|
||||
captcha.setCharType(Captcha.TYPE_DEFAULT);
|
||||
captcha.out(response.getOutputStream());
|
||||
|
||||
//保存到缓存
|
||||
// 保存到缓存
|
||||
setCache(uuid, captcha.text());
|
||||
}
|
||||
|
||||
@@ -56,10 +55,10 @@ public class CaptchaServiceImpl implements CaptchaService {
|
||||
if (StringUtils.isBlank(code)) {
|
||||
return false;
|
||||
}
|
||||
//获取验证码
|
||||
// 获取验证码
|
||||
String captcha = getCache(uuid);
|
||||
|
||||
//效验成功
|
||||
// 效验成功
|
||||
if (code.equalsIgnoreCase(captcha)) {
|
||||
return true;
|
||||
}
|
||||
@@ -80,7 +79,7 @@ public class CaptchaServiceImpl implements CaptchaService {
|
||||
if (open) {
|
||||
key = RedisKeys.getCaptchaKey(key);
|
||||
String captcha = (String) redisUtils.get(key);
|
||||
//删除验证码
|
||||
// 删除验证码
|
||||
if (captcha != null) {
|
||||
redisUtils.delete(key);
|
||||
}
|
||||
@@ -89,7 +88,7 @@ public class CaptchaServiceImpl implements CaptchaService {
|
||||
}
|
||||
|
||||
String captcha = localCache.getIfPresent(key);
|
||||
//删除验证码
|
||||
// 删除验证码
|
||||
if (captcha != null) {
|
||||
localCache.invalidate(key);
|
||||
}
|
||||
|
||||
+3
-8
@@ -1,17 +1,13 @@
|
||||
package xiaozhi.modules.security.service.impl;
|
||||
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import xiaozhi.modules.security.dao.SysUserTokenDao;
|
||||
import xiaozhi.modules.security.entity.SysUserTokenEntity;
|
||||
import xiaozhi.modules.security.service.ShiroService;
|
||||
import xiaozhi.modules.sys.dao.SysUserDao;
|
||||
import xiaozhi.modules.sys.entity.SysUserEntity;
|
||||
import xiaozhi.modules.sys.enums.SuperAdminEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
@@ -19,7 +15,6 @@ public class ShiroServiceImpl implements ShiroService {
|
||||
private final SysUserDao sysUserDao;
|
||||
private final SysUserTokenDao sysUserTokenDao;
|
||||
|
||||
|
||||
@Override
|
||||
public SysUserTokenEntity getByToken(String token) {
|
||||
return sysUserTokenDao.getByToken(token);
|
||||
|
||||
+16
-14
@@ -1,8 +1,11 @@
|
||||
package xiaozhi.modules.security.service.impl;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import xiaozhi.common.page.TokenDTO;
|
||||
@@ -17,11 +20,10 @@ import xiaozhi.modules.sys.dto.PasswordDTO;
|
||||
import xiaozhi.modules.sys.dto.SysUserDTO;
|
||||
import xiaozhi.modules.sys.service.SysUserService;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Service
|
||||
public class SysUserTokenServiceImpl extends BaseServiceImpl<SysUserTokenDao, SysUserTokenEntity> implements SysUserTokenService {
|
||||
public class SysUserTokenServiceImpl extends BaseServiceImpl<SysUserTokenDao, SysUserTokenEntity>
|
||||
implements SysUserTokenService {
|
||||
|
||||
private final SysUserService sysUserService;
|
||||
/**
|
||||
@@ -31,18 +33,18 @@ public class SysUserTokenServiceImpl extends BaseServiceImpl<SysUserTokenDao, Sy
|
||||
|
||||
@Override
|
||||
public Result<TokenDTO> createToken(Long userId) {
|
||||
//用户token
|
||||
// 用户token
|
||||
String token;
|
||||
|
||||
//当前时间
|
||||
// 当前时间
|
||||
Date now = new Date();
|
||||
//过期时间
|
||||
// 过期时间
|
||||
Date expireTime = new Date(now.getTime() + EXPIRE * 1000);
|
||||
|
||||
//判断是否生成过token
|
||||
// 判断是否生成过token
|
||||
SysUserTokenEntity tokenEntity = baseDao.getByUserId(userId);
|
||||
if (tokenEntity == null) {
|
||||
//生成一个token
|
||||
// 生成一个token
|
||||
token = TokenGenerator.generateValue();
|
||||
|
||||
tokenEntity = new SysUserTokenEntity();
|
||||
@@ -51,12 +53,12 @@ public class SysUserTokenServiceImpl extends BaseServiceImpl<SysUserTokenDao, Sy
|
||||
tokenEntity.setUpdateDate(now);
|
||||
tokenEntity.setExpireDate(expireTime);
|
||||
|
||||
//保存token
|
||||
// 保存token
|
||||
this.insert(tokenEntity);
|
||||
} else {
|
||||
//判断token是否过期
|
||||
// 判断token是否过期
|
||||
if (tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) {
|
||||
//token过期,重新生成token
|
||||
// token过期,重新生成token
|
||||
token = TokenGenerator.generateValue();
|
||||
} else {
|
||||
token = tokenEntity.getToken();
|
||||
@@ -66,7 +68,7 @@ public class SysUserTokenServiceImpl extends BaseServiceImpl<SysUserTokenDao, Sy
|
||||
tokenEntity.setUpdateDate(now);
|
||||
tokenEntity.setExpireDate(expireTime);
|
||||
|
||||
//更新token
|
||||
// 更新token
|
||||
this.updateById(tokenEntity);
|
||||
}
|
||||
|
||||
@@ -76,7 +78,7 @@ public class SysUserTokenServiceImpl extends BaseServiceImpl<SysUserTokenDao, Sy
|
||||
tokenDTO.setToken(token);
|
||||
tokenDTO.setExpire(EXPIRE);
|
||||
tokenDTO.setClientHash(clientHash);
|
||||
return new Result().ok(tokenDTO);
|
||||
return new Result<TokenDTO>().ok(tokenDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package xiaozhi.modules.security.user;
|
||||
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
|
||||
/**
|
||||
* Shiro工具类
|
||||
* Copyright (c) 人人开源 All rights reserved.
|
||||
|
||||
+16
-10
@@ -1,13 +1,21 @@
|
||||
package xiaozhi.modules.admin.contrloler;
|
||||
package xiaozhi.modules.sys.controller;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.utils.Result;
|
||||
@@ -16,8 +24,6 @@ import xiaozhi.modules.sys.dto.AdminPageUserDTO;
|
||||
import xiaozhi.modules.sys.service.SysUserService;
|
||||
import xiaozhi.modules.sys.vo.AdminPageUserVO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 管理员控制层
|
||||
*
|
||||
@@ -35,7 +41,7 @@ public class AdminController {
|
||||
@Operation(summary = "分页查找用户")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
@Parameters({
|
||||
@Parameter(name = "mobile", description = "用户手机号码", required = true),
|
||||
@Parameter(name = "mobile", description = "用户手机号码", required = false),
|
||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true),
|
||||
})
|
||||
@@ -64,7 +70,7 @@ public class AdminController {
|
||||
@Operation(summary = "用户删除")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
public Result<Void> delete(@PathVariable Long id) {
|
||||
sysUserService.delete(new Long[]{id});
|
||||
sysUserService.delete(new Long[] { id });
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
@@ -72,13 +78,13 @@ public class AdminController {
|
||||
@Operation(summary = "分页查找设备")
|
||||
@RequiresPermissions("sys:role:superAdmin")
|
||||
@Parameters({
|
||||
@Parameter(name = "keywords", description = "用户手机号码", required = true),
|
||||
@Parameter(name = "keywords", description = "设备关键词", required = false),
|
||||
@Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true),
|
||||
@Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true),
|
||||
})
|
||||
public Result<Void> pageDevice(
|
||||
@Parameter(hidden = true) @RequestParam Map<String, Object> params) {
|
||||
//TODO 等设备功能模块写好
|
||||
return new Result<Void>().error(600,"等设备功能模块写好");
|
||||
// TODO 等设备功能模块写好
|
||||
return new Result<Void>().error(600, "等设备功能模块写好");
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package xiaozhi.modules.sys.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 用户管理
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/sys/user")
|
||||
@Tag(name = "用户管理")
|
||||
public class SysUserController {
|
||||
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
package xiaozhi.modules.sys.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import xiaozhi.common.dao.BaseDao;
|
||||
import xiaozhi.modules.sys.dto.SysDictDataDTO;
|
||||
import xiaozhi.modules.sys.entity.DictData;
|
||||
import xiaozhi.modules.sys.entity.SysDictDataEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 字典数据
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user