diff --git a/nl-base-data/src/main/java/org/nl/device/controller/DeviceController.java b/nl-base-data/src/main/java/org/nl/modular/device/controller/DeviceController.java similarity index 95% rename from nl-base-data/src/main/java/org/nl/device/controller/DeviceController.java rename to nl-base-data/src/main/java/org/nl/modular/device/controller/DeviceController.java index 6d360b5..3b8e861 100644 --- a/nl-base-data/src/main/java/org/nl/device/controller/DeviceController.java +++ b/nl-base-data/src/main/java/org/nl/modular/device/controller/DeviceController.java @@ -10,14 +10,14 @@ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip */ -package org.nl.device.controller; +package org.nl.modular.device.controller; import cn.dev33.satoken.annotation.SaIgnore; import com.alibaba.fastjson.JSONObject; import io.swagger.v3.oas.annotations.tags.Tag; import org.nl.common.pojo.CommonResult; -import org.nl.device.service.dto.DeviceInfo; +import org.nl.modular.device.service.dto.DeviceInfo; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -25,7 +25,6 @@ import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; -import java.util.Random; @Tag(name = "设备模块") @RestController() diff --git a/nl-base-data/src/main/java/org/nl/device/service/dto/DeviceInfo.java b/nl-base-data/src/main/java/org/nl/modular/device/service/dto/DeviceInfo.java similarity index 94% rename from nl-base-data/src/main/java/org/nl/device/service/dto/DeviceInfo.java rename to nl-base-data/src/main/java/org/nl/modular/device/service/dto/DeviceInfo.java index dd5770a..a5a969b 100644 --- a/nl-base-data/src/main/java/org/nl/device/service/dto/DeviceInfo.java +++ b/nl-base-data/src/main/java/org/nl/modular/device/service/dto/DeviceInfo.java @@ -1,4 +1,4 @@ -package org.nl.device.service.dto; +package org.nl.modular.device.service.dto; import com.alibaba.fastjson.JSONObject; import lombok.AllArgsConstructor; diff --git a/nl-base-data/src/main/java/org/nl/point/PointStatusController.java b/nl-base-data/src/main/java/org/nl/modular/point/PointStatusController.java similarity index 95% rename from nl-base-data/src/main/java/org/nl/point/PointStatusController.java rename to nl-base-data/src/main/java/org/nl/modular/point/PointStatusController.java index 53bcaa1..5420673 100644 --- a/nl-base-data/src/main/java/org/nl/point/PointStatusController.java +++ b/nl-base-data/src/main/java/org/nl/modular/point/PointStatusController.java @@ -10,14 +10,13 @@ * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip */ -package org.nl.point; +package org.nl.modular.point; import cn.dev33.satoken.annotation.SaIgnore; -import com.alibaba.fastjson.JSONObject; import io.swagger.v3.oas.annotations.tags.Tag; import org.nl.common.pojo.CommonResult; -import org.nl.point.dto.PointStatus; +import org.nl.modular.point.dto.PointStatus; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/nl-base-data/src/main/java/org/nl/point/dto/PointStatus.java b/nl-base-data/src/main/java/org/nl/modular/point/dto/PointStatus.java similarity index 93% rename from nl-base-data/src/main/java/org/nl/point/dto/PointStatus.java rename to nl-base-data/src/main/java/org/nl/modular/point/dto/PointStatus.java index 6dc65e1..6a72d6f 100644 --- a/nl-base-data/src/main/java/org/nl/point/dto/PointStatus.java +++ b/nl-base-data/src/main/java/org/nl/modular/point/dto/PointStatus.java @@ -1,4 +1,4 @@ -package org.nl.point.dto; +package org.nl.modular.point.dto; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/nl-common/pom.xml b/nl-common/pom.xml index fad9506..3eeaa14 100644 --- a/nl-common/pom.xml +++ b/nl-common/pom.xml @@ -21,10 +21,10 @@ org.openjdk.nashorn nashorn-core - - nl.sdk - language - + + + + org.springframework.boot @@ -132,5 +132,11 @@ com.alibaba easyexcel + + + + cn.dev33 + sa-token-core + diff --git a/nl-common/src/main/java/org/nl/common/localStorage/controller/LocalStorageController.java b/nl-common/src/main/java/org/nl/common/localStorage/controller/LocalStorageController.java index eefdb93..35055e5 100644 --- a/nl-common/src/main/java/org/nl/common/localStorage/controller/LocalStorageController.java +++ b/nl-common/src/main/java/org/nl/common/localStorage/controller/LocalStorageController.java @@ -1,59 +1,59 @@ +//// +//// Source code recreated from a .class file by IntelliJ IDEA +//// (powered by FernFlower decompiler) +//// +//package org.nl.common.localStorage.controller; // -// Source code recreated from a .class file by IntelliJ IDEA -// (powered by FernFlower decompiler) // -package org.nl.common.localStorage.controller; - - -import cn.dev33.satoken.annotation.SaIgnore; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.lang3.StringUtils; -import org.nl.common.localStorage.service.LocalStorageService; -import org.nl.common.localStorage.service.entity.LocalStorage; -import org.nl.common.pojo.CommonResult; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -@RestController -@RequestMapping({"/api/localStorage"}) -@SaIgnore -public class LocalStorageController { - - @Autowired - private LocalStorageService localStorageService; - - @GetMapping - public CommonResult query(@RequestParam String annex) { - List list = new ArrayList<>(); - if (StringUtils.isNotEmpty(annex)){ - String[] split = annex.split(","); - list = localStorageService.listByIds(Arrays.asList(split)); - } - return CommonResult.data(list); - } - @PostMapping({"/upload"}) - public CommonResult upload(@RequestParam MultipartFile file) { - LocalStorage localStorage = localStorageService.upload(file); - return CommonResult.data(localStorage); - } - @GetMapping({"/download"}) - public void download(@RequestParam Long storageId , HttpServletResponse response, HttpServletRequest request) throws IOException { - this.localStorageService.downloadFile(this.localStorageService.getById(storageId),request, response); - } - - - @DeleteMapping - public CommonResult delete(@RequestBody Long[] ids) { - localStorageService.removeByIds(Arrays.asList(ids)); - return CommonResult.ok(); - } - -} +//import cn.dev33.satoken.annotation.SaIgnore; +//import jakarta.servlet.http.HttpServletRequest; +//import jakarta.servlet.http.HttpServletResponse; +//import org.apache.commons.lang3.StringUtils; +//import org.nl.common.localStorage.service.LocalStorageService; +//import org.nl.common.localStorage.service.entity.LocalStorage; +//import org.nl.common.pojo.CommonResult; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.web.bind.annotation.*; +//import org.springframework.web.multipart.MultipartFile; +// +// +//import java.io.IOException; +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.List; +// +//@RestController +//@RequestMapping({"/api/localStorage"}) +//@SaIgnore +//public class LocalStorageController { +// +// @Autowired +// private LocalStorageService localStorageService; +// +// @GetMapping +// public CommonResult query(@RequestParam String annex) { +// List list = new ArrayList<>(); +// if (StringUtils.isNotEmpty(annex)){ +// String[] split = annex.split(","); +// list = localStorageService.listByIds(Arrays.asList(split)); +// } +// return CommonResult.data(list); +// } +// @PostMapping({"/upload"}) +// public CommonResult upload(@RequestParam MultipartFile file) { +// LocalStorage localStorage = localStorageService.upload(file); +// return CommonResult.data(localStorage); +// } +// @GetMapping({"/download"}) +// public void download(@RequestParam Long storageId , HttpServletResponse response, HttpServletRequest request) throws IOException { +// this.localStorageService.downloadFile(this.localStorageService.getById(storageId),request, response); +// } +// +// +// @DeleteMapping +// public CommonResult delete(@RequestBody Long[] ids) { +// localStorageService.removeByIds(Arrays.asList(ids)); +// return CommonResult.ok(); +// } +// +//} diff --git a/nl-plugin-tool-api/pom.xml b/nl-plugin-tool-api/pom.xml new file mode 100644 index 0000000..f820237 --- /dev/null +++ b/nl-plugin-tool-api/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + org.nl + nl-tool-platform + 3.0.0 + + + org.nl.tool.api + nl-plugin-tool-api + 工具类的api模块 + + + + + org.nl + nl-common + + + + \ No newline at end of file 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/pom.xml b/nl-plugin-tool/pom.xml new file mode 100644 index 0000000..e1be04b --- /dev/null +++ b/nl-plugin-tool/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + org.nl + nl-tool-platform + 3.0.0 + + + org.nl.tool + nl-plugin-tool + + + + org.nl.tool.api + nl-plugin-tool-api + + + + org.nl + nl-plugin-auth-api + + + + \ No newline at end of file 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..82c95b0 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/core/config/MockConfigProperties.java @@ -0,0 +1,50 @@ +package org.nl.tool.mock.core.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * Mock配置属性类 + * 使用@ConfigurationProperties从application.yml中绑定mock配置 + * + * 配置示例: + * mock: + * enabled: true + * cache-expiration-seconds: 300 + * log-mock-usage: true + * whitelist: + * - /api/login + * - /api/mes/** + * @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/controller/MockConfigController.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/controller/MockConfigController.java new file mode 100644 index 0000000..c17fe3e --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/controller/MockConfigController.java @@ -0,0 +1,129 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaIgnore; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.nl.common.annotation.CommonLog; +import org.nl.common.pojo.CommonResult; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.nl.tool.mock.modular.mockconfig.entity.MockConfig; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigAddParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigEditParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigIdParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigPageParam; +import org.nl.tool.mock.modular.mockconfig.service.MockConfigService; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import java.util.List; + +/** + * Mock配置表控制器 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ +@Tag(name = "Mock配置表控制器") +@RestController +@Validated +public class MockConfigController { + + @Resource + private MockConfigService mockConfigService; + + /** + * 获取Mock配置表分页 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "获取Mock配置表分页") + @SaCheckPermission("/mock/mockconfig/page") + @GetMapping("/mock/mockconfig/page") + public CommonResult> page(MockConfigPageParam mockConfigPageParam) { + return CommonResult.data(mockConfigService.page(mockConfigPageParam)); + } + + /** + * 添加Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "添加Mock配置表") + @CommonLog("添加Mock配置表") + @SaCheckPermission("/mock/mockconfig/add") + @PostMapping("/mock/mockconfig/add") + public CommonResult add(@RequestBody @Valid MockConfigAddParam mockConfigAddParam) { + mockConfigService.add(mockConfigAddParam); + return CommonResult.ok(); + } + + /** + * 编辑Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "编辑Mock配置表") + @CommonLog("编辑Mock配置表") + @SaCheckPermission("/mock/mockconfig/edit") + @PostMapping("/mock/mockconfig/edit") + public CommonResult edit(@RequestBody @Valid MockConfigEditParam mockConfigEditParam) { + mockConfigService.edit(mockConfigEditParam); + return CommonResult.ok(); + } + + /** + * 删除Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "删除Mock配置表") + @CommonLog("删除Mock配置表") + @SaCheckPermission("/mock/mockconfig/delete") + @PostMapping("/mock/mockconfig/delete") + public CommonResult delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空") + List mockConfigIdParamList) { + mockConfigService.delete(mockConfigIdParamList); + return CommonResult.ok(); + } + + /** + * 获取Mock配置表详情 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "获取Mock配置表详情") + @SaCheckPermission("/mock/mockconfig/detail") + @GetMapping("/mock/mockconfig/detail") + public CommonResult detail(@Valid MockConfigIdParam mockConfigIdParam) { + return CommonResult.data(mockConfigService.detail(mockConfigIdParam)); + } + + @GetMapping("/test") + public String test() { + return "WELCOME"; + } +} 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 new file mode 100644 index 0000000..ab98ca7 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/entity/MockConfig.java @@ -0,0 +1,65 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.entity; + +import com.baomidou.mybatisplus.annotation.*; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * Mock配置表实体 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("tool_mock_config") +public class MockConfig { + + /** 主键ID */ + @TableId + @Schema(description = "主键ID") + private String id; + + /** 接口路径,如:/api/user/info */ + @Schema(description = "接口路径,如:/api/user/info") + private String apiPath; + + /** 请求方法:GET, POST, PUT, DELETE */ + @Schema(description = "请求方法:GET, POST, PUT, DELETE") + private String apiMethod; + + /** 返回的JSON数据 */ + @Schema(description = "返回的JSON数据") + private String responseJson; + + /** 是否启用:true-启用,false-禁用 */ + @Schema(description = "是否启用:true-启用,false-禁用") + private Boolean isEnabled; + + /** 创建时间 */ + @Schema(description = "创建时间") + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** 更新时间 */ + @Schema(description = "更新时间") + @TableField(fill = FieldFill.UPDATE) + private Date updateTime; +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/enums/MockConfigEnum.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/enums/MockConfigEnum.java new file mode 100644 index 0000000..0ffa6ae --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/enums/MockConfigEnum.java @@ -0,0 +1,34 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.enums; + +import lombok.Getter; + +/** + * Mock配置表枚举 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +public enum MockConfigEnum { + + /** 测试 */ + TEST("TEST"); + + private final String value; + + MockConfigEnum(String value) { + this.value = value; + } +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/MockConfigMapper.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/MockConfigMapper.java new file mode 100644 index 0000000..f44717e --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/MockConfigMapper.java @@ -0,0 +1,25 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.nl.tool.mock.modular.mockconfig.entity.MockConfig; + +/** + * Mock配置表Mapper接口 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +public interface MockConfigMapper extends BaseMapper { +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/mapping/MockConfigMapper.xml b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/mapping/MockConfigMapper.xml new file mode 100644 index 0000000..824722c --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/mapping/MockConfigMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigAddParam.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigAddParam.java new file mode 100644 index 0000000..ad85879 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigAddParam.java @@ -0,0 +1,50 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.Date; + +/** + * Mock配置表添加参数 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +@Setter +public class MockConfigAddParam { + + /** 接口路径,如:/api/user/info */ + @Schema(description = "接口路径,如:/api/user/info") + private String apiPath; + + /** 请求方法:GET, POST, PUT, DELETE */ + @Schema(description = "请求方法:GET, POST, PUT, DELETE") + private String apiMethod; + + /** 返回的JSON数据 */ + @Schema(description = "返回的JSON数据") + private String responseJson; + + /** 是否启用:true-启用,false-禁用 */ + @Schema(description = "是否启用:true-启用,false-禁用") + private Boolean isEnabled; + +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigEditParam.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigEditParam.java new file mode 100644 index 0000000..a89adcb --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigEditParam.java @@ -0,0 +1,55 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.Date; + +/** + * Mock配置表编辑参数 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +@Setter +public class MockConfigEditParam { + + /** 主键ID */ + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "id不能为空") + private String id; + + /** 接口路径,如:/api/user/info */ + @Schema(description = "接口路径,如:/api/user/info") + private String apiPath; + + /** 请求方法:GET, POST, PUT, DELETE */ + @Schema(description = "请求方法:GET, POST, PUT, DELETE") + private String apiMethod; + + /** 返回的JSON数据 */ + @Schema(description = "返回的JSON数据") + private String responseJson; + + /** 是否启用:true-启用,false-禁用 */ + @Schema(description = "是否启用:true-启用,false-禁用") + private Boolean isEnabled; + +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigIdParam.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigIdParam.java new file mode 100644 index 0000000..214b29f --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigIdParam.java @@ -0,0 +1,35 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import jakarta.validation.constraints.NotBlank; + +/** + * Mock配置表Id参数 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +@Setter +public class MockConfigIdParam { + + /** 主键ID */ + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "id不能为空") + private String id; +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigPageParam.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigPageParam.java new file mode 100644 index 0000000..1c00df0 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigPageParam.java @@ -0,0 +1,63 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import java.math.BigDecimal; +import java.util.Date; + +/** + * Mock配置表查询参数 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +@Setter +public class MockConfigPageParam { + + /** 当前页 */ + @Schema(description = "当前页码") + private Integer current; + + /** 每页条数 */ + @Schema(description = "每页条数") + private Integer size; + + /** 排序字段 */ + @Schema(description = "排序字段,字段驼峰名称,如:userName") + private String sortField; + + /** 排序方式 */ + @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND") + private String sortOrder; + + /** 关键词 */ + @Schema(description = "关键词") + private String searchKey; + + /** 接口路径,如:/api/user/info */ + @Schema(description = "接口路径,如:/api/user/info") + private String apiPath; + + /** 请求方法:GET, POST, PUT, DELETE */ + @Schema(description = "请求方法:GET, POST, PUT, DELETE") + private String apiMethod; + + /** 是否启用:true-启用,false-禁用 */ + @Schema(description = "是否启用:true-启用,false-禁用") + private Boolean isEnabled; + +} 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 new file mode 100644 index 0000000..1fcf536 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/MockConfigService.java @@ -0,0 +1,83 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.nl.tool.mock.modular.mockconfig.entity.MockConfig; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigAddParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigEditParam; +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接口 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +public interface MockConfigService extends IService { + + /** + * 获取Mock配置表分页 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + Page page(MockConfigPageParam mockConfigPageParam); + + /** + * 添加Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + void add(MockConfigAddParam mockConfigAddParam); + + /** + * 编辑Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + void edit(MockConfigEditParam mockConfigEditParam); + + /** + * 删除Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + void delete(List mockConfigIdParamList); + + /** + * 获取Mock配置表详情 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + MockConfig detail(MockConfigIdParam mockConfigIdParam); + + /** + * 获取Mock配置表详情 + * + * @author liyongde + * @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 new file mode 100644 index 0000000..2b64c68 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/impl/MockConfigServiceImpl.java @@ -0,0 +1,113 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.service.impl; + +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; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.nl.common.enums.CommonSortOrderEnum; +import org.nl.common.exception.CommonException; +import org.nl.common.page.CommonPageRequest; +import org.nl.tool.mock.modular.mockconfig.entity.MockConfig; +import org.nl.tool.mock.modular.mockconfig.mapper.MockConfigMapper; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigAddParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigEditParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigIdParam; +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接口实现类 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Service +public class MockConfigServiceImpl extends ServiceImpl implements MockConfigService { + + @Override + public Page page(MockConfigPageParam mockConfigPageParam) { + QueryWrapper queryWrapper = new QueryWrapper().checkSqlInjection(); + if(ObjectUtil.isNotEmpty(mockConfigPageParam.getApiPath())) { + queryWrapper.lambda().eq(MockConfig::getApiPath, mockConfigPageParam.getApiPath()); + } + if(ObjectUtil.isNotEmpty(mockConfigPageParam.getApiMethod())) { + queryWrapper.lambda().eq(MockConfig::getApiMethod, mockConfigPageParam.getApiMethod()); + } + if(ObjectUtil.isNotEmpty(mockConfigPageParam.getIsEnabled())) { + queryWrapper.lambda().eq(MockConfig::getIsEnabled, mockConfigPageParam.getIsEnabled()); + } + if(ObjectUtil.isAllNotEmpty(mockConfigPageParam.getSortField(), mockConfigPageParam.getSortOrder())) { + CommonSortOrderEnum.validate(mockConfigPageParam.getSortOrder()); + queryWrapper.orderBy(true, mockConfigPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()), + StrUtil.toUnderlineCase(mockConfigPageParam.getSortField())); + } else { + queryWrapper.lambda().orderByAsc(MockConfig::getId); + } + return this.page(CommonPageRequest.defaultPage(), queryWrapper); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void add(MockConfigAddParam mockConfigAddParam) { + MockConfig mockConfig = BeanUtil.toBean(mockConfigAddParam, MockConfig.class); + this.save(mockConfig); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void edit(MockConfigEditParam mockConfigEditParam) { + MockConfig mockConfig = this.queryEntity(mockConfigEditParam.getId()); + BeanUtil.copyProperties(mockConfigEditParam, mockConfig); + this.updateById(mockConfig); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void delete(List mockConfigIdParamList) { + // 执行删除 + this.removeByIds(CollStreamUtil.toList(mockConfigIdParamList, MockConfigIdParam::getId)); + } + + @Override + public MockConfig detail(MockConfigIdParam mockConfigIdParam) { + return this.queryEntity(mockConfigIdParam.getId()); + } + + @Override + public MockConfig queryEntity(String id) { + MockConfig mockConfig = this.getById(id); + if(ObjectUtil.isEmpty(mockConfig)) { + throw new CommonException("Mock配置表不存在,id值为:{}", id); + } + return mockConfig; + } + + @Override + public Optional 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-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/controller/DevFileController.java b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/controller/DevFileController.java index e0e4c7e..d285e80 100644 --- a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/controller/DevFileController.java +++ b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/controller/DevFileController.java @@ -16,23 +16,24 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; +import org.nl.common.annotation.CommonLog; +import org.nl.common.pojo.CommonResult; +import org.nl.dev.api.DevConfigApi; import org.nl.dev.modular.file.entity.DevFile; import org.nl.dev.modular.file.enums.DevFileEngineTypeEnum; import org.nl.dev.modular.file.param.DevFileIdParam; import org.nl.dev.modular.file.param.DevFileListParam; import org.nl.dev.modular.file.param.DevFilePageParam; import org.nl.dev.modular.file.param.DevFileUrlListParam; +import org.nl.dev.modular.file.service.DevFileService; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import org.nl.common.annotation.CommonLog; -import org.nl.common.pojo.CommonResult; -import org.nl.dev.api.DevConfigApi; -import org.nl.dev.modular.file.service.DevFileService; import java.io.IOException; import java.util.List; @@ -262,4 +263,14 @@ public class DevFileController { public CommonResult> getFileListByUrlList(@RequestBody @Valid DevFileUrlListParam devFileUrlListParam) { return CommonResult.data(devFileService.getFileListByUrlList(devFileUrlListParam)); } + + @Operation(summary = "上传文件") + @PostMapping({"/api/localStorage/upload"}) + public CommonResult upload(@RequestParam MultipartFile file) { + return CommonResult.data(devFileService.upload(file)); + } + @GetMapping({"/api/localStorage/download"}) + public void download(@RequestParam String storageId , HttpServletResponse response, HttpServletRequest request) throws IOException { + this.devFileService.downloadFile(this.devFileService.getById(storageId),request, response); + } } diff --git a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/enums/ReturnTypeEnum.java b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/enums/ReturnTypeEnum.java new file mode 100644 index 0000000..cd1a5b7 --- /dev/null +++ b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/enums/ReturnTypeEnum.java @@ -0,0 +1,40 @@ +package org.nl.dev.modular.file.enums; + +/** + * + * @author: lyd + * @date: 2026/1/28 + */ +public enum ReturnTypeEnum { + /** + * 文件id + */ + FILE_ID("1"), + /** + * 下载地址 + */ + DOWNLOAD_URL("2"), + /** + * 文件对象 + */ + FILE("3"); + + private final String code; + + ReturnTypeEnum(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public static ReturnTypeEnum fromCode(String code) { + for (ReturnTypeEnum type : values()) { + if (type.code.equals(code)) { + return type; + } + } + return FILE; // 默认值 + } +} diff --git a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/DevFileService.java b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/DevFileService.java index 44db824..738626a 100644 --- a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/DevFileService.java +++ b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/DevFileService.java @@ -14,6 +14,7 @@ package org.nl.dev.modular.file.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.nl.dev.modular.file.entity.DevFile; import org.nl.dev.modular.file.param.DevFileIdParam; @@ -104,4 +105,8 @@ public interface DevFileService extends IService { * @date 2022/4/24 21:18 */ DevFile queryEntity(String id); + + DevFile upload(MultipartFile file); + + void downloadFile(DevFile file, HttpServletRequest request, HttpServletResponse response); } diff --git a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/impl/DevFileServiceImpl.java b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/impl/DevFileServiceImpl.java index dea6292..e4f75cd 100644 --- a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/impl/DevFileServiceImpl.java +++ b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/impl/DevFileServiceImpl.java @@ -27,10 +27,12 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.math3.dfp.DfpField; import org.nl.dev.modular.file.entity.DevFile; import org.nl.dev.modular.file.enums.DevFileEngineTypeEnum; +import org.nl.dev.modular.file.enums.ReturnTypeEnum; import org.nl.dev.modular.file.param.DevFileIdParam; import org.nl.dev.modular.file.param.DevFileListParam; import org.nl.dev.modular.file.param.DevFilePageParam; @@ -67,12 +69,12 @@ public class DevFileServiceImpl extends ServiceImpl impl @Override public String uploadReturnId(String engine, MultipartFile file) { - return this.storageFile(engine, file, true); + return this.storageFile(engine, file, ReturnTypeEnum.FILE_ID); } @Override public String uploadReturnUrl(String engine, MultipartFile file) { - return this.storageFile(engine, file, false); + return this.storageFile(engine, file, ReturnTypeEnum.DOWNLOAD_URL); } @Override @@ -127,11 +129,11 @@ public class DevFileServiceImpl extends ServiceImpl impl /** * 存储文件 - * + * 默认返回实体 * @author xuyuxiang * @date 2022/6/16 16:24 **/ - private String storageFile(String engine, MultipartFile file, boolean returnFileId) { + private T storageFile(String engine, MultipartFile file, ReturnTypeEnum returnType) { // 如果引擎为空,默认使用本地 if(ObjectUtil.isEmpty(engine)) { @@ -218,13 +220,11 @@ public class DevFileServiceImpl extends ServiceImpl impl this.save(devFile); - // 如果是返回id则返回文件id - if(returnFileId) { - return fileId; - } else { - // 否则返回下载地址 - return downloadUrl; - } + return switch (returnType) { + case FILE_ID -> (T) fileId; + case DOWNLOAD_URL -> (T) downloadUrl; + case FILE -> (T) devFile; + }; } /** @@ -277,6 +277,18 @@ public class DevFileServiceImpl extends ServiceImpl impl return devFile; } + @Override + public DevFile upload(MultipartFile file) { + return this.storageFile(DevFileEngineTypeEnum.LOCAL.getValue(), file, ReturnTypeEnum.FILE); + } + + @Override + public void downloadFile(DevFile file, HttpServletRequest request, HttpServletResponse response) { + String path = file.getStoragePath(); + File file2 = new File(path); + org.nl.common.util.FileUtil.downloadFile(request, response, file2, false); + } + /** * 根据文件后缀判断是否图片 * diff --git a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/user/service/SysUserService.java b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/user/service/SysUserService.java index 4fea2d1..1b5c199 100644 --- a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/user/service/SysUserService.java +++ b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/user/service/SysUserService.java @@ -18,15 +18,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import jakarta.servlet.http.HttpServletResponse; import org.nl.sys.modular.group.entity.SysGroup; +import org.nl.sys.modular.org.entity.SysOrg; import org.nl.sys.modular.position.entity.SysPosition; +import org.nl.sys.modular.role.entity.SysRole; +import org.nl.sys.modular.user.entity.SysUser; import org.nl.sys.modular.user.param.*; import org.nl.sys.modular.user.result.*; import org.springframework.web.multipart.MultipartFile; -import org.nl.sys.modular.user.param.*; -import org.nl.sys.modular.user.result.*; -import org.nl.sys.modular.org.entity.SysOrg; -import org.nl.sys.modular.role.entity.SysRole; -import org.nl.sys.modular.user.entity.SysUser; import java.io.IOException; import java.util.List; diff --git a/nl-vue/src/api/mock/mockConfigApi.js b/nl-vue/src/api/mock/mockConfigApi.js new file mode 100644 index 0000000..aa205ca --- /dev/null +++ b/nl-vue/src/api/mock/mockConfigApi.js @@ -0,0 +1,28 @@ +import { baseRequest } from '@/utils/request' + +const request = (url, ...arg) => baseRequest(`/mock/mockconfig/` + url, ...arg) + +/** + * Mock配置表Api接口管理器 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +export default { + // 获取Mock配置表分页 + mockConfigPage(data) { + return request('page', data, 'get') + }, + // 提交Mock配置表表单 edit为true时为编辑,默认为新增 + mockConfigSubmitForm(data, edit = false) { + return request(edit ? 'edit' : 'add', data) + }, + // 删除Mock配置表 + mockConfigDelete(data) { + return request('delete', data) + }, + // 获取Mock配置表详情 + mockConfigDetail(data) { + return request('detail', data, 'get') + } +} diff --git a/nl-vue/src/views/mock/mockconfig/form.vue b/nl-vue/src/views/mock/mockconfig/form.vue new file mode 100644 index 0000000..e376531 --- /dev/null +++ b/nl-vue/src/views/mock/mockconfig/form.vue @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 关闭 + 保存 + + + + + diff --git a/nl-vue/src/views/mock/mockconfig/index.vue b/nl-vue/src/views/mock/mockconfig/index.vue new file mode 100644 index 0000000..d783d68 --- /dev/null +++ b/nl-vue/src/views/mock/mockconfig/index.vue @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + 新增 + + + + + + + {{ $TOOL.dictTypeData('IS_USED', record.isEnabled === true ? '1' : record.isEnabled === false ? '0' : record.isEnabled) }} + + + + 编辑 + + + 删除 + + + + + + + + + + diff --git a/nl-web-app/pom.xml b/nl-web-app/pom.xml index 0cb2f13..02b77ee 100644 --- a/nl-web-app/pom.xml +++ b/nl-web-app/pom.xml @@ -94,6 +94,11 @@ org.nl nl-plugin-sys + + + org.nl.tool + nl-plugin-tool + diff --git a/nl-web-app/src/main/java/org/nl/core/config/GlobalConfigure.java b/nl-web-app/src/main/java/org/nl/core/config/GlobalConfigure.java index d195108..20bbf1c 100644 --- a/nl-web-app/src/main/java/org/nl/core/config/GlobalConfigure.java +++ b/nl-web-app/src/main/java/org/nl/core/config/GlobalConfigure.java @@ -48,6 +48,7 @@ import org.aspectj.lang.reflect.MethodSignature; import org.mybatis.spring.annotation.MapperScan; import org.nl.core.handler.GlobalExceptionUtil; import org.nl.sys.core.enums.SysBuildInEnum; +import org.nl.tool.mock.core.config.MockConfigProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -59,6 +60,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.stereotype.Component; import org.springframework.util.ResourceUtils; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.nl.auth.core.util.StpClientUtil; @@ -98,15 +100,45 @@ public class GlobalConfigure implements WebMvcConfigurer { @Autowired private SaTokenConfig saTokenConfig; + @Autowired + private MockInterceptor mockInterceptor; private static final String COMMON_REPEAT_SUBMIT_CACHE_KEY = "common-repeatSubmit:"; + public static final String[] NO_CHECK_MOCK = { + "/", + "/test", + "/auth/session/**", + "/auth/third/page", + "/client/user/**", + "/sys/**", + "/dev/**", + "/gen/basic/**", + "/gen/config/**", + "/mobile/menu/**", + "/mobile/module/**", + "/api/agv/status", + "/api/device/**", + "/api/agv/map/**", + "/api/localStorage/**", + "/api/baseData/point/status", + "/api/language/**", + "/favicon.ico", + "/doc.html", + "/webjars/**", + "/v3/api-docs/**", + "/druid/**", + "/mobile/**", + "/auth/**" + }; + /** * 无需登录的接口地址集合 */ public static final String[] NO_LOGIN_PATH_ARR = { /* 主入口 */ "/", + "/test", /*AGV*/ "/api/agv/status", "/api/device/**", @@ -673,4 +705,21 @@ public class GlobalConfigure implements WebMvcConfigurer { public void registerListenerList(List dataChangeListenerList) { CommonDataChangeEventCenter.registerListenerList(dataChangeListenerList); } + + /** + * 注册拦截器 + * 配置拦截路径和排除路径 + * + * @param registry 拦截器注册器 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { +// log.info("Registering MockInterceptor"); + + registry.addInterceptor(mockInterceptor) + .addPathPatterns("/**") // 拦截所有路径 + .excludePathPatterns(CollectionUtil.newArrayList(NO_CHECK_MOCK)); + +// log.info("MockInterceptor registered successfully"); + } } 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..e203b0c --- /dev/null +++ b/nl-web-app/src/main/java/org/nl/core/config/MockInterceptor.java @@ -0,0 +1,104 @@ +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.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.List; +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; + private final PathMatcher pathMatcher = new AntPathMatcher(); + + /** + * 构造函数,注入依赖 + * + * @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 { + log.info("check config..."); + // 检查全局Mock开关 + if (!properties.isEnabled()) { + log.debug("Mock functionality is globally disabled, continuing to controller"); + return true; // Mock功能禁用,继续执行Controller + } + + // 提取请求路径和方法 + 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; + } +} diff --git a/pom.xml b/pom.xml index 75ce2f6..0801dc6 100644 --- a/pom.xml +++ b/pom.xml @@ -41,22 +41,23 @@ nl-web-app + nl-plugin-tool + nl-plugin-tool-api - - - - - - - - - - - - + + org.nl.tool + nl-plugin-tool + ${snowy.version} + + + org.nl.tool.api + nl-plugin-tool-api + ${snowy.version} + + org.nl nl-common
+ * 配置示例: + * mock: + * enabled: true + * cache-expiration-seconds: 300 + * log-mock-usage: true + * whitelist: + * - /api/login + * - /api/mes/** + * @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/controller/MockConfigController.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/controller/MockConfigController.java new file mode 100644 index 0000000..c17fe3e --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/controller/MockConfigController.java @@ -0,0 +1,129 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import cn.dev33.satoken.annotation.SaIgnore; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.nl.common.annotation.CommonLog; +import org.nl.common.pojo.CommonResult; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import org.nl.tool.mock.modular.mockconfig.entity.MockConfig; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigAddParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigEditParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigIdParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigPageParam; +import org.nl.tool.mock.modular.mockconfig.service.MockConfigService; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import java.util.List; + +/** + * Mock配置表控制器 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ +@Tag(name = "Mock配置表控制器") +@RestController +@Validated +public class MockConfigController { + + @Resource + private MockConfigService mockConfigService; + + /** + * 获取Mock配置表分页 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "获取Mock配置表分页") + @SaCheckPermission("/mock/mockconfig/page") + @GetMapping("/mock/mockconfig/page") + public CommonResult> page(MockConfigPageParam mockConfigPageParam) { + return CommonResult.data(mockConfigService.page(mockConfigPageParam)); + } + + /** + * 添加Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "添加Mock配置表") + @CommonLog("添加Mock配置表") + @SaCheckPermission("/mock/mockconfig/add") + @PostMapping("/mock/mockconfig/add") + public CommonResult add(@RequestBody @Valid MockConfigAddParam mockConfigAddParam) { + mockConfigService.add(mockConfigAddParam); + return CommonResult.ok(); + } + + /** + * 编辑Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "编辑Mock配置表") + @CommonLog("编辑Mock配置表") + @SaCheckPermission("/mock/mockconfig/edit") + @PostMapping("/mock/mockconfig/edit") + public CommonResult edit(@RequestBody @Valid MockConfigEditParam mockConfigEditParam) { + mockConfigService.edit(mockConfigEditParam); + return CommonResult.ok(); + } + + /** + * 删除Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "删除Mock配置表") + @CommonLog("删除Mock配置表") + @SaCheckPermission("/mock/mockconfig/delete") + @PostMapping("/mock/mockconfig/delete") + public CommonResult delete(@RequestBody @Valid @NotEmpty(message = "集合不能为空") + List mockConfigIdParamList) { + mockConfigService.delete(mockConfigIdParamList); + return CommonResult.ok(); + } + + /** + * 获取Mock配置表详情 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + @Operation(summary = "获取Mock配置表详情") + @SaCheckPermission("/mock/mockconfig/detail") + @GetMapping("/mock/mockconfig/detail") + public CommonResult detail(@Valid MockConfigIdParam mockConfigIdParam) { + return CommonResult.data(mockConfigService.detail(mockConfigIdParam)); + } + + @GetMapping("/test") + public String test() { + return "WELCOME"; + } +} 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 new file mode 100644 index 0000000..ab98ca7 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/entity/MockConfig.java @@ -0,0 +1,65 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.entity; + +import com.baomidou.mybatisplus.annotation.*; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * Mock配置表实体 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("tool_mock_config") +public class MockConfig { + + /** 主键ID */ + @TableId + @Schema(description = "主键ID") + private String id; + + /** 接口路径,如:/api/user/info */ + @Schema(description = "接口路径,如:/api/user/info") + private String apiPath; + + /** 请求方法:GET, POST, PUT, DELETE */ + @Schema(description = "请求方法:GET, POST, PUT, DELETE") + private String apiMethod; + + /** 返回的JSON数据 */ + @Schema(description = "返回的JSON数据") + private String responseJson; + + /** 是否启用:true-启用,false-禁用 */ + @Schema(description = "是否启用:true-启用,false-禁用") + private Boolean isEnabled; + + /** 创建时间 */ + @Schema(description = "创建时间") + @TableField(fill = FieldFill.INSERT) + private Date createTime; + + /** 更新时间 */ + @Schema(description = "更新时间") + @TableField(fill = FieldFill.UPDATE) + private Date updateTime; +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/enums/MockConfigEnum.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/enums/MockConfigEnum.java new file mode 100644 index 0000000..0ffa6ae --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/enums/MockConfigEnum.java @@ -0,0 +1,34 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.enums; + +import lombok.Getter; + +/** + * Mock配置表枚举 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +public enum MockConfigEnum { + + /** 测试 */ + TEST("TEST"); + + private final String value; + + MockConfigEnum(String value) { + this.value = value; + } +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/MockConfigMapper.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/MockConfigMapper.java new file mode 100644 index 0000000..f44717e --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/MockConfigMapper.java @@ -0,0 +1,25 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.nl.tool.mock.modular.mockconfig.entity.MockConfig; + +/** + * Mock配置表Mapper接口 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +public interface MockConfigMapper extends BaseMapper { +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/mapping/MockConfigMapper.xml b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/mapping/MockConfigMapper.xml new file mode 100644 index 0000000..824722c --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/mapper/mapping/MockConfigMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigAddParam.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigAddParam.java new file mode 100644 index 0000000..ad85879 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigAddParam.java @@ -0,0 +1,50 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.Date; + +/** + * Mock配置表添加参数 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +@Setter +public class MockConfigAddParam { + + /** 接口路径,如:/api/user/info */ + @Schema(description = "接口路径,如:/api/user/info") + private String apiPath; + + /** 请求方法:GET, POST, PUT, DELETE */ + @Schema(description = "请求方法:GET, POST, PUT, DELETE") + private String apiMethod; + + /** 返回的JSON数据 */ + @Schema(description = "返回的JSON数据") + private String responseJson; + + /** 是否启用:true-启用,false-禁用 */ + @Schema(description = "是否启用:true-启用,false-禁用") + private Boolean isEnabled; + +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigEditParam.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigEditParam.java new file mode 100644 index 0000000..a89adcb --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigEditParam.java @@ -0,0 +1,55 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.Date; + +/** + * Mock配置表编辑参数 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +@Setter +public class MockConfigEditParam { + + /** 主键ID */ + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "id不能为空") + private String id; + + /** 接口路径,如:/api/user/info */ + @Schema(description = "接口路径,如:/api/user/info") + private String apiPath; + + /** 请求方法:GET, POST, PUT, DELETE */ + @Schema(description = "请求方法:GET, POST, PUT, DELETE") + private String apiMethod; + + /** 返回的JSON数据 */ + @Schema(description = "返回的JSON数据") + private String responseJson; + + /** 是否启用:true-启用,false-禁用 */ + @Schema(description = "是否启用:true-启用,false-禁用") + private Boolean isEnabled; + +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigIdParam.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigIdParam.java new file mode 100644 index 0000000..214b29f --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigIdParam.java @@ -0,0 +1,35 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import jakarta.validation.constraints.NotBlank; + +/** + * Mock配置表Id参数 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +@Setter +public class MockConfigIdParam { + + /** 主键ID */ + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message = "id不能为空") + private String id; +} diff --git a/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigPageParam.java b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigPageParam.java new file mode 100644 index 0000000..1c00df0 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/param/MockConfigPageParam.java @@ -0,0 +1,63 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import java.math.BigDecimal; +import java.util.Date; + +/** + * Mock配置表查询参数 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Getter +@Setter +public class MockConfigPageParam { + + /** 当前页 */ + @Schema(description = "当前页码") + private Integer current; + + /** 每页条数 */ + @Schema(description = "每页条数") + private Integer size; + + /** 排序字段 */ + @Schema(description = "排序字段,字段驼峰名称,如:userName") + private String sortField; + + /** 排序方式 */ + @Schema(description = "排序方式,升序:ASCEND;降序:DESCEND") + private String sortOrder; + + /** 关键词 */ + @Schema(description = "关键词") + private String searchKey; + + /** 接口路径,如:/api/user/info */ + @Schema(description = "接口路径,如:/api/user/info") + private String apiPath; + + /** 请求方法:GET, POST, PUT, DELETE */ + @Schema(description = "请求方法:GET, POST, PUT, DELETE") + private String apiMethod; + + /** 是否启用:true-启用,false-禁用 */ + @Schema(description = "是否启用:true-启用,false-禁用") + private Boolean isEnabled; + +} 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 new file mode 100644 index 0000000..1fcf536 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/MockConfigService.java @@ -0,0 +1,83 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.nl.tool.mock.modular.mockconfig.entity.MockConfig; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigAddParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigEditParam; +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接口 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +public interface MockConfigService extends IService { + + /** + * 获取Mock配置表分页 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + Page page(MockConfigPageParam mockConfigPageParam); + + /** + * 添加Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + void add(MockConfigAddParam mockConfigAddParam); + + /** + * 编辑Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + void edit(MockConfigEditParam mockConfigEditParam); + + /** + * 删除Mock配置表 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + void delete(List mockConfigIdParamList); + + /** + * 获取Mock配置表详情 + * + * @author liyongde + * @date 2026/01/28 17:50 + */ + MockConfig detail(MockConfigIdParam mockConfigIdParam); + + /** + * 获取Mock配置表详情 + * + * @author liyongde + * @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 new file mode 100644 index 0000000..2b64c68 --- /dev/null +++ b/nl-plugin-tool/src/main/java/org/nl/tool/mock/modular/mockconfig/service/impl/MockConfigServiceImpl.java @@ -0,0 +1,113 @@ +/* + * Copyright [2022] [https://www.xiaonuo.vip] + * + * Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点: + * + * 1.请不要删除和修改根目录下的LICENSE文件。 + * 2.请不要删除和修改Snowy源码头部的版权声明。 + * 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。 + * 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip + * 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。 + * 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip + */ +package org.nl.tool.mock.modular.mockconfig.service.impl; + +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; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.nl.common.enums.CommonSortOrderEnum; +import org.nl.common.exception.CommonException; +import org.nl.common.page.CommonPageRequest; +import org.nl.tool.mock.modular.mockconfig.entity.MockConfig; +import org.nl.tool.mock.modular.mockconfig.mapper.MockConfigMapper; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigAddParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigEditParam; +import org.nl.tool.mock.modular.mockconfig.param.MockConfigIdParam; +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接口实现类 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +@Service +public class MockConfigServiceImpl extends ServiceImpl implements MockConfigService { + + @Override + public Page page(MockConfigPageParam mockConfigPageParam) { + QueryWrapper queryWrapper = new QueryWrapper().checkSqlInjection(); + if(ObjectUtil.isNotEmpty(mockConfigPageParam.getApiPath())) { + queryWrapper.lambda().eq(MockConfig::getApiPath, mockConfigPageParam.getApiPath()); + } + if(ObjectUtil.isNotEmpty(mockConfigPageParam.getApiMethod())) { + queryWrapper.lambda().eq(MockConfig::getApiMethod, mockConfigPageParam.getApiMethod()); + } + if(ObjectUtil.isNotEmpty(mockConfigPageParam.getIsEnabled())) { + queryWrapper.lambda().eq(MockConfig::getIsEnabled, mockConfigPageParam.getIsEnabled()); + } + if(ObjectUtil.isAllNotEmpty(mockConfigPageParam.getSortField(), mockConfigPageParam.getSortOrder())) { + CommonSortOrderEnum.validate(mockConfigPageParam.getSortOrder()); + queryWrapper.orderBy(true, mockConfigPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()), + StrUtil.toUnderlineCase(mockConfigPageParam.getSortField())); + } else { + queryWrapper.lambda().orderByAsc(MockConfig::getId); + } + return this.page(CommonPageRequest.defaultPage(), queryWrapper); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void add(MockConfigAddParam mockConfigAddParam) { + MockConfig mockConfig = BeanUtil.toBean(mockConfigAddParam, MockConfig.class); + this.save(mockConfig); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void edit(MockConfigEditParam mockConfigEditParam) { + MockConfig mockConfig = this.queryEntity(mockConfigEditParam.getId()); + BeanUtil.copyProperties(mockConfigEditParam, mockConfig); + this.updateById(mockConfig); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void delete(List mockConfigIdParamList) { + // 执行删除 + this.removeByIds(CollStreamUtil.toList(mockConfigIdParamList, MockConfigIdParam::getId)); + } + + @Override + public MockConfig detail(MockConfigIdParam mockConfigIdParam) { + return this.queryEntity(mockConfigIdParam.getId()); + } + + @Override + public MockConfig queryEntity(String id) { + MockConfig mockConfig = this.getById(id); + if(ObjectUtil.isEmpty(mockConfig)) { + throw new CommonException("Mock配置表不存在,id值为:{}", id); + } + return mockConfig; + } + + @Override + public Optional 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-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/controller/DevFileController.java b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/controller/DevFileController.java index e0e4c7e..d285e80 100644 --- a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/controller/DevFileController.java +++ b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/controller/DevFileController.java @@ -16,23 +16,24 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; +import org.nl.common.annotation.CommonLog; +import org.nl.common.pojo.CommonResult; +import org.nl.dev.api.DevConfigApi; import org.nl.dev.modular.file.entity.DevFile; import org.nl.dev.modular.file.enums.DevFileEngineTypeEnum; import org.nl.dev.modular.file.param.DevFileIdParam; import org.nl.dev.modular.file.param.DevFileListParam; import org.nl.dev.modular.file.param.DevFilePageParam; import org.nl.dev.modular.file.param.DevFileUrlListParam; +import org.nl.dev.modular.file.service.DevFileService; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import org.nl.common.annotation.CommonLog; -import org.nl.common.pojo.CommonResult; -import org.nl.dev.api.DevConfigApi; -import org.nl.dev.modular.file.service.DevFileService; import java.io.IOException; import java.util.List; @@ -262,4 +263,14 @@ public class DevFileController { public CommonResult> getFileListByUrlList(@RequestBody @Valid DevFileUrlListParam devFileUrlListParam) { return CommonResult.data(devFileService.getFileListByUrlList(devFileUrlListParam)); } + + @Operation(summary = "上传文件") + @PostMapping({"/api/localStorage/upload"}) + public CommonResult upload(@RequestParam MultipartFile file) { + return CommonResult.data(devFileService.upload(file)); + } + @GetMapping({"/api/localStorage/download"}) + public void download(@RequestParam String storageId , HttpServletResponse response, HttpServletRequest request) throws IOException { + this.devFileService.downloadFile(this.devFileService.getById(storageId),request, response); + } } diff --git a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/enums/ReturnTypeEnum.java b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/enums/ReturnTypeEnum.java new file mode 100644 index 0000000..cd1a5b7 --- /dev/null +++ b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/enums/ReturnTypeEnum.java @@ -0,0 +1,40 @@ +package org.nl.dev.modular.file.enums; + +/** + * + * @author: lyd + * @date: 2026/1/28 + */ +public enum ReturnTypeEnum { + /** + * 文件id + */ + FILE_ID("1"), + /** + * 下载地址 + */ + DOWNLOAD_URL("2"), + /** + * 文件对象 + */ + FILE("3"); + + private final String code; + + ReturnTypeEnum(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public static ReturnTypeEnum fromCode(String code) { + for (ReturnTypeEnum type : values()) { + if (type.code.equals(code)) { + return type; + } + } + return FILE; // 默认值 + } +} diff --git a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/DevFileService.java b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/DevFileService.java index 44db824..738626a 100644 --- a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/DevFileService.java +++ b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/DevFileService.java @@ -14,6 +14,7 @@ package org.nl.dev.modular.file.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.nl.dev.modular.file.entity.DevFile; import org.nl.dev.modular.file.param.DevFileIdParam; @@ -104,4 +105,8 @@ public interface DevFileService extends IService { * @date 2022/4/24 21:18 */ DevFile queryEntity(String id); + + DevFile upload(MultipartFile file); + + void downloadFile(DevFile file, HttpServletRequest request, HttpServletResponse response); } diff --git a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/impl/DevFileServiceImpl.java b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/impl/DevFileServiceImpl.java index dea6292..e4f75cd 100644 --- a/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/impl/DevFileServiceImpl.java +++ b/nl-plugin/nl-plugin-dev/src/main/java/org/nl/dev/modular/file/service/impl/DevFileServiceImpl.java @@ -27,10 +27,12 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.math3.dfp.DfpField; import org.nl.dev.modular.file.entity.DevFile; import org.nl.dev.modular.file.enums.DevFileEngineTypeEnum; +import org.nl.dev.modular.file.enums.ReturnTypeEnum; import org.nl.dev.modular.file.param.DevFileIdParam; import org.nl.dev.modular.file.param.DevFileListParam; import org.nl.dev.modular.file.param.DevFilePageParam; @@ -67,12 +69,12 @@ public class DevFileServiceImpl extends ServiceImpl impl @Override public String uploadReturnId(String engine, MultipartFile file) { - return this.storageFile(engine, file, true); + return this.storageFile(engine, file, ReturnTypeEnum.FILE_ID); } @Override public String uploadReturnUrl(String engine, MultipartFile file) { - return this.storageFile(engine, file, false); + return this.storageFile(engine, file, ReturnTypeEnum.DOWNLOAD_URL); } @Override @@ -127,11 +129,11 @@ public class DevFileServiceImpl extends ServiceImpl impl /** * 存储文件 - * + * 默认返回实体 * @author xuyuxiang * @date 2022/6/16 16:24 **/ - private String storageFile(String engine, MultipartFile file, boolean returnFileId) { + private T storageFile(String engine, MultipartFile file, ReturnTypeEnum returnType) { // 如果引擎为空,默认使用本地 if(ObjectUtil.isEmpty(engine)) { @@ -218,13 +220,11 @@ public class DevFileServiceImpl extends ServiceImpl impl this.save(devFile); - // 如果是返回id则返回文件id - if(returnFileId) { - return fileId; - } else { - // 否则返回下载地址 - return downloadUrl; - } + return switch (returnType) { + case FILE_ID -> (T) fileId; + case DOWNLOAD_URL -> (T) downloadUrl; + case FILE -> (T) devFile; + }; } /** @@ -277,6 +277,18 @@ public class DevFileServiceImpl extends ServiceImpl impl return devFile; } + @Override + public DevFile upload(MultipartFile file) { + return this.storageFile(DevFileEngineTypeEnum.LOCAL.getValue(), file, ReturnTypeEnum.FILE); + } + + @Override + public void downloadFile(DevFile file, HttpServletRequest request, HttpServletResponse response) { + String path = file.getStoragePath(); + File file2 = new File(path); + org.nl.common.util.FileUtil.downloadFile(request, response, file2, false); + } + /** * 根据文件后缀判断是否图片 * diff --git a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/user/service/SysUserService.java b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/user/service/SysUserService.java index 4fea2d1..1b5c199 100644 --- a/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/user/service/SysUserService.java +++ b/nl-plugin/nl-plugin-sys/src/main/java/org/nl/sys/modular/user/service/SysUserService.java @@ -18,15 +18,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import jakarta.servlet.http.HttpServletResponse; import org.nl.sys.modular.group.entity.SysGroup; +import org.nl.sys.modular.org.entity.SysOrg; import org.nl.sys.modular.position.entity.SysPosition; +import org.nl.sys.modular.role.entity.SysRole; +import org.nl.sys.modular.user.entity.SysUser; import org.nl.sys.modular.user.param.*; import org.nl.sys.modular.user.result.*; import org.springframework.web.multipart.MultipartFile; -import org.nl.sys.modular.user.param.*; -import org.nl.sys.modular.user.result.*; -import org.nl.sys.modular.org.entity.SysOrg; -import org.nl.sys.modular.role.entity.SysRole; -import org.nl.sys.modular.user.entity.SysUser; import java.io.IOException; import java.util.List; diff --git a/nl-vue/src/api/mock/mockConfigApi.js b/nl-vue/src/api/mock/mockConfigApi.js new file mode 100644 index 0000000..aa205ca --- /dev/null +++ b/nl-vue/src/api/mock/mockConfigApi.js @@ -0,0 +1,28 @@ +import { baseRequest } from '@/utils/request' + +const request = (url, ...arg) => baseRequest(`/mock/mockconfig/` + url, ...arg) + +/** + * Mock配置表Api接口管理器 + * + * @author liyongde + * @date 2026/01/28 17:50 + **/ +export default { + // 获取Mock配置表分页 + mockConfigPage(data) { + return request('page', data, 'get') + }, + // 提交Mock配置表表单 edit为true时为编辑,默认为新增 + mockConfigSubmitForm(data, edit = false) { + return request(edit ? 'edit' : 'add', data) + }, + // 删除Mock配置表 + mockConfigDelete(data) { + return request('delete', data) + }, + // 获取Mock配置表详情 + mockConfigDetail(data) { + return request('detail', data, 'get') + } +} diff --git a/nl-vue/src/views/mock/mockconfig/form.vue b/nl-vue/src/views/mock/mockconfig/form.vue new file mode 100644 index 0000000..e376531 --- /dev/null +++ b/nl-vue/src/views/mock/mockconfig/form.vue @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + 关闭 + 保存 + + + + + diff --git a/nl-vue/src/views/mock/mockconfig/index.vue b/nl-vue/src/views/mock/mockconfig/index.vue new file mode 100644 index 0000000..d783d68 --- /dev/null +++ b/nl-vue/src/views/mock/mockconfig/index.vue @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + 查询 + 重置 + + + + + + + + + 新增 + + + + + + + {{ $TOOL.dictTypeData('IS_USED', record.isEnabled === true ? '1' : record.isEnabled === false ? '0' : record.isEnabled) }} + + + + 编辑 + + + 删除 + + + + + + + + + + diff --git a/nl-web-app/pom.xml b/nl-web-app/pom.xml index 0cb2f13..02b77ee 100644 --- a/nl-web-app/pom.xml +++ b/nl-web-app/pom.xml @@ -94,6 +94,11 @@ org.nl nl-plugin-sys + + + org.nl.tool + nl-plugin-tool + diff --git a/nl-web-app/src/main/java/org/nl/core/config/GlobalConfigure.java b/nl-web-app/src/main/java/org/nl/core/config/GlobalConfigure.java index d195108..20bbf1c 100644 --- a/nl-web-app/src/main/java/org/nl/core/config/GlobalConfigure.java +++ b/nl-web-app/src/main/java/org/nl/core/config/GlobalConfigure.java @@ -48,6 +48,7 @@ import org.aspectj.lang.reflect.MethodSignature; import org.mybatis.spring.annotation.MapperScan; import org.nl.core.handler.GlobalExceptionUtil; import org.nl.sys.core.enums.SysBuildInEnum; +import org.nl.tool.mock.core.config.MockConfigProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -59,6 +60,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.jdbc.support.JdbcUtils; import org.springframework.stereotype.Component; import org.springframework.util.ResourceUtils; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.nl.auth.core.util.StpClientUtil; @@ -98,15 +100,45 @@ public class GlobalConfigure implements WebMvcConfigurer { @Autowired private SaTokenConfig saTokenConfig; + @Autowired + private MockInterceptor mockInterceptor; private static final String COMMON_REPEAT_SUBMIT_CACHE_KEY = "common-repeatSubmit:"; + public static final String[] NO_CHECK_MOCK = { + "/", + "/test", + "/auth/session/**", + "/auth/third/page", + "/client/user/**", + "/sys/**", + "/dev/**", + "/gen/basic/**", + "/gen/config/**", + "/mobile/menu/**", + "/mobile/module/**", + "/api/agv/status", + "/api/device/**", + "/api/agv/map/**", + "/api/localStorage/**", + "/api/baseData/point/status", + "/api/language/**", + "/favicon.ico", + "/doc.html", + "/webjars/**", + "/v3/api-docs/**", + "/druid/**", + "/mobile/**", + "/auth/**" + }; + /** * 无需登录的接口地址集合 */ public static final String[] NO_LOGIN_PATH_ARR = { /* 主入口 */ "/", + "/test", /*AGV*/ "/api/agv/status", "/api/device/**", @@ -673,4 +705,21 @@ public class GlobalConfigure implements WebMvcConfigurer { public void registerListenerList(List dataChangeListenerList) { CommonDataChangeEventCenter.registerListenerList(dataChangeListenerList); } + + /** + * 注册拦截器 + * 配置拦截路径和排除路径 + * + * @param registry 拦截器注册器 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) { +// log.info("Registering MockInterceptor"); + + registry.addInterceptor(mockInterceptor) + .addPathPatterns("/**") // 拦截所有路径 + .excludePathPatterns(CollectionUtil.newArrayList(NO_CHECK_MOCK)); + +// log.info("MockInterceptor registered successfully"); + } } 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..e203b0c --- /dev/null +++ b/nl-web-app/src/main/java/org/nl/core/config/MockInterceptor.java @@ -0,0 +1,104 @@ +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.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.util.List; +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; + private final PathMatcher pathMatcher = new AntPathMatcher(); + + /** + * 构造函数,注入依赖 + * + * @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 { + log.info("check config..."); + // 检查全局Mock开关 + if (!properties.isEnabled()) { + log.debug("Mock functionality is globally disabled, continuing to controller"); + return true; // Mock功能禁用,继续执行Controller + } + + // 提取请求路径和方法 + 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; + } +} diff --git a/pom.xml b/pom.xml index 75ce2f6..0801dc6 100644 --- a/pom.xml +++ b/pom.xml @@ -41,22 +41,23 @@ nl-web-app + nl-plugin-tool + nl-plugin-tool-api - - - - - - - - - - - - + + org.nl.tool + nl-plugin-tool + ${snowy.version} + + + org.nl.tool.api + nl-plugin-tool-api + ${snowy.version} + + org.nl nl-common