diff --git a/urbanLifelineServ/ai/pom.xml b/urbanLifelineServ/ai/pom.xml index 215d1d7c..d0fa3cc4 100644 --- a/urbanLifelineServ/ai/pom.xml +++ b/urbanLifelineServ/ai/pom.xml @@ -67,6 +67,21 @@ org.apache.dubbo dubbo-spring-boot-starter + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.nacos + nacos-logback-adapter-12 + + + com.alibaba.nacos + logback-adapter + + + org.mybatis.spring.boot mybatis-spring-boot-starter diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/AgentController.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/AgentController.java index af1a0c73..88363b36 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/AgentController.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/AgentController.java @@ -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 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 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 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 getAgent(@PathVariable("agentId") @NotNull String agentId) { log.info("获取智能体: agentId={}", agentId); ResultDomain 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 getAgentPage(@RequestBody PageRequest 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 getAgentList(TbAgent tbAgent) { log.info("获取智能体列表"); return agentService.getAgentList(tbAgent); diff --git a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/KnowledgeController.java b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/KnowledgeController.java index aa74b770..89dbca07 100644 --- a/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/KnowledgeController.java +++ b/urbanLifelineServ/ai/src/main/java/org/xyzh/ai/controller/KnowledgeController.java @@ -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 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 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 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 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 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 pageKnowledges(@RequestBody @Valid PageRequest 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 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> 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 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 batchUploadToKnowledge( @RequestParam("files") @NotEmpty List 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 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 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 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 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 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 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 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 updateDocumentStatus( @PathVariable("datasetId") @NotBlank String datasetId, diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/GatewayAuthConfig.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/GatewayAuthConfig.java index c524072e..abd2c0da 100644 --- a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/GatewayAuthConfig.java +++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/config/GatewayAuthConfig.java @@ -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 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 loadUserAuthorities(HttpServletRequest request) { + List 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; + } } } diff --git a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/utils/LoginUtil.java b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/utils/LoginUtil.java index 65a219d7..0efb9e7e 100644 --- a/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/utils/LoginUtil.java +++ b/urbanLifelineServ/common/common-auth/src/main/java/org/xyzh/common/auth/utils/LoginUtil.java @@ -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) { // 忽略异常 diff --git a/urbanLifelineServ/file/pom.xml b/urbanLifelineServ/file/pom.xml index 368c526a..2e93cd82 100644 --- a/urbanLifelineServ/file/pom.xml +++ b/urbanLifelineServ/file/pom.xml @@ -74,6 +74,16 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.nacos + nacos-logback-adapter-12 + + + com.alibaba.nacos + logback-adapter + + diff --git a/urbanLifelineServ/log/pom.xml b/urbanLifelineServ/log/pom.xml index 44b9653b..3015b126 100644 --- a/urbanLifelineServ/log/pom.xml +++ b/urbanLifelineServ/log/pom.xml @@ -65,6 +65,16 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.nacos + nacos-logback-adapter-12 + + + com.alibaba.nacos + logback-adapter + + diff --git a/urbanLifelineServ/pom.xml b/urbanLifelineServ/pom.xml index 10032083..0f68c454 100644 --- a/urbanLifelineServ/pom.xml +++ b/urbanLifelineServ/pom.xml @@ -128,6 +128,16 @@ com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery ${nacos.version} + + + com.alibaba.nacos + nacos-logback-adapter-12 + + + com.alibaba.nacos + logback-adapter + + com.alibaba.cloud diff --git a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/DeptRoleController.java b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/DeptRoleController.java index 5db42104..57c56e07 100644 --- a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/DeptRoleController.java +++ b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/DeptRoleController.java @@ -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 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 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 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 getDeptPage(@RequestBody PageRequest 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 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 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 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 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 getRolePage(@RequestBody PageRequest 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 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 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 getRolePermissionList(@RequestBody PermissionVO permissionVO) { return deptRoleService.getRolePermissionList(permissionVO); } diff --git a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/PermissionController.java b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/PermissionController.java index 11c3edf4..1932f601 100644 --- a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/PermissionController.java +++ b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/PermissionController.java @@ -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 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 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 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 getModulePage(@RequestBody PageRequest 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 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 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 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 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 getModulePermissionPage(@RequestBody PageRequest 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 getModulePermissionList(@RequestBody PermissionVO filter) { return modulePermissionService.getModulePermissionList(filter); } diff --git a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/SysConfigController.java b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/SysConfigController.java index 2e49c096..dd33a4e4 100644 --- a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/SysConfigController.java +++ b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/SysConfigController.java @@ -30,31 +30,31 @@ public class SysConfigController { // ================= 系统配置相关接口 ================= @PostMapping - @PreAuthorize("@ss.hasPermi('config:config:edit')") + @PreAuthorize("hasAuthority('config:config:edit')") public ResultDomain createConfig(@RequestBody TbSysConfigDTO configDTO) { return sysConfigService.insertConfig(configDTO); } @PutMapping - @PreAuthorize("@ss.hasPermi('config:config:edit')") + @PreAuthorize("hasAuthority('config:config:edit')") public ResultDomain updateConfig(@RequestBody TbSysConfigDTO configDTO) { return sysConfigService.updateConfig(configDTO); } @DeleteMapping - @PreAuthorize("@ss.hasPermi('config:config:edit')") + @PreAuthorize("hasAuthority('config:config:edit')") public ResultDomain deleteConfig(@RequestBody TbSysConfigDTO configDTO) { return sysConfigService.deleteConfig(configDTO); } @PostMapping("/page") - @PreAuthorize("@ss.hasPermi('config:config:view')") + @PreAuthorize("hasAuthority('config:config:view')") public ResultDomain getConfigPage(@RequestBody PageRequest pageRequest) { return sysConfigService.getConfigPage(pageRequest); } @PostMapping("/list") - @PreAuthorize("@ss.hasPermi('config:config:view')") + @PreAuthorize("hasAuthority('config:config:view')") public ResultDomain getConfigList(@RequestBody SysConfigVO filter) { return sysConfigService.getConfigList(filter); } diff --git a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/UserController.java b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/UserController.java index 0b2a9769..b2830d1e 100644 --- a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/UserController.java +++ b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/UserController.java @@ -32,31 +32,31 @@ public class UserController { // ================= 用户相关接口 ================= @PostMapping - @PreAuthorize("@ss.hasPermi('system:user:create')") + @PreAuthorize("hasAuthority('system:user:create')") public ResultDomain createUser(@RequestBody SysUserVO userVO) { return sysUserService.insertUser(userVO); } @PutMapping - @PreAuthorize("@ss.hasPermi('system:user:edit')") + @PreAuthorize("hasAuthority('system:user:edit')") public ResultDomain updateUser(@RequestBody SysUserVO userVO) { return sysUserService.updateUser(userVO); } @DeleteMapping - @PreAuthorize("@ss.hasPermi('system:user:delete')") + @PreAuthorize("hasAuthority('system:user:delete')") public ResultDomain deleteUser(@RequestBody TbSysUserDTO userDTO) { return sysUserService.deleteUser(userDTO); } @PostMapping("/page") - @PreAuthorize("@ss.hasPermi('system:user:view')") + @PreAuthorize("hasAuthority('system:user:view')") public ResultDomain getUserPage(@RequestBody PageRequest pageRequest) { return sysUserService.getUserPage(pageRequest); } @PostMapping("/list") - @PreAuthorize("@ss.hasPermi('system:user:view')") + @PreAuthorize("hasAuthority('system:user:view')") public ResultDomain 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 updateUserInfo(@RequestBody TbSysUserInfoDTO userInfoDTO) { return sysUserService.updateUserInfo(userInfoDTO); } @GetMapping("/info/{userId}") - @PreAuthorize("@ss.hasPermi('system:user:view')") + @PreAuthorize("hasAuthority('system:user:view')") public ResultDomain getUserInfo(@PathVariable("userId") String userId) { return sysUserService.getUserInfo(userId); } diff --git a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/ViewController.java b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/ViewController.java index c6c1d490..20874211 100644 --- a/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/ViewController.java +++ b/urbanLifelineServ/system/src/main/java/org/xyzh/system/controller/ViewController.java @@ -28,44 +28,44 @@ public class ViewController { // ================= 视图相关接口 ================= @PostMapping - @PreAuthorize("@ss.hasPermi('system:permission:manage')") + @PreAuthorize("hasAuthority('system:permission:manage')") public ResultDomain createView(@RequestBody TbSysViewDTO viewDTO) { return viewService.insertView(viewDTO); } @PutMapping - @PreAuthorize("@ss.hasPermi('system:permission:manage')") + @PreAuthorize("hasAuthority('system:permission:manage')") public ResultDomain updateView(@RequestBody TbSysViewDTO viewDTO) { return viewService.updateView(viewDTO); } @DeleteMapping - @PreAuthorize("@ss.hasPermi('system:permission:manage')") + @PreAuthorize("hasAuthority('system:permission:manage')") public ResultDomain deleteView(@RequestBody TbSysViewDTO viewDTO) { return viewService.deleteView(viewDTO); } @PostMapping("/page") - @PreAuthorize("@ss.hasPermi('system:permission:view')") + @PreAuthorize("hasAuthority('system:permission:view')") public ResultDomain getViewPage(@RequestBody PageRequest pageRequest) { return viewService.getViewPage(pageRequest); } @PostMapping("/list") - @PreAuthorize("@ss.hasPermi('system:permission:view')") + @PreAuthorize("hasAuthority('system:permission:view')") public ResultDomain getViewList(@RequestBody PermissionVO filter) { return viewService.getViewList(filter); } // ================= 视图权限相关接口 ================== @PostMapping("/permission/bind") - @PreAuthorize("@ss.hasPermi('system:permission:manage')") + @PreAuthorize("hasAuthority('system:permission:manage')") public ResultDomain bindViewPermission(@RequestBody PermissionVO permissionVO) { return viewService.setViewPermissions(permissionVO); } @PostMapping("/permission/list") - @PreAuthorize("@ss.hasPermi('system:permission:view')") + @PreAuthorize("hasAuthority('system:permission:view')") public ResultDomain getViewPermissions(@RequestBody PermissionVO permissionVO) { return viewService.getViewPermissionList(permissionVO); } diff --git a/urbanLifelineServ/workcase/pom.xml b/urbanLifelineServ/workcase/pom.xml index dfcafead..d67898ee 100644 --- a/urbanLifelineServ/workcase/pom.xml +++ b/urbanLifelineServ/workcase/pom.xml @@ -46,10 +46,10 @@ org.xyzh.common common-jdbc - + org.springframework.boot spring-boot-starter-web @@ -78,6 +78,21 @@ + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.alibaba.nacos + nacos-logback-adapter-12 + + + com.alibaba.nacos + logback-adapter + + + + com.baomidou mybatis-plus-boot-starter diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/config/WeChatKefuConfig.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/config/WeChatKefuConfig.java deleted file mode 100644 index b637fcca..00000000 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/config/WeChatKefuConfig.java +++ /dev/null @@ -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); - } - -} diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseChatContorller.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseChatContorller.java index 14bb1de7..4b4f924a 100644 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseChatContorller.java +++ b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/controller/WorkcaseChatContorller.java @@ -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 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 = "添加词云") diff --git a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/handler/WorkcaseKefuHandler.java b/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/handler/WorkcaseKefuHandler.java deleted file mode 100644 index d45c76ab..00000000 --- a/urbanLifelineServ/workcase/src/main/java/org/xyzh/workcase/handler/WorkcaseKefuHandler.java +++ /dev/null @@ -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(); - } - -} diff --git a/urbanLifelineServ/workcase/工单流程.md b/urbanLifelineServ/workcase/工单流程.md index 943dc531..1f666c2f 100644 --- a/urbanLifelineServ/workcase/工单流程.md +++ b/urbanLifelineServ/workcase/工单流程.md @@ -1,13 +1,12 @@ # 小程序用户聊天和工单的产生逻辑 接口实现方式: + 0. 用户进行微信小程序,1个IM聊天室,默认回复人员是ai 1. WorkcaseChatServiceImpl通过ai接口进行ai回复,对话人员是来客和ai 2. 当连续3次ai聊天后,询问是否转人工 3. 用户触发转人工(可能是一开始,就手动触发,没有聊天记录),跳转到微信客服的功能服务 4. 用户跳转前,必须创建工单 5. ai根据聊天对话,自动生成部分工单信息,预填入小程序的工单创建的表单, 6. 创建工单后,同步工单到CRM - 7. 将工单信息作为微信客服的欢迎语,进行放送,让来客和员工能看到工单的基本信息,从而实现让员工知道工单是哪一个 - - 8. 同步用户和员工在微信客服上的聊天记录,同步到tb_chat表里面,对话人员是来客和客服。(把ai替换成员工进行对话的续接) + 8. 员工进入聊天室和客户聊天(ai退出聊天室)的聊天记录,同步到tb_chat表里面,对话人员是来客和客服。(把ai替换成员工进行对话的续接) 9. 员工自己更新工单状态,如果在CRM更新工单状态会触发receiveWorkcaseFromCrm,如果在本系统更新工单会触发工单同步到CRM 10. 在工单是完成、撤销后,工单、对话进行总结,并更新词云 \ No newline at end of file diff --git a/urbanLifelineWeb/packages/workcase/src/views/admin/workcase/WorkcaseView.vue b/urbanLifelineWeb/packages/workcase/src/views/admin/workcase/WorkcaseView.vue index 964535d2..0655bea5 100644 --- a/urbanLifelineWeb/packages/workcase/src/views/admin/workcase/WorkcaseView.vue +++ b/urbanLifelineWeb/packages/workcase/src/views/admin/workcase/WorkcaseView.vue @@ -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)