From 18cd02517fd2f94bc30f9aeeb51da36314c87b3d Mon Sep 17 00:00:00 2001 From: gitee Date: Wed, 30 Jul 2025 09:56:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LiveDigitalAvatarManageApplication.java | 17 +++ .../config/ExceptionHandlerConfig.java | 63 +++++++++ .../config/MyMetaObjectHandler.java | 26 ++++ .../config/MybatisPlusConfig.java | 44 ++++++ .../config/OllamaChatModelAspect.java | 45 ++++++ .../config/OllamaConfig.java | 28 ++++ .../config/SecurityConfig.java | 84 +++++++++++ .../constant/ResultStatusEnum.java | 55 ++++++++ .../controller/AuthController.java | 52 +++++++ .../controller/OllamaAgentController.java | 33 +++++ .../domain/OllamaDialogueLog.java | 67 +++++++++ .../domain/Permissions.java | 47 +++++++ .../domain/RolePermissions.java | 41 ++++++ .../livedigitalavatarmanage/domain/Roles.java | 52 +++++++ .../domain/UserRoles.java | 42 ++++++ .../livedigitalavatarmanage/domain/Users.java | 72 ++++++++++ .../dto/OllamaDialogueLogDTO.java | 86 ++++++++++++ .../livedigitalavatarmanage/dto/R.java | 131 ++++++++++++++++++ .../dto/UserDetail.java | 61 ++++++++ .../enums/UserDisabledEnum.java | 17 +++ .../exception/BusinessException.java | 76 ++++++++++ .../exception/UnauthorizedException.java | 15 ++ .../filter/JwtAuthenticationFilter.java | 62 +++++++++ .../mapper/OllamaDialogueLogMapper.java | 18 +++ .../mapper/PermissionsMapper.java | 18 +++ .../mapper/RolePermissionsMapper.java | 18 +++ .../mapper/RolesMapper.java | 18 +++ .../mapper/UserRolesMapper.java | 18 +++ .../mapper/UsersMapper.java | 18 +++ .../service/AuthService.java | 14 ++ .../service/OllamaAgentService.java | 16 +++ .../service/OllamaDialogueLogService.java | 13 ++ .../service/PermissionsService.java | 13 ++ .../service/RolePermissionsService.java | 13 ++ .../service/RolesService.java | 13 ++ .../service/ThinkTagState.java | 59 ++++++++ .../service/UserRolesService.java | 13 ++ .../service/UsersService.java | 22 +++ .../service/impl/AuthServiceImpl.java | 77 ++++++++++ .../service/impl/OllamaAgentServiceImpl.java | 84 +++++++++++ .../impl/OllamaDialogueLogServiceImpl.java | 22 +++ .../service/impl/PermissionsServiceImpl.java | 22 +++ .../impl/RolePermissionsServiceImpl.java | 22 +++ .../service/impl/RolesServiceImpl.java | 22 +++ .../service/impl/UserDetailsServiceImpl.java | 46 ++++++ .../service/impl/UserRolesServiceImpl.java | 22 +++ .../service/impl/UsersServiceImpl.java | 43 ++++++ .../util/JwtUtils.java | 87 ++++++++++++ .../util/UserUtil.java | 34 +++++ .../vo/LoginReqVo.java | 11 ++ .../vo/ollama/MessageDTO.java | 11 ++ .../vo/ollama/OllamResponse.java | 63 +++++++++ .../vo/ollama/OllamaDTO.java | 42 ++++++ src/main/resources/application.yml | 38 +++++ .../mapper/OllamaDialogueLogMapper.xml | 24 ++++ .../resources/mapper/PermissionsMapper.xml | 19 +++ .../mapper/RolePermissionsMapper.xml | 18 +++ src/main/resources/mapper/RolesMapper.xml | 20 +++ src/main/resources/mapper/UserRolesMapper.xml | 18 +++ src/main/resources/mapper/UsersMapper.xml | 26 ++++ ...veDigitalAvatarManageApplicationTests.java | 13 ++ 61 files changed, 2284 insertions(+) create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/LiveDigitalAvatarManageApplication.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/config/ExceptionHandlerConfig.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/config/MyMetaObjectHandler.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/config/MybatisPlusConfig.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/config/OllamaChatModelAspect.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/config/OllamaConfig.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/config/SecurityConfig.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/constant/ResultStatusEnum.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/controller/AuthController.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/controller/OllamaAgentController.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/domain/OllamaDialogueLog.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/domain/Permissions.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/domain/RolePermissions.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/domain/Roles.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/domain/UserRoles.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/domain/Users.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/dto/OllamaDialogueLogDTO.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/dto/R.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/dto/UserDetail.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/enums/UserDisabledEnum.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/exception/BusinessException.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/exception/UnauthorizedException.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/filter/JwtAuthenticationFilter.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/mapper/OllamaDialogueLogMapper.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/mapper/PermissionsMapper.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/mapper/RolePermissionsMapper.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/mapper/RolesMapper.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/mapper/UserRolesMapper.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/mapper/UsersMapper.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/AuthService.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/OllamaAgentService.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/OllamaDialogueLogService.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/PermissionsService.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/RolePermissionsService.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/RolesService.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/ThinkTagState.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/UserRolesService.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/UsersService.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/impl/AuthServiceImpl.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/impl/OllamaAgentServiceImpl.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/impl/OllamaDialogueLogServiceImpl.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/impl/PermissionsServiceImpl.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/impl/RolePermissionsServiceImpl.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/impl/RolesServiceImpl.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UserDetailsServiceImpl.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UserRolesServiceImpl.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UsersServiceImpl.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/util/JwtUtils.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/util/UserUtil.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/vo/LoginReqVo.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/MessageDTO.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/OllamResponse.java create mode 100644 src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/OllamaDTO.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/mapper/OllamaDialogueLogMapper.xml create mode 100644 src/main/resources/mapper/PermissionsMapper.xml create mode 100644 src/main/resources/mapper/RolePermissionsMapper.xml create mode 100644 src/main/resources/mapper/RolesMapper.xml create mode 100644 src/main/resources/mapper/UserRolesMapper.xml create mode 100644 src/main/resources/mapper/UsersMapper.xml create mode 100644 src/test/java/com/supervision/livedigitalavatarmanage/LiveDigitalAvatarManageApplicationTests.java diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/LiveDigitalAvatarManageApplication.java b/src/main/java/com/supervision/livedigitalavatarmanage/LiveDigitalAvatarManageApplication.java new file mode 100644 index 0000000..f345188 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/LiveDigitalAvatarManageApplication.java @@ -0,0 +1,17 @@ +package com.supervision.livedigitalavatarmanage; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +@EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = true) +@MapperScan(basePackages = {"com.supervision.**.mapper"}) +@SpringBootApplication +public class LiveDigitalAvatarManageApplication { + + public static void main(String[] args) { + SpringApplication.run(LiveDigitalAvatarManageApplication.class, args); + } + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/config/ExceptionHandlerConfig.java b/src/main/java/com/supervision/livedigitalavatarmanage/config/ExceptionHandlerConfig.java new file mode 100644 index 0000000..a347a1a --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/config/ExceptionHandlerConfig.java @@ -0,0 +1,63 @@ +package com.supervision.livedigitalavatarmanage.config; + +import com.supervision.livedigitalavatarmanage.constant.ResultStatusEnum; +import com.supervision.livedigitalavatarmanage.dto.R; +import com.supervision.livedigitalavatarmanage.exception.BusinessException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.multipart.MaxUploadSizeExceededException; + +/** + * 统一异常处理器配置 + * + * @author wb + * @date 2022/3/10 13:24 + */ +@Slf4j +@Configuration +@RestControllerAdvice(annotations = RestController.class, basePackages = {"com.supervision.ai.service.**.controller"}) +public class ExceptionHandlerConfig { + + /** + * 添加手动校验参数的异常处理 + * + * @param exception 参数验证异常 + * @return 通用返回值 + */ + @ExceptionHandler(IllegalArgumentException.class) + public R manualValidationExceptionResponse(IllegalArgumentException exception) { + log.error("=========手动校验参数异常=========>>>"); + log.error(exception.getMessage(), exception); + log.error("<<<=========手动校验参数异常========="); + return R.fail(ResultStatusEnum.ILLEGAL_ARGUMENT.getCode(), exception.getMessage()); + } + + @ExceptionHandler(BusinessException.class) + public R businessExceptionResponse(BusinessException exception) { + log.error("=========运行异常=========>>>"); + log.error(exception.getMessage(), exception); + log.error("<<<=========运行异常========="); + + return R.fail(511, exception.getMessage()); + } + + @ExceptionHandler(RuntimeException.class) + public R manualValidationExceptionResponse(RuntimeException exception) { + log.error("=========运行异常=========>>>"); + log.error(exception.getMessage(), exception); + log.error("<<<=========运行异常========="); + + return R.fail(ResultStatusEnum.RUNTIME_EXCEPTION.getCode(), exception.getMessage()); + } + + @ExceptionHandler(MaxUploadSizeExceededException.class) + public R handleMaxSizeException(MaxUploadSizeExceededException exception) { + log.error("=========文件大小超出限制异常=========>>>"); + log.error(exception.getMessage(), exception); + log.error("<<<=========文件大小超出限制异常========="); + return R.fail(ResultStatusEnum.EXCEED_FILE_SIZE.getCode(), exception.getMessage()); + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/config/MyMetaObjectHandler.java b/src/main/java/com/supervision/livedigitalavatarmanage/config/MyMetaObjectHandler.java new file mode 100644 index 0000000..6da7c2f --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/config/MyMetaObjectHandler.java @@ -0,0 +1,26 @@ +package com.supervision.livedigitalavatarmanage.config; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; + +import java.time.LocalDateTime; + +/** + * @author Ray + */ +public class MyMetaObjectHandler implements MetaObjectHandler { + public MyMetaObjectHandler() { + } + + @Override + public void insertFill(MetaObject metaObject) { + this.setFieldValByName("createTime", LocalDateTime.now(), metaObject); + this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); + } + + @Override + public void updateFill(MetaObject metaObject) { + this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); + } + +} \ No newline at end of file diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/config/MybatisPlusConfig.java b/src/main/java/com/supervision/livedigitalavatarmanage/config/MybatisPlusConfig.java new file mode 100644 index 0000000..13f4e30 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/config/MybatisPlusConfig.java @@ -0,0 +1,44 @@ +package com.supervision.livedigitalavatarmanage.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * MybatisPlus配置 + * + * @author qmy + * @version 1.0.0 2020/10/22 9:47 + * @since JDK1.8 + */ +@Configuration +public class MybatisPlusConfig { + + @Bean + public MyMetaObjectHandler myMetaObjectHandler() { + return new MyMetaObjectHandler(); + } + + /** + * 拦截器配置 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(this.paginationInterceptor()); + return interceptor; + } + + + private PaginationInnerInterceptor paginationInterceptor() { + PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(); + paginationInterceptor.setOverflow(false); + /** + * 注意! 此处要设置数据库类型. + */ + paginationInterceptor.setDbType(DbType.POSTGRE_SQL); + return paginationInterceptor; + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/config/OllamaChatModelAspect.java b/src/main/java/com/supervision/livedigitalavatarmanage/config/OllamaChatModelAspect.java new file mode 100644 index 0000000..4de0455 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/config/OllamaChatModelAspect.java @@ -0,0 +1,45 @@ +package com.supervision.livedigitalavatarmanage.config; + +import cn.hutool.core.util.StrUtil; +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; + +@Aspect +@Component +public class OllamaChatModelAspect { + + @Value("${spring.ai.ollama.chat.model}") + private String model; + + private String callStringMessage = "String org.springframework.ai.chat.model.ChatModel.call(String)"; + + + /** + * 修改ollama的call方法,添加/no_think参数,返回结果去掉think标签 + * @param joinPoint joinPoint + * @return Object + * @throws Throwable + */ + @Around("execution(* org.springframework.ai.chat.model.ChatModel.call(..))") + public Object aroundMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable { + + String signature = joinPoint.getSignature().toString(); + // 获取原始参数 + Object[] args = joinPoint.getArgs(); + // 如果是String类型的call方法,修改其参数 + /*if (StrUtil.equals(signature, callStringMessage) && args.length > 0) { + args[0] = args[0] + "\n /no_think"; + }*/ + // 执行原方法 + Object result = joinPoint.proceed(args); + if (StrUtil.equals(model,"qwen3:30b-a3b")|| StrUtil.equals(model,"qwen3:32b")) { + if(StrUtil.equals(signature, callStringMessage)){ + result = ((String) result).replaceAll("(?is)]*>(.*?)", "").trim(); + } + } + return result; + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/config/OllamaConfig.java b/src/main/java/com/supervision/livedigitalavatarmanage/config/OllamaConfig.java new file mode 100644 index 0000000..d4ee72c --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/config/OllamaConfig.java @@ -0,0 +1,28 @@ +package com.supervision.livedigitalavatarmanage.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.JdkClientHttpRequestFactory; +import org.springframework.web.client.RestClient; + +import java.net.http.HttpClient; +import java.time.Duration; + +@Configuration +public class OllamaConfig { + + @Value("${spring.ai.ollama.chat.options.timeout:180000}") + private int timeout; + + @Bean + @Qualifier("restClientBuilder") + public RestClient.Builder restClientBuilder() { + JdkClientHttpRequestFactory requestFactory = new JdkClientHttpRequestFactory( + HttpClient.newHttpClient()); + requestFactory.setReadTimeout(Duration.ofMillis(timeout)); + return RestClient.builder().requestFactory(requestFactory); + } + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/config/SecurityConfig.java b/src/main/java/com/supervision/livedigitalavatarmanage/config/SecurityConfig.java new file mode 100644 index 0000000..a08acf2 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/config/SecurityConfig.java @@ -0,0 +1,84 @@ +package com.supervision.livedigitalavatarmanage.config; + +import com.supervision.livedigitalavatarmanage.filter.JwtAuthenticationFilter; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import java.util.Base64; + + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final UserDetailsService userDetailsService; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) // 禁用CSRF + .cors(Customizer.withDefaults()) + .authorizeHttpRequests(auth -> auth + .requestMatchers("/auth/login","/ollama/generate").permitAll() + .anyRequest().authenticated() + ) + .sessionManagement(session -> session + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .authenticationProvider(authenticationProvider()) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + // 禁用 formLogin 和 httpBasic + .formLogin(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable); + return http.build(); + } + + + @Bean + public AuthenticationProvider authenticationProvider() { + // 使用DaoAuthenticationProvider,并注入自定义的UserDetailsService和PasswordEncoder + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService); // 从数据库读取用户进行认证 + authProvider.setPasswordEncoder(passwordEncoder()); // 使用BCrypt密码器验证密码 + return authProvider; + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { + // 从AuthenticationConfiguration中获取AuthenticationManager实例 + return config.getAuthenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + // 使用BCryptPasswordEncoder作为密码加密器 + return new BCryptPasswordEncoder(); + } + + public static void main(String[] args) { + String s = Base64.getEncoder().encodeToString(Keys.secretKeyFor(SignatureAlgorithm.HS256).getEncoded()); + BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); + String encode = bCryptPasswordEncoder.encode("sst123456#"); + System.out.println(encode); + } + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/constant/ResultStatusEnum.java b/src/main/java/com/supervision/livedigitalavatarmanage/constant/ResultStatusEnum.java new file mode 100644 index 0000000..75b5c22 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/constant/ResultStatusEnum.java @@ -0,0 +1,55 @@ +package com.supervision.livedigitalavatarmanage.constant; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * 响应结果状态枚举类 + * @author qimaoyu + * @create 2019-07-14 10:22 + */ +@NoArgsConstructor +@AllArgsConstructor +public enum ResultStatusEnum { + + AUTHENTICATION_FAILED(320, "token失效,请重新登录!"), + NO_ACCESS_TO_THIS_INTERFACE(320, "无权访问此接口!"), + FAILED_TO_GENERATE_TOKEN(321, "生成token失败!"), + ACCOUNT_PASSWORD_INCORRECT(322, "账号或密码错误!"), + ACCOUNT_NOT_CREATE(323, "账号未创建!"), + HAS_BEEN_PULLED_BLACK(324, "已被删除或禁用,无法登录!"), + USERNAME_MAIL_IS_EXIST(341, "登录名称已经被注册!"), + USERNAME_IS_BLANK(342, "登录名称为空!"), + VERIFICATION_CODE_EXPIRED(350,"验证码已过期,请重新获取。"), + VERIFICATION_CODE_FAILURE(351,"验证码输入错误。"), + OPERATE_FAIL(360,"修改毕业生信息失败。"), + DATA_IS_EMPTY(370,"查询到的结果为空"), + SYSTEM_ABNORMAL(500, "系统繁忙,请稍后重试!"), + UPLOAD_EXCEPTION(501, "文件上传异常!"), + EXPORT_EXCEPTION(502, "文件导出异常!"), + INCORRECT_FILE_FORMAT(503, "文件格式不正确!"), + PARAMETER_CANNOT_BE_EMPTY(504, "参数不能为空,操作失败!"), + NO_TEMP_UPLOADFILEPATH(505,"未配置文件上传临时存储路径"), + USER_DOES_NOT_EXIST(507, "用户不存在,操作失败!"), + + ILLEGAL_ARGUMENT(508, "参数校验失败!"), + RUNTIME_EXCEPTION(509, "程序运行异常!"), + EXCEED_FILE_SIZE(510, "文件大小超出限制!"), + IMPORT_COMPANY_FORMAT_ERROR(521,"Excel表格格式错误!"), + IMPORT_COMPANY_FAIL(522,"部分数据导入失败"), + INSERT_FAIL(600,"新增失败"), + DuplicateKeyException(601,"该条信息已经存在,请勿重复添加"), + UPDATE_FAIL(700,"更新失败"), + DELETE_FAIL(800,"删除失败"), + YEAR_IS_CLOSE(1001,"该年度暂未开启"); + + @Getter + @Setter + private int code; + + @Getter + @Setter + private String message; +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/controller/AuthController.java b/src/main/java/com/supervision/livedigitalavatarmanage/controller/AuthController.java new file mode 100644 index 0000000..d23d8d7 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/controller/AuthController.java @@ -0,0 +1,52 @@ +package com.supervision.livedigitalavatarmanage.controller; + +import cn.hutool.core.lang.Assert; +import com.supervision.livedigitalavatarmanage.domain.Users; +import com.supervision.livedigitalavatarmanage.dto.R; +import com.supervision.livedigitalavatarmanage.dto.UserDetail; +import com.supervision.livedigitalavatarmanage.service.AuthService; +import com.supervision.livedigitalavatarmanage.util.UserUtil; +import com.supervision.livedigitalavatarmanage.vo.LoginReqVo; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/auth") +@RequiredArgsConstructor +public class AuthController { + + private final AuthService authService; + + /** + * 用户登录接口 + * @param loginReqVo 登录请求对象,包含用户名和密码 + * @return + */ + @PostMapping("/login") + public R login(@RequestBody LoginReqVo loginReqVo) { + Assert.notEmpty(loginReqVo.getUsername(), "用户名不能为空"); + String token = authService.login(loginReqVo.getUsername(), loginReqVo.getPassword()); + return R.ok(token); + } + + /** + * 心跳接口,用于保持用户会话活跃 + * @return + */ + @GetMapping("/heartbeat") + public R heartbeat() { + UserDetail userDetail = UserUtil.currentUser(); + authService.heartbeat(userDetail.getUserId()); + return R.ok(); + } + + @GetMapping("/me") + public R getCurrentUserDetails() { + + Users currentUser = authService.getCurrentUser(UserUtil.currentUser().getUserId()); + + return R.ok(currentUser); + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/controller/OllamaAgentController.java b/src/main/java/com/supervision/livedigitalavatarmanage/controller/OllamaAgentController.java new file mode 100644 index 0000000..d046f8f --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/controller/OllamaAgentController.java @@ -0,0 +1,33 @@ +package com.supervision.livedigitalavatarmanage.controller; + +import com.supervision.livedigitalavatarmanage.service.OllamaAgentService; +import com.supervision.livedigitalavatarmanage.vo.ollama.OllamResponse; +import com.supervision.livedigitalavatarmanage.vo.ollama.OllamaDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; + +@Slf4j +@RestController +@RequestMapping("/ollama") +@RequiredArgsConstructor +public class OllamaAgentController { + + private final OllamaAgentService agentService; + + /** + * 流式智能体问答 + * @param ollamaDTO agentChatReqDTO + * @return + */ + @PostMapping(value = "/generate", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux streamChat(@RequestBody OllamaDTO ollamaDTO) { + //UserDetail userDetail = UserUtil.currentUser(); + return agentService.streamChat(ollamaDTO); + } + + + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/domain/OllamaDialogueLog.java b/src/main/java/com/supervision/livedigitalavatarmanage/domain/OllamaDialogueLog.java new file mode 100644 index 0000000..44db2e4 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/domain/OllamaDialogueLog.java @@ -0,0 +1,67 @@ +package com.supervision.livedigitalavatarmanage.domain; + +import com.baomidou.mybatisplus.annotation.*; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; +import lombok.Data; + +/** + * ollama对话日志 + * @TableName ollama_dialogue_log + */ +@TableName(value ="ollama_dialogue_log") +@Data +public class OllamaDialogueLog implements Serializable { + /** + * 主键 + */ + @TableId + private String id; + + /** + * 用户id + */ + private String userId; + + /** + * 用户输入 + */ + private String userInput; + + /** + * 系统输出 + */ + private String systemOut; + + /** + * 耗时 + */ + private Long timeCost; + + /** + * 回答内容类型 0:正常回答 1:异常回答 + */ + private Integer answerType; + + /** + * 模型 + */ + private String model; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/domain/Permissions.java b/src/main/java/com/supervision/livedigitalavatarmanage/domain/Permissions.java new file mode 100644 index 0000000..8fdd2c6 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/domain/Permissions.java @@ -0,0 +1,47 @@ +package com.supervision.livedigitalavatarmanage.domain; + +import com.baomidou.mybatisplus.annotation.*; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; +import lombok.Data; + +/** + * 权限表 + * @TableName permissions + */ +@TableName(value ="permissions") +@Data +public class Permissions implements Serializable { + /** + * 主键 + */ + @TableId + private String id; + + /** + * 允许访问的url + */ + private String url; + + /** + * 描述 + */ + private String description; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/domain/RolePermissions.java b/src/main/java/com/supervision/livedigitalavatarmanage/domain/RolePermissions.java new file mode 100644 index 0000000..4cddad9 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/domain/RolePermissions.java @@ -0,0 +1,41 @@ +package com.supervision.livedigitalavatarmanage.domain; + +import com.baomidou.mybatisplus.annotation.*; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; +import lombok.Data; + +/** + * + * @TableName role_permissions + */ +@TableName(value ="role_permissions") +@Data +public class RolePermissions implements Serializable { + /** + * 角色id + */ + private String roleId; + + /** + * 权限id + */ + private String permissionId; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/domain/Roles.java b/src/main/java/com/supervision/livedigitalavatarmanage/domain/Roles.java new file mode 100644 index 0000000..7d53f84 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/domain/Roles.java @@ -0,0 +1,52 @@ +package com.supervision.livedigitalavatarmanage.domain; + +import com.baomidou.mybatisplus.annotation.*; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; +import lombok.Data; + +/** + * + * @TableName roles + */ +@TableName(value ="roles") +@Data +public class Roles implements Serializable { + /** + * 主键 + */ + @TableId + private String id; + + /** + * 角色code + */ + private String code; + + /** + * 角色名 + */ + private String name; + + /** + * 是否是系统角色 0:非系统角色 1:系统角色 + */ + private String isSystemRole; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/domain/UserRoles.java b/src/main/java/com/supervision/livedigitalavatarmanage/domain/UserRoles.java new file mode 100644 index 0000000..78dddbf --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/domain/UserRoles.java @@ -0,0 +1,42 @@ +package com.supervision.livedigitalavatarmanage.domain; + +import com.baomidou.mybatisplus.annotation.*; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Date; +import lombok.Data; + +/** + * + * @TableName user_roles + */ +@TableName(value ="user_roles") +@Data +public class UserRoles implements Serializable { + /** + * 用户id + */ + @TableId + private String userId; + + /** + * 角色id + */ + private String roleId; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/domain/Users.java b/src/main/java/com/supervision/livedigitalavatarmanage/domain/Users.java new file mode 100644 index 0000000..f592d7e --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/domain/Users.java @@ -0,0 +1,72 @@ +package com.supervision.livedigitalavatarmanage.domain; + +import com.baomidou.mybatisplus.annotation.*; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import lombok.Data; + +/** + * + * @TableName users + */ +@TableName(value ="users") +@Data +public class Users implements Serializable { + /** + * 用户表 + */ + @TableId + private String id; + + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 是否禁用 0:启用 1:禁用 + */ + private String disabled; + + /** + * 最后活跃时间 + */ + private LocalDateTime lastActive; + + /** + * 登录错误次数 + */ + private Integer failedLoginAttempts; + + /** + * 最后登录时间 + */ + private LocalDateTime lastLoginDate; + + /** + * 过期时间 + */ + private LocalDateTime expirationDate; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/dto/OllamaDialogueLogDTO.java b/src/main/java/com/supervision/livedigitalavatarmanage/dto/OllamaDialogueLogDTO.java new file mode 100644 index 0000000..18d04e9 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/dto/OllamaDialogueLogDTO.java @@ -0,0 +1,86 @@ +package com.supervision.livedigitalavatarmanage.dto; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.json.JSONUtil; +import com.supervision.livedigitalavatarmanage.domain.OllamaDialogueLog; +import com.supervision.livedigitalavatarmanage.vo.ollama.MessageDTO; +import com.supervision.livedigitalavatarmanage.vo.ollama.OllamaDTO; +import lombok.Data; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + +@Data +public class OllamaDialogueLogDTO { + + /** + * 对话日志id + */ + private String id; + + /** + * 用户id + */ + private String userId; + + /** + * 对话id + */ + private String conversationId; + + /** + * 用户输入 + */ + private String userInput; + + /** + * 系统输出 + */ + private String systemOut; + + /** + * 耗时 + */ + private Long timeCost; + + /** + * 回答内容类型 0:正常回答 1:异常回答 + */ + private Integer answerType = 0; + + /** + * 智能体id + */ + private String model; + + /** + * 问题创建时间 + */ + private LocalDateTime createTime; + + public OllamaDialogueLogDTO() { + } + + + public OllamaDialogueLogDTO(OllamaDTO ollamaDTO) { + this.createTime = LocalDateTime.now(); + this.userInput = JSONUtil.toJsonStr(ollamaDTO.getMessages()); + this.model = ollamaDTO.getModel(); + } + + public OllamaDialogueLog toDialogueLog(){ + OllamaDialogueLog dialogueLog = new OllamaDialogueLog(); + dialogueLog.setId(this.id); + dialogueLog.setUserId(this.userId); + dialogueLog.setAnswerType(this.answerType); + dialogueLog.setUserInput(this.userInput); + dialogueLog.setSystemOut(this.systemOut); + if (null != this.createTime){ + dialogueLog.setTimeCost(Duration.between(this.createTime, LocalDateTime.now()).toMillis()); + } + dialogueLog.setModel(this.model); + + return dialogueLog; + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/dto/R.java b/src/main/java/com/supervision/livedigitalavatarmanage/dto/R.java new file mode 100644 index 0000000..96abe9e --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/dto/R.java @@ -0,0 +1,131 @@ +package com.supervision.livedigitalavatarmanage.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.*; + +/** + * 响应信息主体 + * + * @author qimaoyu + */ +@Data +public class R implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final String TOTAL_COUNT = "total"; + public static final String RESULT_LIST = "result"; + + /** + * 成功 + */ + public static final int SUCCESS = 200; + + /** + * 失败 + */ + public static final int FAIL = 500; + + private int code; + + private String msg; + + private T data; + + public static R ok() { + return restResult(null, SUCCESS, null); + } + + public static R judgeResult(Boolean bo, String successMessage, String failMessage) { + if (bo) { + return restResult(null, SUCCESS, successMessage); + } else { + return restResult(null, FAIL, failMessage); + } + } + + public static R okMsg(String msg) { + return restResult(null, SUCCESS, msg); + } + + public static R ok(T data) { + return restResult(data, SUCCESS, null); + } + + public static R ok(T data, String msg) { + return restResult(data, SUCCESS, msg); + } + + public static R fail() { + return restResult(null, FAIL, null); + } + + public static R fail(String msg) { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) { + return restResult(data, FAIL, null); + } + + public static R fail(int code, String msg) { + return restResult(null, code, msg); + } + + + + private static R restResult(T data, int code, String msg) { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + + public static Map buildDataMap(List list) { + Map dataMap = new HashMap<>(); + if (list == null) { + dataMap.put(TOTAL_COUNT, 0); + dataMap.put(RESULT_LIST, new ArrayList<>()); + } else { + dataMap.put(TOTAL_COUNT, list.size()); + dataMap.put(RESULT_LIST, list); + } + return dataMap; + } + + public static Map buildDataMap(List list, Long total) { + Map dataMap = new HashMap<>(); + dataMap.put(TOTAL_COUNT, total); + dataMap.put(RESULT_LIST, list); + return dataMap; + } + + public static Map buildDataMap(Set list) { + Map dataMap = new HashMap<>(); + if (list == null) { + dataMap.put(TOTAL_COUNT, 0); + dataMap.put(RESULT_LIST, new ArrayList<>()); + } else { + dataMap.put(TOTAL_COUNT, list.size()); + dataMap.put(RESULT_LIST, list); + } + return dataMap; + } + + public static Map buildDataMap(Object object) { + if (object == null) { + return null; + } + List resultList = new ArrayList<>(); + resultList.add(object); + Map dataMap = new HashMap<>(); + dataMap.put(TOTAL_COUNT, resultList.size()); + dataMap.put(RESULT_LIST, resultList); + return dataMap; + } + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/dto/UserDetail.java b/src/main/java/com/supervision/livedigitalavatarmanage/dto/UserDetail.java new file mode 100644 index 0000000..2b11ad3 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/dto/UserDetail.java @@ -0,0 +1,61 @@ +package com.supervision.livedigitalavatarmanage.dto; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; + +import java.time.LocalDateTime; +import java.util.Collection; + +public class UserDetail extends User { + private String userId; + + /** + * 最后登录时间 + */ + private LocalDateTime lastLoginDate; + + /** + * 过期时间 + */ + private LocalDateTime expirationDate; + + + public UserDetail(String userId , String username, String password, Collection authorities) { + super(username, password, authorities); + this.userId = userId; + } + + public UserDetail(String username, String password, Collection authorities) { + super(username, password, authorities); + } + + public UserDetail(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public LocalDateTime getExpirationDate() { + return expirationDate; + } + + public void setExpirationDate(LocalDateTime expirationDate) { + this.expirationDate = expirationDate; + } + + public LocalDateTime getLastLoginDate() { + return lastLoginDate; + } + + public void setLastLoginDate(LocalDateTime lastLoginDate) { + this.lastLoginDate = lastLoginDate; + } + + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/enums/UserDisabledEnum.java b/src/main/java/com/supervision/livedigitalavatarmanage/enums/UserDisabledEnum.java new file mode 100644 index 0000000..e754f57 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/enums/UserDisabledEnum.java @@ -0,0 +1,17 @@ +package com.supervision.livedigitalavatarmanage.enums; + +import lombok.Getter; + +@Getter +public enum UserDisabledEnum { + ENABLED("0", "启用"), + DISABLED("1", "禁用"); + + private final String code; + private final String description; + + UserDisabledEnum(String code, String description) { + this.code = code; + this.description = description; + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/exception/BusinessException.java b/src/main/java/com/supervision/livedigitalavatarmanage/exception/BusinessException.java new file mode 100644 index 0000000..35b56f2 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/exception/BusinessException.java @@ -0,0 +1,76 @@ +/* + * 文 件 名: CustomException + * 版 权: + * 描 述: <描述> + * 修 改 人: RedName + * 修改时间: 2022/8/5 + * 跟踪单号: <跟踪单号> + * 修改单号: <修改单号> + * 修改内容: <修改内容> + */ +package com.supervision.livedigitalavatarmanage.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; + +/** + * <功能详细描述> + * 自定义异常 + * + * @author ljt + * @version [版本号, 2022/8/5] + * @see [相关类/方法] + * @since [产品/模块版本] + */ +@Slf4j +public class BusinessException extends RuntimeException { + /** + * 异常编码 + */ + private final Integer code; + + /** + * 异常信息 + */ + private final String message; + + public BusinessException(Throwable cause) { + super(cause); + this.code = HttpStatus.INTERNAL_SERVER_ERROR.value(); + this.message = null; + + } + + public BusinessException(Throwable cause, String message) { + super(cause); + this.code = HttpStatus.INTERNAL_SERVER_ERROR.value(); + this.message = message; + + } + + public BusinessException(String message) { + this.code = HttpStatus.INTERNAL_SERVER_ERROR.value(); + this.message = message; + } + + public BusinessException(String message, Integer code) { + this.message = message; + this.code = code; + } + + public BusinessException(String message, Throwable e) { + super(message, e); + log.error(message, e); + this.code = HttpStatus.INTERNAL_SERVER_ERROR.value(); + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + public Integer getCode() { + return code; + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/exception/UnauthorizedException.java b/src/main/java/com/supervision/livedigitalavatarmanage/exception/UnauthorizedException.java new file mode 100644 index 0000000..58061d8 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/exception/UnauthorizedException.java @@ -0,0 +1,15 @@ +package com.supervision.livedigitalavatarmanage.exception; + +public class UnauthorizedException extends RuntimeException { + public UnauthorizedException() { + super("用户未登录"); + } + + public UnauthorizedException(String message) { + super(message); + } + + public UnauthorizedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/filter/JwtAuthenticationFilter.java b/src/main/java/com/supervision/livedigitalavatarmanage/filter/JwtAuthenticationFilter.java new file mode 100644 index 0000000..f84e39f --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/filter/JwtAuthenticationFilter.java @@ -0,0 +1,62 @@ +package com.supervision.livedigitalavatarmanage.filter; + +import com.supervision.livedigitalavatarmanage.dto.UserDetail; +import com.supervision.livedigitalavatarmanage.util.JwtUtils; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtUtils jwtUtils; + private final UserDetailsService userDetailsService; + + @Override + protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) throws ServletException, IOException { + String authHeader = request.getHeader("Authorization"); + if (authHeader != null && authHeader.startsWith("Bearer ")) { + // 提取JWT Token(去掉前缀"Bearer ") + String token = authHeader.substring(7); + String username; + try { + // 从JWT中解析用户名 + username = jwtUtils.getUsernameFromToken(token); + } catch (Exception e) { + // 如果JWT格式不正确或过期,直接放行(后续的过滤器会处理认证失败) + filterChain.doFilter(request, response); + return; + } + // 如果成功提取到用户名,并且当前没有已认证的用户 + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + // 根据用户名从数据库加载用户信息 + UserDetail userDetails = (UserDetail) userDetailsService.loadUserByUsername(username); + // 验证登录时间是否一致,如果不一致说明登录已过期 + // 验证Token的有效性(是否未过期) + if (jwtUtils.validateLoginTime(token, userDetails.getLastLoginDate()) + && !jwtUtils.isTokenExpired(token)) { + // 将用户信息封装到Authentication对象中,标记为已认证 + UsernamePasswordAuthenticationToken authToken = + new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + // 将Authentication对象放入SecurityContext,表示当前请求已通过认证 + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } + } + filterChain.doFilter(request, response); + } + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/mapper/OllamaDialogueLogMapper.java b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/OllamaDialogueLogMapper.java new file mode 100644 index 0000000..b60ffcf --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/OllamaDialogueLogMapper.java @@ -0,0 +1,18 @@ +package com.supervision.livedigitalavatarmanage.mapper; + +import com.supervision.livedigitalavatarmanage.domain.OllamaDialogueLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author Administrator +* @description 针对表【ollama_dialogue_log(ollama对话日志)】的数据库操作Mapper +* @createDate 2025-07-28 15:10:10 +* @Entity com.supervision.livedigitalavatarmanage.domain.OllamaDialogueLog +*/ +public interface OllamaDialogueLogMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/mapper/PermissionsMapper.java b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/PermissionsMapper.java new file mode 100644 index 0000000..d56965f --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/PermissionsMapper.java @@ -0,0 +1,18 @@ +package com.supervision.livedigitalavatarmanage.mapper; + +import com.supervision.livedigitalavatarmanage.domain.Permissions; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author Administrator +* @description 针对表【permissions(权限表)】的数据库操作Mapper +* @createDate 2025-07-28 14:11:27 +* @Entity com.supervision.ivedigitalavatarmanage.domain.Permissions +*/ +public interface PermissionsMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/mapper/RolePermissionsMapper.java b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/RolePermissionsMapper.java new file mode 100644 index 0000000..f9d9ea0 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/RolePermissionsMapper.java @@ -0,0 +1,18 @@ +package com.supervision.livedigitalavatarmanage.mapper; + +import com.supervision.livedigitalavatarmanage.domain.RolePermissions; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author Administrator +* @description 针对表【role_permissions】的数据库操作Mapper +* @createDate 2025-07-28 14:11:27 +* @Entity com.supervision.ivedigitalavatarmanage.domain.RolePermissions +*/ +public interface RolePermissionsMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/mapper/RolesMapper.java b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/RolesMapper.java new file mode 100644 index 0000000..329ebd3 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/RolesMapper.java @@ -0,0 +1,18 @@ +package com.supervision.livedigitalavatarmanage.mapper; + +import com.supervision.livedigitalavatarmanage.domain.Roles; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author Administrator +* @description 针对表【roles】的数据库操作Mapper +* @createDate 2025-07-28 14:11:27 +* @Entity com.supervision.ivedigitalavatarmanage.domain.Roles +*/ +public interface RolesMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/mapper/UserRolesMapper.java b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/UserRolesMapper.java new file mode 100644 index 0000000..6fd4926 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/UserRolesMapper.java @@ -0,0 +1,18 @@ +package com.supervision.livedigitalavatarmanage.mapper; + +import com.supervision.livedigitalavatarmanage.domain.UserRoles; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author Administrator +* @description 针对表【user_roles】的数据库操作Mapper +* @createDate 2025-07-28 14:11:27 +* @Entity com.supervision.ivedigitalavatarmanage.domain.UserRoles +*/ +public interface UserRolesMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/mapper/UsersMapper.java b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/UsersMapper.java new file mode 100644 index 0000000..ec2bf67 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/mapper/UsersMapper.java @@ -0,0 +1,18 @@ +package com.supervision.livedigitalavatarmanage.mapper; + +import com.supervision.livedigitalavatarmanage.domain.Users; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author Administrator +* @description 针对表【users】的数据库操作Mapper +* @createDate 2025-07-28 14:11:27 +* @Entity com.supervision.ivedigitalavatarmanage.domain.Users +*/ +public interface UsersMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/AuthService.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/AuthService.java new file mode 100644 index 0000000..04eaf96 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/AuthService.java @@ -0,0 +1,14 @@ +package com.supervision.livedigitalavatarmanage.service; + +import com.supervision.livedigitalavatarmanage.domain.Users; + +public interface AuthService { + + String login(String username, String password); + + + void heartbeat(String userId); + + Users getCurrentUser(String userId); + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/OllamaAgentService.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/OllamaAgentService.java new file mode 100644 index 0000000..ae33fb7 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/OllamaAgentService.java @@ -0,0 +1,16 @@ +package com.supervision.livedigitalavatarmanage.service; + +import com.supervision.livedigitalavatarmanage.vo.ollama.OllamResponse; +import com.supervision.livedigitalavatarmanage.vo.ollama.OllamaDTO; +import reactor.core.publisher.Flux; + +public interface OllamaAgentService { + + /** + * 流式智能体问答 + * @param agentChatReqDTO agentChatReqDTO + * @return + */ + Flux streamChat(OllamaDTO ollamaDTO); + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/OllamaDialogueLogService.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/OllamaDialogueLogService.java new file mode 100644 index 0000000..ca7ed97 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/OllamaDialogueLogService.java @@ -0,0 +1,13 @@ +package com.supervision.livedigitalavatarmanage.service; + +import com.supervision.livedigitalavatarmanage.domain.OllamaDialogueLog; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author Administrator +* @description 针对表【ollama_dialogue_log(ollama对话日志)】的数据库操作Service +* @createDate 2025-07-28 15:10:10 +*/ +public interface OllamaDialogueLogService extends IService { + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/PermissionsService.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/PermissionsService.java new file mode 100644 index 0000000..fb98783 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/PermissionsService.java @@ -0,0 +1,13 @@ +package com.supervision.livedigitalavatarmanage.service; + +import com.supervision.livedigitalavatarmanage.domain.Permissions; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author Administrator +* @description 针对表【permissions(权限表)】的数据库操作Service +* @createDate 2025-07-28 14:11:27 +*/ +public interface PermissionsService extends IService { + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/RolePermissionsService.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/RolePermissionsService.java new file mode 100644 index 0000000..a025a1c --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/RolePermissionsService.java @@ -0,0 +1,13 @@ +package com.supervision.livedigitalavatarmanage.service; + +import com.supervision.livedigitalavatarmanage.domain.RolePermissions; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author Administrator +* @description 针对表【role_permissions】的数据库操作Service +* @createDate 2025-07-28 14:11:27 +*/ +public interface RolePermissionsService extends IService { + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/RolesService.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/RolesService.java new file mode 100644 index 0000000..8f94d5e --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/RolesService.java @@ -0,0 +1,13 @@ +package com.supervision.livedigitalavatarmanage.service; + +import com.supervision.livedigitalavatarmanage.domain.Roles; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author Administrator +* @description 针对表【roles】的数据库操作Service +* @createDate 2025-07-28 14:11:27 +*/ +public interface RolesService extends IService { + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/ThinkTagState.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/ThinkTagState.java new file mode 100644 index 0000000..6c131e8 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/ThinkTagState.java @@ -0,0 +1,59 @@ +package com.supervision.livedigitalavatarmanage.service; + +import org.springframework.ai.chat.model.ChatResponse; + +public class ThinkTagState { + private boolean inThink = false; + private final StringBuilder buffer = new StringBuilder(); + + private ChatResponse chatResponse; + + private boolean filerThink = false; + + public ThinkTagState(boolean filerThink) { + this.filerThink = filerThink; + } + + public ThinkTagState process(String chunk) { + StringBuilder output = new StringBuilder(); + String remaining = chunk; + if (!filerThink) { + buffer.append(remaining); + return this; + } + while (!remaining.isEmpty()) { + if (!inThink) { + int startIdx = remaining.indexOf(""); + if (startIdx == -1) { + output.append(remaining); + break; + } + output.append(remaining.substring(0, startIdx)); + inThink = true; + remaining = remaining.substring(startIdx + "".length()); + } else { + int endIdx = remaining.indexOf(""); + if (endIdx == -1) { + break; // 等待后续分块 + } + inThink = false; + remaining = remaining.substring(endIdx + "".length()); + } + } + + buffer.append(output); + return this; + } + + public String getFilteredText() { + return buffer.toString(); + } + + public ChatResponse getChatResponse() { + return chatResponse; + } + + public void setChatResponse(ChatResponse chatResponse) { + this.chatResponse = chatResponse; + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/UserRolesService.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/UserRolesService.java new file mode 100644 index 0000000..3af6b15 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/UserRolesService.java @@ -0,0 +1,13 @@ +package com.supervision.livedigitalavatarmanage.service; + +import com.supervision.livedigitalavatarmanage.domain.UserRoles; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author Administrator +* @description 针对表【user_roles】的数据库操作Service +* @createDate 2025-07-28 14:11:27 +*/ +public interface UserRolesService extends IService { + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/UsersService.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/UsersService.java new file mode 100644 index 0000000..2a61892 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/UsersService.java @@ -0,0 +1,22 @@ +package com.supervision.livedigitalavatarmanage.service; + +import com.supervision.livedigitalavatarmanage.domain.Users; +import com.baomidou.mybatisplus.extension.service.IService; + +import java.time.LocalDateTime; + +/** +* @author Administrator +* @description 针对表【users】的数据库操作Service +* @createDate 2025-07-28 14:11:27 +*/ +public interface UsersService extends IService { + + Users getByUsername(String username); + + + void updateByUserId(String userId); + + + LocalDateTime updateUserLoginTime(String userId); +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/AuthServiceImpl.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/AuthServiceImpl.java new file mode 100644 index 0000000..e57aaff --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/AuthServiceImpl.java @@ -0,0 +1,77 @@ +package com.supervision.livedigitalavatarmanage.service.impl; + +import com.supervision.livedigitalavatarmanage.domain.Users; +import com.supervision.livedigitalavatarmanage.dto.UserDetail; +import com.supervision.livedigitalavatarmanage.enums.UserDisabledEnum; +import com.supervision.livedigitalavatarmanage.exception.BusinessException; +import com.supervision.livedigitalavatarmanage.service.AuthService; +import com.supervision.livedigitalavatarmanage.service.UsersService; +import com.supervision.livedigitalavatarmanage.util.JwtUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +@Service +@RequiredArgsConstructor +public class AuthServiceImpl implements AuthService { + + private final AuthenticationManager authenticationManager; + + private final JwtUtils jwtUtils; + + private final UsersService userService; + + // 掉线时间,单位:秒 + private long offlineTime = 90; // 默认90秒 + + /** + * 登录认证,返回JWT Token + */ + @Override + public String login(String username, String password) { + Users user = userService.getByUsername(username); + if (null == user) { + throw new RuntimeException("用户名或密码有误!"); + } + if (UserDisabledEnum.DISABLED.getCode().equals(user.getDisabled())) { + throw new RuntimeException("用户已被禁用"); + } + LocalDateTime lastActive = user.getLastActive(); + if (null != lastActive && lastActive.plusSeconds(offlineTime).isAfter(LocalDateTime.now())){ + throw new BusinessException("一个账号同时只能登录一个设备,请先下线其他设备"); + } + try { + Authentication authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(username, password) + ); + // 更新用户登录时间 + LocalDateTime localDateTime = userService.updateUserLoginTime(user.getId()); + UserDetail userDetails = (UserDetail) authentication.getPrincipal(); + userDetails.setLastLoginDate(localDateTime); + return jwtUtils.generateToken(userDetails); + } catch (BadCredentialsException e) { + throw new RuntimeException("用户名或密码错误"); + } catch (DisabledException e) { + throw new RuntimeException("用户已被禁用"); + } + } + + @Override + public void heartbeat(String userId) { + userService.updateByUserId(userId); + } + + @Override + public Users getCurrentUser(String userId) { + Users users = userService.getById(userId); + users.setPassword(null); + return users; + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/OllamaAgentServiceImpl.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/OllamaAgentServiceImpl.java new file mode 100644 index 0000000..8c929fa --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/OllamaAgentServiceImpl.java @@ -0,0 +1,84 @@ +package com.supervision.livedigitalavatarmanage.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.supervision.livedigitalavatarmanage.dto.OllamaDialogueLogDTO; +import com.supervision.livedigitalavatarmanage.dto.UserDetail; +import com.supervision.livedigitalavatarmanage.service.OllamaAgentService; +import com.supervision.livedigitalavatarmanage.service.OllamaDialogueLogService; +import com.supervision.livedigitalavatarmanage.service.ThinkTagState; +import com.supervision.livedigitalavatarmanage.util.UserUtil; +import com.supervision.livedigitalavatarmanage.vo.ollama.OllamResponse; +import com.supervision.livedigitalavatarmanage.vo.ollama.OllamaDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.ollama.OllamaChatModel; +import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class OllamaAgentServiceImpl implements OllamaAgentService { + + + private final OllamaChatModel ollamaChatModel; + + private final OllamaDialogueLogService agentDialogueLogService; + @Override + public Flux streamChat(OllamaDTO ollamaDTO) { + UserDetail userDetail = UserUtil.currentUser(); + OllamaDialogueLogDTO dialogueLogDTO = new OllamaDialogueLogDTO(ollamaDTO); + dialogueLogDTO.setUserId(userDetail.getUserId()); + StringBuilder aiResponseBuilder = new StringBuilder(); + Flux responseFlux = ollamaChatModel.stream(ollamaDTO.toPrompt()) + .scan(new ThinkTagState(ollamaDTO.isFilterThink()), (state, response) -> { + String chunk = response.getResult().getOutput().getText(); + state.setChatResponse(response); + return state.process(chunk); + }) + .filter(state -> StrUtil.isNotBlank(state.getFilteredText())) + .map(response -> { + OllamResponse responseDTO = new OllamResponse(response.getChatResponse()); + aiResponseBuilder.append(response.getChatResponse().getResult().getOutput().getText()); + return responseDTO; + }).doOnComplete(() -> { + dialogueLogDTO.setSystemOut(aiResponseBuilder.toString()); + agentDialogueLogService.save(dialogueLogDTO.toDialogueLog()); + }).doOnError(e -> { + log.error("Error during AI chat stream: {}", e.getMessage()); + dialogueLogDTO.setSystemOut(aiResponseBuilder.toString()); + dialogueLogDTO.setAnswerType(1); + agentDialogueLogService.save(dialogueLogDTO.toDialogueLog()); + }); + + if (ollamaDTO.isStream()){ + return responseFlux; + } + return mergeResponses(responseFlux); + } + + + /** + * 合并响应 + * @param responseFlux 响应流 + * @return + */ + private Flux mergeResponses(Flux responseFlux) { + return responseFlux + .collectList() + .flatMapMany(list -> { + if (list.isEmpty()) { + return Flux.empty(); + } + OllamResponse last = CollUtil.getLast(list); + String combinedText = list.stream() + .map(r -> r.getMessage().getContent()) + .collect(Collectors.joining()); + last.getMessage().setContent(combinedText); + return Flux.just(last); + }); + } + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/OllamaDialogueLogServiceImpl.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/OllamaDialogueLogServiceImpl.java new file mode 100644 index 0000000..60862f2 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/OllamaDialogueLogServiceImpl.java @@ -0,0 +1,22 @@ +package com.supervision.livedigitalavatarmanage.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.supervision.livedigitalavatarmanage.domain.OllamaDialogueLog; +import com.supervision.livedigitalavatarmanage.service.OllamaDialogueLogService; +import com.supervision.livedigitalavatarmanage.mapper.OllamaDialogueLogMapper; +import org.springframework.stereotype.Service; + +/** +* @author Administrator +* @description 针对表【ollama_dialogue_log(ollama对话日志)】的数据库操作Service实现 +* @createDate 2025-07-28 15:10:10 +*/ +@Service +public class OllamaDialogueLogServiceImpl extends ServiceImpl + implements OllamaDialogueLogService{ + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/PermissionsServiceImpl.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/PermissionsServiceImpl.java new file mode 100644 index 0000000..ca7da82 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/PermissionsServiceImpl.java @@ -0,0 +1,22 @@ +package com.supervision.livedigitalavatarmanage.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.supervision.livedigitalavatarmanage.domain.Permissions; +import com.supervision.livedigitalavatarmanage.service.PermissionsService; +import com.supervision.livedigitalavatarmanage.mapper.PermissionsMapper; +import org.springframework.stereotype.Service; + +/** +* @author Administrator +* @description 针对表【permissions(权限表)】的数据库操作Service实现 +* @createDate 2025-07-28 14:11:27 +*/ +@Service +public class PermissionsServiceImpl extends ServiceImpl + implements PermissionsService{ + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/RolePermissionsServiceImpl.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/RolePermissionsServiceImpl.java new file mode 100644 index 0000000..e0c4e52 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/RolePermissionsServiceImpl.java @@ -0,0 +1,22 @@ +package com.supervision.livedigitalavatarmanage.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.supervision.livedigitalavatarmanage.domain.RolePermissions; +import com.supervision.livedigitalavatarmanage.service.RolePermissionsService; +import com.supervision.livedigitalavatarmanage.mapper.RolePermissionsMapper; +import org.springframework.stereotype.Service; + +/** +* @author Administrator +* @description 针对表【role_permissions】的数据库操作Service实现 +* @createDate 2025-07-28 14:11:27 +*/ +@Service +public class RolePermissionsServiceImpl extends ServiceImpl + implements RolePermissionsService{ + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/RolesServiceImpl.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/RolesServiceImpl.java new file mode 100644 index 0000000..9f96d1b --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/RolesServiceImpl.java @@ -0,0 +1,22 @@ +package com.supervision.livedigitalavatarmanage.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.supervision.livedigitalavatarmanage.domain.Roles; +import com.supervision.livedigitalavatarmanage.service.RolesService; +import com.supervision.livedigitalavatarmanage.mapper.RolesMapper; +import org.springframework.stereotype.Service; + +/** +* @author Administrator +* @description 针对表【roles】的数据库操作Service实现 +* @createDate 2025-07-28 14:11:27 +*/ +@Service +public class RolesServiceImpl extends ServiceImpl + implements RolesService{ + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UserDetailsServiceImpl.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UserDetailsServiceImpl.java new file mode 100644 index 0000000..b3a0703 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UserDetailsServiceImpl.java @@ -0,0 +1,46 @@ +package com.supervision.livedigitalavatarmanage.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.supervision.livedigitalavatarmanage.domain.Users; +import com.supervision.livedigitalavatarmanage.dto.UserDetail; +import com.supervision.livedigitalavatarmanage.enums.UserDisabledEnum; +import com.supervision.livedigitalavatarmanage.service.UsersService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class UserDetailsServiceImpl implements UserDetailsService { + + private final UsersService usersService; + + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + + // 将查询到的用户信息组装成UserDetails对象 + // **扩展点**:如需加载用户角色权限,可在此处查询 sys_user_role 表关联的角色,并将角色加入 authorities 列表 + List authorities = Collections.emptyList(); + // 使用Spring Security提供的User对象作为UserDetails返回 + Users sysUser = usersService.getByUsername(username); + if (sysUser == null) { + throw new UsernameNotFoundException("用户不存在: " + username); + } + if (StrUtil.equals(UserDisabledEnum.DISABLED.getCode(), sysUser.getDisabled())) { + throw new UsernameNotFoundException("用户已被禁用: " + username); + } + UserDetail userDetail = new UserDetail(sysUser.getId(), sysUser.getUsername(), sysUser.getPassword(), authorities); + userDetail.setExpirationDate(sysUser.getExpirationDate()); + userDetail.setLastLoginDate(sysUser.getLastLoginDate()); + return userDetail; + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UserRolesServiceImpl.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UserRolesServiceImpl.java new file mode 100644 index 0000000..650b70f --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UserRolesServiceImpl.java @@ -0,0 +1,22 @@ +package com.supervision.livedigitalavatarmanage.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.supervision.livedigitalavatarmanage.domain.UserRoles; +import com.supervision.livedigitalavatarmanage.service.UserRolesService; +import com.supervision.livedigitalavatarmanage.mapper.UserRolesMapper; +import org.springframework.stereotype.Service; + +/** +* @author Administrator +* @description 针对表【user_roles】的数据库操作Service实现 +* @createDate 2025-07-28 14:11:27 +*/ +@Service +public class UserRolesServiceImpl extends ServiceImpl + implements UserRolesService{ + +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UsersServiceImpl.java b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UsersServiceImpl.java new file mode 100644 index 0000000..1230af5 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/service/impl/UsersServiceImpl.java @@ -0,0 +1,43 @@ +package com.supervision.livedigitalavatarmanage.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.supervision.livedigitalavatarmanage.domain.Users; +import com.supervision.livedigitalavatarmanage.service.UsersService; +import com.supervision.livedigitalavatarmanage.mapper.UsersMapper; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +/** +* @author Administrator +* @description 针对表【users】的数据库操作Service实现 +* @createDate 2025-07-28 14:11:27 +*/ +@Service +public class UsersServiceImpl extends ServiceImpl + implements UsersService{ + + @Override + public Users getByUsername(String username) { + return super.lambdaQuery().eq(Users::getUsername, username).one(); + } + + @Override + public void updateByUserId(String userId) { + this.lambdaUpdate().eq(Users::getId, userId) + .set(Users::getLastActive, LocalDateTime.now()).update(); + } + + @Override + public LocalDateTime updateUserLoginTime(String userId) { + LocalDateTime now = LocalDateTime.now(); + this.lambdaUpdate().eq(Users::getId, userId) + .set(Users::getLastLoginDate, now) + .set(Users::getLastActive, now).update(); + return now; + } +} + + + + diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/util/JwtUtils.java b/src/main/java/com/supervision/livedigitalavatarmanage/util/JwtUtils.java new file mode 100644 index 0000000..c3c9519 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/util/JwtUtils.java @@ -0,0 +1,87 @@ +package com.supervision.livedigitalavatarmanage.util; + + +import cn.hutool.json.JSONUtil; +import com.supervision.livedigitalavatarmanage.dto.UserDetail; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.time.LocalDateTime; +import java.util.Base64; +import java.util.Date; +import java.util.Map; + +@Component +public class JwtUtils { + + @Value("${jwt.secret}") + private String Secret; + + @Value("${jwt.expiration}") + private long Expiration; // 1天 + + private SecretKey secretKey; + + @PostConstruct + public void initKey() { + this.secretKey = Keys.hmacShaKeyFor(Base64.getDecoder().decode(Secret)); + } + + // 生成Token + public String generateToken(UserDetails userDetails) { + + Map sub = new java.util.HashMap<>(Map.of("username", userDetails.getUsername())); + if (userDetails instanceof UserDetail userDetail) { + sub.put("lastLoginTime", userDetail.getLastLoginDate()); + } + return Jwts.builder() + .subject(JSONUtil.toJsonStr(sub)) + .issuedAt(new Date()) + .expiration(new Date(System.currentTimeMillis() + Expiration)) + .signWith(secretKey, Jwts.SIG.HS256) + .compact(); + } + + // 从 Token 中解析用户名 + public String getUsernameFromToken(String token) { + Jws jws = Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token); + String subject = jws.getPayload().getSubject(); + return JSONUtil.parseObj(subject).getStr("username"); + } + + // 判断是否过期 + public boolean isTokenExpired(String token) { + Jws jws = Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token); + return jws.getPayload().getExpiration().before(new Date()); + } + + /** + * 验证登录时间是否相等 + */ + public boolean validateLoginTime(String token, LocalDateTime lastLoginTime) { + Jws jws = Jwts.parser() + .verifyWith(secretKey) + .build() + .parseSignedClaims(token); + String subject = jws.getPayload().getSubject(); + + LocalDateTime tokenLoginTime = JSONUtil.parseObj(subject).getLocalDateTime("lastLoginTime", null); + // 相差不超过500毫秒 + return tokenLoginTime != null && tokenLoginTime.isAfter(lastLoginTime.minusNanos(500_000_000)) && + tokenLoginTime.isBefore(lastLoginTime.plusNanos(500_000_000)); + } + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/util/UserUtil.java b/src/main/java/com/supervision/livedigitalavatarmanage/util/UserUtil.java new file mode 100644 index 0000000..bcd7d54 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/util/UserUtil.java @@ -0,0 +1,34 @@ +package com.supervision.livedigitalavatarmanage.util; + +import com.supervision.livedigitalavatarmanage.dto.UserDetail; +import com.supervision.livedigitalavatarmanage.exception.UnauthorizedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.socket.WebSocketSession; +import java.security.Principal; + +public class UserUtil { + + public static UserDetail currentUser() { + SecurityContext context = SecurityContextHolder.getContext(); + if (null == context) { + throw new UnauthorizedException("未登录或登录已过期,请重新登录"); + } + Authentication authentication = context.getAuthentication(); + Object principal = authentication.getPrincipal(); + if ("anonymousUser".equals(principal)) { + throw new UnauthorizedException("未登录或登录已过期,请重新登录"); + } + return (UserDetail) authentication.getPrincipal(); + } + + public static UserDetail currentUser(WebSocketSession session){ + Principal principal = session.getPrincipal(); + if (principal instanceof UserDetail userDetail) { + return userDetail; + } else { + throw new UnauthorizedException("未登录或登录已过期,请重新登录"); + } + } +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/vo/LoginReqVo.java b/src/main/java/com/supervision/livedigitalavatarmanage/vo/LoginReqVo.java new file mode 100644 index 0000000..33708d8 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/vo/LoginReqVo.java @@ -0,0 +1,11 @@ +package com.supervision.livedigitalavatarmanage.vo; + +import lombok.Data; + +@Data +public class LoginReqVo { + + private String username; + + private String password; +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/MessageDTO.java b/src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/MessageDTO.java new file mode 100644 index 0000000..17aa6df --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/MessageDTO.java @@ -0,0 +1,11 @@ +package com.supervision.livedigitalavatarmanage.vo.ollama; + +import lombok.Data; + +@Data +public class MessageDTO { + + private String role; + private String content; + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/OllamResponse.java b/src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/OllamResponse.java new file mode 100644 index 0000000..42e36d9 --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/OllamResponse.java @@ -0,0 +1,63 @@ +package com.supervision.livedigitalavatarmanage.vo.ollama; + +import lombok.Data; +import org.springframework.ai.chat.messages.AssistantMessage; +import org.springframework.ai.chat.metadata.ChatResponseMetadata; +import org.springframework.ai.chat.model.ChatResponse; +import org.springframework.ai.chat.model.Generation; + +import java.util.Date; + + +@Data +public class OllamResponse { + + private String model; + + private Date created_at; + + private MessageDTO message; + + private String done_reason; + + private Boolean done; + + private Long total_duration; + + private Long load_duration; + + private Long prompt_eval_count; + + private Long prompt_eval_duration; + + private Long eval_count; + + private Long eval_duration; + + public OllamResponse() { + } + + public OllamResponse(ChatResponse chatResponse) { + ChatResponseMetadata metadata = chatResponse.getMetadata(); + this.model = metadata.getModel(); + Object createdAt = metadata.get("created-at"); + this.created_at = createdAt instanceof Date ? (Date) createdAt : null; + Object evalCount = metadata.get("eval-count"); + this.eval_count = evalCount instanceof Number ? ((Number) evalCount).longValue() : null; + Object evalDuration = metadata.get("eval-duration"); + this.eval_duration = evalDuration instanceof Number ? ((Number) evalDuration).longValue() : null; + Object totalDuration = metadata.get("total-duration"); + this.total_duration = totalDuration instanceof Number ? ((Number) totalDuration).longValue() : null; + Object promptEvalCount = metadata.get("prompt-eval-count"); + this.prompt_eval_count = promptEvalCount instanceof Number ? ((Number) promptEvalCount).longValue() : null; + Object promptEvalDuration = metadata.get("prompt-eval-duration"); + this.prompt_eval_duration = promptEvalDuration instanceof Number ? ((Number) promptEvalDuration).longValue() : null; + this.done = metadata.get("done"); + AssistantMessage output = chatResponse.getResult().getOutput(); + this.message = new MessageDTO(); + this.message.setRole(output.getMessageType().name().toLowerCase()); + this.message.setContent(output.getText()); + + } + +} diff --git a/src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/OllamaDTO.java b/src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/OllamaDTO.java new file mode 100644 index 0000000..c00f3cc --- /dev/null +++ b/src/main/java/com/supervision/livedigitalavatarmanage/vo/ollama/OllamaDTO.java @@ -0,0 +1,42 @@ +package com.supervision.livedigitalavatarmanage.vo.ollama; + +import cn.hutool.core.lang.Assert; +import lombok.Data; +import org.springframework.ai.chat.messages.*; +import org.springframework.ai.chat.prompt.Prompt; +import org.springframework.ai.ollama.api.OllamaOptions; + +import java.util.List; +import java.util.stream.Collectors; + +@Data +public class OllamaDTO { + + private String model; + + private boolean stream; + + private boolean filterThink; + + private List messages; + + + public Prompt toPrompt() { + List list = messages.stream().map(messageDTO -> { + String role = messageDTO.getRole(); + if ("user".equals(role)) { + return (Message)new UserMessage(messageDTO.getContent()); + } else if ("assistant".equals(role)) { + return new AssistantMessage(messageDTO.getContent()); + } else if ("system".equals(role)) { + return new SystemMessage(messageDTO.getContent()); + } else { + throw new IllegalArgumentException("Unknown role: " + role); + } + }).toList(); + OllamaOptions.Builder optionsBuilder = OllamaOptions.builder(); + optionsBuilder.model(model); + optionsBuilder.model(model); + return new Prompt(list,optionsBuilder.build()); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..6d3c201 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,38 @@ +server: + port: 9909 + servlet: + context-path: /live-digital-avatar-manage +spring: + application: + name: live-digital-avatar-manage + datasource: + druid: + url: jdbc:postgresql://192.168.10.137:54321/live-digital-avatar-manage + username: postgres + password: 123456 + driver-class-name: org.postgresql.Driver + servlet: + multipart: + max-file-size: 10MB + max-request-size: 100MB + ai: + ollama: + baseUrl: http://192.168.10.70:11434 + chat: + model: qwen3:30b-a3b + #model: qwen3:32b + options: + max_tokens: 51200 + top_p: 0.9 + top_k: 40 + temperature: 0.7 + timeout: 180000 + embedding: + model: dengcao/Qwen3-Embedding-0.6B:F16 +mybatis-plus: + mapper-locations: classpath*:mapper/*.xml + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl +jwt: + secret: "DlHaPUePiN6MyvpMpsMq/t6swzMHqtrRFd2YnofKz4k=" # JWT?? ?????????? Base64.getEncoder().encodeToString(Keys.secretKeyFor(SignatureAlgorithm.HS256).getEncoded()); + expiration: 2592000000 # ??? \ No newline at end of file diff --git a/src/main/resources/mapper/OllamaDialogueLogMapper.xml b/src/main/resources/mapper/OllamaDialogueLogMapper.xml new file mode 100644 index 0000000..9f44ef8 --- /dev/null +++ b/src/main/resources/mapper/OllamaDialogueLogMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + id,user_id,user_input, + system_out,time_cost,answer_type, + model,create_time,update_time + + diff --git a/src/main/resources/mapper/PermissionsMapper.xml b/src/main/resources/mapper/PermissionsMapper.xml new file mode 100644 index 0000000..e864544 --- /dev/null +++ b/src/main/resources/mapper/PermissionsMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + id,url,description, + create_time,update_time + + diff --git a/src/main/resources/mapper/RolePermissionsMapper.xml b/src/main/resources/mapper/RolePermissionsMapper.xml new file mode 100644 index 0000000..ddd991b --- /dev/null +++ b/src/main/resources/mapper/RolePermissionsMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + role_id,permission_id ,create_time, + update_time + + diff --git a/src/main/resources/mapper/RolesMapper.xml b/src/main/resources/mapper/RolesMapper.xml new file mode 100644 index 0000000..9ca605d --- /dev/null +++ b/src/main/resources/mapper/RolesMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + id,code,name, + is_system_role,create_time,update_time + + diff --git a/src/main/resources/mapper/UserRolesMapper.xml b/src/main/resources/mapper/UserRolesMapper.xml new file mode 100644 index 0000000..8992af3 --- /dev/null +++ b/src/main/resources/mapper/UserRolesMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + user_id ,role_id ,create_time, + update_time + + diff --git a/src/main/resources/mapper/UsersMapper.xml b/src/main/resources/mapper/UsersMapper.xml new file mode 100644 index 0000000..f5d1344 --- /dev/null +++ b/src/main/resources/mapper/UsersMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + id,username,password, + disabled,last_active,failed_login_attempts, + online_status,expiration_date,create_time, + update_time + + diff --git a/src/test/java/com/supervision/livedigitalavatarmanage/LiveDigitalAvatarManageApplicationTests.java b/src/test/java/com/supervision/livedigitalavatarmanage/LiveDigitalAvatarManageApplicationTests.java new file mode 100644 index 0000000..6462ddc --- /dev/null +++ b/src/test/java/com/supervision/livedigitalavatarmanage/LiveDigitalAvatarManageApplicationTests.java @@ -0,0 +1,13 @@ +package com.supervision.livedigitalavatarmanage; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class LiveDigitalAvatarManageApplicationTests { + + @Test + void contextLoads() { + } + +}