diff --git a/app/src/main/java/com/tinyengine/it/config/filter/FilterConfig.java b/app/src/main/java/com/tinyengine/it/config/filter/FilterConfig.java index d8ee5648..827082e9 100644 --- a/app/src/main/java/com/tinyengine/it/config/filter/FilterConfig.java +++ b/app/src/main/java/com/tinyengine/it/config/filter/FilterConfig.java @@ -15,8 +15,6 @@ import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; @@ -56,16 +54,4 @@ public FilterRegistrationBean requestIdFilter() { registrationBean.setFilter(new RequestIdFilter()); return registrationBean; } - - /** - * Object mapper object mapper. - * - * @return the object mapper - */ - @Bean - public ObjectMapper objectMapper() { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new JavaTimeModule()); - return mapper; - } } diff --git a/app/src/main/java/com/tinyengine/it/config/filter/WebConfig.java b/app/src/main/java/com/tinyengine/it/config/filter/WebConfig.java index f760e11b..839421f0 100644 --- a/app/src/main/java/com/tinyengine/it/config/filter/WebConfig.java +++ b/app/src/main/java/com/tinyengine/it/config/filter/WebConfig.java @@ -1,13 +1,12 @@ /** * Copyright (c) 2023 - present TinyEngine Authors. * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. - * + *

* Use of this source code is governed by an MIT-style license. - * + *

* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. - * */ package com.tinyengine.it.config.filter; @@ -16,31 +15,35 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.reactive.CorsWebFilter; -import org.springframework.web.reactive.config.WebFluxConfigurer; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.Arrays; import java.util.List; @Configuration -public class WebConfig implements WebFluxConfigurer { +public class WebConfig implements WebMvcConfigurer { @Value("${cors.allowed-origins}") private String allowedOrigins; @Bean - public CorsWebFilter corsFilter() { + public CorsFilter corsFilter() { + // 跨域配置地址 List crosDomainList = Arrays.asList(allowedOrigins.split(",")); CorsConfiguration corsConfiguration = new CorsConfiguration(); + // 1、允许来源 corsConfiguration.setAllowedOriginPatterns(crosDomainList); + // 2、允许任何请求头 corsConfiguration.addAllowedHeader(CorsConfiguration.ALL); + // 3、允许任何方法 corsConfiguration.addAllowedMethod(CorsConfiguration.ALL); + // 4、允许凭证 corsConfiguration.setAllowCredentials(true); - org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource source = - new org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource(); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfiguration); - - return new CorsWebFilter(source); + return new CorsFilter(source); } -} \ No newline at end of file +} diff --git a/base/src/main/java/com/tinyengine/it/common/handler/ListTypeHandler.java b/base/src/main/java/com/tinyengine/it/common/handler/ListTypeHandler.java index f76053d5..2464b066 100644 --- a/base/src/main/java/com/tinyengine/it/common/handler/ListTypeHandler.java +++ b/base/src/main/java/com/tinyengine/it/common/handler/ListTypeHandler.java @@ -14,8 +14,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.tinyengine.it.common.utils.JsonUtils; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.BaseTypeHandler; @@ -38,14 +38,12 @@ */ @Slf4j public class ListTypeHandler extends BaseTypeHandler> { - private final ObjectMapper objectMapper = new ObjectMapper(); - @Override public void setNonNullParameter(PreparedStatement ps, int i, List parameter, JdbcType jdbcType) throws SQLException { // 将 List 转换为字符串,并设置到 PreparedStatement 中的相应参数 try { - String json = objectMapper.writeValueAsString(parameter); + String json = JsonUtils.MAPPER.writeValueAsString(parameter); ps.setString(i, json); } catch (IOException e) { throw new SQLException("Error converting List to JSON", e); @@ -90,10 +88,10 @@ private List convetJsonList(String jsonString) throws JsonProcessingException return Collections.emptyList(); } else if (jsonString.startsWith("[{") && jsonString.endsWith("}]")) { // 尝试将 JSON 字符串转换为 List> - return objectMapper.readValue(jsonString, new TypeReference>>() {}); + return JsonUtils.MAPPER.readValue(jsonString, new TypeReference>>() {}); } else { // 尝试将 JSON 字符串转换为 List - return objectMapper.readValue(jsonString, new TypeReference>() {}); + return JsonUtils.MAPPER.readValue(jsonString, new TypeReference>() {}); } } } diff --git a/base/src/main/java/com/tinyengine/it/common/handler/MapTypeHandler.java b/base/src/main/java/com/tinyengine/it/common/handler/MapTypeHandler.java index 3ea157cb..67a0a105 100644 --- a/base/src/main/java/com/tinyengine/it/common/handler/MapTypeHandler.java +++ b/base/src/main/java/com/tinyengine/it/common/handler/MapTypeHandler.java @@ -15,8 +15,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.tinyengine.it.common.utils.JsonUtils; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.type.BaseTypeHandler; @@ -37,13 +37,11 @@ */ @Slf4j public class MapTypeHandler extends BaseTypeHandler> { - private final ObjectMapper objectMapper = new ObjectMapper(); - @Override public void setNonNullParameter(PreparedStatement ps, int i, Map parameter, JdbcType jdbcType) throws SQLException { try { - String json = objectMapper.writeValueAsString(parameter); + String json = JsonUtils.MAPPER.writeValueAsString(parameter); ps.setString(i, json); } catch (JsonProcessingException e) { throw new SQLException("Error converting Map to JSON", e); @@ -71,9 +69,9 @@ private Map parseJson(String json) throws SQLException { return new HashMap<>(); } try { - JsonNode jsonNode = objectMapper.readTree(json); + JsonNode jsonNode = JsonUtils.MAPPER.readTree(json); if (jsonNode.isObject()) { - return objectMapper.readValue(json, new TypeReference>() {}); + return JsonUtils.MAPPER.readValue(json, new TypeReference>() {}); } else { // 非对象类型也返回空的 Map return new HashMap<>(); diff --git a/base/src/main/java/com/tinyengine/it/common/utils/JsonUtils.java b/base/src/main/java/com/tinyengine/it/common/utils/JsonUtils.java new file mode 100644 index 00000000..ea72cb32 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/common/utils/JsonUtils.java @@ -0,0 +1,349 @@ +/** + * Copyright (c) 2023 - present TinyEngine Authors. + * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. + * + * Use of this source code is governed by an MIT-style license. + * + * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, + * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR + * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + * + */ + +package com.tinyengine.it.common.utils; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.tinyengine.it.common.exception.ExceptionEnum; +import com.tinyengine.it.common.exception.ServiceException; +import com.tinyengine.it.common.log.SystemLogAspect; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; + +/** + * The type json Utils. + * + * @since 2025-06-19 + */ +public class JsonUtils { + public static final ObjectMapper MAPPER = MapperFactory.getDefaultMapper(); + public static final ObjectMapper PRETTY_MAPPER = MapperFactory.getDefaultMapper(); + public static final DefaultPrettyPrinter PRINTER = new DefaultPrettyPrinter(); + private static final Logger LOGGER = LoggerFactory.getLogger(SystemLogAspect.class); + + public JsonUtils() { + } + + private static void initialize() { + SimpleModule module = new SimpleModule(); + MAPPER.findAndRegisterModules(); + MAPPER.registerModule(module); + MAPPER.registerModule(new JavaTimeModule()); + PRETTY_MAPPER.configure(JsonParser.Feature.ALLOW_COMMENTS, true); + PRETTY_MAPPER.configure(SerializationFeature.INDENT_OUTPUT, true); + PRETTY_MAPPER.registerModule(module); + PRETTY_MAPPER.findAndRegisterModules(); + PRINTER.indentObjectsWith(new DefaultIndenter(" ", "\n")); + PRETTY_MAPPER.setDefaultPrettyPrinter(PRINTER); + } + + private static T invoke(Callable callable) { + try { + return callable.call(); + } catch (Exception var2) { + Exception e = var2; + throw new ServiceException(ExceptionEnum.CM001.getResultCode(), ExceptionEnum.CM001.getResultMsg() + e.getMessage()); + } + } + + /** + * To mapper. + * + * @return the MAPPER + */ + public static ObjectMapper mapper() { + return MAPPER; + } + + /** + * To pretty mapper. + * + * @return the string + */ + public static ObjectMapper prettyMapper() { + return PRETTY_MAPPER; + } + + /** + * To encode. + * + * @param obj the obj + * @return the string + */ + public static String encode(Object obj) { + return (String) invoke(() -> { + return MAPPER.writeValueAsString(obj); + }); + } + + /** + * To encode prettily. + * + * @param obj the obj + * @return the string + */ + public static String encodePrettily(Object obj) { + return (String) invoke(() -> { + return PRETTY_MAPPER.writeValueAsString(obj); + }); + } + + /** + * To encode as bytes. + * + * @param obj the obj + * @return the byte + */ + public static byte[] encodeAsBytes(Object obj) { + return (byte[]) invoke(() -> { + return MAPPER.writeValueAsBytes(obj); + }); + } + + /** + * To decode. + * + * @param src the src + * @return the T + */ + public static T decode(byte[] src) { + return (T) decode(src, Object.class); + } + + /** + * To decode. + * + * @param src the src + * @param valueType the valueType + * @return the T + */ + public static T decode(byte[] src, Class valueType) { + return invoke(() -> { + return MAPPER.readValue(src, valueType); + }); + } + + /** + * To decode. + * + * @param src the src + * @param valueTypeRef the valueTypeRef + * @return the T + */ + public static T decode(byte[] src, TypeReference valueTypeRef) { + return invoke(() -> { + return MAPPER.readValue(src, valueTypeRef); + }); + } + + /** + * To decode. + * + * @param url the url + * @param valueType the valueType + * @return the T + */ + public static T decode(URL url, Class valueType) { + return invoke(() -> { + return MAPPER.readValue(url, valueType); + }); + } + + /** + * To decode. + * + * @param src the src + * @return the T + */ + public static T decode(String src) { + return (T) decode(src, Object.class); + } + + /** + * To decode. + * + * @param src the src + * @param valueType the valueType + * @return the T + */ + public static T decode(String src, Class valueType) { + return invoke(() -> { + return MAPPER.readValue(src, valueType); + }); + } + + /** + * To decode. + * + * @param src the src + * @param valueType the valueType + * @return the T + */ + public static T decode(String src, JavaType valueType) { + return invoke(() -> { + return MAPPER.readValue(src, valueType); + }); + } + + /** + * To decode. + * + * @param src the src + * @param valueTypeRef the valueTypeRef + * @return the T + */ + public static T decode(String src, TypeReference valueTypeRef) { + return invoke(() -> { + return MAPPER.readValue(src, valueTypeRef); + }); + } + + /** + * To decode. + * + * @param src the src + * @param valueType the valueType + * @return the T + */ + public static T decode(ByteBuf src, Class valueType) { + return (T) invoke(() -> { + InputStream inputStream = new ByteBufInputStream(src); + Object var3; + try { + var3 = MAPPER.readValue(inputStream, valueType); + } catch (Throwable var6) { + try { + inputStream.close(); + } catch (Throwable var5) { + var6.addSuppressed(var5); + } + throw var6; + } + inputStream.close(); + return var3; + }); + } + + /** + * To decode. + * + * @param src the src + * @param valueType the valueType + * @return the T + */ + public static T decode(InputStream src, Class valueType) { + return invoke(() -> { + return MAPPER.readValue(src, valueType); + }); + } + + /** + * To convert value. + * + * @param fromValue the fromValue + * @param toValueType the toValueType + * @return the T + */ + public static T convertValue(Object fromValue, Class toValueType) { + return invoke(() -> { + return MAPPER.convertValue(fromValue, toValueType); + }); + } + + /** + * To convert value. + * + * @param fromValue the fromValue + * @param toValueType the toValueType + * @return the T + */ + public static T convertValue(Object fromValue, JavaType toValueType) { + return invoke(() -> { + return MAPPER.convertValue(fromValue, toValueType); + }); + } + + /** + * To convert value. + * + * @param fromValue the fromValue + * @param toValueType the toValueType + * @return the T + */ + public static T convertValue(Object fromValue, TypeReference toValueType) { + return invoke(() -> { + return MAPPER.convertValue(fromValue, toValueType); + }); + } + + /** + * Converts various input types (String, Array, List) to a trimmed List of Strings. + * + * @param inputs the inputs + * @return List of trimmed strings, empty list if input is null or unsupported type + */ + public static List getList(Object inputs) { + if (inputs == null) { + return Collections.emptyList(); + } + + // Handle Array → List + if (inputs.getClass().isArray()) { + if (inputs instanceof Object[]) { + inputs = Arrays.asList((Object[]) inputs); + } else { + // Handle primitive arrays by converting to string representation + inputs = Collections.singletonList(inputs.toString()); + } + } + + // Handle String → List + if (inputs instanceof String) { + return Arrays.stream(((String) inputs).split(",")) + .map(String::trim) + .collect(Collectors.toList()); + } + + // Handle List → List + if (inputs instanceof List) { + return ((List) inputs).stream() + .map(Object::toString) + .map(String::trim) + .collect(Collectors.toList()); + } + + LOGGER.error("Bad data type: {}, it should be String, Array, or List.", inputs.getClass().getName()); + return Collections.emptyList(); + } + + static { + initialize(); + } +} diff --git a/base/src/main/java/com/tinyengine/it/common/utils/MapperFactory.java b/base/src/main/java/com/tinyengine/it/common/utils/MapperFactory.java new file mode 100644 index 00000000..ae3dfffb --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/common/utils/MapperFactory.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2023 - present TinyEngine Authors. + * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. + * + * Use of this source code is governed by an MIT-style license. + * + * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, + * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR + * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + * + */ + +package com.tinyengine.it.common.utils; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.core.io.support.SpringFactoriesLoader; + +import java.time.OffsetDateTime; +import java.util.List; + +/** + * The type mapper Factory. + * + * @since 2025-06-19 + */ +public class MapperFactory { + public MapperFactory() { + } + + /** + * To register modules. + * + * @param mapper the mapper + * @return + */ + public static void registerModules(ObjectMapper mapper) { + List modules = SpringFactoriesLoader.loadFactories(Module.class, (ClassLoader) null); + mapper.registerModules((Module[]) modules.toArray(new Module[0])); + mapper.registerModule(new SimpleModule()); + mapper.registerModule(new JavaTimeModule()); + mapper.registerModules(new Module[]{(new SimpleModule()).addDeserializer(OffsetDateTime.class, + new OffsetDateTimeDeserializer())}); + } + + /** + * To get default builder. + * + * @return JsonMapper.Builder the JsonMapper.Builder + */ + public static JsonMapper.Builder getDefaultBuilder() { + return (JsonMapper.Builder) ((JsonMapper.Builder) ((JsonMapper.Builder) + ((JsonMapper.Builder) ((JsonMapper.Builder) ((JsonMapper.Builder) + ((JsonMapper.Builder) ((JsonMapper.Builder) ((JsonMapper.Builder) JsonMapper.builder() + .disable(new DeserializationFeature[]{DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES})) + .disable(new SerializationFeature[] {SerializationFeature.FAIL_ON_EMPTY_BEANS})) + .disable(new MapperFeature[]{MapperFeature.DEFAULT_VIEW_INCLUSION})) + .disable(new SerializationFeature[]{SerializationFeature.WRITE_DATES_AS_TIMESTAMPS})) + .disable(new JsonParser.Feature[]{JsonParser.Feature.AUTO_CLOSE_SOURCE})) + .serializationInclusion(JsonInclude.Include.NON_NULL)) + .enable(new DeserializationFeature[]{DeserializationFeature.UNWRAP_SINGLE_VALUE_ARRAYS})) + .enable(new DeserializationFeature[]{DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY})) + .enable(new DeserializationFeature[]{DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT}); + } + + /** + * To get default mapper. + * + * @return ObjectMapper the ObjectMapper. + */ + public static ObjectMapper getDefaultMapper() { + return getDefaultBuilder().build(); + } + + /** + * To get modules registered mapper. + * + * @return ObjectMapper the ObjectMapper. + */ + public static ObjectMapper getModulesRegisteredMapper() { + ObjectMapper mapper = getDefaultMapper(); + registerModules(mapper); + return mapper; + } +} diff --git a/base/src/main/java/com/tinyengine/it/common/utils/OffsetDateTimeDeserializer.java b/base/src/main/java/com/tinyengine/it/common/utils/OffsetDateTimeDeserializer.java new file mode 100644 index 00000000..e8e36ab4 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/common/utils/OffsetDateTimeDeserializer.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2023 - present TinyEngine Authors. + * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. + * + * Use of this source code is governed by an MIT-style license. + * + * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, + * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR + * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + * + */ + +package com.tinyengine.it.common.utils; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import org.springframework.util.StringUtils; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.TemporalAccessor; + +/** + * The type offset date time Deserializer. + * + * @since 2025-06-19 + */ +public class OffsetDateTimeDeserializer extends JsonDeserializer { + private static final DateTimeFormatter ISO_8601_FORMATTER; + + public OffsetDateTimeDeserializer() { + } + + /** + * To Deserialize. + * + * @param jsonParser the jsonParser + * @param deserializationContext the deserializationContext + * @return OffsetDateTime yhe OffsetDateTime + */ + @Override + public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + if (StringUtils.isEmpty(jsonParser.getText())) { + return null; + } else { + TemporalAccessor temporalAccessor = ISO_8601_FORMATTER.parseBest(jsonParser.getText(), ZonedDateTime::from, LocalDateTime::from); + if (temporalAccessor instanceof ZonedDateTime) { + return ((ZonedDateTime) temporalAccessor).toOffsetDateTime(); + } else { + return temporalAccessor instanceof LocalDateTime ? ((LocalDateTime) temporalAccessor).atOffset(ZoneOffset.UTC) : null; + } + } + + } + + static { + ISO_8601_FORMATTER = (new DateTimeFormatterBuilder().parseCaseInsensitive() + .append(DateTimeFormatter.ISO_LOCAL_DATE).optionalStart() + .appendLiteral('T').optionalEnd().optionalStart().appendLiteral(' ').optionalEnd() + .append(DateTimeFormatter.ISO_LOCAL_TIME).optionalStart().appendOffsetId().optionalEnd().toFormatter()); + } +} diff --git a/base/src/main/java/com/tinyengine/it/common/utils/SecurityFileCheckUtil.java b/base/src/main/java/com/tinyengine/it/common/utils/SecurityFileCheckUtil.java index deae55e3..820315ab 100644 --- a/base/src/main/java/com/tinyengine/it/common/utils/SecurityFileCheckUtil.java +++ b/base/src/main/java/com/tinyengine/it/common/utils/SecurityFileCheckUtil.java @@ -12,7 +12,6 @@ package com.tinyengine.it.common.utils; import cn.hutool.core.util.ObjectUtil; -import com.fasterxml.jackson.databind.ObjectMapper; import com.tinyengine.it.common.exception.ExceptionEnum; import com.tinyengine.it.common.exception.ServiceException; import org.springframework.util.StringUtils; @@ -190,10 +189,9 @@ private static String getFileName(String filePath) { * @param file the file */ public static void isValidJson(MultipartFile file) { - ObjectMapper objectMapper = new ObjectMapper(); try { // 将 MultipartFile 转换为 InputStream 并解析 JSON - objectMapper.readTree(file.getInputStream()); + JsonUtils.MAPPER.readTree(file.getInputStream()); } catch (IOException e) { throw new ServiceException(ExceptionEnum.CM308.getResultCode(), ExceptionEnum.CM308.getResultMsg()); } diff --git a/base/src/main/java/com/tinyengine/it/common/utils/Utils.java b/base/src/main/java/com/tinyengine/it/common/utils/Utils.java index af23969c..b7836835 100644 --- a/base/src/main/java/com/tinyengine/it/common/utils/Utils.java +++ b/base/src/main/java/com/tinyengine/it/common/utils/Utils.java @@ -13,9 +13,6 @@ package com.tinyengine.it.common.utils; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.tinyengine.it.common.base.Result; import com.tinyengine.it.common.enums.Enums; import com.tinyengine.it.common.exception.ExceptionEnum; @@ -161,22 +158,6 @@ public static String toLine(String name) { return result.toString(); } - /** - * Convert map. - * - * @param obj the obj - * @return the map - */ - // 对象转map - public static Map convert(Object obj) { - ObjectMapper objectMapper = new ObjectMapper(); - - objectMapper.registerModule(new JavaTimeModule()); - objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - // 将对象转换为 JSON 字符串,然后再解析为 Map - return objectMapper.convertValue(obj, Map.class); - } - /** * 解压并处理zip文件,把读取到的JSON文件内容以字符串返回 * @@ -386,9 +367,8 @@ public static Result parseJsonFileStream(MultipartFile file) { String jsonContent = new String(fileBytes, StandardCharsets.UTF_8); String jsonString = removeBOM(jsonContent); - ObjectMapper objectMapper = new ObjectMapper(); Map jsonData = - objectMapper.readValue(jsonString, new TypeReference>() { + JsonUtils.MAPPER.readValue(jsonString, new TypeReference>() { }); jsonFile.setFileName(fileName); diff --git a/base/src/main/java/com/tinyengine/it/controller/BlockController.java b/base/src/main/java/com/tinyengine/it/controller/BlockController.java index 8665b243..bbcfa8dd 100644 --- a/base/src/main/java/com/tinyengine/it/controller/BlockController.java +++ b/base/src/main/java/com/tinyengine/it/controller/BlockController.java @@ -14,7 +14,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.enums.Enums; import com.tinyengine.it.common.log.SystemControllerLog; +import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.mapper.BlockMapper; import com.tinyengine.it.mapper.TenantMapper; import com.tinyengine.it.model.dto.BlockBuildDto; @@ -33,6 +35,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -46,6 +49,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -344,7 +350,7 @@ public Result> getBlockGroups(@Valid @RequestParam(required = false) /** * 修改block * - * @param blockParam blockParam + * @param request request * @param id id * @return block dto */ @@ -358,10 +364,24 @@ public Result> getBlockGroups(@Valid @RequestParam(required = false) }) @SystemControllerLog(description = "区块修改api") @PostMapping("/block/update/{id}") - public Result updateBlocks(@Valid @RequestBody BlockParam blockParam, @PathVariable Integer id, - @RequestParam(value = "appId", required = false) Integer appId) { + public Result updateBlocks(HttpServletRequest request, @PathVariable Integer id, + @RequestParam(value = "appId", required = false) Integer appId) throws IOException { + // Validate content type + String contentType = request.getContentType(); + if (contentType == null || !contentType.contains(Enums.FileType.JSON.getValue())) { + return Result.failed("Content-Type must be application/json"); + } + InputStream inputStream = request.getInputStream(); + String json = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + BlockParam blockParam = null; + try { + blockParam = JsonUtils.decode(json, BlockParam.class); + } catch (Exception e) { + return Result.failed("Invalid JSON format: " + e.getMessage()); + } blockParam.setId(id); - return blockService.updateBlockById(blockParam, appId); + blockParam.setAppId(appId); + return blockService.updateBlockById(blockParam); } /** diff --git a/base/src/main/java/com/tinyengine/it/controller/PageController.java b/base/src/main/java/com/tinyengine/it/controller/PageController.java index 208c7baa..c2166b9c 100644 --- a/base/src/main/java/com/tinyengine/it/controller/PageController.java +++ b/base/src/main/java/com/tinyengine/it/controller/PageController.java @@ -12,7 +12,9 @@ package com.tinyengine.it.controller; import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.enums.Enums; import com.tinyengine.it.common.log.SystemControllerLog; +import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.model.dto.PreviewDto; import com.tinyengine.it.model.dto.PreviewParam; import com.tinyengine.it.model.entity.Page; @@ -26,6 +28,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; @@ -38,6 +41,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.List; /** @@ -135,7 +141,7 @@ public Result createPage(@Valid @RequestBody Page page) throws Exception { /** * 修改页面 * - * @param page the page + * @param request the request * @return result * @throws Exception the exception */ @@ -148,16 +154,28 @@ public Result createPage(@Valid @RequestBody Page page) throws Exception { }) @SystemControllerLog(description = "修改页面") @PostMapping("/pages/update/{id}") - public Result updatePage(@RequestBody Page page) throws Exception { - page.setLastUpdatedTime(null); - page.setCreatedTime(null); - page.setLastUpdatedBy(null); - if (page.getIsPage()) { - // 更新页面 - return pageService.updatePage(page); - } else { - // 更新文件夹 - return pageService.update(page); + public Result updatePage(HttpServletRequest request) throws IOException { + // Validate content type + String contentType = request.getContentType(); + if (contentType == null || !contentType.contains(Enums.FileType.JSON.getValue())) { + return Result.failed("Content-Type must be application/json"); + } + try (InputStream inputStream = request.getInputStream()) { + String json = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + Page page = JsonUtils.decode(json, Page.class); + page.setLastUpdatedTime(null); + page.setLastUpdatedTime(null); + page.setCreatedTime(null); + page.setLastUpdatedBy(null); + if (page.getIsPage()) { + // 更新页面 + return pageService.updatePage(page); + } else { + // 更新文件夹 + return pageService.update(page); + } + } catch (IOException e) { + return Result.failed("Failed to read request body: " + e.getMessage()); } } diff --git a/base/src/main/java/com/tinyengine/it/controller/PageHistoryController.java b/base/src/main/java/com/tinyengine/it/controller/PageHistoryController.java index b156c418..bce7300a 100644 --- a/base/src/main/java/com/tinyengine/it/controller/PageHistoryController.java +++ b/base/src/main/java/com/tinyengine/it/controller/PageHistoryController.java @@ -15,7 +15,9 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.tinyengine.it.common.base.PageQueryVo; import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.enums.Enums; import com.tinyengine.it.common.log.SystemControllerLog; +import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.model.dto.PublishedPageVo; import com.tinyengine.it.model.entity.PageHistory; import com.tinyengine.it.service.app.PageHistoryService; @@ -26,7 +28,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; @@ -38,6 +40,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.regex.Pattern; @@ -124,7 +129,7 @@ public Result getPageHistoryById(@PathVariable Integer historyId) { /** * 创建页面历史记录 * - * @param pageHistory the page history + * @param request the request * @return result */ @Operation(summary = "创建页面历史记录", description = "创建页面历史记录", parameters = { @@ -136,17 +141,29 @@ public Result getPageHistoryById(@PathVariable Integer historyId) { }) @SystemControllerLog(description = "创建页面历史记录") @PostMapping("/pages/history/create") - public Result createPageHistory(@Valid @RequestBody PageHistory pageHistory) { - PageHistory result; + public Result createPageHistory(HttpServletRequest request) throws IOException { + // Validate content type + String contentType = request.getContentType(); + if (contentType == null || !contentType.contains(Enums.FileType.JSON.getValue())) { + return Result.failed("Content-Type must be application/json"); + } + InputStream inputStream = request.getInputStream(); + String json = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + PageHistory pageHistory = null; + try { + pageHistory = JsonUtils.decode(json, PageHistory.class); + } catch (Exception e) { + return Result.failed("Invalid JSON format: " + e.getMessage()); + } if (pageHistory.getPage() != null && Pattern.matches("^[0-9]+$", pageHistory.getPage().toString()) && pageHistory.getPageContent() != null) { pageHistoryService.createPageHistory(pageHistory); int historyId = pageHistory.getId(); - result = pageHistoryService.findPageHistoryById(historyId); + pageHistory = pageHistoryService.findPageHistoryById(historyId); } else { return Result.failed("The request body is missing some parameters"); } - return Result.success(result); + return Result.success(pageHistory); } /** diff --git a/base/src/main/java/com/tinyengine/it/controller/PageTemplateController.java b/base/src/main/java/com/tinyengine/it/controller/PageTemplateController.java index f490d1fd..53cb2342 100644 --- a/base/src/main/java/com/tinyengine/it/controller/PageTemplateController.java +++ b/base/src/main/java/com/tinyengine/it/controller/PageTemplateController.java @@ -13,7 +13,9 @@ package com.tinyengine.it.controller; import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.enums.Enums; import com.tinyengine.it.common.log.SystemControllerLog; +import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.model.entity.PageTemplate; import com.tinyengine.it.service.app.PageTemplateService; @@ -23,7 +25,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; @@ -35,6 +37,8 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.List; /** @@ -59,7 +63,7 @@ public class PageTemplateController { /** * 创建pageTemplate * - * @param pageTemplate pageTemplate + * @param request request * @return pageTemplate信息 */ @Operation(summary = "创建页面模版", description = "创建页面模版", parameters = { @@ -71,7 +75,20 @@ public class PageTemplateController { }) @SystemControllerLog(description = "创建页面模版") @PostMapping("/page-template/create") - public Result createPageTemplate(@Valid @RequestBody PageTemplate pageTemplate) { + public Result createPageTemplate(HttpServletRequest request) throws Exception { + // Validate content type + String contentType = request.getContentType(); + if (contentType == null || !contentType.contains(Enums.FileType.JSON.getValue())) { + return Result.failed("Content-Type must be application/json"); + } + InputStream inputStream = request.getInputStream(); + String json = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + PageTemplate pageTemplate = null; + try { + pageTemplate = JsonUtils.decode(json, PageTemplate.class); + } catch (Exception e) { + return Result.failed("Invalid JSON format: " + e.getMessage()); + } return pageTemplateService.createPageTemplate(pageTemplate); } diff --git a/base/src/main/java/com/tinyengine/it/gateway/ai/AiChatClient.java b/base/src/main/java/com/tinyengine/it/gateway/ai/AiChatClient.java index 0433f3b2..1f4c9bbb 100644 --- a/base/src/main/java/com/tinyengine/it/gateway/ai/AiChatClient.java +++ b/base/src/main/java/com/tinyengine/it/gateway/ai/AiChatClient.java @@ -12,20 +12,21 @@ package com.tinyengine.it.gateway.ai; -import static com.tinyengine.it.common.exception.ExceptionEnum.CM322; - -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.tinyengine.it.common.exception.ExceptionEnum; import com.tinyengine.it.common.exception.ServiceException; +import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.config.AiChatConfig; import com.tinyengine.it.model.dto.AiParam; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import reactor.core.publisher.Mono; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; import java.util.Collections; import java.util.Map; @@ -38,7 +39,9 @@ @Slf4j public class AiChatClient { private final Map config; - private WebClient webClient; + // 新增 Setter 方法,便于测试时注入 Mock 对象 + @Setter + private RestTemplate restTemplate; /** * Instantiates a new Ai chat client. @@ -47,8 +50,8 @@ public class AiChatClient { */ public AiChatClient(String model, String token) { this.config = AiChatConfig.getAiChatConfig(model, token); - // Optional: Default base URL - this.webClient = WebClient.builder().baseUrl("https://default.api.url").build(); + // Use RestTemplate for WebMVC + this.restTemplate = new RestTemplate(); } /** @@ -72,26 +75,12 @@ public Map executeChatRequest(AiParam openAiBodyDto) { log.info("Headers: " + configData.headers); HttpMethod method = "POST".equalsIgnoreCase(httpRequestOption.method) ? HttpMethod.POST : HttpMethod.GET; - WebClient.RequestHeadersSpec requestSpec = webClient.method(method).uri(httpRequestUrl); - - for (Map.Entry header : configData.headers.entrySet()) { - requestSpec.header(header.getKey(), header.getValue()); - } + HttpHeaders headers = new HttpHeaders(); + configData.headers.forEach(headers::set); - if ("POST".equalsIgnoreCase(httpRequestOption.method) && !openAiBodyDto.getMessages().isEmpty()) { - if (requestSpec instanceof WebClient.RequestBodySpec) { - requestSpec = ((WebClient.RequestBodySpec) requestSpec).bodyValue(openAiBodyDto); - // Add request body - } - } - - Mono stringMono = requestSpec.retrieve().bodyToMono(String.class); - return stringMono.map(response -> { - try { - return new ObjectMapper().readValue(response, new TypeReference>() {}); - } catch (JsonProcessingException e) { - throw new ServiceException(CM322.getResultCode(), e.getMessage()); - } - }).block(); // 等待结果 + HttpEntity entity = new HttpEntity<>(openAiBodyDto, headers); + ResponseEntity responseEntity = restTemplate.exchange(httpRequestUrl, method, entity, String.class); + return JsonUtils.decode(responseEntity.getBody(), new TypeReference>() {}); } -} + +} \ No newline at end of file diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/I18nEntryServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/I18nEntryServiceImpl.java index f258e5d3..cc305cc2 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/I18nEntryServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/I18nEntryServiceImpl.java @@ -16,12 +16,12 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import com.tinyengine.it.common.base.Result; import com.tinyengine.it.common.enums.Enums; import com.tinyengine.it.common.exception.ExceptionEnum; import com.tinyengine.it.common.exception.ServiceException; import com.tinyengine.it.common.log.SystemServiceLog; +import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.common.utils.SecurityFileCheckUtil; import com.tinyengine.it.common.utils.Utils; import com.tinyengine.it.mapper.I18nEntryMapper; @@ -492,12 +492,11 @@ public List parseZipFileStream(MultipartFile file) throws Exception */ public List parseZip(FileInfo fileInfo) throws ServiceException { List entriesItems = new ArrayList<>(); - ObjectMapper objectMapper = new ObjectMapper(); if (!fileInfo.getIsDirectory()) { EntriesItem entriesItem = setLang(fileInfo.getName()); // 处理 JSON 内容 try { - Map jsonData = objectMapper.readValue(fileInfo.getContent(), + Map jsonData = JsonUtils.MAPPER.readValue(fileInfo.getContent(), new TypeReference>() {}); entriesItem.setEntries(Utils.flat(jsonData)); } catch (JsonProcessingException e) { diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImpl.java index b7f60f03..d2846ed0 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImpl.java @@ -12,8 +12,10 @@ package com.tinyengine.it.service.app.impl.v1; +import static com.tinyengine.it.common.utils.JsonUtils.convertValue; import static com.tinyengine.it.common.utils.Utils.findMaxVersion; +import com.fasterxml.jackson.core.type.TypeReference; import com.tinyengine.it.common.exception.ServiceException; import com.tinyengine.it.common.log.SystemServiceLog; import com.tinyengine.it.common.utils.Schema; @@ -235,7 +237,7 @@ private void mergeMaps(Map source, Map target) { private SchemaMeta getSchemaMeta(MetaDto metaDto) { App metaDtoApp = metaDto.getApp(); - Map appData = Utils.convert(metaDtoApp); + Map appData = convertValue(metaDtoApp, new TypeReference>() {}); Map config = new HashMap<>(); config.put("sdkVersion", "1.0.3"); config.put("historyMode", "hash"); @@ -464,7 +466,7 @@ public List getSchemaComponentsTree(MetaDto metaDto) { resKeys.add("is_page"); resKeys.add("is_default"); for (Page pageInfo : pageList) { - Map data = Utils.convert(pageInfo); + Map data = convertValue(pageInfo, new TypeReference>() {}); boolean isToLine = false; Map page = formatDataFields(data, resKeys, isToLine); if (app.getHomePage() != null) { diff --git a/base/src/main/java/com/tinyengine/it/service/material/BlockService.java b/base/src/main/java/com/tinyengine/it/service/material/BlockService.java index efc62c24..0b14e77c 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/BlockService.java +++ b/base/src/main/java/com/tinyengine/it/service/material/BlockService.java @@ -65,11 +65,11 @@ public interface BlockService extends IService { /** * 根据主键id更新表t_block信息 - * @param appId appId + * * @param blockParam the block param * @return the BlockDto */ - Result updateBlockById(BlockParam blockParam, Integer appId); + Result updateBlockById(BlockParam blockParam); /** * 新增表t_block数据 diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/BlockServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/BlockServiceImpl.java index 20191322..5ac83521 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/BlockServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/BlockServiceImpl.java @@ -19,12 +19,12 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.tinyengine.it.common.base.Result; import com.tinyengine.it.common.context.LoginUserContext; import com.tinyengine.it.common.enums.Enums; import com.tinyengine.it.common.exception.ExceptionEnum; import com.tinyengine.it.common.log.SystemServiceLog; +import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.mapper.AppMapper; import com.tinyengine.it.mapper.BlockGroupBlockMapper; import com.tinyengine.it.mapper.BlockGroupMapper; @@ -160,11 +160,10 @@ public Integer deleteBlockById(Integer id) { * 根据主键id更新表t_block数据 * * @param blockParam blockParam - * @param appId * @return blockDto */ @Override - public Result updateBlockById(BlockParam blockParam, Integer appId) { + public Result updateBlockById(BlockParam blockParam) { if (blockParam == null || blockParam.getId() == null) { return Result.failed(ExceptionEnum.CM002); } @@ -172,7 +171,7 @@ public Result updateBlockById(BlockParam blockParam, Integer appId) { if (blockResult == null) { return Result.failed(ExceptionEnum.CM001); } - if (!Objects.equals(blockResult.getAppId(), appId)) { + if (!Objects.equals(blockResult.getAppId(), blockParam.getAppId())) { return Result.failed(ExceptionEnum.CM007); } // 把前端传参赋值给实体 @@ -277,10 +276,9 @@ public Result createBlock(BlockParam blockParam) { */ public Map> getBlockAssets(Map pageContent, String framework) { List block = new ArrayList<>(); - ObjectMapper objectMapper = new ObjectMapper(); try { - traverseBlocks(objectMapper.writeValueAsString(pageContent), block); + traverseBlocks(JsonUtils.MAPPER.writeValueAsString(pageContent), block); } catch (JsonProcessingException e) { log.error(e.getMessage()); } @@ -350,23 +348,22 @@ public List getBlockInfo(List block, String framework) { * @throws JsonProcessingException the json processing exception */ public void traverseBlocks(String content, List block) throws JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - JsonNode jsonNode = objectMapper.readTree(content); + JsonNode jsonNode = JsonUtils.MAPPER.readTree(content); // 判断传过来的参数是JSON数组还是JSON对象 if (content != null && jsonNode.isArray()) { - List schema = objectMapper.readValue(content, List.class); + List schema = JsonUtils.MAPPER.readValue(content, List.class); for (Object prop : schema) { - traverseBlocks(objectMapper.writeValueAsString(prop), block); + traverseBlocks(JsonUtils.MAPPER.writeValueAsString(prop), block); } } else { - Map schemaMap = objectMapper.readValue(content, Map.class); + Map schemaMap = JsonUtils.MAPPER.readValue(content, Map.class); if (isBlock(schemaMap) && !block.contains(schemaMap.get("componentName"))) { if (schemaMap.get("componentName") instanceof String) { block.add((String) schemaMap.get("componentName")); } } if (schemaMap.containsKey("children") && schemaMap.get("children") instanceof List) { - traverseBlocks(objectMapper.writeValueAsString(schemaMap.get("children")), block); + traverseBlocks(JsonUtils.MAPPER.writeValueAsString(schemaMap.get("children")), block); } } } @@ -563,8 +560,9 @@ public Result deploy(BlockBuildDto blockBuildDto) { blockParam.setLatestHistoryId(blockHistory); blockParam.setLatestVersion(blockHistory.getVersion()); blockParam.setId(blockDto.getId()); + blockParam.setAppId(blockDto.getAppId()); blockParam.setGroups(null); - return updateBlockById(blockParam, blockDto.getAppId()); + return updateBlockById(blockParam); } catch (Exception e) { return Result.failed(ExceptionEnum.CM001); } diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ComponentServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ComponentServiceImpl.java index ccf1f334..14d50aa0 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/ComponentServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ComponentServiceImpl.java @@ -147,7 +147,8 @@ public Result readFileAndBulkCreate(MultipartFile file) { componentLibrary.setIsOfficial(true); ComponentLibrary library = new ComponentLibrary(); library.setName(componentLibrary.getName()); - library.setVersion(componentLibrary.getVersion()); + // 默认覆盖更新 其他业务需求多版本组件可放开 + // library.setVersion(componentLibrary.getVersion()); // 查询是否存在组件库 List componentLibraryList = componentLibraryMapper.queryComponentLibraryByCondition( library); diff --git a/base/src/test/java/com/tinyengine/it/common/utils/UtilsTest.java b/base/src/test/java/com/tinyengine/it/common/utils/UtilsTest.java index c3171e2d..b32bc49a 100644 --- a/base/src/test/java/com/tinyengine/it/common/utils/UtilsTest.java +++ b/base/src/test/java/com/tinyengine/it/common/utils/UtilsTest.java @@ -62,14 +62,6 @@ void testToLine() { assertEquals("name", result); } - @Test - void testConvert() { - Map mapData = new HashMap(); - mapData.put("key", "value"); - Map result = Utils.convert(mapData); - assertEquals("value", result.get("key")); - } - @Test void testFlat() { Map mapData = new HashMap(); diff --git a/base/src/test/java/com/tinyengine/it/controller/BlockControllerTest.java b/base/src/test/java/com/tinyengine/it/controller/BlockControllerTest.java index 2372099d..c16c54dd 100644 --- a/base/src/test/java/com/tinyengine/it/controller/BlockControllerTest.java +++ b/base/src/test/java/com/tinyengine/it/controller/BlockControllerTest.java @@ -13,7 +13,6 @@ package com.tinyengine.it.controller; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; @@ -40,7 +39,10 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockHttpServletRequest; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -194,17 +196,18 @@ void testGetAllBlockCategories() { } @Test - void testUpdateBlocks() { + void testUpdateBlocks() throws IOException { BlockParam blockParam = new BlockParam(); blockParam.setName("Updated Block"); BlockDto returnData = new BlockDto(); returnData.setName("Updated Block"); - when(blockService.updateBlockById(any(BlockParam.class), anyInt())).thenReturn(Result.success(returnData)); + when(blockService.updateBlockById(any(BlockParam.class))).thenReturn(Result.success(returnData)); when(blockService.queryBlockById(anyInt())).thenReturn(returnData); - - Result result = blockController.updateBlocks(blockParam, Integer.valueOf(0), Integer.valueOf(1)); + String json = "{\"isDefault\":true}"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContent(json.getBytes(StandardCharsets.UTF_8)); // 设置请求体 + request.setContentType("application/json"); + Result result = blockController.updateBlocks(request, Integer.valueOf(0), Integer.valueOf(1)); Assertions.assertEquals(returnData, result.getData()); - verify(blockService).updateBlockById( - argThat(param -> param.getName().equals("Updated Block") && param.getId().equals(0)), eq(1)); } } diff --git a/base/src/test/java/com/tinyengine/it/controller/PageControllerTest.java b/base/src/test/java/com/tinyengine/it/controller/PageControllerTest.java index 04758f1e..403c8e34 100644 --- a/base/src/test/java/com/tinyengine/it/controller/PageControllerTest.java +++ b/base/src/test/java/com/tinyengine/it/controller/PageControllerTest.java @@ -27,7 +27,9 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockHttpServletRequest; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; @@ -79,10 +81,13 @@ void testCreatePage() throws Exception { @Test void testUpdatePage() throws Exception { when(pageService.updatePage(any(Page.class))).thenReturn(new Result()); - + String json = "{\"isPage\":true}"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContent(json.getBytes(StandardCharsets.UTF_8)); // 设置请求体 + request.setContentType("application/json"); Page page = new Page(); page.setIsPage(true); - Result result = pageController.updatePage(page); + Result result = pageController.updatePage(request); Assertions.assertNull(result.getData()); } diff --git a/base/src/test/java/com/tinyengine/it/controller/PageHistoryControllerTest.java b/base/src/test/java/com/tinyengine/it/controller/PageHistoryControllerTest.java index 86a942ef..b66dfb58 100644 --- a/base/src/test/java/com/tinyengine/it/controller/PageHistoryControllerTest.java +++ b/base/src/test/java/com/tinyengine/it/controller/PageHistoryControllerTest.java @@ -26,7 +26,9 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockHttpServletRequest; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -67,15 +69,18 @@ void testGetPageHistoryById() { } @Test - void testCreatePageHistory() { + void testCreatePageHistory() throws Exception { PageHistory mockData = new PageHistory(); mockData.setPage(1); mockData.setId(1); mockData.setPageContent(new HashMap<>()); when(pageHistoryService.findPageHistoryById(anyInt())).thenReturn(mockData); - when(pageHistoryService.createPageHistory(any(PageHistory.class))).thenReturn(Integer.valueOf(0)); - - Result result = pageHistoryController.createPageHistory(mockData); + when(pageHistoryService.createPageHistory(any(PageHistory.class))).thenReturn(1); + String json = "{\"id\": 1, \"page\": 1, \"isPage\": true, \"page_content\": {\"id\": 1}}"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContent(json.getBytes(StandardCharsets.UTF_8)); // 设置请求体 + request.setContentType("application/json"); + Result result = pageHistoryController.createPageHistory(request); Assertions.assertEquals(mockData, result.getData()); } diff --git a/base/src/test/java/com/tinyengine/it/controller/PageTemplateControllerTest.java b/base/src/test/java/com/tinyengine/it/controller/PageTemplateControllerTest.java index 243a2f71..7696d4ee 100644 --- a/base/src/test/java/com/tinyengine/it/controller/PageTemplateControllerTest.java +++ b/base/src/test/java/com/tinyengine/it/controller/PageTemplateControllerTest.java @@ -27,7 +27,9 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.mock.web.MockHttpServletRequest; +import java.nio.charset.StandardCharsets; import java.util.List; /** @@ -47,11 +49,14 @@ void setUp() { } @Test - void testCreatePageTemplate() { + void testCreatePageTemplate() throws Exception { when(pageTemplateService.createPageTemplate(any(PageTemplate.class))) .thenReturn(new Result()); - - Result result = pageTemplateController.createPageTemplate(new PageTemplate()); + String json = "{\"isPage\":true}"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setContent(json.getBytes(StandardCharsets.UTF_8)); // 设置请求体 + request.setContentType("application/json"); + Result result = pageTemplateController.createPageTemplate(request); Assertions.assertEquals(new Result(), result); } diff --git a/base/src/test/java/com/tinyengine/it/gateway/ai/AiChatClientTest.java b/base/src/test/java/com/tinyengine/it/gateway/ai/AiChatClientTest.java index 7918bc4e..7f829d04 100644 --- a/base/src/test/java/com/tinyengine/it/gateway/ai/AiChatClientTest.java +++ b/base/src/test/java/com/tinyengine/it/gateway/ai/AiChatClientTest.java @@ -12,9 +12,13 @@ package com.tinyengine.it.gateway.ai; +import static javax.management.Query.eq; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.tinyengine.it.common.utils.TestUtil; @@ -22,8 +26,6 @@ import com.tinyengine.it.model.dto.AiMessages; import com.tinyengine.it.model.dto.AiParam; -import reactor.core.publisher.Mono; - import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -31,11 +33,13 @@ import org.mockito.InjectMocks; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; -import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; -import java.util.ArrayList; -import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -45,52 +49,51 @@ * @since 2024-10-29 */ class AiChatClientTest { - @InjectMocks - private AiChatClient aiChatClient; - @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); } @Test - void testExecuteChatRequest() throws NoSuchFieldException, IllegalAccessException { - HashMap headers = new HashMap() { - { - put("headers", "headers"); - } - }; + void testExecuteChatRequest() { + // 1. 构造测试数据 String modelName = "ERNIE-4.0-8K"; + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json"); + AiChatConfig.HttpRequestOption option = new AiChatConfig.HttpRequestOption("POST", "json", "json", 100); - AiChatConfig.AiChatConfigData configData = new AiChatConfig.AiChatConfigData("httpRequestUrl", option, headers, - null); - Map config = new HashMap<>(); - config.put(modelName, configData); - WebClient mockClient = Mockito.mock(WebClient.class, Answers.RETURNS_DEEP_STUBS); - WebClient.RequestBodyUriSpec bodyUriSpec = Mockito.mock(WebClient.RequestBodyUriSpec.class, RETURNS_DEEP_STUBS); - Mono mono = Mockito.mock(Mono.class, RETURNS_DEEP_STUBS); - Map result = new HashMap<>(); - result.put("data", "data"); - when(mono.map(any()).block()).thenReturn(result); - when(mockClient.method(any(HttpMethod.class)).uri(anyString())).thenReturn(bodyUriSpec); - WebClient.RequestHeadersSpec headersSpec = Mockito.mock(WebClient.RequestHeadersSpec.class, RETURNS_DEEP_STUBS); - when(bodyUriSpec.bodyValue(any())).thenReturn(headersSpec); - when(headersSpec.retrieve().bodyToMono(String.class)).thenReturn(mono); + AiChatConfig.AiChatConfigData configData = new AiChatConfig.AiChatConfigData( + "http://mock-api-url", option, headers, null); + + // 2. 构造 AiParam 请求参数 + Map foundationModel = new HashMap<>(); + foundationModel.put("model", modelName); + foundationModel.put("token", "mock-token"); + + AiMessages message = new AiMessages(); + message.setRole("user"); + message.setContent("Hello, AI!"); + AiParam param = new AiParam(foundationModel, Collections.singletonList(message)); + + // 3. Mock RestTemplate 的行为 + RestTemplate mockRestTemplate = Mockito.mock(RestTemplate.class); + ResponseEntity mockResponse = new ResponseEntity<>( + "{\"data\":\"mock-response\"}", HttpStatus.OK); + + // 关键点:设置 Mock 行为,匹配任意参数 + when(mockRestTemplate.exchange( + anyString(), // 任意 URL + any(HttpMethod.class), // 任意 HTTP 方法 + any(HttpEntity.class), // 任意请求体 + any(Class.class) // 任意返回类型(如 String.class) + )).thenReturn(mockResponse); - HashMap foundationModel = new HashMap<>(); - foundationModel.put("model", "ERNIE-4.0-8K"); - foundationModel.put("token", "asdf"); + // 4. 创建 AiChatClient 并注入 Mock 的 RestTemplate + AiChatClient aiChatClient = new AiChatClient("ERNIE-4.0-8K", "mock-token"); + aiChatClient.setRestTemplate(mockRestTemplate); - ArrayList messages = new ArrayList<>(); - AiMessages aiMessages = new AiMessages(); - aiMessages.setContent("dddd编码时遵从以下几条要求aaa"); - aiMessages.setName("John"); - aiMessages.setRole("user"); - messages.add(aiMessages); - AiParam param = new AiParam(foundationModel, Arrays.asList(aiMessages)); - TestUtil.setPrivateValue(aiChatClient, "config", config); - TestUtil.setPrivateValue(aiChatClient, "webClient", mockClient); - Map returnData = aiChatClient.executeChatRequest(param); - Assertions.assertEquals("data", returnData.get("data")); + // 5. 调用方法并验证结果 + Map result = aiChatClient.executeChatRequest(param); + assertEquals("mock-response", result.get("data")); // 验证返回的数据 } -} +} \ No newline at end of file diff --git a/base/src/test/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImplTest.java b/base/src/test/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImplTest.java index 469b210b..02a1d11b 100644 --- a/base/src/test/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImplTest.java +++ b/base/src/test/java/com/tinyengine/it/service/app/impl/v1/AppV1ServiceImplTest.java @@ -18,7 +18,7 @@ import static org.mockito.Mockito.when; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.mapper.AppExtensionMapper; import com.tinyengine.it.mapper.AppMapper; import com.tinyengine.it.mapper.BlockGroupMapper; @@ -116,8 +116,7 @@ void testAppSchema() throws JsonProcessingException { String json = "{\"dataHandler\":{\"type\":\"JSFunction\",\"value\":\"function dataHanlder(res){\\n return res;\\n}\"}}"; - ObjectMapper objectMapper = new ObjectMapper(); - Map dataSourceGlobal = objectMapper.readValue(json, Map.class); + Map dataSourceGlobal = JsonUtils.MAPPER.readValue(json, Map.class); app.setDataSourceGlobal(dataSourceGlobal); when(appMapper.queryAppById(anyInt())).thenReturn(app); diff --git a/base/src/test/java/com/tinyengine/it/service/material/impl/BlockServiceImplTest.java b/base/src/test/java/com/tinyengine/it/service/material/impl/BlockServiceImplTest.java index e561482e..57e3c6c0 100644 --- a/base/src/test/java/com/tinyengine/it/service/material/impl/BlockServiceImplTest.java +++ b/base/src/test/java/com/tinyengine/it/service/material/impl/BlockServiceImplTest.java @@ -138,13 +138,14 @@ void testCreateBlock() { @Test void testUpdateBlockById() { BlockParam blockParam = new BlockParam(); + blockParam.setAppId(1); when(blockMapper.updateBlockById(any())).thenReturn(1); when(blockMapper.findBlockAndGroupAndHistoByBlockId(anyInt())).thenReturn(new BlockDto()); Block block = new Block(); block.setAppId(1); when(blockMapper.queryBlockById(blockParam.getId())).thenReturn(block); - Result result = blockServiceImpl.updateBlockById(blockParam, 1); + Result result = blockServiceImpl.updateBlockById(blockParam); Assertions.assertEquals(null, result.getData()); } diff --git a/pom.xml b/pom.xml index c6d4b3b3..be68c86c 100644 --- a/pom.xml +++ b/pom.xml @@ -30,9 +30,10 @@ 3.3.3 2.3.32 2.5.4 - 2.5.0 + 2.5.0 4.11.0 6.1.0 + 4.1.118.Final 1.0-SNAPSHOT @@ -42,7 +43,7 @@ org.springframework.boot - spring-boot-starter-webflux + spring-boot-starter-web org.springframework.boot @@ -102,8 +103,8 @@ org.springdoc - springdoc-openapi-starter-webflux-ui - ${springdoc-openapi-starter-webflux-ui.version} + springdoc-openapi-starter-webmvc-ui + ${springdoc-openapi-starter-webmvc-ui.version} @@ -129,6 +130,11 @@ test ${mockito-inline.version} + + io.netty + netty-buffer + ${netty-buffer-version} + org.junit.vintage junit-vintage-engine