初始化项目
commit
18cd02517f
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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<OllamResponse> streamChat(@RequestBody OllamaDTO ollamaDTO) {
|
||||
//UserDetail userDetail = UserUtil.currentUser();
|
||||
return agentService.streamChat(ollamaDTO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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<T> 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 <T> R<T> ok() {
|
||||
return restResult(null, SUCCESS, null);
|
||||
}
|
||||
|
||||
public static <T> R<T> judgeResult(Boolean bo, String successMessage, String failMessage) {
|
||||
if (bo) {
|
||||
return restResult(null, SUCCESS, successMessage);
|
||||
} else {
|
||||
return restResult(null, FAIL, failMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> R<T> okMsg(String msg) {
|
||||
return restResult(null, SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(T data) {
|
||||
return restResult(data, SUCCESS, null);
|
||||
}
|
||||
|
||||
public static <T> R<T> ok(T data, String msg) {
|
||||
return restResult(data, SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail() {
|
||||
return restResult(null, FAIL, null);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(String msg) {
|
||||
return restResult(null, FAIL, msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(T data) {
|
||||
return restResult(data, FAIL, null);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(int code, String msg) {
|
||||
return restResult(null, code, msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static <T> R<T> restResult(T data, int code, String msg) {
|
||||
R<T> apiResult = new R<>();
|
||||
apiResult.setCode(code);
|
||||
apiResult.setData(data);
|
||||
apiResult.setMsg(msg);
|
||||
return apiResult;
|
||||
}
|
||||
|
||||
|
||||
public static Map<String, Object> buildDataMap(List list) {
|
||||
Map<String, Object> 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<String, Object> buildDataMap(List list, Long total) {
|
||||
Map<String, Object> dataMap = new HashMap<>();
|
||||
dataMap.put(TOTAL_COUNT, total);
|
||||
dataMap.put(RESULT_LIST, list);
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
public static Map<String, Object> buildDataMap(Set list) {
|
||||
Map<String, Object> 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<String, Object> buildDataMap(Object object) {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
List<Object> resultList = new ArrayList<>();
|
||||
resultList.add(object);
|
||||
Map<String, Object> dataMap = new HashMap<>();
|
||||
dataMap.put(TOTAL_COUNT, resultList.size());
|
||||
dataMap.put(RESULT_LIST, resultList);
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
}
|
@ -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<? extends GrantedAuthority> authorities) {
|
||||
super(username, password, authorities);
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public UserDetail(String username, String password, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(username, password, authorities);
|
||||
}
|
||||
|
||||
public UserDetail(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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<OllamaDialogueLog> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<Permissions> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<RolePermissions> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<Roles> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<UserRoles> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<Users> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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<OllamResponse> streamChat(OllamaDTO ollamaDTO);
|
||||
|
||||
}
|
@ -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<OllamaDialogueLog> {
|
||||
|
||||
}
|
@ -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<Permissions> {
|
||||
|
||||
}
|
@ -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<RolePermissions> {
|
||||
|
||||
}
|
@ -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<Roles> {
|
||||
|
||||
}
|
@ -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("<think>");
|
||||
if (startIdx == -1) {
|
||||
output.append(remaining);
|
||||
break;
|
||||
}
|
||||
output.append(remaining.substring(0, startIdx));
|
||||
inThink = true;
|
||||
remaining = remaining.substring(startIdx + "<think>".length());
|
||||
} else {
|
||||
int endIdx = remaining.indexOf("</think>");
|
||||
if (endIdx == -1) {
|
||||
break; // 等待后续分块
|
||||
}
|
||||
inThink = false;
|
||||
remaining = remaining.substring(endIdx + "</think>".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;
|
||||
}
|
||||
}
|
@ -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<UserRoles> {
|
||||
|
||||
}
|
@ -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> {
|
||||
|
||||
Users getByUsername(String username);
|
||||
|
||||
|
||||
void updateByUserId(String userId);
|
||||
|
||||
|
||||
LocalDateTime updateUserLoginTime(String userId);
|
||||
}
|
@ -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<OllamResponse> streamChat(OllamaDTO ollamaDTO) {
|
||||
UserDetail userDetail = UserUtil.currentUser();
|
||||
OllamaDialogueLogDTO dialogueLogDTO = new OllamaDialogueLogDTO(ollamaDTO);
|
||||
dialogueLogDTO.setUserId(userDetail.getUserId());
|
||||
StringBuilder aiResponseBuilder = new StringBuilder();
|
||||
Flux<OllamResponse> 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<OllamResponse> mergeResponses(Flux<OllamResponse> 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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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<OllamaDialogueLogMapper, OllamaDialogueLog>
|
||||
implements OllamaDialogueLogService{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<PermissionsMapper, Permissions>
|
||||
implements PermissionsService{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<RolePermissionsMapper, RolePermissions>
|
||||
implements RolePermissionsService{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<RolesMapper, Roles>
|
||||
implements RolesService{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<GrantedAuthority> 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;
|
||||
}
|
||||
}
|
@ -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<UserRolesMapper, UserRoles>
|
||||
implements UserRolesService{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<UsersMapper, Users>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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<String, Object> 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<Claims> 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<Claims> jws = Jwts.parser()
|
||||
.verifyWith(secretKey)
|
||||
.build()
|
||||
.parseSignedClaims(token);
|
||||
return jws.getPayload().getExpiration().before(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证登录时间是否相等
|
||||
*/
|
||||
public boolean validateLoginTime(String token, LocalDateTime lastLoginTime) {
|
||||
Jws<Claims> 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));
|
||||
}
|
||||
|
||||
}
|
@ -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("未登录或登录已过期,请重新登录");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.supervision.livedigitalavatarmanage.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class LoginReqVo {
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.supervision.livedigitalavatarmanage.vo.ollama;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MessageDTO {
|
||||
|
||||
private String role;
|
||||
private String content;
|
||||
|
||||
}
|
@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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<MessageDTO> messages;
|
||||
|
||||
|
||||
public Prompt toPrompt() {
|
||||
List<Message> 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());
|
||||
}
|
||||
}
|
@ -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 # ???
|
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.supervision.livedigitalavatarmanage.mapper.OllamaDialogueLogMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.supervision.livedigitalavatarmanage.domain.OllamaDialogueLog">
|
||||
<id property="id" column="id" jdbcType="VARCHAR"/>
|
||||
<result property="userId" column="user_id" jdbcType="VARCHAR"/>
|
||||
<result property="userInput" column="user_input" jdbcType="VARCHAR"/>
|
||||
<result property="systemOut" column="system_out" jdbcType="VARCHAR"/>
|
||||
<result property="timeCost" column="time_cost" jdbcType="BIGINT"/>
|
||||
<result property="answerType" column="answer_type" jdbcType="INTEGER"/>
|
||||
<result property="model" column="model" jdbcType="VARCHAR"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id,user_id,user_input,
|
||||
system_out,time_cost,answer_type,
|
||||
model,create_time,update_time
|
||||
</sql>
|
||||
</mapper>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.supervision.livedigitalavatarmanage.mapper.PermissionsMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.supervision.livedigitalavatarmanage.domain.Permissions">
|
||||
<id property="id" column="id" jdbcType="VARCHAR"/>
|
||||
<result property="url" column="url" jdbcType="VARCHAR"/>
|
||||
<result property="description" column="description" jdbcType="VARCHAR"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id,url,description,
|
||||
create_time,update_time
|
||||
</sql>
|
||||
</mapper>
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.supervision.livedigitalavatarmanage.mapper.RolePermissionsMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.supervision.livedigitalavatarmanage.domain.RolePermissions">
|
||||
<result property="roleId" column="role_id" jdbcType="VARCHAR"/>
|
||||
<result property="permissionId" column="permission_id " jdbcType="VARCHAR"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
role_id,permission_id ,create_time,
|
||||
update_time
|
||||
</sql>
|
||||
</mapper>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.supervision.livedigitalavatarmanage.mapper.RolesMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.supervision.livedigitalavatarmanage.domain.Roles">
|
||||
<id property="id" column="id" jdbcType="VARCHAR"/>
|
||||
<result property="code" column="code" jdbcType="VARCHAR"/>
|
||||
<result property="name" column="name" jdbcType="VARCHAR"/>
|
||||
<result property="isSystemRole" column="is_system_role" jdbcType="VARCHAR"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id,code,name,
|
||||
is_system_role,create_time,update_time
|
||||
</sql>
|
||||
</mapper>
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.supervision.livedigitalavatarmanage.mapper.UserRolesMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.supervision.livedigitalavatarmanage.domain.UserRoles">
|
||||
<id property="userId" column="user_id " jdbcType="VARCHAR"/>
|
||||
<result property="roleId" column="role_id " jdbcType="VARCHAR"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
user_id ,role_id ,create_time,
|
||||
update_time
|
||||
</sql>
|
||||
</mapper>
|
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.supervision.livedigitalavatarmanage.mapper.UsersMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.supervision.livedigitalavatarmanage.domain.Users">
|
||||
<id property="id" column="id" jdbcType="VARCHAR"/>
|
||||
<result property="username" column="username" jdbcType="VARCHAR"/>
|
||||
<result property="password" column="password" jdbcType="VARCHAR"/>
|
||||
<result property="disabled" column="disabled" jdbcType="VARCHAR"/>
|
||||
<result property="lastActive" column="last_active" jdbcType="TIMESTAMP"/>
|
||||
<result property="failedLoginAttempts" column="failed_login_attempts" jdbcType="INTEGER"/>
|
||||
<result property="lastLoginDate" column="TIMESTAMP" jdbcType="TIMESTAMP"/>
|
||||
<result property="expirationDate" column="expiration_date" jdbcType="TIMESTAMP"/>
|
||||
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
|
||||
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id,username,password,
|
||||
disabled,last_active,failed_login_attempts,
|
||||
online_status,expiration_date,create_time,
|
||||
update_time
|
||||
</sql>
|
||||
</mapper>
|
@ -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() {
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue