服务调用
This commit is contained in:
12
urbanLifelineServ/.vscode/launch.json
vendored
12
urbanLifelineServ/.vscode/launch.json
vendored
@@ -160,11 +160,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "java",
|
"type": "java",
|
||||||
"name": "Agent (8190)",
|
"name": "AI (8190)",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mainClass": "org.xyzh.agent.AgentApp",
|
"mainClass": "org.xyzh.ai.AiApp",
|
||||||
"projectName": "agent",
|
"projectName": "ai",
|
||||||
"cwd": "${workspaceFolder}/agent",
|
"cwd": "${workspaceFolder}/ai",
|
||||||
"args": [
|
"args": [
|
||||||
"--spring.profiles.active=dev"
|
"--spring.profiles.active=dev"
|
||||||
],
|
],
|
||||||
@@ -200,9 +200,9 @@
|
|||||||
// "Message (8185)",
|
// "Message (8185)",
|
||||||
// "Bidding (8186)",
|
// "Bidding (8186)",
|
||||||
// "Platform (8187)",
|
// "Platform (8187)",
|
||||||
// "Workcase (8188)",
|
"Workcase (8188)",
|
||||||
// "Crontab (8189)",
|
// "Crontab (8189)",
|
||||||
// "Agent (8190)"
|
"AI (8190)"
|
||||||
],
|
],
|
||||||
"stopAll": true,
|
"stopAll": true,
|
||||||
"presentation": {
|
"presentation": {
|
||||||
|
|||||||
@@ -56,6 +56,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.dubbo</groupId>
|
<groupId>org.apache.dubbo</groupId>
|
||||||
@@ -64,6 +70,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mybatis.spring.boot</groupId>
|
<groupId>org.mybatis.spring.boot</groupId>
|
||||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
|
|||||||
@@ -10,15 +10,15 @@ import org.springframework.context.annotation.ComponentScan;
|
|||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableDubbo // 启用 Dubbo 服务
|
@EnableDubbo // 启用 Dubbo 服务
|
||||||
@ComponentScan(basePackages = {
|
@ComponentScan(basePackages = {
|
||||||
"org.xyzh.agent", // 当前agent模块
|
"org.xyzh.ai", // 当前ai模块
|
||||||
"org.xyzh.common" // 公共模块
|
"org.xyzh.common" // 公共模块
|
||||||
})
|
})
|
||||||
public class AgentApp {
|
public class AiApp {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(AgentApp.class);
|
private static final Logger logger = LoggerFactory.getLogger(AiApp.class);
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
logger.info("======================== AgentApp 启动中 =========================");
|
logger.info("======================== AI服务启动中 =========================");
|
||||||
SpringApplication.run(AgentApp.class, args);
|
SpringApplication.run(AiApp.class, args);
|
||||||
logger.info("======================== AgentApp 启动成功 =========================");
|
logger.info("======================== AI服务启动完成 =========================");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,8 @@ package org.xyzh.ai.config;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
|
import org.apache.dubbo.config.annotation.DubboService;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.xyzh.api.system.service.SysConfigService;
|
import org.xyzh.api.system.service.SysConfigService;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ import jakarta.annotation.PostConstruct;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class DifyConfig {
|
public class DifyConfig {
|
||||||
|
|
||||||
@Autowired
|
@DubboReference(version = "1.0.0", group = "system", timeout = 30000, retries = 0)
|
||||||
private SysConfigService sysConfigService;
|
private SysConfigService sysConfigService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package org.xyzh.ai.controller;
|
package org.xyzh.ai.controller;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.xyzh.ai.service.impl.AgentServiceImpl;
|
import org.xyzh.api.ai.service.AgentService;
|
||||||
import org.xyzh.api.ai.dto.TbAgent;
|
import org.xyzh.api.ai.dto.TbAgent;
|
||||||
import org.xyzh.common.core.domain.ResultDomain;
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
import org.xyzh.common.core.page.PageRequest;
|
import org.xyzh.common.core.page.PageRequest;
|
||||||
@@ -30,8 +30,8 @@ import java.util.Arrays;
|
|||||||
@RequestMapping("/ai/agent")
|
@RequestMapping("/ai/agent")
|
||||||
public class AgentController {
|
public class AgentController {
|
||||||
|
|
||||||
@Autowired
|
@DubboReference(version = "1.0.0", group = "ai", timeout = 3000, retries = 0, scope = "local")
|
||||||
private AgentServiceImpl agentService;
|
private AgentService agentService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 创建智能体
|
* @description 创建智能体
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package org.xyzh.ai.controller;
|
|||||||
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -38,10 +38,10 @@ import java.util.Map;
|
|||||||
@RequestMapping("/ai/chat")
|
@RequestMapping("/ai/chat")
|
||||||
public class ChatController {
|
public class ChatController {
|
||||||
|
|
||||||
@Autowired
|
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000, retries = 0, scope = "local")
|
||||||
private AgentChatService chatService;
|
private AgentChatService chatService;
|
||||||
|
|
||||||
@Autowired
|
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000, retries = 0, scope = "local")
|
||||||
private AIFileUploadService fileUploadService;
|
private AIFileUploadService fileUploadService;
|
||||||
|
|
||||||
// ====================== 会话管理 ======================
|
// ====================== 会话管理 ======================
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import jakarta.validation.constraints.NotEmpty;
|
|||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import org.xyzh.ai.service.impl.DifyProxyServiceImpl;
|
import org.xyzh.api.ai.service.DifyProxyService;
|
||||||
import org.xyzh.ai.service.impl.KnowledgeServiceImpl;
|
import org.xyzh.api.ai.service.KnowledgeService;
|
||||||
import org.xyzh.api.ai.dto.TbKnowledge;
|
import org.xyzh.api.ai.dto.TbKnowledge;
|
||||||
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
import org.xyzh.api.ai.dto.TbKnowledgeFile;
|
||||||
import org.xyzh.common.core.domain.ResultDomain;
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
@@ -40,11 +40,11 @@ import java.util.Map;
|
|||||||
public class KnowledgeController {
|
public class KnowledgeController {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(KnowledgeController.class);
|
private static final Logger logger = LoggerFactory.getLogger(KnowledgeController.class);
|
||||||
|
|
||||||
@Autowired
|
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000, retries = 0, scope = "local")
|
||||||
private KnowledgeServiceImpl knowledgeService;
|
private KnowledgeService knowledgeService;
|
||||||
|
|
||||||
@Autowired
|
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000, retries = 0, scope = "local")
|
||||||
private DifyProxyServiceImpl difyProxyService;
|
private DifyProxyService difyProxyService;
|
||||||
|
|
||||||
// ====================== 知识库管理 ======================
|
// ====================== 知识库管理 ======================
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ public class KnowledgeController {
|
|||||||
* @since 2025-12-18
|
* @since 2025-12-18
|
||||||
*/
|
*/
|
||||||
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:upload')")
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:upload')")
|
||||||
@PostMapping(name = "/file/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
@PostMapping(value = "/file/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
public ResultDomain<TbKnowledgeFile> uploadToKnowledge(
|
public ResultDomain<TbKnowledgeFile> uploadToKnowledge(
|
||||||
@RequestParam("file") @NotNull MultipartFile file,
|
@RequestParam("file") @NotNull MultipartFile file,
|
||||||
@RequestParam("knowledgeId") @NotBlank String knowledgeId,
|
@RequestParam("knowledgeId") @NotBlank String knowledgeId,
|
||||||
@@ -201,7 +201,7 @@ public class KnowledgeController {
|
|||||||
* @since 2025-12-18
|
* @since 2025-12-18
|
||||||
*/
|
*/
|
||||||
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:upload')")
|
@PreAuthorize("@ss.hasPermission('ai:knowledge:file:upload')")
|
||||||
@PostMapping(name = "/file/batch-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
@PostMapping(value = "/file/batch-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
public ResultDomain<TbKnowledgeFile> batchUploadToKnowledge(
|
public ResultDomain<TbKnowledgeFile> batchUploadToKnowledge(
|
||||||
@RequestParam("files") @NotEmpty List<MultipartFile> files,
|
@RequestParam("files") @NotEmpty List<MultipartFile> files,
|
||||||
@RequestParam("knowledgeId") @NotBlank String knowledgeId,
|
@RequestParam("knowledgeId") @NotBlank String knowledgeId,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.xyzh.ai.service.impl;
|
package org.xyzh.ai.service.impl;
|
||||||
|
|
||||||
|
import org.apache.dubbo.config.annotation.DubboReference;
|
||||||
import org.apache.dubbo.config.annotation.DubboService;
|
import org.apache.dubbo.config.annotation.DubboService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -12,6 +13,7 @@ import org.xyzh.ai.client.dto.DocumentUploadRequest;
|
|||||||
import org.xyzh.ai.client.dto.DocumentUploadResponse;
|
import org.xyzh.ai.client.dto.DocumentUploadResponse;
|
||||||
import org.xyzh.api.ai.dto.TbAgent;
|
import org.xyzh.api.ai.dto.TbAgent;
|
||||||
import org.xyzh.api.ai.service.AIFileUploadService;
|
import org.xyzh.api.ai.service.AIFileUploadService;
|
||||||
|
import org.xyzh.api.ai.service.AgentService;
|
||||||
import org.xyzh.common.auth.utils.LoginUtil;
|
import org.xyzh.common.auth.utils.LoginUtil;
|
||||||
import org.xyzh.common.core.domain.ResultDomain;
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
|
|
||||||
@@ -33,8 +35,8 @@ public class AIFileUploadServiceImpl implements AIFileUploadService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private DifyApiClient difyApiClient;
|
private DifyApiClient difyApiClient;
|
||||||
|
|
||||||
@Autowired
|
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000, retries = 0, scope = "local")
|
||||||
private AgentServiceImpl agentService;
|
private AgentService agentService;
|
||||||
|
|
||||||
// ============================ 对话文件管理 ============================
|
// ============================ 对话文件管理 ============================
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class AgentChatServiceImpl implements AgentChatService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TbChatMessageMapper chatMessageMapper;
|
private TbChatMessageMapper chatMessageMapper;
|
||||||
|
|
||||||
@DubboReference
|
@DubboReference(version = "1.0.0", group = "ai", timeout = 3000, retries = 0, scope = "local")
|
||||||
private AgentService agentService;
|
private AgentService agentService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ public class KnowledgeServiceImpl implements KnowledgeService {
|
|||||||
@DubboReference(version = "1.0.0", group = "file", timeout = 30000)
|
@DubboReference(version = "1.0.0", group = "file", timeout = 30000)
|
||||||
private FileService fileService;
|
private FileService fileService;
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000)
|
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000, scope = "local")
|
||||||
private AIFileUploadService aiFileUploadService;
|
private AIFileUploadService aiFileUploadService;
|
||||||
|
|
||||||
// ================================= 知识库管理 =================================
|
// ================================= 知识库管理 =================================
|
||||||
|
|||||||
85
urbanLifelineServ/ai/src/main/resources/application-dev.yml
Normal file
85
urbanLifelineServ/ai/src/main/resources/application-dev.yml
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# ================== Server ==================
|
||||||
|
server:
|
||||||
|
port: 8090
|
||||||
|
# servlet:
|
||||||
|
# context-path: /urban-lifeline/agent
|
||||||
|
|
||||||
|
# ================== Auth ====================
|
||||||
|
auth:
|
||||||
|
enabled: true
|
||||||
|
gateway-mode: true
|
||||||
|
whitelist:
|
||||||
|
- /swagger-ui/**
|
||||||
|
- /swagger-ui.html
|
||||||
|
- /v3/api-docs/**
|
||||||
|
- /webjars/**
|
||||||
|
- /favicon.ico
|
||||||
|
- /error
|
||||||
|
- /actuator/health
|
||||||
|
- /actuator/info
|
||||||
|
- /ai/chat/* # AI对话,有非系统用户对话的接口,无登录状态
|
||||||
|
|
||||||
|
security:
|
||||||
|
aes:
|
||||||
|
# AES-256 密钥(Base64编码,必须与所有服务保持一致)
|
||||||
|
# 警告:这是开发环境密钥,生产环境请使用密钥管理系统
|
||||||
|
secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
|
||||||
|
|
||||||
|
# ================== Spring ==================
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: ai-service
|
||||||
|
|
||||||
|
# ================== Spring Cloud Nacos ==================
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
server-addr: 127.0.0.1:8848
|
||||||
|
namespace: dev
|
||||||
|
group: DEFAULT_GROUP
|
||||||
|
|
||||||
|
# ================== DataSource ==================
|
||||||
|
datasource:
|
||||||
|
url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline
|
||||||
|
username: postgres
|
||||||
|
password: postgres
|
||||||
|
driver-class-name: org.postgresql.Driver
|
||||||
|
|
||||||
|
# ================== Redis ==================
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 6379
|
||||||
|
database: 0
|
||||||
|
# password: ""
|
||||||
|
|
||||||
|
# ================== SpringDoc ==================
|
||||||
|
springdoc:
|
||||||
|
api-docs:
|
||||||
|
enabled: true
|
||||||
|
path: /v3/api-docs
|
||||||
|
swagger-ui:
|
||||||
|
enabled: true
|
||||||
|
path: /swagger-ui.html
|
||||||
|
group-configs:
|
||||||
|
- group: 'default'
|
||||||
|
display-name: 'AI代理服务 API'
|
||||||
|
paths-to-match: '/**'
|
||||||
|
|
||||||
|
# ================== Dubbo + Nacos ==================
|
||||||
|
dubbo:
|
||||||
|
application:
|
||||||
|
name: urban-lifeline-agent
|
||||||
|
qos-enable: false
|
||||||
|
protocol:
|
||||||
|
name: dubbo
|
||||||
|
port: -1
|
||||||
|
registry:
|
||||||
|
address: nacos://127.0.0.1:8848
|
||||||
|
scan:
|
||||||
|
base-packages: org.xyzh.ai.service.impl
|
||||||
|
|
||||||
|
# ================== MyBatis ==================
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath:mapper/**/*.xml
|
||||||
|
type-aliases-package: org.xyzh.common.dto, org.xyzh.api
|
||||||
@@ -21,12 +21,14 @@ auth:
|
|||||||
|
|
||||||
security:
|
security:
|
||||||
aes:
|
aes:
|
||||||
secret-key: 1234567890qwer
|
# AES-256 密钥(Base64编码,必须与所有服务保持一致)
|
||||||
|
# 警告:这是开发环境密钥,生产环境请使用密钥管理系统
|
||||||
|
secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
|
||||||
|
|
||||||
# ================== Spring ==================
|
# ================== Spring ==================
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: agent-service
|
name: ai-service
|
||||||
|
|
||||||
# ================== Spring Cloud Nacos ==================
|
# ================== Spring Cloud Nacos ==================
|
||||||
cloud:
|
cloud:
|
||||||
@@ -75,7 +77,7 @@ dubbo:
|
|||||||
registry:
|
registry:
|
||||||
address: nacos://127.0.0.1:8848
|
address: nacos://127.0.0.1:8848
|
||||||
scan:
|
scan:
|
||||||
base-packages: org.xyzh.agent.service.impl
|
base-packages: org.xyzh.ai.service.impl
|
||||||
|
|
||||||
# ================== MyBatis ==================
|
# ================== MyBatis ==================
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<Properties>
|
<Properties>
|
||||||
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
|
<property name="LOG_PATTERN" value="%date{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
|
||||||
<property name="FILE_PATH" value="./logs" />
|
<property name="FILE_PATH" value="./logs" />
|
||||||
<property name="FILE_NAME" value="agent-service" />
|
<property name="FILE_NAME" value="ai-service" />
|
||||||
<property name="file.encoding" value="UTF-8" />
|
<property name="file.encoding" value="UTF-8" />
|
||||||
<property name="console.encoding" value="UTF-8" />
|
<property name="console.encoding" value="UTF-8" />
|
||||||
</Properties>
|
</Properties>
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package org.xyzh.file.controller;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
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;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.xyzh.api.file.dto.TbSysFileDTO;
|
||||||
|
import org.xyzh.api.file.service.FileService;
|
||||||
|
import org.xyzh.common.core.domain.ResultDomain;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 文件管理控制器
|
||||||
|
* @filename FileController.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-12-19
|
||||||
|
*/
|
||||||
|
@Tag(name = "文件管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/file")
|
||||||
|
public class FileController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FileService fileService;
|
||||||
|
|
||||||
|
// ========================= 文件上传 =========================
|
||||||
|
|
||||||
|
@Operation(summary = "上传文件")
|
||||||
|
@PostMapping("/upload")
|
||||||
|
public ResultDomain<TbSysFileDTO> uploadFile(
|
||||||
|
@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam(value = "module", required = false) String module,
|
||||||
|
@RequestParam(value = "businessId", required = false) String businessId) {
|
||||||
|
return fileService.uploadFile(file, module, businessId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "批量上传文件")
|
||||||
|
@PostMapping("/upload/batch")
|
||||||
|
public ResultDomain<TbSysFileDTO> batchUploadFiles(
|
||||||
|
@RequestParam("files") MultipartFile[] files,
|
||||||
|
@RequestParam(value = "module", required = false) String module,
|
||||||
|
@RequestParam(value = "businessId", required = false) String businessId,
|
||||||
|
@RequestParam(value = "uploader", required = false) String uploader) {
|
||||||
|
return fileService.batchUploadFiles(files, module, businessId, uploader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "上传临时文件")
|
||||||
|
@PostMapping("/upload/temp")
|
||||||
|
public ResultDomain<TbSysFileDTO> saveTempFile(
|
||||||
|
@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam(value = "module", required = false) String module,
|
||||||
|
@RequestParam(value = "businessId", required = false) String businessId) {
|
||||||
|
return fileService.saveTempFile(file, module, businessId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "上传新版本文件")
|
||||||
|
@PostMapping("/upload/version")
|
||||||
|
public ResultDomain<TbSysFileDTO> uploadFileVersion(
|
||||||
|
@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam(value = "module", required = false) String module,
|
||||||
|
@RequestParam(value = "businessId", required = false) String businessId,
|
||||||
|
@RequestParam("fileRootId") String fileRootId) {
|
||||||
|
return fileService.uploadFileVersion(file, module, businessId, fileRootId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================= 文件查询 =========================
|
||||||
|
|
||||||
|
@Operation(summary = "获取文件信息")
|
||||||
|
@GetMapping("/{fileId}")
|
||||||
|
public ResultDomain<TbSysFileDTO> getFileById(@PathVariable String fileId) {
|
||||||
|
return fileService.getFileById(fileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================= 文件下载 =========================
|
||||||
|
|
||||||
|
@Operation(summary = "下载文件")
|
||||||
|
@GetMapping("/download/{fileId}")
|
||||||
|
public ResponseEntity<byte[]> downloadFile(@PathVariable String fileId) {
|
||||||
|
ResultDomain<byte[]> result = fileService.downloadFile(fileId);
|
||||||
|
if (!result.getSuccess() || result.getData() == null) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultDomain<TbSysFileDTO> fileInfo = fileService.getFileById(fileId);
|
||||||
|
String filename = fileInfo.getData() != null ? fileInfo.getData().getName() : "download";
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
|
||||||
|
.contentType(MediaType.APPLICATION_OCTET_STREAM)
|
||||||
|
.body(result.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================= 文件删除 =========================
|
||||||
|
|
||||||
|
@Operation(summary = "删除文件")
|
||||||
|
@DeleteMapping("/{fileId}")
|
||||||
|
public ResultDomain<Boolean> deleteFile(@PathVariable String fileId) {
|
||||||
|
return fileService.deleteFile(fileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "批量删除文件")
|
||||||
|
@DeleteMapping("/batch")
|
||||||
|
public ResultDomain<TbSysFileDTO> batchDeleteFiles(@RequestParam("fileIds") String[] fileIds) {
|
||||||
|
return fileService.batchDeleteFiles(fileIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -20,7 +20,9 @@ urban-lifeline:
|
|||||||
|
|
||||||
security:
|
security:
|
||||||
aes:
|
aes:
|
||||||
secret-key: 1234567890qwer
|
# AES-256 密钥(Base64编码,必须与所有服务保持一致)
|
||||||
|
# 警告:这是开发环境密钥,生产环境请使用密钥管理系统
|
||||||
|
secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
|
||||||
|
|
||||||
# ================== Spring ==================
|
# ================== Spring ==================
|
||||||
spring:
|
spring:
|
||||||
|
|||||||
@@ -108,11 +108,11 @@ spring:
|
|||||||
filters:
|
filters:
|
||||||
- StripPrefix=1
|
- StripPrefix=1
|
||||||
|
|
||||||
# ==================== AI Agent 服务路由 ====================
|
# ==================== AI 服务路由 ====================
|
||||||
- id: agent-service
|
- id: ai-service
|
||||||
uri: lb://agent-service
|
uri: lb://ai-service
|
||||||
predicates:
|
predicates:
|
||||||
- Path=/urban-lifeline/agent/**
|
- Path=/urban-lifeline/ai/**
|
||||||
filters:
|
filters:
|
||||||
- StripPrefix=1
|
- StripPrefix=1
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class DeptRoleController {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(DeptRoleController.class);
|
private static final Logger logger = LoggerFactory.getLogger(DeptRoleController.class);
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0)
|
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0, scope = "local")
|
||||||
private DeptRoleService deptRoleService;
|
private DeptRoleService deptRoleService;
|
||||||
|
|
||||||
// ================= 部门角色相关接口 =================
|
// ================= 部门角色相关接口 =================
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import jakarta.validation.constraints.NotNull;
|
|||||||
@RequestMapping("/system/guest")
|
@RequestMapping("/system/guest")
|
||||||
public class GuestController {
|
public class GuestController {
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0)
|
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0, scope = "local")
|
||||||
private GuestService guestService;
|
private GuestService guestService;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class PermissionController {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(PermissionController.class);
|
private static final Logger logger = LoggerFactory.getLogger(PermissionController.class);
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0)
|
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0, scope = "local")
|
||||||
private ModulePermissionService modulePermissionService;
|
private ModulePermissionService modulePermissionService;
|
||||||
|
|
||||||
// ================= 模块相关接口 =================
|
// ================= 模块相关接口 =================
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class SysConfigController {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(SysConfigController.class);
|
private static final Logger logger = LoggerFactory.getLogger(SysConfigController.class);
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0)
|
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0, scope = "local")
|
||||||
private SysConfigService sysConfigService;
|
private SysConfigService sysConfigService;
|
||||||
|
|
||||||
// ================= 系统配置相关接口 =================
|
// ================= 系统配置相关接口 =================
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public class UserController {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
|
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0)
|
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0, scope = "local")
|
||||||
private SysUserService sysUserService;
|
private SysUserService sysUserService;
|
||||||
|
|
||||||
// ================= 用户相关接口 =================
|
// ================= 用户相关接口 =================
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class ViewController {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(ViewController.class);
|
private static final Logger logger = LoggerFactory.getLogger(ViewController.class);
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0)
|
@DubboReference(version = "1.0.0", group = "system", timeout = 3000, retries = 0, scope = "local")
|
||||||
private ViewService viewService;
|
private ViewService viewService;
|
||||||
|
|
||||||
// ================= 视图相关接口 =================
|
// ================= 视图相关接口 =================
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<id column="user_id" property="userId" jdbcType="VARCHAR"/>
|
<id column="user_id" property="userId" jdbcType="VARCHAR"/>
|
||||||
<result column="password" property="password" jdbcType="VARCHAR"/>
|
<result column="password" property="password" jdbcType="VARCHAR"/>
|
||||||
<result column="email" property="email" jdbcType="VARCHAR"/>
|
<result column="email" property="email" jdbcType="VARCHAR"/>
|
||||||
<result column="phone" property="phone" jdbcType="VARCHAR" typeHandler="org.xyzh.common.utils.crypto.EncryptedStringTypeHandler"/>
|
<result column="phone" property="phone" jdbcType="VARCHAR" typeHandler="org.xyzh.common.jdbc.handler.EncryptedStringTypeHandler"/>
|
||||||
<result column="wechat_id" property="wechatId" jdbcType="VARCHAR"/>
|
<result column="wechat_id" property="wechatId" jdbcType="VARCHAR"/>
|
||||||
<result column="status" property="status" jdbcType="INTEGER"/>
|
<result column="status" property="status" jdbcType="INTEGER"/>
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<id column="user_id" property="userId" jdbcType="VARCHAR"/>
|
<id column="user_id" property="userId" jdbcType="VARCHAR"/>
|
||||||
<result column="password" property="password" jdbcType="VARCHAR"/>
|
<result column="password" property="password" jdbcType="VARCHAR"/>
|
||||||
<result column="email" property="email" jdbcType="VARCHAR"/>
|
<result column="email" property="email" jdbcType="VARCHAR"/>
|
||||||
<result column="phone" property="phone" jdbcType="VARCHAR" typeHandler="org.xyzh.common.utils.crypto.EncryptedStringTypeHandler"/>
|
<result column="phone" property="phone" jdbcType="VARCHAR" typeHandler="org.xyzh.common.jdbc.handler.EncryptedStringTypeHandler"/>
|
||||||
<result column="wechat_id" property="wechatId" jdbcType="VARCHAR"/>
|
<result column="wechat_id" property="wechatId" jdbcType="VARCHAR"/>
|
||||||
<result column="status" property="status" jdbcType="INTEGER"/>
|
<result column="status" property="status" jdbcType="INTEGER"/>
|
||||||
<result column="creator" property="creator" jdbcType="VARCHAR"/>
|
<result column="creator" property="creator" jdbcType="VARCHAR"/>
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
#{password},
|
#{password},
|
||||||
<!-- 可空/有默认值字段对应的值 -->
|
<!-- 可空/有默认值字段对应的值 -->
|
||||||
<if test="email != null and email != ''">#{email},</if>
|
<if test="email != null and email != ''">#{email},</if>
|
||||||
<if test="phone != null and phone != ''">#{phone, typeHandler=org.xyzh.common.utils.crypto.EncryptedStringTypeHandler},</if>
|
<if test="phone != null and phone != ''">#{phone, typeHandler=org.xyzh.common.jdbc.handler.EncryptedStringTypeHandler},</if>
|
||||||
<if test="wechatId != null and wechatId != ''">#{wechatId},</if>
|
<if test="wechatId != null and wechatId != ''">#{wechatId},</if>
|
||||||
<if test="createTime != null">#{createTime},</if>
|
<if test="createTime != null">#{createTime},</if>
|
||||||
<if test="updateTime != null">#{updateTime},</if>
|
<if test="updateTime != null">#{updateTime},</if>
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
email = #{email},
|
email = #{email},
|
||||||
</if>
|
</if>
|
||||||
<if test="phone != null and phone != ''">
|
<if test="phone != null and phone != ''">
|
||||||
phone = #{phone, typeHandler=org.xyzh.common.utils.crypto.EncryptedStringTypeHandler},
|
phone = #{phone, typeHandler=org.xyzh.common.jdbc.handler.EncryptedStringTypeHandler},
|
||||||
</if>
|
</if>
|
||||||
<if test="wechatId != null and wechatId != ''">
|
<if test="wechatId != null and wechatId != ''">
|
||||||
wechat_id = #{wechatId},
|
wechat_id = #{wechatId},
|
||||||
|
|||||||
@@ -42,6 +42,10 @@
|
|||||||
<groupId>org.xyzh.common</groupId>
|
<groupId>org.xyzh.common</groupId>
|
||||||
<artifactId>common-exception</artifactId>
|
<artifactId>common-exception</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.xyzh.common</groupId>
|
||||||
|
<artifactId>common-jdbc</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.xyzh.common</groupId>
|
<groupId>org.xyzh.common</groupId>
|
||||||
<artifactId>common-wechat</artifactId>
|
<artifactId>common-wechat</artifactId>
|
||||||
@@ -62,6 +66,11 @@
|
|||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -43,7 +43,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
@RequestMapping("/workcase/chat")
|
@RequestMapping("/workcase/chat")
|
||||||
public class WorkcaseChatContorller {
|
public class WorkcaseChatContorller {
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "workcase", check = false)
|
@DubboReference(version = "1.0.0", group = "workcase", check = false, scope = "local")
|
||||||
private WorkcaseChatService workcaseChatService;
|
private WorkcaseChatService workcaseChatService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
|||||||
@RequestMapping("/workcase")
|
@RequestMapping("/workcase")
|
||||||
public class WorkcaseController {
|
public class WorkcaseController {
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "workcase", check = false)
|
@DubboReference(version = "1.0.0", group = "workcase", check = false, scope = "local")
|
||||||
private WorkcaseService workcaseService;
|
private WorkcaseService workcaseService;
|
||||||
|
|
||||||
// ========================= 工单管理 =========================
|
// ========================= 工单管理 =========================
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ public class WorkcaseKefuHandler implements KefuMessageHandler {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private KefuMessageService kefuMessageService;
|
private KefuMessageService kefuMessageService;
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "workcase", check = false)
|
@DubboReference(version = "1.0.0", group = "workcase", check = false, scope = "local")
|
||||||
private WorkcaseService workcaseService;
|
private WorkcaseService workcaseService;
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "workcase", check = false)
|
@DubboReference(version = "1.0.0", group = "workcase", check = false, scope = "local")
|
||||||
private WorkcaseChatService workcaseChatService;
|
private WorkcaseChatService workcaseChatService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class WorkcaseServiceImpl implements WorkcaseService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TbWorkcaseDeviceMapper workcaseDeviceMapper;
|
private TbWorkcaseDeviceMapper workcaseDeviceMapper;
|
||||||
|
|
||||||
@DubboReference(version = "1.0.0", group = "workcase", check = false)
|
@DubboReference(version = "1.0.0", group = "workcase", check = false, scope = "local")
|
||||||
private WorkcaseChatService workcaseChatService;
|
private WorkcaseChatService workcaseChatService;
|
||||||
|
|
||||||
// ====================== 工单管理 ======================
|
// ====================== 工单管理 ======================
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# ================== Server ==================
|
||||||
|
server:
|
||||||
|
port: 8088
|
||||||
|
# servlet:
|
||||||
|
# context-path: /urban-lifeline/workcase
|
||||||
|
|
||||||
|
# ================== Auth ====================
|
||||||
|
auth:
|
||||||
|
enabled: true
|
||||||
|
gate-way: true
|
||||||
|
whitelist:
|
||||||
|
- /swagger-ui/**
|
||||||
|
- /swagger-ui.html
|
||||||
|
- /v3/api-docs/**
|
||||||
|
- /webjars/**
|
||||||
|
- /favicon.ico
|
||||||
|
- /error
|
||||||
|
- /actuator/health
|
||||||
|
- /actuator/info
|
||||||
|
# 微信客服回调接口(无需鉴权)
|
||||||
|
- /workcase/chat/kefu/callback
|
||||||
|
# CRM回调接口(无需鉴权,但需签名验证)
|
||||||
|
- /workcase/receive/crm
|
||||||
|
|
||||||
|
security:
|
||||||
|
aes:
|
||||||
|
# AES-256 密钥(Base64编码,必须与所有服务保持一致)
|
||||||
|
# 警告:这是开发环境密钥,生产环境请使用密钥管理系统
|
||||||
|
secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
|
||||||
|
|
||||||
|
# ================== Spring ==================
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: workcase-service
|
||||||
|
|
||||||
|
# ================== Spring Cloud Nacos ==================
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
server-addr: 127.0.0.1:8848
|
||||||
|
namespace: dev
|
||||||
|
group: DEFAULT_GROUP
|
||||||
|
|
||||||
|
# ================== DataSource ==================
|
||||||
|
datasource:
|
||||||
|
url: jdbc:postgresql://127.0.0.1:5432/urban_lifeline
|
||||||
|
username: postgres
|
||||||
|
password: postgres
|
||||||
|
driver-class-name: org.postgresql.Driver
|
||||||
|
|
||||||
|
# ================== Redis ==================
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 6379
|
||||||
|
database: 0
|
||||||
|
# password: ""
|
||||||
|
|
||||||
|
# ================== SpringDoc ==================
|
||||||
|
springdoc:
|
||||||
|
api-docs:
|
||||||
|
enabled: true
|
||||||
|
path: /v3/api-docs
|
||||||
|
swagger-ui:
|
||||||
|
enabled: true
|
||||||
|
path: /swagger-ui.html
|
||||||
|
group-configs:
|
||||||
|
- group: 'default'
|
||||||
|
display-name: '工单服务 API'
|
||||||
|
paths-to-match: '/**'
|
||||||
|
|
||||||
|
# ================== Dubbo + Nacos ==================
|
||||||
|
dubbo:
|
||||||
|
application:
|
||||||
|
name: urban-lifeline-workcase
|
||||||
|
qos-enable: false
|
||||||
|
protocol:
|
||||||
|
name: dubbo
|
||||||
|
port: -1
|
||||||
|
registry:
|
||||||
|
address: nacos://127.0.0.1:8848
|
||||||
|
scan:
|
||||||
|
base-packages: org.xyzh.workcase.service.impl
|
||||||
|
|
||||||
|
# ================== MyBatis ==================
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath:mapper/**/*.xml
|
||||||
|
type-aliases-package: org.xyzh.common.dto, org.xyzh.api
|
||||||
@@ -24,7 +24,9 @@ auth:
|
|||||||
|
|
||||||
security:
|
security:
|
||||||
aes:
|
aes:
|
||||||
secret-key: 1234567890qwer
|
# AES-256 密钥(Base64编码,必须与所有服务保持一致)
|
||||||
|
# 警告:这是开发环境密钥,生产环境请使用密钥管理系统
|
||||||
|
secret-key: MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=
|
||||||
|
|
||||||
# ================== Spring ==================
|
# ================== Spring ==================
|
||||||
spring:
|
spring:
|
||||||
|
|||||||
Reference in New Issue
Block a user