gateway tomcat去除
This commit is contained in:
@@ -63,7 +63,7 @@ CREATE TABLE workcase.tb_chat_room_member(
|
||||
member_id VARCHAR(50) NOT NULL, -- 成员记录ID
|
||||
room_id VARCHAR(50) NOT NULL, -- 聊天室ID
|
||||
user_id VARCHAR(50) NOT NULL, -- 用户ID(来客ID或员工ID)
|
||||
user_type VARCHAR(20) NOT NULL, -- 用户类型:guest-来客 stuff-客服 ai-AI助手
|
||||
user_type VARCHAR(20) NOT NULL, -- 用户类型:guest-来客 staff-客服 ai-AI助手
|
||||
user_name VARCHAR(100) NOT NULL, -- 用户名称
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- 状态:active-活跃 left-已离开 removed-被移除
|
||||
unread_count INTEGER NOT NULL DEFAULT 0, -- 该成员的未读消息数
|
||||
|
||||
@@ -30,8 +30,8 @@ INSERT INTO sys.tb_sys_role (
|
||||
('ROLE-0003', 'role_user', '普通用户', '系统普通用户角色',
|
||||
'global', NULL, true, 'system', NULL, now(), false),
|
||||
|
||||
-- 访客(全局)
|
||||
('ROLE-0004', 'role_guest', '访客', '系统访客角色,仅限查看基础信息',
|
||||
-- 访客(全局)- 注册用户默认角色,具备客服聊天和工单的所有接口权限
|
||||
('ROLE-0004', 'role_guest', '访客', '访客角色,具备客服聊天和工单的所有接口权限',
|
||||
'global', NULL, true, 'system', NULL, now(), false);
|
||||
|
||||
-- =============================
|
||||
@@ -138,7 +138,30 @@ INSERT INTO sys.tb_sys_permission (
|
||||
('PERM-0624', 'perm_workcase_tickets', '工单管理', 'workcase:tickets:view', '访问工单管理', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0625', 'perm_workcase_conversation', '对话数据', 'workcase:conversation:view', '访问对话数据管理', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0626', 'perm_workcase_agent', '智能体管理', 'workcase:agent:view', '访问智能体管理', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0627', 'perm_workcase_log', '日志管理', 'workcase:log:view', '访问日志管理', 'module_workcase', true, 'system', NULL, now(), false);
|
||||
('PERM-0627', 'perm_workcase_log', '日志管理', 'workcase:log:view', '访问日志管理', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0628', 'perm_workcase_chatroom', '聊天室控制台', 'workcase:chatroom:view', '访问聊天室控制台', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
|
||||
-- Workcase 接口权限(访客用户可用)
|
||||
-- AI对话接口权限
|
||||
('PERM-0701', 'perm_workcase_chat_create', 'AI对话创建', 'workcase:chat:create', '创建对话', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0702', 'perm_workcase_chat_update', 'AI对话更新', 'workcase:chat:update', '更新对话', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0703', 'perm_workcase_chat_list', 'AI对话查询', 'workcase:chat:list', '查询对话列表', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0704', 'perm_workcase_chat_message', 'AI对话消息', 'workcase:chat:message', '获取对话消息列表', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0705', 'perm_workcase_chat_stream', 'AI流式对话', 'workcase:chat:stream', '流式对话接口', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0706', 'perm_workcase_chat_analyze', 'AI对话分析', 'workcase:chat:analyze', '分析对话生成工单信息', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
-- 聊天室接口权限
|
||||
('PERM-0711', 'perm_workcase_room_create', '聊天室创建', 'workcase:room:create', '创建聊天室', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0712', 'perm_workcase_room_update', '聊天室更新', 'workcase:room:update', '更新聊天室', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0713', 'perm_workcase_room_close', '聊天室关闭', 'workcase:room:close', '关闭聊天室', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0714', 'perm_workcase_room_view', '聊天室查看', 'workcase:room:view', '查看聊天室', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0715', 'perm_workcase_room_member', '聊天室成员', 'workcase:room:member', '管理聊天室成员', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0716', 'perm_workcase_room_message', '聊天室消息', 'workcase:room:message', '发送和查看聊天室消息', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
-- 工单接口权限
|
||||
('PERM-0721', 'perm_workcase_ticket_create', '工单创建', 'workcase:ticket:create', '创建工单', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0722', 'perm_workcase_ticket_update', '工单更新', 'workcase:ticket:update', '更新工单', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0723', 'perm_workcase_ticket_view', '工单查看', 'workcase:ticket:view', '查看工单详情和列表', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0724', 'perm_workcase_ticket_process', '工单处理', 'workcase:ticket:process', '工单处理过程管理', 'module_workcase', true, 'system', NULL, now(), false),
|
||||
('PERM-0725', 'perm_workcase_ticket_device', '工单设备', 'workcase:ticket:device', '工单设备管理', 'module_workcase', true, 'system', NULL, now(), false);
|
||||
-- =============================
|
||||
-- 5. 初始化视图(菜单)
|
||||
-- =============================
|
||||
@@ -224,6 +247,9 @@ INSERT INTO sys.tb_sys_view (
|
||||
('VIEW-W001', 'view_workcase_home', '智能客服', NULL, '/aichat', 'public/AIChat/AIChatView.vue', 'Home', 1,
|
||||
'route', NULL, 'workcase', 'SubSidebarLayout', 10, '智能客服首页', 'system', now(), false),
|
||||
|
||||
('VIEW-W002', 'view_workcase_chatroom', '聊天室控制台', NULL, '/chatroom', 'public/ChatRoom/ChatRoomView.vue', 'MessageSquare', 1,
|
||||
'route', NULL, 'workcase', 'SubSidebarLayout', 20, '实时聊天室控制台', 'system', now(), false),
|
||||
|
||||
-- 管理端视图(使用 SubSidebarLayout 布局)
|
||||
('VIEW-W101', 'view_workcase_admin_overview', '数据概览', NULL, '/admin/overview', 'admin/overview/OverviewView.vue', 'BarChart3', 1,
|
||||
'route', NULL, 'workcase', 'SubSidebarLayout', 110, '泰豪小电数据概览', 'system', now(), false),
|
||||
@@ -308,17 +334,39 @@ INSERT INTO sys.tb_sys_role_permission (
|
||||
('RP-U-0015', 'role_user', 'perm_message_view', 'system', NULL, now(), false),
|
||||
('RP-U-0016', 'role_user', 'perm_config_view', 'system', NULL, now(), false);
|
||||
|
||||
-- 访客权限(仅查看 + 基础菜单访问)
|
||||
-- 访客权限(基础菜单 + workcase聊天和工单全部接口权限)
|
||||
INSERT INTO sys.tb_sys_role_permission (
|
||||
optsn, role_id, permission_id, creator, dept_path, create_time, deleted
|
||||
) VALUES
|
||||
-- 平台基础菜单访问权限
|
||||
('RP-G-0001', 'role_guest', 'perm_platform_home', 'system', NULL, now(), false),
|
||||
('RP-G-0002', 'role_guest', 'perm_platform_chat', 'system', NULL, now(), false),
|
||||
('RP-G-0003', 'role_guest', 'perm_platform_workcase', 'system', NULL, now(), false),
|
||||
-- 系统功能权限(仅查看)
|
||||
('RP-G-0011', 'role_guest', 'perm_user_view', 'system', NULL, now(), false),
|
||||
('RP-G-0012', 'role_guest', 'perm_file_view', 'system', NULL, now(), false),
|
||||
('RP-G-0013', 'role_guest', 'perm_message_view', 'system', NULL, now(), false);
|
||||
('RP-G-0013', 'role_guest', 'perm_message_view', 'system', NULL, now(), false),
|
||||
-- Workcase AI对话接口权限
|
||||
('RP-G-0021', 'role_guest', 'perm_workcase_chat_create', 'system', NULL, now(), false),
|
||||
('RP-G-0022', 'role_guest', 'perm_workcase_chat_update', 'system', NULL, now(), false),
|
||||
('RP-G-0023', 'role_guest', 'perm_workcase_chat_list', 'system', NULL, now(), false),
|
||||
('RP-G-0024', 'role_guest', 'perm_workcase_chat_message', 'system', NULL, now(), false),
|
||||
('RP-G-0025', 'role_guest', 'perm_workcase_chat_stream', 'system', NULL, now(), false),
|
||||
('RP-G-0026', 'role_guest', 'perm_workcase_chat_analyze', 'system', NULL, now(), false),
|
||||
-- Workcase 聊天室接口权限
|
||||
('RP-G-0031', 'role_guest', 'perm_workcase_room_create', 'system', NULL, now(), false),
|
||||
('RP-G-0032', 'role_guest', 'perm_workcase_room_update', 'system', NULL, now(), false),
|
||||
('RP-G-0033', 'role_guest', 'perm_workcase_room_close', 'system', NULL, now(), false),
|
||||
('RP-G-0034', 'role_guest', 'perm_workcase_room_view', 'system', NULL, now(), false),
|
||||
('RP-G-0035', 'role_guest', 'perm_workcase_room_member', 'system', NULL, now(), false),
|
||||
('RP-G-0036', 'role_guest', 'perm_workcase_room_message', 'system', NULL, now(), false),
|
||||
('RP-G-0037', 'role_guest', 'perm_workcase_chatroom', 'system', NULL, now(), false),
|
||||
-- Workcase 工单接口权限
|
||||
('RP-G-0041', 'role_guest', 'perm_workcase_ticket_create', 'system', NULL, now(), false),
|
||||
('RP-G-0042', 'role_guest', 'perm_workcase_ticket_update', 'system', NULL, now(), false),
|
||||
('RP-G-0043', 'role_guest', 'perm_workcase_ticket_view', 'system', NULL, now(), false),
|
||||
('RP-G-0044', 'role_guest', 'perm_workcase_ticket_process', 'system', NULL, now(), false),
|
||||
('RP-G-0045', 'role_guest', 'perm_workcase_ticket_device', 'system', NULL, now(), false);
|
||||
|
||||
-- =============================
|
||||
-- 7. 视图权限关联
|
||||
@@ -345,10 +393,9 @@ INSERT INTO sys.tb_sys_view_permission (
|
||||
('VP-P203', 'view_platform_admin_knowledge', 'perm_platform_admin_knowledge', 'system', NULL, now(), false),
|
||||
('VP-P204', 'view_platform_admin_config', 'perm_platform_admin_config', 'system', NULL, now(), false),
|
||||
|
||||
-- Workcase服务用户端视图关联(使用同一个workcase访问权限)
|
||||
-- Workcase服务用户端视图关联
|
||||
('VP-W001', 'view_workcase_home', 'perm_platform_workcase', 'system', NULL, now(), false),
|
||||
('VP-W002', 'view_workcase_list', 'perm_platform_workcase', 'system', NULL, now(), false),
|
||||
('VP-W003', 'view_workcase_detail', 'perm_platform_workcase', 'system', NULL, now(), false),
|
||||
('VP-W002', 'view_workcase_chatroom', 'perm_workcase_chatroom', 'system', NULL, now(), false),
|
||||
|
||||
-- Workcase服务管理端视图关联
|
||||
('VP-W101', 'view_workcase_admin_overview', 'perm_workcase_overview', 'system', NULL, now(), false),
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
-- 初始化聊天室人员
|
||||
-- user_admin
|
||||
INSERT INTO workcase.tb_chat_room_member(
|
||||
optsn, member_id, room_id, user_id, user_type, user_name, status, unread_count, last_read_time, last_read_msg_id, join_time, leave_time, creator, create_time, update_time
|
||||
) VALUES
|
||||
('MEM-0001', 'member_admin', 'room_0001', 'user_admin', 'staff', '系统管理员', 'active', 0, null, null, now(), null, 'system', now(), null);
|
||||
@@ -104,6 +104,11 @@
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp-sse</artifactId>
|
||||
</dependency>
|
||||
<!-- SpringDoc OpenAPI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -82,6 +82,35 @@
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jakarta Servlet API (用于 HttpServletRequest 等) -->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Web (用于 MultipartFile 等) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring WebMVC (用于 SseEmitter 等) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger/OpenAPI 注解 (用于 @Schema 等) -->
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations-jakarta</artifactId>
|
||||
<version>2.2.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -59,6 +59,24 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringDoc OpenAPI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -100,14 +100,13 @@ public class GatewayAuthConfig {
|
||||
List<GrantedAuthority> authorities = new ArrayList<>();
|
||||
|
||||
try {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
logger.info("Authorization header: {}", authHeader != null ? "Bearer ***" : "null");
|
||||
String token = extractToken(request);
|
||||
logger.debug("提取到Token: {}", token != null ? "***" : "null");
|
||||
|
||||
if (StringUtils.hasText(authHeader) && authHeader.startsWith(BEARER_PREFIX)) {
|
||||
String token = authHeader.substring(BEARER_PREFIX.length());
|
||||
if (StringUtils.hasText(token)) {
|
||||
String cacheKey = LOGIN_TOKEN_PREFIX + token;
|
||||
LoginDomain login = redisService.get(cacheKey, LoginDomain.class);
|
||||
logger.info("Redis key: {}, login: {}", cacheKey, login != null ? "loaded" : "null");
|
||||
logger.debug("Redis key: {}, login: {}", cacheKey, login != null ? "loaded" : "null");
|
||||
|
||||
if (login != null) {
|
||||
if (login.getUserPermissions() != null) {
|
||||
@@ -118,7 +117,7 @@ public class GatewayAuthConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info("加载用户权限: {} 个", authorities.size());
|
||||
logger.debug("加载用户权限: {} 个", authorities.size());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -127,5 +126,25 @@ public class GatewayAuthConfig {
|
||||
|
||||
return authorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头或URL参数提取Token
|
||||
*/
|
||||
private String extractToken(HttpServletRequest request) {
|
||||
// 1. 优先从请求头获取
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (StringUtils.hasText(authHeader) && authHeader.startsWith(BEARER_PREFIX)) {
|
||||
return authHeader.substring(BEARER_PREFIX.length()).trim();
|
||||
}
|
||||
|
||||
// 2. 从URL参数获取(用于WebSocket连接)
|
||||
String tokenParam = request.getParameter("token");
|
||||
if (StringUtils.hasText(tokenParam)) {
|
||||
logger.debug("从URL参数获取Token");
|
||||
return tokenParam.trim();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
// 从Redis加载 LoginDomain,并将权限装配到 Spring Security 上下文
|
||||
if (redisService != null) {
|
||||
Object obj = redisService.get(REDIS_LOGIN_PREFIX + userId);
|
||||
Object obj = redisService.get(REDIS_LOGIN_PREFIX + token);
|
||||
if (obj instanceof LoginDomain loginDomain) {
|
||||
// 组装权限码 authorities(已存在)
|
||||
List<SimpleGrantedAuthority> permAuthorities = null;
|
||||
|
||||
@@ -35,6 +35,14 @@
|
||||
<artifactId>common-utils</artifactId>
|
||||
<version>${urban-lifeline.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger/OpenAPI 注解 (用于 @Schema 等) -->
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations-jakarta</artifactId>
|
||||
<version>2.2.36</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -23,4 +23,10 @@ public class TbSysUserRoleDTO extends BaseDTO {
|
||||
|
||||
@Schema(description = "角色ID")
|
||||
private String roleId;
|
||||
|
||||
@Schema(description = "部门ID")
|
||||
private String deptId;
|
||||
|
||||
@Schema(description = "部门全路径")
|
||||
private String deptPath;
|
||||
}
|
||||
@@ -19,6 +19,25 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Jakarta Servlet API (用于 ServletUtils) -->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Web (用于 FastJsonConfiguration) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache POI for Excel -->
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.xyzh.common.utils.config;
|
||||
|
||||
import com.alibaba.fastjson2.support.config.FastJsonConfig;
|
||||
import com.alibaba.fastjson2.support.spring6.http.converter.FastJsonHttpMessageConverter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
* @since 2025-11-28
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(WebMvcConfigurer.class)
|
||||
public class FastJsonConfiguration implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,4 +79,24 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- Jakarta Servlet API (用于 ServletUtils) -->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Web (用于 FastJsonConfiguration) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -127,6 +127,12 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringDoc OpenAPI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -18,6 +18,89 @@
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<!-- Gateway 是 WebFlux 应用,必须排除所有 Servlet/Tomcat 相关依赖 -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- 覆盖父 pom 继承的 spring-boot-starter-web,设为 test scope 使其不参与编译和运行 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- 覆盖父 pom 继承的 spring-boot-starter-security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- 覆盖父 pom 继承的 springdoc -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- 排除 Tomcat -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
<version>10.1.48</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-el</artifactId>
|
||||
<version>10.1.48</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-websocket</artifactId>
|
||||
<version>10.1.48</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- 排除 spring-webmvc -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<version>${spring-framework.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Cloud Gateway -->
|
||||
<dependency>
|
||||
@@ -81,24 +164,81 @@
|
||||
<dependency>
|
||||
<groupId>org.xyzh.common</groupId>
|
||||
<artifactId>common-auth</artifactId>
|
||||
<exclusions>
|
||||
<!-- 排除Tomcat,Gateway必须使用Netty -->
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis(用于Token验证、限流等) -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh.common</groupId>
|
||||
<artifactId>common-redis</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类 -->
|
||||
<dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>org.xyzh.common</groupId>
|
||||
<artifactId>common-utils</artifactId>
|
||||
</dependency>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency> -->
|
||||
|
||||
<!-- 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>org.xyzh.common</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
<artifactId>tomcat-embed-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Actuator(健康检查) -->
|
||||
@@ -138,6 +278,30 @@
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Gateway 需要 WebFlux 版本的 Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Servlet API (provided - 仅用于编译 common-auth 中的类,运行时不需要) -->
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring WebMVC (provided - 仅用于编译 common-auth/common-utils 中的类) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -6,9 +6,6 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.xyzh.common.auth.config.SecurityConfig;
|
||||
import org.xyzh.common.auth.config.WebMvcConfig;
|
||||
import org.xyzh.common.auth.config.GatewayAuthConfig;
|
||||
|
||||
/**
|
||||
* @description Gateway 网关启动类
|
||||
@@ -25,11 +22,12 @@ import org.xyzh.common.auth.config.GatewayAuthConfig;
|
||||
"org.xyzh.common" // 公共模块(包括 common-auth)
|
||||
},
|
||||
excludeFilters = {
|
||||
// 排除 Spring MVC 相关配置,Gateway 使用 WebFlux
|
||||
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
|
||||
SecurityConfig.class, // Spring MVC Security配置
|
||||
WebMvcConfig.class, // Spring MVC配置
|
||||
GatewayAuthConfig.class // 微服务Gateway模式配置(使用Servlet Filter)
|
||||
// 使用正则表达式排除 Spring MVC 相关配置,避免加载 WebMvcConfigurer 类
|
||||
@ComponentScan.Filter(type = FilterType.REGEX, pattern = {
|
||||
"org\\.xyzh\\.common\\.auth\\.config\\.SecurityConfig",
|
||||
"org\\.xyzh\\.common\\.auth\\.config\\.WebMvcConfig",
|
||||
"org\\.xyzh\\.common\\.auth\\.config\\.GatewayAuthConfig",
|
||||
"org\\.xyzh\\.common\\.utils\\.config\\.FastJsonConfiguration"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
@@ -131,27 +131,32 @@ public class AuthGlobalFilter implements GlobalFilter, Ordered {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头中提取 Token
|
||||
* 从请求头或URL参数中提取 Token
|
||||
*/
|
||||
private String extractToken(ServerHttpRequest request) {
|
||||
// 1. 优先从请求头获取
|
||||
List<String> headers = request.getHeaders().get(authProperties.getTokenHeader());
|
||||
if (headers == null || headers.isEmpty()) {
|
||||
return null;
|
||||
if (headers != null && !headers.isEmpty()) {
|
||||
String header = headers.get(0);
|
||||
if (StringUtils.hasText(header)) {
|
||||
// 支持 Bearer 前缀
|
||||
String prefix = authProperties.getTokenPrefix();
|
||||
if (StringUtils.hasText(prefix) && header.startsWith(prefix)) {
|
||||
return header.substring(prefix.length()).trim();
|
||||
}
|
||||
// 也支持直接传递 Token(不带前缀)
|
||||
return header.trim();
|
||||
}
|
||||
}
|
||||
|
||||
String header = headers.get(0);
|
||||
if (!StringUtils.hasText(header)) {
|
||||
return null;
|
||||
// 2. 从URL参数获取(用于WebSocket连接)
|
||||
String tokenParam = request.getQueryParams().getFirst("token");
|
||||
if (StringUtils.hasText(tokenParam)) {
|
||||
log.debug("从URL参数获取Token");
|
||||
return tokenParam.trim();
|
||||
}
|
||||
|
||||
// 支持 Bearer 前缀
|
||||
String prefix = authProperties.getTokenPrefix();
|
||||
if (StringUtils.hasText(prefix) && header.startsWith(prefix)) {
|
||||
return header.substring(prefix.length()).trim();
|
||||
}
|
||||
|
||||
// 也支持直接传递 Token(不带前缀)
|
||||
return header.trim();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -92,6 +92,14 @@ spring:
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# ==================== 工单服务 WebSocket 路由 ====================
|
||||
- id: workcase-websocket
|
||||
uri: lb:ws://workcase-service
|
||||
predicates:
|
||||
- Path=/urban-lifeline/workcase/ws/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
|
||||
# ==================== 工单服务路由 ====================
|
||||
- id: workcase-service
|
||||
uri: lb://workcase-service
|
||||
|
||||
@@ -101,6 +101,11 @@
|
||||
<artifactId>mybatis-spring</artifactId>
|
||||
<version>${mybatis.spring.version}</version>
|
||||
</dependency>
|
||||
<!-- SpringDoc OpenAPI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -59,6 +59,22 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- Spring Boot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- SpringDoc OpenAPI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -434,41 +434,13 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<!-- Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Log4j2 日志(统一配置,所有子模块继承) -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringDoc OpenAPI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- dubbo依赖管理 -->
|
||||
<dependency>
|
||||
|
||||
@@ -85,6 +85,24 @@
|
||||
<artifactId>mybatis-spring</artifactId>
|
||||
<version>${mybatis.spring.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringDoc OpenAPI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,6 +1,11 @@
|
||||
package org.xyzh.system.controller;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -15,15 +20,22 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.xyzh.api.auth.service.AuthService;
|
||||
import org.xyzh.api.system.service.GuestService;
|
||||
import org.xyzh.api.system.service.ModulePermissionService;
|
||||
import org.xyzh.api.system.vo.PermissionVO;
|
||||
import org.xyzh.common.core.domain.LoginDomain;
|
||||
import org.xyzh.common.core.domain.LoginParam;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.page.PageRequest;
|
||||
import org.xyzh.common.dto.sys.TbGuestDTO;
|
||||
import org.xyzh.common.dto.sys.TbSysPermissionDTO;
|
||||
import org.xyzh.common.dto.sys.TbSysUserDTO;
|
||||
import org.xyzh.common.dto.sys.TbSysUserInfoDTO;
|
||||
import org.xyzh.common.dto.sys.TbSysUserRoleDTO;
|
||||
import org.xyzh.common.dto.sys.TbSysViewDTO;
|
||||
import org.xyzh.common.utils.id.IdUtil;
|
||||
import org.xyzh.common.utils.validation.ValidationUtils;
|
||||
import org.xyzh.common.auth.utils.JwtTokenUtil;
|
||||
import org.xyzh.common.redis.service.RedisService;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -48,6 +60,15 @@ public class GuestController {
|
||||
@DubboReference(version = "1.0.0", group = "auth", timeout = 5000, check = false, retries = 0)
|
||||
private AuthService authService;
|
||||
|
||||
@DubboReference(version = "1.0.0", group = "system", timeout = 5000, check = false, retries = 0)
|
||||
private ModulePermissionService modulePermissionService;
|
||||
|
||||
@Autowired
|
||||
private JwtTokenUtil jwtTokenUtil;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
|
||||
@PostMapping
|
||||
public ResultDomain<TbGuestDTO> createGuest(TbGuestDTO guest) {
|
||||
@@ -191,8 +212,53 @@ public class GuestController {
|
||||
userInfoDTO.setUsername(guest.getName());
|
||||
loginDomain.setUserInfo(userInfoDTO);
|
||||
|
||||
// 设置角色信息
|
||||
List<TbSysUserRoleDTO> userRoles = new ArrayList<>();
|
||||
TbSysUserRoleDTO userRole = new TbSysUserRoleDTO();
|
||||
userRole.setUserId(guest.getUserId());
|
||||
userRole.setRoleId("role_guest");
|
||||
userRole.setDeptId("dept_root");
|
||||
userRoles.add(userRole);
|
||||
loginDomain.setUserRoles(userRoles);
|
||||
|
||||
// 获取用户权限信息
|
||||
List<TbSysPermissionDTO> userPermissions = new ArrayList<>();
|
||||
List<TbSysViewDTO> userViews = new ArrayList<>();
|
||||
|
||||
ResultDomain<PermissionVO> permissionsResult = modulePermissionService.getUserPermissions(guest.getUserId());
|
||||
if (permissionsResult.getSuccess() && permissionsResult.getDataList() != null) {
|
||||
for (PermissionVO permission : permissionsResult.getDataList()) {
|
||||
if (permission.getPermissionId() != null) {
|
||||
TbSysPermissionDTO permissionDTO = PermissionVO.toPermissionDTO(permission);
|
||||
if (permissionDTO != null) {
|
||||
userPermissions.add(permissionDTO);
|
||||
}
|
||||
}
|
||||
if (permission.getViewId() != null) {
|
||||
TbSysViewDTO viewDTO = PermissionVO.toViewDTO(permission);
|
||||
if (viewDTO != null) {
|
||||
userViews.add(viewDTO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loginDomain.setUserPermissions(userPermissions);
|
||||
loginDomain.setUserViews(userViews);
|
||||
|
||||
loginDomain.setLoginType("wechat_miniprogram");
|
||||
|
||||
// 生成 JWT Token
|
||||
String token = jwtTokenUtil.generateToken(loginDomain);
|
||||
loginDomain.setToken(token);
|
||||
|
||||
// 将登录信息存储到 Redis(24小时有效期)
|
||||
String loginKey = "login:token:" + token;
|
||||
redisService.set(loginKey, JSON.toJSONString(loginDomain), 24, TimeUnit.HOURS);
|
||||
|
||||
// 存储用户登录状态
|
||||
String userLoginKey = "login:user:" + guest.getUserId();
|
||||
redisService.set(userLoginKey, token, 24, TimeUnit.HOURS);
|
||||
|
||||
return loginDomain;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,11 @@ import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.page.PageDomain;
|
||||
import org.xyzh.common.core.page.PageRequest;
|
||||
import org.xyzh.common.dto.sys.TbGuestDTO;
|
||||
import org.xyzh.common.dto.sys.TbSysUserRoleDTO;
|
||||
import org.xyzh.system.mapper.user.TbGuestMapper;
|
||||
import org.xyzh.system.mapper.user.TbSysUserRoleMapper;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
@@ -34,10 +38,22 @@ public class GuestServiceImpl implements GuestService{
|
||||
@Autowired
|
||||
private TbGuestMapper guestMapper;
|
||||
|
||||
@Autowired
|
||||
private TbSysUserRoleMapper userRoleMapper;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public ResultDomain<TbGuestDTO> createGuest(TbGuestDTO guest) {
|
||||
guestMapper.insertGuest(guest);
|
||||
|
||||
// 绑定访客角色(role_guest)
|
||||
TbSysUserRoleDTO userRole = new TbSysUserRoleDTO();
|
||||
userRole.setUserId(guest.getUserId());
|
||||
userRole.setRoleId("role_guest");
|
||||
userRole.setDeptId("dept_root");
|
||||
userRole.setCreateTime(new Date());
|
||||
userRoleMapper.insertUserRole(userRole);
|
||||
|
||||
return ResultDomain.success("创建成功",guest);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,35 +4,34 @@ server:
|
||||
servlet:
|
||||
context-path: /urban-lifeline/system
|
||||
# ================== Auth ====================
|
||||
urban-lifeline:
|
||||
auth:
|
||||
enabled: true
|
||||
auth:
|
||||
enabled: true
|
||||
gateway-mode: true
|
||||
# 认证接口:可以按服务自定义
|
||||
login-path: /urban-lifeline/auth/login
|
||||
logout-path: /urban-lifeline/auth/logout
|
||||
captcha-path: /urban-lifeline/auth/captcha
|
||||
refresh-path: /urban-lifeline/auth/refresh
|
||||
|
||||
# 认证接口:可以按服务自定义
|
||||
login-path: /urban-lifeline/auth/login
|
||||
logout-path: /urban-lifeline/auth/logout
|
||||
captcha-path: /urban-lifeline/auth/captcha
|
||||
refresh-path: /urban-lifeline/auth/refresh
|
||||
# 通用白名单(非认证接口)
|
||||
whitelist:
|
||||
# Swagger/OpenAPI 文档相关(建议不带 context-path)
|
||||
- /swagger-ui/**
|
||||
- /swagger-ui.html
|
||||
- /v3/api-docs/**
|
||||
- /webjars/**
|
||||
|
||||
# 通用白名单(非认证接口)
|
||||
whitelist:
|
||||
# Swagger/OpenAPI 文档相关(建议不带 context-path)
|
||||
- /swagger-ui/**
|
||||
- /swagger-ui.html
|
||||
- /v3/api-docs/**
|
||||
- /webjars/**
|
||||
# 静态资源
|
||||
- /favicon.ico
|
||||
- /error
|
||||
|
||||
# 静态资源
|
||||
- /favicon.ico
|
||||
- /error
|
||||
|
||||
# 健康检查
|
||||
- /actuator/health
|
||||
- /actuator/info
|
||||
|
||||
# 其他需要放行的路径
|
||||
# - /public/**
|
||||
# - /api/public/**
|
||||
# 健康检查
|
||||
- /actuator/health
|
||||
- /actuator/info
|
||||
|
||||
# 其他需要放行的路径
|
||||
# - /public/**
|
||||
# - /api/public/**
|
||||
|
||||
|
||||
# ================== Security ==================
|
||||
|
||||
@@ -4,35 +4,35 @@ server:
|
||||
# servlet:
|
||||
# context-path: /urban-lifeline/system # 微服务架构下,context-path由Gateway管理
|
||||
# ================== Auth ====================
|
||||
urban-lifeline:
|
||||
auth:
|
||||
enabled: true
|
||||
|
||||
# 认证接口:可以按服务自定义
|
||||
login-path: /urban-lifeline/auth/login
|
||||
logout-path: /urban-lifeline/auth/logout
|
||||
captcha-path: /urban-lifeline/auth/captcha
|
||||
refresh-path: /urban-lifeline/auth/refresh
|
||||
auth:
|
||||
enabled: true
|
||||
gateway-mode: true
|
||||
# 认证接口:可以按服务自定义
|
||||
login-path: /urban-lifeline/auth/login
|
||||
logout-path: /urban-lifeline/auth/logout
|
||||
captcha-path: /urban-lifeline/auth/captcha
|
||||
refresh-path: /urban-lifeline/auth/refresh
|
||||
|
||||
# 通用白名单(非认证接口)
|
||||
whitelist:
|
||||
# Swagger/OpenAPI 文档相关(建议不带 context-path)
|
||||
- /swagger-ui/**
|
||||
- /swagger-ui.html
|
||||
- /v3/api-docs/**
|
||||
- /webjars/**
|
||||
# 通用白名单(非认证接口)
|
||||
whitelist:
|
||||
# Swagger/OpenAPI 文档相关(建议不带 context-path)
|
||||
- /swagger-ui/**
|
||||
- /swagger-ui.html
|
||||
- /v3/api-docs/**
|
||||
- /webjars/**
|
||||
|
||||
# 静态资源
|
||||
- /favicon.ico
|
||||
- /error
|
||||
# 静态资源
|
||||
- /favicon.ico
|
||||
- /error
|
||||
|
||||
# 健康检查
|
||||
- /actuator/health
|
||||
- /actuator/info
|
||||
|
||||
# 其他需要放行的路径
|
||||
# - /public/**
|
||||
# - /api/public/**
|
||||
# 健康检查
|
||||
- /actuator/health
|
||||
- /actuator/info
|
||||
|
||||
# 其他需要放行的路径
|
||||
# - /public/**
|
||||
# - /api/public/**
|
||||
|
||||
security:
|
||||
aes:
|
||||
|
||||
@@ -116,6 +116,12 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringDoc OpenAPI -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,110 @@
|
||||
package org.xyzh.workcase.config;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.server.HandshakeInterceptor;
|
||||
import org.xyzh.common.auth.contants.AuthContants;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* WebSocket握手拦截器
|
||||
* 从网关传递的请求头中获取已验证的用户信息
|
||||
*/
|
||||
@Component
|
||||
public class WebSocketAuthInterceptor implements HandshakeInterceptor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WebSocketAuthInterceptor.class);
|
||||
|
||||
@Override
|
||||
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
|
||||
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
|
||||
|
||||
log.info("WebSocket握手开始,URI: {}", request.getURI());
|
||||
|
||||
try {
|
||||
// 从网关传递的请求头中获取用户ID(网关已完成token验证)
|
||||
String userId = extractUserIdFromRequest(request);
|
||||
|
||||
if (!StringUtils.hasText(userId)) {
|
||||
log.warn("WebSocket握手失败:请求头中未找到用户ID,网关认证可能失败");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 可选:也获取token,供后续使用
|
||||
String token = extractTokenFromRequest(request);
|
||||
|
||||
// 将用户信息存储到WebSocket会话属性中,供后续使用
|
||||
attributes.put("userId", userId);
|
||||
if (StringUtils.hasText(token)) {
|
||||
attributes.put("token", token);
|
||||
}
|
||||
|
||||
log.info("WebSocket握手成功,userId: {}", userId);
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("WebSocket握手异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
|
||||
WebSocketHandler wsHandler, Exception exception) {
|
||||
if (exception != null) {
|
||||
log.error("WebSocket握手后发生异常", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从网关传递的请求头中提取用户ID
|
||||
*/
|
||||
private String extractUserIdFromRequest(ServerHttpRequest request) {
|
||||
if (request instanceof ServletServerHttpRequest) {
|
||||
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
|
||||
|
||||
// 从网关传递的请求头获取(标准微服务架构方式)
|
||||
String userId = servletRequest.getHeaders().getFirst(AuthContants.USER_ID_ATTRIBUTE);
|
||||
if (StringUtils.hasText(userId)) {
|
||||
log.debug("从网关请求头获取用户ID: {}", userId);
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("未能从请求头中获取用户ID");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求头中提取token(可选)
|
||||
*/
|
||||
private String extractTokenFromRequest(ServerHttpRequest request) {
|
||||
if (request instanceof ServletServerHttpRequest) {
|
||||
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
|
||||
|
||||
// 从网关传递的请求头获取
|
||||
String token = servletRequest.getHeaders().getFirst(AuthContants.TOKEN_ATTRIBUTE);
|
||||
if (StringUtils.hasText(token)) {
|
||||
return token;
|
||||
}
|
||||
|
||||
// 也支持从Authorization头获取
|
||||
String authHeader = servletRequest.getHeaders().getFirst("Authorization");
|
||||
if (StringUtils.hasText(authHeader)) {
|
||||
if (authHeader.startsWith("Bearer ")) {
|
||||
return authHeader.substring(7);
|
||||
}
|
||||
return authHeader;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.xyzh.workcase.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
@@ -14,6 +15,9 @@ import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerCo
|
||||
@EnableWebSocketMessageBroker
|
||||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Autowired
|
||||
private WebSocketAuthInterceptor webSocketAuthInterceptor;
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry config) {
|
||||
// 配置消息代理
|
||||
@@ -29,9 +33,15 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
// 注册STOMP端点
|
||||
registry.addEndpoint("/ws/chat")
|
||||
.setAllowedOriginPatterns("*") // 允许跨域
|
||||
.withSockJS(); // 支持SockJS降级方案
|
||||
// 原生WebSocket端点(不使用SockJS)
|
||||
registry.addEndpoint("/workcase/ws/chat")
|
||||
.setAllowedOriginPatterns("*")
|
||||
.addInterceptors(webSocketAuthInterceptor);
|
||||
|
||||
// SockJS端点(用于不支持WebSocket的浏览器降级)
|
||||
registry.addEndpoint("/workcase/ws/chat-sockjs")
|
||||
.setAllowedOriginPatterns("*")
|
||||
.addInterceptors(webSocketAuthInterceptor)
|
||||
.withSockJS();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.xyzh.workcase.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -13,9 +15,18 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
import org.xyzh.api.ai.dto.ChatPrepareData;
|
||||
import org.xyzh.api.ai.dto.TbChat;
|
||||
import org.xyzh.api.ai.dto.TbChatMessage;
|
||||
import org.xyzh.api.workcase.dto.TbChatRoomDTO;
|
||||
import org.xyzh.api.workcase.dto.TbChatRoomMemberDTO;
|
||||
import org.xyzh.api.workcase.dto.TbChatRoomMessageDTO;
|
||||
import org.xyzh.api.workcase.dto.TbCustomerServiceDTO;
|
||||
import org.xyzh.api.workcase.dto.TbWordCloudDTO;
|
||||
import org.xyzh.api.workcase.dto.TbWorkcaseDTO;
|
||||
import org.xyzh.api.workcase.service.ChatRoomService;
|
||||
import org.xyzh.api.workcase.service.WorkcaseChatService;
|
||||
import org.xyzh.api.workcase.vo.ChatMemberVO;
|
||||
import org.xyzh.api.workcase.vo.ChatRoomMessageVO;
|
||||
import org.xyzh.api.workcase.vo.ChatRoomVO;
|
||||
import org.xyzh.api.workcase.vo.CustomerServiceVO;
|
||||
import org.xyzh.common.core.domain.ResultDomain;
|
||||
import org.xyzh.common.core.page.PageRequest;
|
||||
import org.xyzh.common.utils.validation.ValidationResult;
|
||||
@@ -28,7 +39,8 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* @description 工单对话控制器
|
||||
* - AI对话管理
|
||||
* - AI对话管理(智能问答)
|
||||
* - ChatRoom聊天室管理(实时IM)
|
||||
* - 微信客服消息接收
|
||||
* - 词云管理
|
||||
* @filename WorkcaseChatController.java
|
||||
@@ -44,9 +56,13 @@ public class WorkcaseChatContorller {
|
||||
@Autowired
|
||||
private WorkcaseChatService workcaseChatService;
|
||||
|
||||
@Autowired
|
||||
private ChatRoomService chatRoomService;
|
||||
|
||||
// ========================= AI对话管理 =========================
|
||||
|
||||
@Operation(summary = "创建对话")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:create')")
|
||||
@PostMapping
|
||||
public ResultDomain<TbChat> createChat(@RequestBody TbChat chat) {
|
||||
ValidationResult vr = ValidationUtils.validate(chat, Arrays.asList(
|
||||
@@ -59,6 +75,7 @@ public class WorkcaseChatContorller {
|
||||
}
|
||||
|
||||
@Operation(summary = "更新对话")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:update')")
|
||||
@PutMapping
|
||||
public ResultDomain<TbChat> updateChat(@RequestBody TbChat chat) {
|
||||
ValidationResult vr = ValidationUtils.validate(chat, Arrays.asList(
|
||||
@@ -71,12 +88,14 @@ public class WorkcaseChatContorller {
|
||||
}
|
||||
|
||||
@Operation(summary = "查询对话列表")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:list')")
|
||||
@PostMapping("/list")
|
||||
public ResultDomain<TbChat> getChatList(@RequestBody TbChat filter) {
|
||||
return workcaseChatService.getChatList(filter);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取对话消息列表")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:message')")
|
||||
@PostMapping("/message/list")
|
||||
public ResultDomain<TbChatMessage> getChatMessageList(@RequestBody TbChat filter) {
|
||||
ValidationResult vr = ValidationUtils.validate(filter, Arrays.asList(
|
||||
@@ -89,6 +108,7 @@ public class WorkcaseChatContorller {
|
||||
}
|
||||
|
||||
@Operation(summary = "准备对话会话")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:stream')")
|
||||
@PostMapping("/prepare")
|
||||
public ResultDomain<String> prepareChatMessageSession(@RequestBody ChatPrepareData prepareData) {
|
||||
ValidationResult vr = ValidationUtils.validate(prepareData, Arrays.asList(
|
||||
@@ -102,18 +122,21 @@ public class WorkcaseChatContorller {
|
||||
}
|
||||
|
||||
@Operation(summary = "流式对话(SSE)")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:stream')")
|
||||
@GetMapping(value = "/stream/{sessionId}", produces = "text/event-stream")
|
||||
public SseEmitter streamChatMessage(@PathVariable String sessionId) {
|
||||
return workcaseChatService.streamChatMessageWithSse(sessionId);
|
||||
}
|
||||
|
||||
@Operation(summary = "停止对话")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:stream')")
|
||||
@PostMapping("/stop/{taskId}")
|
||||
public ResultDomain<Boolean> stopChat(@RequestBody TbChat filter, @PathVariable String taskId) {
|
||||
return workcaseChatService.stopChatMessageByTaskId(filter, taskId);
|
||||
}
|
||||
|
||||
@Operation(summary = "评论对话消息")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:message')")
|
||||
@PostMapping("/comment")
|
||||
public ResultDomain<Boolean> commentChatMessage(@RequestBody TbChat filter,
|
||||
@RequestParam String messageId, @RequestParam String comment) {
|
||||
@@ -123,43 +146,233 @@ public class WorkcaseChatContorller {
|
||||
// ========================= 对话分析 =========================
|
||||
|
||||
@Operation(summary = "分析对话(AI预填工单信息)")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:analyze')")
|
||||
@GetMapping("/analyze/{chatId}")
|
||||
public ResultDomain<TbWorkcaseDTO> analyzeChat(@PathVariable String chatId) {
|
||||
return workcaseChatService.analyzeChat(chatId);
|
||||
}
|
||||
|
||||
@Operation(summary = "总结对话")
|
||||
@PreAuthorize("hasAuthority('workcase:chat:analyze')")
|
||||
@PostMapping("/summary/{chatId}")
|
||||
public ResultDomain<TbWorkcaseDTO> summaryChat(@PathVariable String chatId) {
|
||||
return workcaseChatService.summaryChat(chatId);
|
||||
}
|
||||
|
||||
// ========================= ChatRoom聊天室管理(实时IM) =========================
|
||||
|
||||
@Operation(summary = "创建聊天室")
|
||||
@PreAuthorize("hasAuthority('workcase:room:create')")
|
||||
@PostMapping("/room")
|
||||
public ResultDomain<TbChatRoomDTO> createChatRoom(@RequestBody TbChatRoomDTO chatRoom) {
|
||||
ValidationResult vr = ValidationUtils.validate(chatRoom, Arrays.asList(
|
||||
ValidationUtils.requiredString("workcaseId", "工单ID"),
|
||||
ValidationUtils.requiredString("guestId", "来客ID")
|
||||
));
|
||||
if (!vr.isValid()) {
|
||||
return ResultDomain.failure(vr.getAllErrors());
|
||||
}
|
||||
return chatRoomService.createChatRoom(chatRoom);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新聊天室")
|
||||
@PreAuthorize("hasAuthority('workcase:room:update')")
|
||||
@PutMapping("/room")
|
||||
public ResultDomain<TbChatRoomDTO> updateChatRoom(@RequestBody TbChatRoomDTO chatRoom) {
|
||||
ValidationResult vr = ValidationUtils.validate(chatRoom, Arrays.asList(
|
||||
ValidationUtils.requiredString("roomId", "聊天室ID")
|
||||
));
|
||||
if (!vr.isValid()) {
|
||||
return ResultDomain.failure(vr.getAllErrors());
|
||||
}
|
||||
return chatRoomService.updateChatRoom(chatRoom);
|
||||
}
|
||||
|
||||
@Operation(summary = "关闭聊天室")
|
||||
@PreAuthorize("hasAuthority('workcase:room:close')")
|
||||
@PostMapping("/room/{roomId}/close")
|
||||
public ResultDomain<Boolean> closeChatRoom(@PathVariable String roomId, @RequestParam String closedBy) {
|
||||
return chatRoomService.closeChatRoom(roomId, closedBy);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取聊天室详情")
|
||||
@PreAuthorize("hasAuthority('workcase:room:view')")
|
||||
@GetMapping("/room/{roomId}")
|
||||
public ResultDomain<TbChatRoomDTO> getChatRoomById(@PathVariable String roomId) {
|
||||
return chatRoomService.getChatRoomById(roomId);
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询聊天室")
|
||||
@PreAuthorize("hasAuthority('workcase:room:view')")
|
||||
@PostMapping("/room/page")
|
||||
public ResultDomain<ChatRoomVO> getChatRoomPage(@RequestBody PageRequest<TbChatRoomDTO> pageRequest) {
|
||||
ValidationResult vr = ValidationUtils.validate(pageRequest, Arrays.asList(
|
||||
ValidationUtils.requiredNumber("pageParam.page", "页码", 1, null),
|
||||
ValidationUtils.requiredNumber("pageParam.pageSize", "每页数量", 1, 100)
|
||||
));
|
||||
if (!vr.isValid()) {
|
||||
return ResultDomain.failure(vr.getAllErrors());
|
||||
}
|
||||
return chatRoomService.getChatRoomPage(pageRequest);
|
||||
}
|
||||
|
||||
// ========================= ChatRoom成员管理 =========================
|
||||
|
||||
@Operation(summary = "添加聊天室成员")
|
||||
@PreAuthorize("hasAuthority('workcase:room:member')")
|
||||
@PostMapping("/room/member")
|
||||
public ResultDomain<TbChatRoomMemberDTO> addChatRoomMember(@RequestBody TbChatRoomMemberDTO member) {
|
||||
ValidationResult vr = ValidationUtils.validate(member, Arrays.asList(
|
||||
ValidationUtils.requiredString("roomId", "聊天室ID"),
|
||||
ValidationUtils.requiredString("userId", "用户ID")
|
||||
));
|
||||
if (!vr.isValid()) {
|
||||
return ResultDomain.failure(vr.getAllErrors());
|
||||
}
|
||||
return chatRoomService.addChatRoomMember(member);
|
||||
}
|
||||
|
||||
@Operation(summary = "移除聊天室成员")
|
||||
@PreAuthorize("hasAuthority('workcase:room:member')")
|
||||
@DeleteMapping("/room/member/{memberId}")
|
||||
public ResultDomain<Boolean> removeChatRoomMember(@PathVariable String memberId) {
|
||||
return chatRoomService.removeChatRoomMember(memberId);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取聊天室成员列表")
|
||||
@PreAuthorize("hasAuthority('workcase:room:member')")
|
||||
@GetMapping("/room/{roomId}/members")
|
||||
public ResultDomain<ChatMemberVO> getChatRoomMemberList(@PathVariable String roomId) {
|
||||
return chatRoomService.getChatRoomMemberList(roomId);
|
||||
}
|
||||
|
||||
// ========================= ChatRoom消息管理 =========================
|
||||
|
||||
@Operation(summary = "发送聊天室消息")
|
||||
@PreAuthorize("hasAuthority('workcase:room:message')")
|
||||
@PostMapping("/room/message")
|
||||
public ResultDomain<TbChatRoomMessageDTO> sendMessage(@RequestBody TbChatRoomMessageDTO message) {
|
||||
ValidationResult vr = ValidationUtils.validate(message, Arrays.asList(
|
||||
ValidationUtils.requiredString("roomId", "聊天室ID"),
|
||||
ValidationUtils.requiredString("senderId", "发送者ID"),
|
||||
ValidationUtils.requiredString("content", "消息内容")
|
||||
));
|
||||
if (!vr.isValid()) {
|
||||
return ResultDomain.failure(vr.getAllErrors());
|
||||
}
|
||||
return chatRoomService.sendMessage(message);
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询聊天室消息")
|
||||
@PreAuthorize("hasAuthority('workcase:room:message')")
|
||||
@PostMapping("/room/message/page")
|
||||
public ResultDomain<ChatRoomMessageVO> getChatMessagePage(@RequestBody PageRequest<TbChatRoomMessageDTO> pageRequest) {
|
||||
ValidationResult vr = ValidationUtils.validate(pageRequest, Arrays.asList(
|
||||
ValidationUtils.requiredNumber("pageParam.page", "页码", 1, null),
|
||||
ValidationUtils.requiredNumber("pageParam.pageSize", "每页数量", 1, 100)
|
||||
));
|
||||
if (!vr.isValid()) {
|
||||
return ResultDomain.failure(vr.getAllErrors());
|
||||
}
|
||||
return chatRoomService.getChatMessagePage(pageRequest);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除聊天室消息")
|
||||
@PreAuthorize("hasAuthority('workcase:room:message')")
|
||||
@DeleteMapping("/room/message/{messageId}")
|
||||
public ResultDomain<Boolean> deleteRoomMessage(@PathVariable String messageId) {
|
||||
return chatRoomService.deleteMessage(messageId);
|
||||
}
|
||||
|
||||
// ========================= 客服人员管理 =========================
|
||||
|
||||
@Operation(summary = "添加客服人员")
|
||||
@PostMapping("/customer-service")
|
||||
public ResultDomain<TbCustomerServiceDTO> addCustomerService(@RequestBody TbCustomerServiceDTO customerService) {
|
||||
ValidationResult vr = ValidationUtils.validate(customerService, Arrays.asList(
|
||||
ValidationUtils.requiredString("userId", "员工ID")
|
||||
));
|
||||
if (!vr.isValid()) {
|
||||
return ResultDomain.failure(vr.getAllErrors());
|
||||
}
|
||||
return chatRoomService.addCustomerService(customerService);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新客服人员")
|
||||
@PutMapping("/customer-service")
|
||||
public ResultDomain<TbCustomerServiceDTO> updateCustomerService(@RequestBody TbCustomerServiceDTO customerService) {
|
||||
ValidationResult vr = ValidationUtils.validate(customerService, Arrays.asList(
|
||||
ValidationUtils.requiredString("userId", "员工ID")
|
||||
));
|
||||
if (!vr.isValid()) {
|
||||
return ResultDomain.failure(vr.getAllErrors());
|
||||
}
|
||||
return chatRoomService.updateCustomerService(customerService);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除客服人员")
|
||||
@DeleteMapping("/customer-service/{userId}")
|
||||
public ResultDomain<Boolean> deleteCustomerService(@PathVariable String userId) {
|
||||
return chatRoomService.deleteCustomerService(userId);
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询客服人员")
|
||||
@PostMapping("/customer-service/page")
|
||||
public ResultDomain<CustomerServiceVO> getCustomerServicePage(@RequestBody PageRequest<TbCustomerServiceDTO> pageRequest) {
|
||||
ValidationResult vr = ValidationUtils.validate(pageRequest, Arrays.asList(
|
||||
ValidationUtils.requiredNumber("pageParam.page", "页码", 1, null),
|
||||
ValidationUtils.requiredNumber("pageParam.pageSize", "每页数量", 1, 100)
|
||||
));
|
||||
if (!vr.isValid()) {
|
||||
return ResultDomain.failure(vr.getAllErrors());
|
||||
}
|
||||
return chatRoomService.getCustomerServicePage(pageRequest);
|
||||
}
|
||||
|
||||
@Operation(summary = "更新客服在线状态")
|
||||
@PostMapping("/customer-service/{userId}/status")
|
||||
public ResultDomain<Boolean> updateCustomerServiceStatus(@PathVariable String userId, @RequestParam String status) {
|
||||
return chatRoomService.updateCustomerServiceStatus(userId, status);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取可接待客服列表")
|
||||
@GetMapping("/customer-service/available")
|
||||
public ResultDomain<CustomerServiceVO> getAvailableCustomerServices() {
|
||||
return chatRoomService.getAvailableCustomerServices();
|
||||
}
|
||||
|
||||
@Operation(summary = "自动分配客服")
|
||||
@PostMapping("/room/{roomId}/assign")
|
||||
public ResultDomain<CustomerServiceVO> assignCustomerService(@PathVariable String roomId) {
|
||||
return chatRoomService.assignCustomerService(roomId);
|
||||
}
|
||||
|
||||
// ========================= 微信客服消息回调 =========================
|
||||
|
||||
@Operation(summary = "微信客服消息回调验证(GET)")
|
||||
@GetMapping("/kefu/callback")
|
||||
public String kefuCallbackVerify(
|
||||
@RequestParam("msg_signature") String msgSignature,
|
||||
@RequestParam("timestamp") String timestamp,
|
||||
@RequestParam("nonce") String nonce,
|
||||
@RequestParam("echostr") String echostr) {
|
||||
// TODO: 验证签名并返回 echostr
|
||||
// 实际应使用微信提供的加解密工具验证
|
||||
return echostr;
|
||||
}
|
||||
// @Operation(summary = "微信客服消息回调验证(GET)")
|
||||
// @GetMapping("/kefu/callback")
|
||||
// public String kefuCallbackVerify(
|
||||
// @RequestParam("msg_signature") String msgSignature,
|
||||
// @RequestParam("timestamp") String timestamp,
|
||||
// @RequestParam("nonce") String nonce,
|
||||
// @RequestParam("echostr") String echostr) {
|
||||
// // TODO: 验证签名并返回 echostr
|
||||
// // 实际应使用微信提供的加解密工具验证
|
||||
// return echostr;
|
||||
// }
|
||||
|
||||
@Operation(summary = "微信客服消息回调(POST)")
|
||||
@PostMapping("/kefu/callback")
|
||||
public String kefuCallback(
|
||||
@RequestParam("msg_signature") String msgSignature,
|
||||
@RequestParam("timestamp") String timestamp,
|
||||
@RequestParam("nonce") String nonce,
|
||||
@RequestBody String xmlBody) {
|
||||
// TODO: 解密消息,调用同步接口拉取消息
|
||||
// 收到回调后,应调用 kefuMessageService.syncMessages() 拉取新消息
|
||||
// 然后通过 processMessages() 处理消息
|
||||
return "success";
|
||||
}
|
||||
// @Operation(summary = "微信客服消息回调(POST)")
|
||||
// @PostMapping("/kefu/callback")
|
||||
// public String kefuCallback(
|
||||
// @RequestParam("msg_signature") String msgSignature,
|
||||
// @RequestParam("timestamp") String timestamp,
|
||||
// @RequestParam("nonce") String nonce,
|
||||
// @RequestBody String xmlBody) {
|
||||
// // TODO: 解密消息,调用同步接口拉取消息
|
||||
// // 收到回调后,应调用 kefuMessageService.syncMessages() 拉取新消息
|
||||
// // 然后通过 processMessages() 处理消息
|
||||
// return "success";
|
||||
// }
|
||||
|
||||
// ========================= 词云管理 =========================
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.xyzh.workcase.controller;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@@ -42,6 +43,7 @@ public class WorkcaseController {
|
||||
// ========================= 工单管理 =========================
|
||||
|
||||
@Operation(summary = "创建工单")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:create')")
|
||||
@PostMapping
|
||||
public ResultDomain<TbWorkcaseDTO> createWorkcase(@RequestBody TbWorkcaseDTO workcase) {
|
||||
ValidationResult vr = ValidationUtils.validate(workcase, Arrays.asList(
|
||||
@@ -55,6 +57,7 @@ public class WorkcaseController {
|
||||
}
|
||||
|
||||
@Operation(summary = "更新工单")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:update')")
|
||||
@PutMapping
|
||||
public ResultDomain<TbWorkcaseDTO> updateWorkcase(@RequestBody TbWorkcaseDTO workcase) {
|
||||
ValidationResult vr = ValidationUtils.validate(workcase, Arrays.asList(
|
||||
@@ -67,6 +70,7 @@ public class WorkcaseController {
|
||||
}
|
||||
|
||||
@Operation(summary = "删除工单")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:update')")
|
||||
@DeleteMapping("/{workcaseId}")
|
||||
public ResultDomain<TbWorkcaseDTO> deleteWorkcase(@PathVariable String workcaseId) {
|
||||
TbWorkcaseDTO workcase = new TbWorkcaseDTO();
|
||||
@@ -75,18 +79,21 @@ public class WorkcaseController {
|
||||
}
|
||||
|
||||
@Operation(summary = "获取工单详情")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:view')")
|
||||
@GetMapping("/{workcaseId}")
|
||||
public ResultDomain<TbWorkcaseDTO> getWorkcaseById(@PathVariable String workcaseId) {
|
||||
return workcaseService.getWorkcaseById(workcaseId);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询工单列表")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:view')")
|
||||
@PostMapping("/list")
|
||||
public ResultDomain<TbWorkcaseDTO> getWorkcaseList(@RequestBody TbWorkcaseDTO filter) {
|
||||
return workcaseService.getWorkcaseList(filter);
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询工单")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:view')")
|
||||
@PostMapping("/page")
|
||||
public ResultDomain<TbWorkcaseDTO> getWorkcasePage(@RequestBody PageRequest<TbWorkcaseDTO> pageRequest) {
|
||||
ValidationResult vr = ValidationUtils.validate(pageRequest, Arrays.asList(
|
||||
@@ -123,6 +130,7 @@ public class WorkcaseController {
|
||||
// ========================= 工单处理过程 =========================
|
||||
|
||||
@Operation(summary = "创建工单处理过程")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:process')")
|
||||
@PostMapping("/process")
|
||||
public ResultDomain<TbWorkcaseProcessDTO> createWorkcaseProcess(@RequestBody TbWorkcaseProcessDTO process) {
|
||||
ValidationResult vr = ValidationUtils.validate(process, Arrays.asList(
|
||||
@@ -136,6 +144,7 @@ public class WorkcaseController {
|
||||
}
|
||||
|
||||
@Operation(summary = "更新工单处理过程")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:process')")
|
||||
@PutMapping("/process")
|
||||
public ResultDomain<TbWorkcaseProcessDTO> updateWorkcaseProcess(@RequestBody TbWorkcaseProcessDTO process) {
|
||||
ValidationResult vr = ValidationUtils.validate(process, Arrays.asList(
|
||||
@@ -148,6 +157,7 @@ public class WorkcaseController {
|
||||
}
|
||||
|
||||
@Operation(summary = "删除工单处理过程")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:process')")
|
||||
@DeleteMapping("/process/{processId}")
|
||||
public ResultDomain<TbWorkcaseProcessDTO> deleteWorkcaseProcess(@PathVariable String processId) {
|
||||
TbWorkcaseProcessDTO process = new TbWorkcaseProcessDTO();
|
||||
@@ -156,12 +166,14 @@ public class WorkcaseController {
|
||||
}
|
||||
|
||||
@Operation(summary = "查询工单处理过程列表")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:process')")
|
||||
@PostMapping("/process/list")
|
||||
public ResultDomain<TbWorkcaseProcessDTO> getWorkcaseProcessList(@RequestBody TbWorkcaseProcessDTO filter) {
|
||||
return workcaseService.getWorkcaseProcessList(filter);
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询工单处理过程")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:process')")
|
||||
@PostMapping("/process/page")
|
||||
public ResultDomain<TbWorkcaseProcessDTO> getWorkcaseProcessPage(@RequestBody PageRequest<TbWorkcaseProcessDTO> pageRequest) {
|
||||
ValidationResult vr = ValidationUtils.validate(pageRequest, Arrays.asList(
|
||||
@@ -177,6 +189,7 @@ public class WorkcaseController {
|
||||
// ========================= 工单设备管理 =========================
|
||||
|
||||
@Operation(summary = "创建工单设备")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:device')")
|
||||
@PostMapping("/device")
|
||||
public ResultDomain<TbWorkcaseDeviceDTO> createWorkcaseDevice(@RequestBody TbWorkcaseDeviceDTO device) {
|
||||
ValidationResult vr = ValidationUtils.validate(device, Arrays.asList(
|
||||
@@ -190,6 +203,7 @@ public class WorkcaseController {
|
||||
}
|
||||
|
||||
@Operation(summary = "更新工单设备")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:device')")
|
||||
@PutMapping("/device")
|
||||
public ResultDomain<TbWorkcaseDeviceDTO> updateWorkcaseDevice(@RequestBody TbWorkcaseDeviceDTO device) {
|
||||
ValidationResult vr = ValidationUtils.validate(device, Arrays.asList(
|
||||
@@ -203,6 +217,7 @@ public class WorkcaseController {
|
||||
}
|
||||
|
||||
@Operation(summary = "删除工单设备")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:device')")
|
||||
@DeleteMapping("/device/{workcaseId}/{device}")
|
||||
public ResultDomain<TbWorkcaseDeviceDTO> deleteWorkcaseDevice(@PathVariable String workcaseId, @PathVariable String device) {
|
||||
TbWorkcaseDeviceDTO deviceDTO = new TbWorkcaseDeviceDTO();
|
||||
@@ -212,12 +227,14 @@ public class WorkcaseController {
|
||||
}
|
||||
|
||||
@Operation(summary = "查询工单设备列表")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:device')")
|
||||
@PostMapping("/device/list")
|
||||
public ResultDomain<TbWorkcaseDeviceDTO> getWorkcaseDeviceList(@RequestBody TbWorkcaseDeviceDTO filter) {
|
||||
return workcaseService.getWorkcaseDeviceList(filter);
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询工单设备")
|
||||
@PreAuthorize("hasAuthority('workcase:ticket:device')")
|
||||
@PostMapping("/device/page")
|
||||
public ResultDomain<TbWorkcaseDeviceDTO> getWorkcaseDevicePage(@RequestBody PageRequest<TbWorkcaseDeviceDTO> pageRequest) {
|
||||
ValidationResult vr = ValidationUtils.validate(pageRequest, Arrays.asList(
|
||||
|
||||
@@ -7,7 +7,7 @@ server:
|
||||
# ================== Auth ====================
|
||||
auth:
|
||||
enabled: true
|
||||
gate-way: true
|
||||
gateway-mode: true
|
||||
whitelist:
|
||||
- /swagger-ui/**
|
||||
- /swagger-ui.html
|
||||
|
||||
Reference in New Issue
Block a user