diff --git a/nl-plugin-tool-api/src/main/java/org/nl/tool/api/MockApi.java b/nl-plugin-tool-api/src/main/java/org/nl/tool/api/MockApi.java
new file mode 100644
index 0000000..2143fce
--- /dev/null
+++ b/nl-plugin-tool-api/src/main/java/org/nl/tool/api/MockApi.java
@@ -0,0 +1,22 @@
+package org.nl.tool.api;
+
+import com.alibaba.fastjson.JSONObject;
+import org.nl.tool.core.enums.HttpMethodEnum;
+
+/**
+ * mock-api提供者接口
+ * @author: lyd
+ * @date: 2026/1/28
+ */
+public interface MockApi {
+
+ /**
+ * 通用的执行请求
+ * @param url 路径(ip:端口号/api地址)
+ * @param method 方法类型:GET、POST、PUT、DELETED
+ * @param body 数据内容:JSONObject.toString
+ * @see JSONObject#toJSONString()
+ * @return
+ */
+ String executeRequestCommon(String url, HttpMethodEnum method, String body);
+}
diff --git a/nl-plugin-tool-api/src/main/java/org/nl/tool/core/enums/HttpMethodEnum.java b/nl-plugin-tool-api/src/main/java/org/nl/tool/core/enums/HttpMethodEnum.java
new file mode 100644
index 0000000..ec87d29
--- /dev/null
+++ b/nl-plugin-tool-api/src/main/java/org/nl/tool/core/enums/HttpMethodEnum.java
@@ -0,0 +1,22 @@
+package org.nl.tool.core.enums;
+
+import lombok.Getter;
+
+/**
+ *
+ * @author: lyd
+ * @date: 2026/1/29
+ */
+@Getter
+public enum HttpMethodEnum {
+ GET("GET"),
+ POST("POST"),
+ PUT("PUT"),
+ DELETE("DELETE");
+
+ private final String code;
+
+ HttpMethodEnum(String code) {
+ this.code = code;
+ }
+}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/config/MockConfigProperties.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/config/MockConfigProperties.java
new file mode 100644
index 0000000..3a9971f
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/config/MockConfigProperties.java
@@ -0,0 +1,44 @@
+package org.nl.tool.mock.core.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * Mock配置属性类
+ * 使用@ConfigurationProperties从application.yml中绑定mock配置
+ *
+ * 配置示例:
+ * mock:
+ * enabled: true
+ * cache-expiration-seconds: 300
+ * log-mock-usage: true
+ * @author: lyd
+ * @date: 2026/1/28
+ */
+@Component
+@ConfigurationProperties(prefix = "mock")
+@Data
+public class MockConfigProperties {
+
+ /**
+ * 全局Mock功能开关
+ * 当设置为false时,系统将绕过所有Mock逻辑,直接执行实际请求
+ * 默认值:true
+ */
+ private boolean enabled = true;
+
+ /**
+ * 缓存过期时间(秒)
+ * Mock配置在缓存中的存活时间,超过此时间后将从数据库重新加载
+ * 默认值:300秒(5分钟)
+ */
+ private long cacheExpirationSeconds = 300;
+
+ /**
+ * 是否记录Mock使用日志
+ * 当设置为true时,系统将记录每次使用Mock数据的详细信息
+ * 默认值:true
+ */
+ private boolean logMockUsage = true;
+}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/handle/HttpClientWrapper.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/handle/HttpClientWrapper.java
new file mode 100644
index 0000000..1a2d45d
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/handle/HttpClientWrapper.java
@@ -0,0 +1,189 @@
+package org.nl.tool.mock.core.handle;
+
+import cn.hutool.http.HttpRequest;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.nl.tool.mock.core.config.MockConfigProperties;
+import org.nl.tool.mock.modular.mockconfig.entity.MockConfig;
+import org.springframework.stereotype.Component;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Optional;
+
+/**
+ * HTTP客户端封装器
+ * 封装Hutool的HTTP请求方法,提供出站Mock功能
+ *
+ * 工作流程:
+ * 1. 检查全局Mock开关
+ * 2. 从URL提取路径
+ * 3. 查询Mock配置
+ * 4. 如果Mock启用,返回Mock数据;否则发起实际HTTP请求
+ *
+ * 支持的HTTP方法:GET, POST, PUT, DELETE
+ * @author: lyd
+ * @date: 2026/1/28
+ */
+@Component
+@Slf4j
+public class HttpClientWrapper {
+ @Resource
+ private MockService mockService;
+ @Resource
+ private MockConfigProperties properties;
+ /**
+ * 发起GET请求
+ *
+ * @param url 目标URL
+ * @return 响应内容
+ */
+ public String get(String url) {
+ return executeRequest(url, "GET", null);
+ }
+
+ /**
+ * 发起POST请求
+ *
+ * @param url 目标URL
+ * @param body 请求体
+ * @return 响应内容
+ */
+ public String post(String url, String body) {
+ return executeRequest(url, "POST", body);
+ }
+
+ /**
+ * 发起PUT请求
+ *
+ * @param url 目标URL
+ * @param body 请求体
+ * @return 响应内容
+ */
+ public String put(String url, String body) {
+ return executeRequest(url, "PUT", body);
+ }
+
+ /**
+ * 发起DELETE请求
+ *
+ * @param url 目标URL
+ * @return 响应内容
+ */
+ public String delete(String url) {
+ return executeRequest(url, "DELETE", null);
+ }
+
+ /**
+ * 执行HTTP请求的核心逻辑
+ *
+ * 流程:
+ * 1. 检查全局Mock开关,如果禁用则直接发起实际请求
+ * 2. 从URL提取路径
+ * 3. 查询Mock配置
+ * 4. 如果Mock配置存在且启用,返回Mock数据
+ * 5. 否则发起实际HTTP请求
+ *
+ * @param url 目标URL
+ * @param method HTTP方法
+ * @param body 请求体(可为null)
+ * @return 响应内容
+ */
+ private String executeRequest(String url, String method, String body) {
+ // 检查全局Mock开关
+ if (!properties.isEnabled()) {
+ log.debug("Mock is globally disabled, making real request to {} {}", method, url);
+ return makeRealRequest(url, method, body);
+ }
+
+ try {
+ // 从URL提取路径
+ String path = extractPath(url);
+
+ // 查询Mock配置
+ Optional mockConfig = mockService.getMockConfig(path, method);
+
+ // 如果Mock配置存在且启用,返回Mock数据
+ if (mockConfig.isPresent() && mockConfig.get().getIsEnabled()) {
+ if (properties.isLogMockUsage()) {
+ log.info("Mock response used for outbound {} {}", method, url);
+ }
+ return mockConfig.get().getResponseJson();
+ }
+
+ // Mock未启用或不存在,发起实际请求
+ log.debug("No active mock config found for {} {}, making real request", method, path);
+ return makeRealRequest(url, method, body);
+
+ } catch (Exception e) {
+ log.error("Error during mock check for {} {}, falling back to real request: {}",
+ method, url, e.getMessage());
+ return makeRealRequest(url, method, body);
+ }
+ }
+
+ /**
+ * 发起实际的HTTP请求
+ * 使用Hutool的HttpRequest工具类
+ *
+ * @param url 目标URL
+ * @param method HTTP方法
+ * @param body 请求体(可为null)
+ * @return 响应内容
+ * @throws IllegalArgumentException 如果HTTP方法不支持
+ */
+ private String makeRealRequest(String url, String method, String body) {
+ log.debug("Making real HTTP request: {} {}", method, url);
+
+ try {
+ String response = switch (method.toUpperCase()) {
+ case "GET" -> HttpRequest.get(url).execute().body();
+ case "POST" -> HttpRequest.post(url).body(body).execute().body();
+ case "PUT" -> HttpRequest.put(url).body(body).execute().body();
+ case "DELETE" -> HttpRequest.delete(url).execute().body();
+ default -> {
+ log.error("Unsupported HTTP method: {}", method);
+ throw new IllegalArgumentException("Unsupported HTTP method: " + method);
+ }
+ };
+
+ log.debug("Real HTTP request completed: {} {}, response length: {}",
+ method, url, response != null ? response.length() : 0);
+ return response;
+
+ } catch (Exception e) {
+ log.error("Error making real HTTP request to {} {}: {}", method, url, e.getMessage(), e);
+ throw new RuntimeException("Failed to make HTTP request: " + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 从完整URL中提取路径部分
+ *
+ * 例如:
+ * - "http://example.com/api/user/info" -> "/api/user/info"
+ * - "https://example.com:8080/api/data?id=1" -> "/api/data"
+ *
+ * @param url 完整URL
+ * @return URL的路径部分
+ * @throws IllegalArgumentException 如果URL格式无效
+ */
+ private String extractPath(String url) {
+ try {
+ URI uri = new URI(url);
+ String path = uri.getPath();
+
+ if (path == null || path.isEmpty()) {
+ log.warn("URL has no path component: {}, using root path '/'", url);
+ return "/";
+ }
+
+ log.debug("Extracted path '{}' from URL '{}'", path, url);
+ return path;
+
+ } catch (URISyntaxException e) {
+ log.error("Invalid URL format: {}", url, e);
+ throw new IllegalArgumentException("Invalid URL: " + url, e);
+ }
+ }
+}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/handle/MockCacheService.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/handle/MockCacheService.java
new file mode 100644
index 0000000..1832ae5
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/handle/MockCacheService.java
@@ -0,0 +1,131 @@
+package org.nl.tool.mock.core.handle;
+
+import lombok.extern.slf4j.Slf4j;
+import org.nl.tool.mock.core.config.MockConfigProperties;
+import org.nl.tool.mock.modular.mockconfig.entity.MockConfig;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Mock配置缓存服务
+ * 使用ConcurrentHashMap实现线程安全的内存缓存,提升Mock配置查询性能
+ * 支持缓存过期机制,确保数据的时效性
+ * @author: lyd
+ * @date: 2026/1/29
+ */
+@Service
+@Slf4j
+public class MockCacheService {
+ private final ConcurrentHashMap> cache;
+ private final long cacheExpirationMs;
+
+ /**
+ * 构造函数,初始化缓存和过期时间
+ *
+ * @param properties Mock配置属性,包含缓存过期时间配置
+ */
+ public MockCacheService(MockConfigProperties properties) {
+ this.cache = new ConcurrentHashMap<>();
+ this.cacheExpirationMs = properties.getCacheExpirationSeconds() * 1000;
+ log.info("MockCacheService initialized with expiration time: {} ms", cacheExpirationMs);
+ }
+
+ /**
+ * 从缓存中获取Mock配置
+ * 如果缓存条目已过期,将自动移除并返回空
+ *
+ * @param cacheKey 缓存键,格式为 "apiPath:apiMethod"
+ * @return Optional包装的MockConfig,如果不存在或已过期则返回空
+ */
+ public Optional get(String cacheKey) {
+ CacheEntry entry = cache.get(cacheKey);
+
+ if (entry == null) {
+ log.debug("Cache miss for key: {}", cacheKey);
+ return Optional.empty();
+ }
+
+ if (entry.isExpired(cacheExpirationMs)) {
+ log.debug("Cache entry expired for key: {}, removing from cache", cacheKey);
+ cache.remove(cacheKey);
+ return Optional.empty();
+ }
+
+ log.debug("Cache hit for key: {}", cacheKey);
+ return Optional.of(entry.getValue());
+ }
+
+ /**
+ * 将Mock配置放入缓存
+ *
+ * @param cacheKey 缓存键,格式为 "apiPath:apiMethod"
+ * @param config 要缓存的MockConfig对象
+ */
+ public void put(String cacheKey, MockConfig config) {
+ CacheEntry entry = new CacheEntry<>(config);
+ cache.put(cacheKey, entry);
+ log.debug("Cache updated for key: {}", cacheKey);
+ }
+
+ /**
+ * 使指定缓存条目失效(移除)
+ * 通常在Mock配置被更新或删除时调用
+ *
+ * @param cacheKey 要失效的缓存键
+ */
+ public void invalidate(String cacheKey) {
+ cache.remove(cacheKey);
+ log.debug("Cache invalidated for key: {}", cacheKey);
+ }
+
+ /**
+ * 清空所有缓存条目
+ * 通常在需要强制刷新所有缓存时调用
+ */
+ public void clear() {
+ int size = cache.size();
+ cache.clear();
+ log.info("Cache cleared, removed {} entries", size);
+ }
+
+ /**
+ * 缓存条目内部类
+ * 封装缓存值和时间戳,用于实现过期检查
+ *
+ * @param 缓存值的类型
+ */
+ private static class CacheEntry {
+ private final T value;
+ private final long timestamp;
+
+ /**
+ * 构造函数,创建缓存条目并记录当前时间戳
+ *
+ * @param value 要缓存的值
+ */
+ public CacheEntry(T value) {
+ this.value = value;
+ this.timestamp = System.currentTimeMillis();
+ }
+
+ /**
+ * 获取缓存的值
+ * @return 缓存的值
+ */
+ public T getValue() {
+ return value;
+ }
+
+ /**
+ * 检查缓存条目是否已过期
+ *
+ * @param expirationMs 过期时间(毫秒)
+ * @return true如果已过期,否则返回false
+ */
+ public boolean isExpired(long expirationMs) {
+ return System.currentTimeMillis() - timestamp > expirationMs;
+ }
+ }
+}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/handle/MockService.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/handle/MockService.java
new file mode 100644
index 0000000..bd514c6
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/handle/MockService.java
@@ -0,0 +1,59 @@
+package org.nl.tool.mock.core.handle;
+
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.nl.tool.mock.modular.mockconfig.entity.MockConfig;
+import org.nl.tool.mock.modular.mockconfig.service.MockConfigService;
+import org.springframework.stereotype.Service;
+
+import java.util.Optional;
+
+/**
+ * Mock配置服务
+ * 提供Mock配置的业务逻辑,包括CRUD操作、缓存管理和JSON验证
+ * 遵循DDD应用层服务模式
+ * @author: lyd
+ * @date: 2026/1/29
+ */
+@Service
+@Slf4j
+public class MockService {
+ @Resource
+ private MockConfigService mockConfigService;
+ @Resource
+ private MockCacheService mockCacheService;
+
+ /**
+ * 获取Mock配置(集成缓存查询)
+ * 优先从缓存获取,缓存未命中时从数据库查询并更新缓存
+ *
+ * @param apiPath API路径
+ * @param apiMethod HTTP方法
+ * @return Optional包装的MockConfig
+ */
+ public Optional getMockConfig(String apiPath, String apiMethod) {
+ String cacheKey = apiPath + ":" + apiMethod;
+
+ // 先查缓存
+ Optional cached = mockCacheService.get(cacheKey);
+ if (cached.isPresent()) {
+ log.debug("Mock config found in cache for {} {}", apiMethod, apiPath);
+ return cached;
+ }
+
+ // 缓存未命中,查数据库
+ Optional config = mockConfigService.findByApiPathAndApiMethod(apiPath, apiMethod);
+
+ // 如果找到,更新缓存
+ config.ifPresent(c -> {
+ mockCacheService.put(cacheKey, c);
+ log.debug("Mock config loaded from database and cached for {} {}", apiMethod, apiPath);
+ });
+
+ if (config.isEmpty()) {
+ log.debug("Mock config not found for {} {}", apiMethod, apiPath);
+ }
+
+ return config;
+ }
+}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/package-info.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/package-info.java
new file mode 100644
index 0000000..d6b9a1a
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 核心包:配置、插件之类
+ * @author: lyd
+ * @date: 2026/1/28
+ */
+package org.nl.tool.mock.core;
\ No newline at end of file
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/CreateMockConfigDTO.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/CreateMockConfigDTO.java
new file mode 100644
index 0000000..043af29
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/CreateMockConfigDTO.java
@@ -0,0 +1,50 @@
+package org.nl.tool.mock.modular.mockconfig.dto;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ *
+ * @author: lyd
+ * @date: 2026/1/29
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class CreateMockConfigDTO {
+
+ /**
+ * API路径
+ * 不能为空,最大长度500
+ */
+ @NotBlank(message = "API path cannot be blank")
+ @Size(max = 500, message = "API path too long")
+ private String apiPath;
+
+ /**
+ * HTTP请求方法
+ * 不能为空,必须是GET、POST、PUT、DELETE之一
+ */
+ @NotBlank(message = "API method cannot be blank")
+ @Pattern(regexp = "GET|POST|PUT|DELETE", message = "Invalid HTTP method")
+ private String apiMethod;
+
+ /**
+ * 返回的JSON数据
+ * 不能为空
+ */
+ @NotBlank(message = "Response JSON cannot be blank")
+ private String responseJson;
+
+ /**
+ * 是否启用
+ * 默认为true
+ */
+ private Boolean isEnabled = true;
+}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/MockConfigDTOMapper.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/MockConfigDTOMapper.java
new file mode 100644
index 0000000..cfdfab6
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/MockConfigDTOMapper.java
@@ -0,0 +1,84 @@
+package org.nl.tool.mock.modular.mockconfig.dto;
+
+import org.nl.tool.mock.modular.mockconfig.entity.MockConfig;
+import org.springframework.stereotype.Component;
+
+/**
+ * Mock配置DTO映射器
+ * 负责在Entity和DTO之间进行转换
+ * @author: lyd
+ * @date: 2026/1/29
+ */
+@Component
+public class MockConfigDTOMapper {
+
+ /**
+ * 将MockConfig实体转换为MockConfigVO
+ *
+ * @param entity MockConfig实体
+ * @return MockConfigVO视图对象
+ */
+ public MockConfigVO toVO(MockConfig entity) {
+ if (entity == null) {
+ return null;
+ }
+
+ return MockConfigVO.builder()
+ .id(entity.getId())
+ .apiPath(entity.getApiPath())
+ .apiMethod(entity.getApiMethod())
+ .responseJson(entity.getResponseJson())
+ .isEnabled(entity.getIsEnabled())
+ .createTime(entity.getCreateTime())
+ .updateTime(entity.getUpdateTime())
+ .build();
+ }
+
+ /**
+ * 将CreateMockConfigDTO转换为MockConfig实体
+ *
+ * @param dto 创建DTO
+ * @return MockConfig实体
+ */
+ public MockConfig toEntity(CreateMockConfigDTO dto) {
+ if (dto == null) {
+ return null;
+ }
+
+ return MockConfig.builder()
+ .apiPath(dto.getApiPath())
+ .apiMethod(dto.getApiMethod())
+ .responseJson(dto.getResponseJson())
+ .isEnabled(dto.getIsEnabled() != null ? dto.getIsEnabled() : true)
+ .build();
+ }
+
+ /**
+ * 使用UpdateMockConfigDTO更新MockConfig实体
+ * 只更新DTO中非null的字段
+ *
+ * @param entity 要更新的实体
+ * @param dto 更新DTO
+ */
+ public void updateEntity(MockConfig entity, UpdateMockConfigDTO dto) {
+ if (entity == null || dto == null) {
+ return;
+ }
+
+ if (dto.getApiPath() != null) {
+ entity.setApiPath(dto.getApiPath());
+ }
+
+ if (dto.getApiMethod() != null) {
+ entity.setApiMethod(dto.getApiMethod());
+ }
+
+ if (dto.getResponseJson() != null) {
+ entity.setResponseJson(dto.getResponseJson());
+ }
+
+ if (dto.getIsEnabled() != null) {
+ entity.setIsEnabled(dto.getIsEnabled());
+ }
+ }
+}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/MockConfigVO.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/MockConfigVO.java
new file mode 100644
index 0000000..760233b
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/MockConfigVO.java
@@ -0,0 +1,54 @@
+package org.nl.tool.mock.modular.mockconfig.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * Mock配置的视图对象(Value Object)
+ * 用于向客户端返回Mock配置数据
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MockConfigVO {
+
+ /**
+ * 配置ID
+ */
+ private String id;
+
+ /**
+ * API路径
+ */
+ private String apiPath;
+
+ /**
+ * HTTP请求方法
+ */
+ private String apiMethod;
+
+ /**
+ * 返回的JSON数据
+ */
+ private String responseJson;
+
+ /**
+ * 是否启用
+ */
+ private Boolean isEnabled;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/UpdateMockConfigDTO.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/UpdateMockConfigDTO.java
new file mode 100644
index 0000000..5434c52
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/dto/UpdateMockConfigDTO.java
@@ -0,0 +1,47 @@
+package org.nl.tool.mock.modular.mockconfig.dto;
+
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ *
+ * @author: lyd
+ * @date: 2026/1/29
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class UpdateMockConfigDTO {
+
+ /**
+ * API路径
+ * 可选,如果提供则最大长度500
+ */
+ @Size(max = 500, message = "API path too long")
+ private String apiPath;
+
+ /**
+ * HTTP请求方法
+ * 可选,如果提供则必须是GET、POST、PUT、DELETE之一
+ */
+ @Pattern(regexp = "GET|POST|PUT|DELETE", message = "Invalid HTTP method")
+ private String apiMethod;
+
+ /**
+ * 返回的JSON数据
+ * 可选
+ */
+ private String responseJson;
+
+ /**
+ * 是否启用
+ * 可选
+ */
+ private Boolean isEnabled;
+}
+
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/entity/MockConfig.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/entity/MockConfig.java
index a0a4620..ab98ca7 100644
--- a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/entity/MockConfig.java
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/entity/MockConfig.java
@@ -14,8 +14,8 @@ package org.nl.tool.mock.modular.mockconfig.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Getter;
-import lombok.Setter;
+import lombok.*;
+
import java.math.BigDecimal;
import java.util.Date;
@@ -25,8 +25,10 @@ import java.util.Date;
* @author liyongde
* @date 2026/01/28 17:50
**/
-@Getter
-@Setter
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
@TableName("tool_mock_config")
public class MockConfig {
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/provider/MockApiProvider.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/provider/MockApiProvider.java
new file mode 100644
index 0000000..0baa0f0
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/provider/MockApiProvider.java
@@ -0,0 +1,35 @@
+package org.nl.tool.mock.modular.mockconfig.provider;
+
+import jakarta.annotation.Resource;
+import org.nl.tool.api.MockApi;
+import org.nl.tool.core.enums.HttpMethodEnum;
+import org.nl.tool.mock.core.handle.HttpClientWrapper;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * @author: lyd
+ * @date: 2026/1/28
+ */
+@Service
+public class MockApiProvider implements MockApi {
+ @Resource
+ private HttpClientWrapper httpClientWrapper;
+
+ /**
+ * 执行器,获取得到的数据自行转换(此功能只是通用调用三方接口,不做业务处理)
+ * @param url 路径(ip:端口号/api地址)
+ * @param method 方法类型:GET、POST、PUT、DELETED
+ * @param body 数据内容:JSONObject.toString
+ * @return
+ */
+ @Override
+ public String executeRequestCommon(String url, HttpMethodEnum method, String body) {
+ return switch (method) {
+ case GET -> httpClientWrapper.get(url);
+ case POST -> httpClientWrapper.post(url, body);
+ case PUT -> httpClientWrapper.put(url, body);
+ case DELETE -> httpClientWrapper.delete(url);
+ };
+ }
+}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/MockConfigService.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/MockConfigService.java
index 5dd5bf3..1fcf536 100644
--- a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/MockConfigService.java
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/MockConfigService.java
@@ -21,6 +21,7 @@ import org.nl.tool.mock.modular.mockconfig.param.MockConfigIdParam;
import org.nl.tool.mock.modular.mockconfig.param.MockConfigPageParam;
import java.util.List;
+import java.util.Optional;
/**
* Mock配置表Service接口
@@ -77,4 +78,6 @@ public interface MockConfigService extends IService {
* @date 2026/01/28 17:50
**/
MockConfig queryEntity(String id);
+
+ Optional findByApiPathAndApiMethod(String apiPath, String apiMethod);
}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/impl/MockConfigServiceImpl.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/impl/MockConfigServiceImpl.java
index 5f66230..2b64c68 100644
--- a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/impl/MockConfigServiceImpl.java
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/impl/MockConfigServiceImpl.java
@@ -16,6 +16,7 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -33,6 +34,7 @@ import org.nl.tool.mock.modular.mockconfig.param.MockConfigPageParam;
import org.nl.tool.mock.modular.mockconfig.service.MockConfigService;
import java.util.List;
+import java.util.Optional;
/**
* Mock配置表Service接口实现类
@@ -100,4 +102,12 @@ public class MockConfigServiceImpl extends ServiceImpl findByApiPathAndApiMethod(String apiPath, String apiMethod) {
+ LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
+ wrapper.eq(MockConfig::getApiPath, apiPath)
+ .eq(MockConfig::getApiMethod, apiMethod);
+ return Optional.ofNullable(getOne(wrapper));
+ }
}
diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/package-info.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/package-info.java
new file mode 100644
index 0000000..6d6ec1c
--- /dev/null
+++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 不同业务领域
+ * @author: lyd
+ * @date: 2026/1/28
+ */
+package org.nl.tool.mock.modular;
\ No newline at end of file
diff --git a/nl-plugin-tool/src/main/resources/application.yml b/nl-plugin-tool/src/main/resources/application.yml
new file mode 100644
index 0000000..89bb1d9
--- /dev/null
+++ b/nl-plugin-tool/src/main/resources/application.yml
@@ -0,0 +1,19 @@
+# ============================================
+# Mock系统核心配置
+# ============================================
+mock:
+ # 全局Mock功能开关
+ # true: 启用Mock功能(开发/测试环境)
+ # false: 禁用Mock功能(生产环境)
+ enabled: true
+
+ # 缓存过期时间(秒)
+ # 建议值:300秒(5分钟)
+ # 较短的过期时间可以更快地反映配置变化
+ # 较长的过期时间可以减少数据库访问
+ cache-expiration-seconds: 300
+
+ # 是否记录Mock使用日志
+ # true: 记录每次Mock数据的使用(便于调试)
+ # false: 不记录(减少日志量)
+ log-mock-usage: true
\ No newline at end of file
diff --git a/nl-vue/src/views/mock/mockconfig/form.vue b/nl-vue/src/views/mock/mockconfig/form.vue
index ecd6b6c..e376531 100644
--- a/nl-vue/src/views/mock/mockconfig/form.vue
+++ b/nl-vue/src/views/mock/mockconfig/form.vue
@@ -58,11 +58,22 @@
// 打开抽屉
const onOpen = (record) => {
open.value = true
- if (record) {
- let recordData = cloneDeep(record)
- formData.value = Object.assign({}, recordData)
- }
+ // 加载是否启用字典(0=否,1=是)
isEnabledOptions.value = tool.dictList('IS_USED')
+ if (record) {
+ const recordData = cloneDeep(record)
+ // 后端/表格里是布尔值 true/false,这里统一转换成字典需要的 '0' / '1'
+ if (typeof recordData.isEnabled === 'boolean') {
+ recordData.isEnabled = recordData.isEnabled ? '1' : '0'
+ } else if (recordData.isEnabled === 1 || recordData.isEnabled === 0) {
+ recordData.isEnabled = String(recordData.isEnabled)
+ }
+ formData.value = Object.assign({}, recordData)
+ } else {
+ // 新增时默认启用:'1'
+ formData.value = {}
+ formData.value.isEnabled = '1'
+ }
}
// 关闭抽屉
const onClose = () => {
@@ -80,6 +91,12 @@
.then(() => {
submitLoading.value = true
const formDataParam = cloneDeep(formData.value)
+ // 提交前将 '0' / '1' 转回数字 0 / 1(如果后端需要的话)
+ if (formDataParam.isEnabled === '1') {
+ formDataParam.isEnabled = 1
+ } else if (formDataParam.isEnabled === '0') {
+ formDataParam.isEnabled = 0
+ }
mockConfigApi
.mockConfigSubmitForm(formDataParam, formDataParam.id)
.then(() => {
diff --git a/nl-vue/src/views/mock/mockconfig/index.vue b/nl-vue/src/views/mock/mockconfig/index.vue
index 0d8e71a..d783d68 100644
--- a/nl-vue/src/views/mock/mockconfig/index.vue
+++ b/nl-vue/src/views/mock/mockconfig/index.vue
@@ -50,7 +50,7 @@
- {{ record.isEnabled ? '启用' : '禁用' }}
+ {{ $TOOL.dictTypeData('IS_USED', record.isEnabled === true ? '1' : record.isEnabled === false ? '0' : record.isEnabled) }}
diff --git a/nl-web-app/src/main/java/org/nl/core/config/MockInterceptor.java b/nl-web-app/src/main/java/org/nl/core/config/MockInterceptor.java
new file mode 100644
index 0000000..01ee46a
--- /dev/null
+++ b/nl-web-app/src/main/java/org/nl/core/config/MockInterceptor.java
@@ -0,0 +1,100 @@
+package org.nl.core.config;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.nl.tool.mock.core.config.MockConfigProperties;
+import org.nl.tool.mock.core.handle.MockService;
+import org.nl.tool.mock.modular.mockconfig.entity.MockConfig;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import java.util.Optional;
+
+/**
+ * Mock拦截器
+ * 用于拦截入站HTTP请求,根据Mock配置返回Mock数据
+ * 实现HandlerInterceptor接口,在Controller方法执行前进行拦截
+ *
+ * 工作流程:
+ * 1. 检查全局Mock开关是否启用
+ * 2. 提取请求路径和HTTP方法
+ * 3. 查询Mock配置(优先从缓存获取)
+ * 4. 如果Mock配置存在且启用,直接返回Mock数据
+ * 5. 否则,继续执行Controller方法
+ */
+@Component
+@Slf4j
+public class MockInterceptor implements HandlerInterceptor {
+
+ private final MockService mockService;
+ private final MockConfigProperties properties;
+
+ /**
+ * 构造函数,注入依赖
+ *
+ * @param mockService Mock服务
+ * @param properties Mock配置属性
+ */
+ public MockInterceptor(MockService mockService, MockConfigProperties properties) {
+ this.mockService = mockService;
+ this.properties = properties;
+ }
+
+ /**
+ * 在Controller方法执行前拦截请求
+ *
+ * @param request HTTP请求
+ * @param response HTTP响应
+ * @param handler 处理器
+ * @return true表示继续执行Controller,false表示拦截并返回Mock数据
+ * @throws Exception 处理过程中的异常
+ */
+ @Override
+ public boolean preHandle(HttpServletRequest request,
+ HttpServletResponse response,
+ Object handler) throws Exception {
+ // 检查全局Mock开关
+ if (!properties.isEnabled()) {
+ log.debug("Mock functionality is globally disabled, continuing to controller");
+ return true; // Mock功能禁用,继续执行Controller
+ }
+
+ // todo: 检测白名单
+
+ // 提取请求路径和方法
+ String path = request.getRequestURI();
+ String method = request.getMethod();
+
+ log.debug("Intercepting request: {} {}", method, path);
+
+ // 查询Mock配置
+ Optional mockConfig = mockService.getMockConfig(path, method);
+
+ // 检查Mock配置是否存在且启用
+ if (mockConfig.isPresent() && mockConfig.get().getIsEnabled()) {
+ MockConfig config = mockConfig.get();
+
+ // 设置响应头
+ response.setContentType("application/json;charset=UTF-8");
+ response.setStatus(HttpStatus.OK.value());
+
+ // 写入Mock响应数据
+ response.getWriter().write(config.getResponseJson());
+ response.getWriter().flush();
+
+ // 记录Mock使用日志
+ if (properties.isLogMockUsage()) {
+ log.info("Mock response returned for {} {} (config id: {})",
+ method, path, config.getId());
+ }
+
+ return false; // 拦截请求,不继续执行Controller
+ }
+
+ // Mock配置不存在或未启用,继续执行Controller
+ log.debug("No active mock config found for {} {}, continuing to controller", method, path);
+ return true;
+ }
+}