loginDomain修正

This commit is contained in:
2025-12-19 18:19:04 +08:00
parent 9c4f73ac9c
commit 1131a34c6e
19 changed files with 206 additions and 252 deletions

View File

@@ -67,6 +67,21 @@
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!-- Spring Cloud Nacos 服务发现Gateway 路由需要) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-logback-adapter-12</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>logback-adapter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>

View File

@@ -40,7 +40,7 @@ public class AgentController {
* @since 2025-12-17
*/
@PostMapping
@PreAuthorize("@ss.hasPermi('ai:agent:create')")
@PreAuthorize("hasAuthority('ai:agent:create')")
public ResultDomain<TbAgent> createAgent(@RequestBody TbAgent agent) {
log.info("创建智能体: name={}", agent.getName());
// 参数校验
@@ -62,7 +62,7 @@ public class AgentController {
* @since 2025-12-17
*/
@PutMapping
@PreAuthorize("@ss.hasPermi('ai:agent:update')")
@PreAuthorize("hasAuthority('ai:agent:update')")
public ResultDomain<TbAgent> updateAgent(@RequestBody TbAgent agent) {
log.info("更新智能体: agentId={}", agent.getAgentId());
// 参数校验
@@ -85,7 +85,7 @@ public class AgentController {
* @since 2025-12-17
*/
@DeleteMapping("/{agentId}")
@PreAuthorize("@ss.hasPermi('ai:agent:delete')")
@PreAuthorize("hasAuthority('ai:agent:delete')")
public ResultDomain<TbAgent> deleteAgent(@PathVariable("agentId") @NotNull String agentId) {
log.info("删除智能体: agentId={}", agentId);
TbAgent agent = new TbAgent();
@@ -100,7 +100,7 @@ public class AgentController {
* @since 2025-12-17
*/
@GetMapping("/{agentId}")
@PreAuthorize("@ss.hasPermi('ai:agent:view')")
@PreAuthorize("hasAuthority('ai:agent:view')")
public ResultDomain<TbAgent> getAgent(@PathVariable("agentId") @NotNull String agentId) {
log.info("获取智能体: agentId={}", agentId);
ResultDomain<TbAgent> agentResult = agentService.selectAgentById(agentId);
@@ -117,7 +117,7 @@ public class AgentController {
* @since 2025-12-17
*/
@PostMapping("/page")
@PreAuthorize("@ss.hasPermi('ai:agent:view')")
@PreAuthorize("hasAuthority('ai:agent:view')")
public ResultDomain<TbAgent> getAgentPage(@RequestBody PageRequest<TbAgent> pageRequest) {
log.info("分页查询智能体");
// 参数校验(支持嵌套属性路径)
@@ -139,7 +139,7 @@ public class AgentController {
* @since 2025-12-17
*/
@GetMapping("/list")
@PreAuthorize("@ss.hasPermi('ai:agent:view')")
@PreAuthorize("hasAuthority('ai:agent:view')")
public ResultDomain<TbAgent> getAgentList(TbAgent tbAgent) {
log.info("获取智能体列表");
return agentService.getAgentList(tbAgent);

View File

@@ -54,7 +54,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
@PreAuthorize("hasAuthority('ai:knowledge:create')")
@PostMapping
public ResultDomain<TbKnowledge> createKnowledge(@RequestBody TbKnowledge knowledge) {
ValidationResult result = ValidationUtils.validate(knowledge, Arrays.asList(
@@ -75,7 +75,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
@PreAuthorize("hasAuthority('ai:knowledge:update')")
@PutMapping
public ResultDomain<TbKnowledge> updateKnowledge(@RequestBody @Valid TbKnowledge knowledge) {
ValidationResult result = ValidationUtils.validate(knowledge, Arrays.asList(
@@ -97,7 +97,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
@PreAuthorize("hasAuthority('ai:knowledge:delete')")
@DeleteMapping("/{knowledgeId}")
public ResultDomain<Boolean> deleteKnowledge(@PathVariable("knowledgeId") @NotBlank String knowledgeId) {
logger.info("删除知识库: knowledgeId={}", knowledgeId);
@@ -110,7 +110,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:view')")
@PreAuthorize("hasAuthority('ai:knowledge:view')")
@GetMapping("/{knowledgeId}")
public ResultDomain<TbKnowledge> getKnowledge(@PathVariable("knowledgeId") @NotBlank String knowledgeId) {
logger.info("获取知识库: knowledgeId={}", knowledgeId);
@@ -123,7 +123,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:view')")
@PreAuthorize("hasAuthority('ai:knowledge:view')")
@PostMapping("/list")
public ResultDomain<TbKnowledge> listKnowledges(@RequestBody(required = false) TbKnowledge filter) {
logger.info("查询知识库列表");
@@ -136,7 +136,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:view')")
@PreAuthorize("hasAuthority('ai:knowledge:view')")
@PostMapping("/page")
public ResultDomain<TbKnowledge> pageKnowledges(@RequestBody @Valid PageRequest<TbKnowledge> pageRequest) {
logger.info("分页查询知识库");
@@ -149,7 +149,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:view')")
@PreAuthorize("hasAuthority('ai:knowledge:view')")
@GetMapping("/{knowledgeId}/stats")
public ResultDomain<TbKnowledge> getKnowledgeStats(@PathVariable("knowledgeId") @NotBlank String knowledgeId) {
logger.info("获取知识库统计: knowledgeId={}", knowledgeId);
@@ -164,7 +164,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:view')")
@PreAuthorize("hasAuthority('ai:knowledge:file:view')")
@GetMapping("/{knowledgeId}/documents")
public ResultDomain<Map<String, Object>> getDocumentList(
@PathVariable("knowledgeId") @NotBlank String knowledgeId,
@@ -182,7 +182,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:upload')")
@PreAuthorize("hasAuthority('ai:knowledge:file:upload')")
@PostMapping(value = "/file/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResultDomain<TbKnowledgeFile> uploadToKnowledge(
@RequestParam("file") @NotNull MultipartFile file,
@@ -200,7 +200,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:upload')")
@PreAuthorize("hasAuthority('ai:knowledge:file:upload')")
@PostMapping(value = "/file/batch-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResultDomain<TbKnowledgeFile> batchUploadToKnowledge(
@RequestParam("files") @NotEmpty List<MultipartFile> files,
@@ -218,7 +218,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:update')")
@PreAuthorize("hasAuthority('ai:knowledge:file:update')")
@PutMapping(value = "/file/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResultDomain<TbKnowledgeFile> updateFile(
@RequestParam("file") @NotNull MultipartFile file,
@@ -234,7 +234,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:delete')")
@PreAuthorize("hasAuthority('ai:knowledge:file:delete')")
@DeleteMapping("/file/{fileId}")
public ResultDomain<Boolean> deleteFile(@PathVariable("fileId") @NotBlank String fileId) {
logger.info("删除知识库文件: fileId={}", fileId);
@@ -247,7 +247,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:view')")
@PreAuthorize("hasAuthority('ai:knowledge:file:view')")
@GetMapping("/file/{fileId}/history")
public ResultDomain<TbKnowledgeFile> getFileHistory(@PathVariable("fileId") @NotBlank String fileRootId) {
logger.info("获取文件历史: fileRootId={}", fileRootId);
@@ -263,7 +263,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:dify:segment:view')")
@PreAuthorize("hasAuthority('ai:dify:segment:view')")
@GetMapping("/datasets/{datasetId}/documents/{documentId}/segments")
public ResultDomain<JSONObject> getDocumentSegments(
@PathVariable("datasetId") @NotBlank String datasetId,
@@ -280,7 +280,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:dify:segment:create')")
@PreAuthorize("hasAuthority('ai:dify:segment:create')")
@PostMapping("/datasets/{datasetId}/documents/{documentId}/segments")
public ResultDomain<String> createSegment(
@PathVariable("datasetId") @NotBlank String datasetId,
@@ -299,7 +299,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:dify:segment:update')")
@PreAuthorize("hasAuthority('ai:dify:segment:update')")
@PatchMapping("/datasets/{datasetId}/documents/{documentId}/segments/{segmentId}")
public ResultDomain<String> updateSegment(
@PathVariable("datasetId") @NotBlank String datasetId,
@@ -318,7 +318,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:dify:segment:delete')")
@PreAuthorize("hasAuthority('ai:dify:segment:delete')")
@DeleteMapping("/datasets/{datasetId}/documents/{documentId}/segments/{segmentId}")
public ResultDomain<String> deleteSegment(
@PathVariable("datasetId") @NotBlank String datasetId,
@@ -338,7 +338,7 @@ public class KnowledgeController {
* @author yslg
* @since 2025-12-18
*/
@PreAuthorize("@ss.hasPermission('ai:dify:document:status')")
@PreAuthorize("hasAuthority('ai:dify:document:status')")
@PatchMapping("/datasets/{datasetId}/documents/{action}/status")
public ResultDomain<String> updateDocumentStatus(
@PathVariable("datasetId") @NotBlank String datasetId,

View File

@@ -17,7 +17,17 @@ import org.springframework.web.filter.OncePerRequestFilter;
import org.xyzh.common.auth.contants.AuthContants;
import java.io.IOException;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.fastjson2.JSON;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.dto.sys.TbSysPermissionDTO;
import org.xyzh.common.redis.service.RedisService;
/**
* @description Gateway 认证模式配置 - 信任 Gateway 传递的用户信息
@@ -30,7 +40,7 @@ import java.util.Collections;
@Configuration
@ConditionalOnProperty(name = "auth.gateway-mode", havingValue = "true")
public class GatewayAuthConfig {
private static final Logger log = LoggerFactory.getLogger(GatewayAuthConfig.class);
private static final Logger logger = LoggerFactory.getLogger(GatewayAuthConfig.class);
/**
* Gateway 信任过滤器 - 从请求头获取用户信息
@@ -38,7 +48,7 @@ public class GatewayAuthConfig {
*/
@Bean
public GatewayTrustFilter gatewayTrustFilter() {
log.info("启用 Gateway 认证模式,微服务将信任 Gateway 传递的用户信息");
logger.info("启用 Gateway 认证模式,微服务将信任 Gateway 传递的用户信息");
return new GatewayTrustFilter();
}
@@ -46,7 +56,12 @@ public class GatewayAuthConfig {
* Gateway 信任过滤器实现
*/
public static class GatewayTrustFilter extends OncePerRequestFilter {
private static final Logger log = LoggerFactory.getLogger(GatewayTrustFilter.class);
private static final Logger logger = LoggerFactory.getLogger(GatewayTrustFilter.class);
private static final String LOGIN_TOKEN_PREFIX = "login:token:";
private static final String BEARER_PREFIX = "Bearer ";
@Autowired
private RedisService redisService;
@Override
protected void doFilterInternal(@NonNull HttpServletRequest request,
@@ -59,12 +74,12 @@ public class GatewayAuthConfig {
String username = request.getHeader(AuthContants.USERNAME_ATTRIBUTE);
if (StringUtils.hasText(userId)) {
// 构造简化的 Principal使用 userId 作为身份标识)
// 注意:完整的 LoginDomain 应该从 Redis 加载,这里只是基本身份验证
// 从 Redis 获取用户权限
List<GrantedAuthority> authorities = loadUserAuthorities(request);
// 设置到 Spring Security 上下文
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userId, null, Collections.emptyList());
new UsernamePasswordAuthenticationToken(userId, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 同时将用户信息设置到 request attributes 中,供业务代码使用
@@ -73,10 +88,55 @@ public class GatewayAuthConfig {
request.setAttribute(AuthContants.USERNAME_ATTRIBUTE, username);
}
log.debug("从 Gateway 获取用户信息: userId={}, username={}", userId, username);
logger.info("从 Gateway 获取用户信息: userId={}, username={}, 权限数={}", userId, username, authorities.size());
}
filterChain.doFilter(request, response);
}
/**
* 从 Redis 加载用户权限列表
*/
private List<GrantedAuthority> loadUserAuthorities(HttpServletRequest request) {
List<GrantedAuthority> authorities = new ArrayList<>();
try {
String authHeader = request.getHeader("Authorization");
logger.info("Authorization header: {}", authHeader != null ? "Bearer ***" : "null");
if (StringUtils.hasText(authHeader) && authHeader.startsWith(BEARER_PREFIX)) {
String token = authHeader.substring(BEARER_PREFIX.length());
String cacheKey = LOGIN_TOKEN_PREFIX + token;
Object obj = redisService.get(cacheKey);
logger.info("Redis key: {}, obj type: {}", cacheKey, obj != null ? obj.getClass().getName() : "null");
LoginDomain login = null;
if (obj instanceof LoginDomain) {
login = (LoginDomain) obj;
} else if (obj instanceof String) {
// Redis 返回的是 JSON 字符串,需要反序列化
login = JSON.parseObject((String) obj, LoginDomain.class);
logger.info("从 JSON 反序列化 LoginDomain");
}
if (login != null) {
if (login.getUserPermissions() != null) {
for (TbSysPermissionDTO permission : login.getUserPermissions()) {
// status 为 null 或 true 时都视为有效权限
if (permission.getCode() != null && !Boolean.FALSE.equals(permission.getStatus())) {
authorities.add(new SimpleGrantedAuthority(permission.getCode()));
}
}
}
logger.info("加载用户权限: {} 个", authorities.size());
}
}
} catch (Exception e) {
logger.warn("加载用户权限失败: {}", e.getMessage(), e);
}
return authorities;
}
}
}

View File

@@ -8,6 +8,8 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.redis.service.RedisService;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
@@ -49,6 +51,9 @@ public class LoginUtil {
Object obj = instance.redisService.get(cacheKey);
if (obj instanceof LoginDomain) {
return (LoginDomain) obj;
} else if (obj instanceof String) {
// Redis 返回的是 JSON 字符串,需要反序列化
return JSON.parseObject((String) obj, LoginDomain.class);
}
} catch (Exception e) {
// 忽略异常

View File

@@ -74,6 +74,16 @@
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-logback-adapter-12</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>logback-adapter</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- MinIO 对象存储 -->

View File

@@ -65,6 +65,16 @@
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-logback-adapter-12</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>logback-adapter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>

View File

@@ -128,6 +128,16 @@
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${nacos.version}</version>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-logback-adapter-12</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>logback-adapter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>

View File

@@ -38,7 +38,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@PostMapping("/dept")
@PreAuthorize("@ss.hasPermi('system:dept:create')")
@PreAuthorize("hasAuthority('system:dept:create')")
public ResultDomain<TbSysDeptDTO> createDept(@RequestBody TbSysDeptDTO deptDTO) {
return deptRoleService.insertDept(deptDTO);
}
@@ -52,7 +52,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@PutMapping("/dept")
@PreAuthorize("@ss.hasPermi('system:dept:edit')")
@PreAuthorize("hasAuthority('system:dept:edit')")
public ResultDomain<TbSysDeptDTO> updateDept(@RequestBody TbSysDeptDTO deptDTO) {
return deptRoleService.updateDept(deptDTO);
}
@@ -65,7 +65,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@DeleteMapping("/dept")
@PreAuthorize("@ss.hasPermi('system:dept:delete')")
@PreAuthorize("hasAuthority('system:dept:delete')")
public ResultDomain<Boolean> deleteDept(@RequestBody TbSysDeptDTO deptDTO) {
return deptRoleService.deleteDept(deptDTO);
}
@@ -78,7 +78,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@GetMapping("/dept/page")
@PreAuthorize("@ss.hasPermi('system:dept:view')")
@PreAuthorize("hasAuthority('system:dept:view')")
public ResultDomain<UserDeptRoleVO> getDeptPage(@RequestBody PageRequest<UserDeptRoleVO> pageRequest) {
return deptRoleService.getDeptPage(pageRequest);
}
@@ -91,7 +91,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@GetMapping("/dept/list")
@PreAuthorize("@ss.hasPermi('system:dept:view')")
@PreAuthorize("hasAuthority('system:dept:view')")
public ResultDomain<UserDeptRoleVO> getDeptList(@RequestBody UserDeptRoleVO filter) {
return deptRoleService.getDeptList(filter);
}
@@ -105,7 +105,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@PostMapping("/role")
@PreAuthorize("@ss.hasPermi('system:role:create')")
@PreAuthorize("hasAuthority('system:role:create')")
public ResultDomain<TbSysRoleDTO> createRole(@RequestBody TbSysRoleDTO roleDTO) {
return deptRoleService.insertRole(roleDTO);
}
@@ -118,7 +118,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@PutMapping("/role")
@PreAuthorize("@ss.hasPermi('system:role:edit')")
@PreAuthorize("hasAuthority('system:role:edit')")
public ResultDomain<TbSysRoleDTO> updateRole(@RequestBody TbSysRoleDTO roleDTO) {
return deptRoleService.updateRole(roleDTO);
}
@@ -131,7 +131,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@DeleteMapping("/role")
@PreAuthorize("@ss.hasPermi('system:role:delete')")
@PreAuthorize("hasAuthority('system:role:delete')")
public ResultDomain<Boolean> deleteRole(@RequestBody TbSysRoleDTO roleDTO) {
return deptRoleService.deleteRole(roleDTO);
}
@@ -144,7 +144,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@GetMapping("/role/page")
@PreAuthorize("@ss.hasPermi('system:role:view')")
@PreAuthorize("hasAuthority('system:role:view')")
public ResultDomain<UserDeptRoleVO> getRolePage(@RequestBody PageRequest<UserDeptRoleVO> pageRequest) {
return deptRoleService.getRolePage(pageRequest);
}
@@ -157,7 +157,7 @@ public class DeptRoleController {
* @since 2025-11-10
*/
@GetMapping("/role/list")
@PreAuthorize("@ss.hasPermi('system:role:view')")
@PreAuthorize("hasAuthority('system:role:view')")
public ResultDomain<UserDeptRoleVO> getRoleList(@RequestBody UserDeptRoleVO filter) {
return deptRoleService.getRoleList(filter);
}
@@ -171,7 +171,7 @@ public class DeptRoleController {
* @since 2025-11-11
*/
@PostMapping("/rolePermission/bind")
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<PermissionVO> getRolePermission(@RequestBody PermissionVO permissionVO) {
return deptRoleService.setRolePermission(permissionVO);
}
@@ -184,7 +184,7 @@ public class DeptRoleController {
* @since 2025-11-11
*/
@PostMapping("/rolePermission/list")
@PreAuthorize("@ss.hasPermi('system:permission:view')")
@PreAuthorize("hasAuthority('system:permission:view')")
public ResultDomain<PermissionVO> getRolePermissionList(@RequestBody PermissionVO permissionVO) {
return deptRoleService.getRolePermissionList(permissionVO);
}

View File

@@ -37,7 +37,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@PostMapping("/module")
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<TbSysModuleDTO> createModule(@RequestBody TbSysModuleDTO moduleDTO) {
return modulePermissionService.insertModule(moduleDTO);
}
@@ -50,7 +50,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@PutMapping("/moudule")
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<TbSysModuleDTO> updateModule(@RequestBody TbSysModuleDTO moduleDTO) {
return modulePermissionService.updateModule(moduleDTO);
}
@@ -63,7 +63,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@DeleteMapping("/module")
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<Boolean> deleteModule(@RequestBody TbSysModuleDTO moduleDTO) {
return modulePermissionService.deleteModule(moduleDTO);
}
@@ -76,7 +76,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@PostMapping("/module/page")
@PreAuthorize("@ss.hasPermi('system:permission:view')")
@PreAuthorize("hasAuthority('system:permission:view')")
public ResultDomain<PermissionVO> getModulePage(@RequestBody PageRequest<PermissionVO> pageRequest) {
return modulePermissionService.getModulePage(pageRequest);
}
@@ -89,7 +89,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@PostMapping("/module/list")
@PreAuthorize("@ss.hasPermi('system:permission:view')")
@PreAuthorize("hasAuthority('system:permission:view')")
public ResultDomain<PermissionVO> getModuleList(@RequestBody PermissionVO filter) {
return modulePermissionService.getModuleList(filter);
}
@@ -103,7 +103,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@PostMapping
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<TbSysPermissionDTO> createPermission(@RequestBody TbSysPermissionDTO permissionDTO) {
return modulePermissionService.insertPermission(permissionDTO);
}
@@ -116,7 +116,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@PutMapping
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<TbSysPermissionDTO> updatePermission(@RequestBody TbSysPermissionDTO permissionDTO) {
return modulePermissionService.updatePermission(permissionDTO);
}
@@ -129,7 +129,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@DeleteMapping
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<Boolean> deletePermission(@RequestBody TbSysPermissionDTO permissionDTO) {
return modulePermissionService.deletePermission(permissionDTO);
}
@@ -142,7 +142,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@PostMapping("/page")
@PreAuthorize("@ss.hasPermi('system:permission:view')")
@PreAuthorize("hasAuthority('system:permission:view')")
public ResultDomain<PermissionVO> getModulePermissionPage(@RequestBody PageRequest<PermissionVO> pageRequest) {
return modulePermissionService.getModulePermissionPage(pageRequest);
}
@@ -155,7 +155,7 @@ public class PermissionController {
* @since 2025-11-10
*/
@PostMapping("/list")
@PreAuthorize("@ss.hasPermi('system:permission:view')")
@PreAuthorize("hasAuthority('system:permission:view')")
public ResultDomain<PermissionVO> getModulePermissionList(@RequestBody PermissionVO filter) {
return modulePermissionService.getModulePermissionList(filter);
}

View File

@@ -30,31 +30,31 @@ public class SysConfigController {
// ================= 系统配置相关接口 =================
@PostMapping
@PreAuthorize("@ss.hasPermi('config:config:edit')")
@PreAuthorize("hasAuthority('config:config:edit')")
public ResultDomain<TbSysConfigDTO> createConfig(@RequestBody TbSysConfigDTO configDTO) {
return sysConfigService.insertConfig(configDTO);
}
@PutMapping
@PreAuthorize("@ss.hasPermi('config:config:edit')")
@PreAuthorize("hasAuthority('config:config:edit')")
public ResultDomain<TbSysConfigDTO> updateConfig(@RequestBody TbSysConfigDTO configDTO) {
return sysConfigService.updateConfig(configDTO);
}
@DeleteMapping
@PreAuthorize("@ss.hasPermi('config:config:edit')")
@PreAuthorize("hasAuthority('config:config:edit')")
public ResultDomain<Boolean> deleteConfig(@RequestBody TbSysConfigDTO configDTO) {
return sysConfigService.deleteConfig(configDTO);
}
@PostMapping("/page")
@PreAuthorize("@ss.hasPermi('config:config:view')")
@PreAuthorize("hasAuthority('config:config:view')")
public ResultDomain<SysConfigVO> getConfigPage(@RequestBody PageRequest<SysConfigVO> pageRequest) {
return sysConfigService.getConfigPage(pageRequest);
}
@PostMapping("/list")
@PreAuthorize("@ss.hasPermi('config:config:view')")
@PreAuthorize("hasAuthority('config:config:view')")
public ResultDomain<SysConfigVO> getConfigList(@RequestBody SysConfigVO filter) {
return sysConfigService.getConfigList(filter);
}

View File

@@ -32,31 +32,31 @@ public class UserController {
// ================= 用户相关接口 =================
@PostMapping
@PreAuthorize("@ss.hasPermi('system:user:create')")
@PreAuthorize("hasAuthority('system:user:create')")
public ResultDomain<TbSysUserDTO> createUser(@RequestBody SysUserVO userVO) {
return sysUserService.insertUser(userVO);
}
@PutMapping
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@PreAuthorize("hasAuthority('system:user:edit')")
public ResultDomain<TbSysUserDTO> updateUser(@RequestBody SysUserVO userVO) {
return sysUserService.updateUser(userVO);
}
@DeleteMapping
@PreAuthorize("@ss.hasPermi('system:user:delete')")
@PreAuthorize("hasAuthority('system:user:delete')")
public ResultDomain<Boolean> deleteUser(@RequestBody TbSysUserDTO userDTO) {
return sysUserService.deleteUser(userDTO);
}
@PostMapping("/page")
@PreAuthorize("@ss.hasPermi('system:user:view')")
@PreAuthorize("hasAuthority('system:user:view')")
public ResultDomain<SysUserVO> getUserPage(@RequestBody PageRequest<SysUserVO> pageRequest) {
return sysUserService.getUserPage(pageRequest);
}
@PostMapping("/list")
@PreAuthorize("@ss.hasPermi('system:user:view')")
@PreAuthorize("hasAuthority('system:user:view')")
public ResultDomain<SysUserVO> getUserList(@RequestBody SysUserVO filter) {
return sysUserService.getUserList(filter);
}
@@ -64,13 +64,13 @@ public class UserController {
// ================= 用户信息相关接口 ==================
@PutMapping("/info")
@PreAuthorize("@ss.hasPermi('system:user:edit')")
@PreAuthorize("hasAuthority('system:user:edit')")
public ResultDomain<TbSysUserInfoDTO> updateUserInfo(@RequestBody TbSysUserInfoDTO userInfoDTO) {
return sysUserService.updateUserInfo(userInfoDTO);
}
@GetMapping("/info/{userId}")
@PreAuthorize("@ss.hasPermi('system:user:view')")
@PreAuthorize("hasAuthority('system:user:view')")
public ResultDomain<SysUserVO> getUserInfo(@PathVariable("userId") String userId) {
return sysUserService.getUserInfo(userId);
}

View File

@@ -28,44 +28,44 @@ public class ViewController {
// ================= 视图相关接口 =================
@PostMapping
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<TbSysViewDTO> createView(@RequestBody TbSysViewDTO viewDTO) {
return viewService.insertView(viewDTO);
}
@PutMapping
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<TbSysViewDTO> updateView(@RequestBody TbSysViewDTO viewDTO) {
return viewService.updateView(viewDTO);
}
@DeleteMapping
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<Boolean> deleteView(@RequestBody TbSysViewDTO viewDTO) {
return viewService.deleteView(viewDTO);
}
@PostMapping("/page")
@PreAuthorize("@ss.hasPermi('system:permission:view')")
@PreAuthorize("hasAuthority('system:permission:view')")
public ResultDomain<PermissionVO> getViewPage(@RequestBody PageRequest<PermissionVO> pageRequest) {
return viewService.getViewPage(pageRequest);
}
@PostMapping("/list")
@PreAuthorize("@ss.hasPermi('system:permission:view')")
@PreAuthorize("hasAuthority('system:permission:view')")
public ResultDomain<PermissionVO> getViewList(@RequestBody PermissionVO filter) {
return viewService.getViewList(filter);
}
// ================= 视图权限相关接口 ==================
@PostMapping("/permission/bind")
@PreAuthorize("@ss.hasPermi('system:permission:manage')")
@PreAuthorize("hasAuthority('system:permission:manage')")
public ResultDomain<PermissionVO> bindViewPermission(@RequestBody PermissionVO permissionVO) {
return viewService.setViewPermissions(permissionVO);
}
@PostMapping("/permission/list")
@PreAuthorize("@ss.hasPermi('system:permission:view')")
@PreAuthorize("hasAuthority('system:permission:view')")
public ResultDomain<PermissionVO> getViewPermissions(@RequestBody PermissionVO permissionVO) {
return viewService.getViewPermissionList(permissionVO);
}

View File

@@ -46,10 +46,10 @@
<groupId>org.xyzh.common</groupId>
<artifactId>common-jdbc</artifactId>
</dependency>
<dependency>
<!-- <dependency>
<groupId>org.xyzh.common</groupId>
<artifactId>common-wechat</artifactId>
</dependency>
</dependency> -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
@@ -78,6 +78,21 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-logback-adapter-12</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>logback-adapter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>

View File

@@ -1,32 +0,0 @@
package org.xyzh.workcase.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.xyzh.common.wechat.kefu.message.KefuMessageService;
import org.xyzh.workcase.handler.WorkcaseKefuHandler;
import jakarta.annotation.PostConstruct;
/**
* @description 工单模块的微信客服配置
* 注册 WorkcaseKefuHandler 到消息服务
* @filename WeChatKefuConfig.java
* @author yslg
* @copyright xyzh
* @since 2025-12-19
*/
@Configuration
public class WeChatKefuConfig {
@Autowired
private KefuMessageService kefuMessageService;
@Autowired
private WorkcaseKefuHandler workcaseKefuHandler;
@PostConstruct
public void init() {
kefuMessageService.setHandler(workcaseKefuHandler);
}
}

View File

@@ -21,7 +21,6 @@ import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.page.PageRequest;
import org.xyzh.common.utils.validation.ValidationResult;
import org.xyzh.common.utils.validation.ValidationUtils;
import org.xyzh.common.wechat.kefu.message.KefuMessageService;
import io.swagger.v3.oas.annotations.Operation;
@@ -46,9 +45,6 @@ public class WorkcaseChatContorller {
@DubboReference(version = "1.0.0", group = "workcase", check = false, scope = "local")
private WorkcaseChatService workcaseChatService;
@Autowired
private KefuMessageService kefuMessageService;
// ========================= AI对话管理 =========================
@Operation(summary = "创建对话")
@@ -166,20 +162,6 @@ public class WorkcaseChatContorller {
return "success";
}
@Operation(summary = "手动同步客服消息")
@PostMapping("/kefu/sync")
public ResultDomain<String> syncKefuMessages(
@RequestParam(required = false) String cursor,
@RequestParam(required = false) String token,
@RequestParam(required = false, defaultValue = "100") Integer limit) {
var response = kefuMessageService.syncMessages(cursor, token, limit);
if (response != null && response.isSuccess()) {
kefuMessageService.processMessages(response);
return ResultDomain.success("同步成功", response.getNextCursor());
}
return ResultDomain.failure("同步失败");
}
// ========================= 词云管理 =========================
@Operation(summary = "添加词云")

View File

@@ -1,120 +0,0 @@
package org.xyzh.workcase.handler;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.xyzh.api.ai.dto.TbChatMessage;
import org.xyzh.api.workcase.dto.TbWorkcaseDTO;
import org.xyzh.api.workcase.service.WorkcaseChatService;
import org.xyzh.api.workcase.service.WorkcaseService;
import org.xyzh.common.wechat.kefu.message.KefuMessageHandler;
import org.xyzh.common.wechat.kefu.message.KefuMessageService;
import org.xyzh.common.wechat.pojo.kefu.KefuSyncMsgResponse.KefuSyncMsg;
/**
* @description 工单模块的微信客服处理器实现
* 处理微信客服消息,同步到工单系统
* @filename WorkcaseKefuHandler.java
* @author yslg
* @copyright xyzh
* @since 2025-12-19
*/
@Component
public class WorkcaseKefuHandler implements KefuMessageHandler {
private static final Logger logger = LoggerFactory.getLogger(WorkcaseKefuHandler.class);
@Autowired
private KefuMessageService kefuMessageService;
@DubboReference(version = "1.0.0", group = "workcase", check = false, scope = "local")
private WorkcaseService workcaseService;
@DubboReference(version = "1.0.0", group = "workcase", check = false, scope = "local")
private WorkcaseChatService workcaseChatService;
@Override
public void onEnterSession(String openKfid, String externalUserid, String scene, String sceneParam, String welcomeCode) {
logger.info("工单客服-用户进入会话: externalUserid={}, scene={}, sceneParam={}", externalUserid, scene, sceneParam);
// sceneParam 可能包含 workcaseId用于关联工单
if (sceneParam != null && !sceneParam.isEmpty()) {
TbWorkcaseDTO filter = new TbWorkcaseDTO();
filter.setWorkcaseId(sceneParam);
var result = workcaseService.getWorkcaseById(sceneParam);
if (result != null && result.getSuccess() && result.getData() != null) {
TbWorkcaseDTO workcase = result.getData();
// 发送工单信息作为欢迎语
String welcomeMsg = buildWelcomeMessage(workcase);
kefuMessageService.sendWelcomeMessage(welcomeCode, welcomeMsg);
logger.info("已发送工单欢迎语: workcaseId={}", workcase.getWorkcaseId());
} else {
// 发送通用欢迎语
kefuMessageService.sendWelcomeMessage(welcomeCode, "您好,欢迎使用客服服务,请问有什么可以帮您?");
}
} else {
kefuMessageService.sendWelcomeMessage(welcomeCode, "您好,欢迎使用客服服务,请问有什么可以帮您?");
}
}
@Override
public void onTextMessage(String openKfid, String externalUserid, String msgid, String content, Long sendTime) {
logger.info("工单客服-收到文本消息: externalUserid={}, content={}", externalUserid, content);
// 同步消息到 tb_chat_message 表
// TODO: 根据 externalUserid 查找关联的 chatId然后保存消息
TbChatMessage chatMessage = new TbChatMessage();
chatMessage.setRole("user");
chatMessage.setContent(content);
// workcaseChatService.saveChatMessage(chatMessage);
}
@Override
public void onImageMessage(String openKfid, String externalUserid, String msgid, String mediaId, Long sendTime) {
logger.info("工单客服-收到图片消息: externalUserid={}, mediaId={}", externalUserid, mediaId);
// 下载图片并保存
// TODO: 实现图片消息处理
}
@Override
public void onSessionStatusChange(String openKfid, String externalUserid, String changeType,
String oldServicerUserid, String newServicerUserid) {
logger.info("工单客服-会话状态变更: externalUserid={}, changeType={}, newServicer={}",
externalUserid, changeType, newServicerUserid);
// 更新工单处理人
// TODO: 根据 externalUserid 查找关联工单,更新 processor
}
@Override
public void onMsgSendFail(String openKfid, String externalUserid, String failMsgid, String failType) {
logger.warn("工单客服-消息发送失败: externalUserid={}, failMsgid={}, failType={}",
externalUserid, failMsgid, failType);
// 记录失败日志,可能需要重试
}
@Override
public void onOtherMessage(KefuSyncMsg msg) {
logger.info("工单客服-收到其他消息: msgtype={}", msg.getMsgtype());
}
private String buildWelcomeMessage(TbWorkcaseDTO workcase) {
StringBuilder sb = new StringBuilder();
sb.append("您好,您的工单已创建。\n");
sb.append("工单编号:").append(workcase.getWorkcaseId()).append("\n");
if (workcase.getType() != null) {
sb.append("问题类型:").append(workcase.getType()).append("\n");
}
if (workcase.getDevice() != null) {
sb.append("设备:").append(workcase.getDevice()).append("\n");
}
sb.append("我们将尽快为您处理。");
return sb.toString();
}
}

View File

@@ -1,13 +1,12 @@
# 小程序用户聊天和工单的产生逻辑
接口实现方式:
0. 用户进行微信小程序1个IM聊天室默认回复人员是ai
1. WorkcaseChatServiceImpl通过ai接口进行ai回复对话人员是来客和ai
2. 当连续3次ai聊天后询问是否转人工
3. 用户触发转人工(可能是一开始,就手动触发,没有聊天记录),跳转到微信客服的功能服务
4. 用户跳转前,必须创建工单
5. ai根据聊天对话自动生成部分工单信息预填入小程序的工单创建的表单
6. 创建工单后同步工单到CRM
7. 将工单信息作为微信客服的欢迎语,进行放送,让来客和员工能看到工单的基本信息,从而实现让员工知道工单是哪一个
<!-- 员工可前往网页查看工单和相关的聊天记录ai和来客的对话以及员工和来客的对话 -->
8. 同步用户和员工在微信客服上的聊天记录同步到tb_chat表里面对话人员是来客和客服。把ai替换成员工进行对话的续接
8. 员工进入聊天室和客户聊天ai退出聊天室的聊天记录同步到tb_chat表里面对话人员是来客和客服。把ai替换成员工进行对话的续接
9. 员工自己更新工单状态如果在CRM更新工单状态会触发receiveWorkcaseFromCrm如果在本系统更新工单会触发工单同步到CRM
10. 在工单是完成、撤销后,工单、对话进行总结,并更新词云

View File

@@ -322,17 +322,17 @@ const filteredTickets = computed(() => {
let result = workcaseList.value
// Mock模式下做本地筛选
if (statusFilter.value !== 'all') {
result = result.filter(t => t.status === statusFilter.value)
result = result.filter((t: TbWorkcaseDTO) => t.status === statusFilter.value)
}
if (typeFilter.value) {
result = result.filter(t => t.type === typeFilter.value)
result = result.filter((t: TbWorkcaseDTO) => t.type === typeFilter.value)
}
if (urgencyFilter.value) {
result = result.filter(t => t.emergency === urgencyFilter.value)
result = result.filter((t: TbWorkcaseDTO) => t.emergency === urgencyFilter.value)
}
if (searchKeyword.value) {
const keyword = searchKeyword.value.toLowerCase()
result = result.filter(t =>
result = result.filter((t: TbWorkcaseDTO) =>
(t.optsn || '').toLowerCase().includes(keyword) ||
(t.username || '').toLowerCase().includes(keyword) ||
(t.device || '').toLowerCase().includes(keyword)