From 4f5d63295f25b52b88cec67f7ba0ff7cc917ca2e Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Thu, 4 Sep 2025 18:48:38 -0700 Subject: [PATCH 01/31] feat: add resource API --- .../sql/h2/create_resources_ddl_2025_0902.sql | 58 +++ .../mysql/create_resources_ddl_2025_0902.sql | 58 +++ .../it/common/utils/ImageInputInfo.java | 46 ++ .../common/utils/ImageThumbnailGenerator.java | 428 ++++++++++++++++++ .../com/tinyengine/it/common/utils/Utils.java | 59 ++- .../it/controller/ResourceController.java | 269 +++++++++++ .../controller/ResourceGroupController.java | 175 +++++++ .../it/mapper/ResourceGroupMapper.java | 77 ++++ .../mapper/ResourceGroupResourceMapper.java | 63 +++ .../tinyengine/it/mapper/ResourceMapper.java | 92 ++++ .../it/model/dto/ResourceRequestDto.java | 27 ++ .../tinyengine/it/model/entity/Resource.java | 67 +++ .../it/model/entity/ResourceGroup.java | 51 +++ .../model/entity/ResourceGroupResource.java | 42 ++ .../app/impl/v1/AiChatV1ServiceImpl.java | 5 +- .../material/ResourceGroupService.java | 64 +++ .../it/service/material/ResourceService.java | 88 ++++ .../material/impl/BlockGroupServiceImpl.java | 10 +- .../impl/ResourceGroupServiceImpl.java | 186 ++++++++ .../material/impl/ResourceServiceImpl.java | 197 ++++++++ .../resources/mappers/ResourceGroupMapper.xml | 327 +++++++++++++ .../mappers/ResourceGroupResourceMapper.xml | 103 +++++ .../main/resources/mappers/ResourceMapper.xml | 290 ++++++++++++ pom.xml | 13 + 24 files changed, 2785 insertions(+), 10 deletions(-) create mode 100644 app/src/main/resources/sql/h2/create_resources_ddl_2025_0902.sql create mode 100644 app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql create mode 100644 base/src/main/java/com/tinyengine/it/common/utils/ImageInputInfo.java create mode 100644 base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java create mode 100644 base/src/main/java/com/tinyengine/it/controller/ResourceController.java create mode 100644 base/src/main/java/com/tinyengine/it/controller/ResourceGroupController.java create mode 100644 base/src/main/java/com/tinyengine/it/mapper/ResourceGroupMapper.java create mode 100644 base/src/main/java/com/tinyengine/it/mapper/ResourceGroupResourceMapper.java create mode 100644 base/src/main/java/com/tinyengine/it/mapper/ResourceMapper.java create mode 100644 base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java create mode 100644 base/src/main/java/com/tinyengine/it/model/entity/Resource.java create mode 100644 base/src/main/java/com/tinyengine/it/model/entity/ResourceGroup.java create mode 100644 base/src/main/java/com/tinyengine/it/model/entity/ResourceGroupResource.java create mode 100644 base/src/main/java/com/tinyengine/it/service/material/ResourceGroupService.java create mode 100644 base/src/main/java/com/tinyengine/it/service/material/ResourceService.java create mode 100644 base/src/main/java/com/tinyengine/it/service/material/impl/ResourceGroupServiceImpl.java create mode 100644 base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java create mode 100644 base/src/main/resources/mappers/ResourceGroupMapper.xml create mode 100644 base/src/main/resources/mappers/ResourceGroupResourceMapper.xml create mode 100644 base/src/main/resources/mappers/ResourceMapper.xml diff --git a/app/src/main/resources/sql/h2/create_resources_ddl_2025_0902.sql b/app/src/main/resources/sql/h2/create_resources_ddl_2025_0902.sql new file mode 100644 index 00000000..b7c3b663 --- /dev/null +++ b/app/src/main/resources/sql/h2/create_resources_ddl_2025_0902.sql @@ -0,0 +1,58 @@ +drop table if exists `t_resource`; + +create table `t_resource` +( + `id` int not null auto_increment comment '主键id', + `app_id` int comment '关联appId', + `platform_id` int comment '关联设计器id', + `name_cn` varchar(255) not null comment '中文名称', + `name_en` varchar(255) not null comment '英文名称', + `resource_url` varchar(255) comment '资源url', + `thumbnail_url` varchar(255) comment '缩略图url', + `category` varchar(255) not null comment '分类', + `description` varchar(2000) comment '描述', + `thumbnail_data` longtext comment '缩略图数据', + `resource_data` longtext comment '资源数据', + `public_status` int comment '公开状态:0,1,2', + `is_default` tinyint(1) comment '是否是默认', + `created_by` varchar(60) not null comment '创建人', + `created_time` timestamp not null default current_timestamp comment '创建时间', + `last_updated_by` varchar(60) not null comment '最后修改人', + `last_updated_time` timestamp not null default current_timestamp comment '更新时间', + `tenant_id` varchar(60) comment '租户id', + `renter_id` varchar(60) comment '业务租户id', + `site_id` varchar(60) comment '站点id,设计预留字段', + primary key (`id`) using btree, + unique index `u_idx_resource` (`name_en`,`name_cn`, `tenant_id`) using btree +) engine = innodb comment = '资源表'; + +drop table if exists `t_resource_group`; + +create table `t_resource_group` +( + `id` int not null auto_increment comment '主键id', + `name` varchar(255) not null comment '中文名称', + `app_id` int comment '关联appId', + `platform_id` int comment '关联设计器id', + `description` varchar(2000) comment '描述', + `created_by` varchar(60) not null comment '创建人', + `created_time` timestamp not null default current_timestamp comment '创建时间', + `last_updated_by` varchar(60) not null comment '最后修改人', + `last_updated_time` timestamp not null default current_timestamp comment '更新时间', + `tenant_id` varchar(60) comment '租户id', + `renter_id` varchar(60) comment '业务租户id', + `site_id` varchar(60) comment '站点id,设计预留字段', + primary key (`id`) using btree, + unique index `u_idx_resource_group` (`name`,`app_id`, `tenant_id`) using btree +) engine = innodb comment = '资源分组表'; + +drop table if exists `r_resource_group_resource`; + +CREATE TABLE `r_resource_group_resource` +( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', + `resource_id` int NOT NULL COMMENT '资源id', + `resource_group_id` int NOT NULL COMMENT '资源分组id', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `u_idx_block_group_block` (`resource_id`,`resource_group_id`) USING BTREE +) engine = innodb comment = '资源及资源分组关系表'; \ No newline at end of file diff --git a/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql b/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql new file mode 100644 index 00000000..b7c3b663 --- /dev/null +++ b/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql @@ -0,0 +1,58 @@ +drop table if exists `t_resource`; + +create table `t_resource` +( + `id` int not null auto_increment comment '主键id', + `app_id` int comment '关联appId', + `platform_id` int comment '关联设计器id', + `name_cn` varchar(255) not null comment '中文名称', + `name_en` varchar(255) not null comment '英文名称', + `resource_url` varchar(255) comment '资源url', + `thumbnail_url` varchar(255) comment '缩略图url', + `category` varchar(255) not null comment '分类', + `description` varchar(2000) comment '描述', + `thumbnail_data` longtext comment '缩略图数据', + `resource_data` longtext comment '资源数据', + `public_status` int comment '公开状态:0,1,2', + `is_default` tinyint(1) comment '是否是默认', + `created_by` varchar(60) not null comment '创建人', + `created_time` timestamp not null default current_timestamp comment '创建时间', + `last_updated_by` varchar(60) not null comment '最后修改人', + `last_updated_time` timestamp not null default current_timestamp comment '更新时间', + `tenant_id` varchar(60) comment '租户id', + `renter_id` varchar(60) comment '业务租户id', + `site_id` varchar(60) comment '站点id,设计预留字段', + primary key (`id`) using btree, + unique index `u_idx_resource` (`name_en`,`name_cn`, `tenant_id`) using btree +) engine = innodb comment = '资源表'; + +drop table if exists `t_resource_group`; + +create table `t_resource_group` +( + `id` int not null auto_increment comment '主键id', + `name` varchar(255) not null comment '中文名称', + `app_id` int comment '关联appId', + `platform_id` int comment '关联设计器id', + `description` varchar(2000) comment '描述', + `created_by` varchar(60) not null comment '创建人', + `created_time` timestamp not null default current_timestamp comment '创建时间', + `last_updated_by` varchar(60) not null comment '最后修改人', + `last_updated_time` timestamp not null default current_timestamp comment '更新时间', + `tenant_id` varchar(60) comment '租户id', + `renter_id` varchar(60) comment '业务租户id', + `site_id` varchar(60) comment '站点id,设计预留字段', + primary key (`id`) using btree, + unique index `u_idx_resource_group` (`name`,`app_id`, `tenant_id`) using btree +) engine = innodb comment = '资源分组表'; + +drop table if exists `r_resource_group_resource`; + +CREATE TABLE `r_resource_group_resource` +( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', + `resource_id` int NOT NULL COMMENT '资源id', + `resource_group_id` int NOT NULL COMMENT '资源分组id', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `u_idx_block_group_block` (`resource_id`,`resource_group_id`) USING BTREE +) engine = innodb comment = '资源及资源分组关系表'; \ No newline at end of file diff --git a/base/src/main/java/com/tinyengine/it/common/utils/ImageInputInfo.java b/base/src/main/java/com/tinyengine/it/common/utils/ImageInputInfo.java new file mode 100644 index 00000000..f0ecb80d --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/common/utils/ImageInputInfo.java @@ -0,0 +1,46 @@ +/** + * 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; + +/** + * 图片输入信息类 + */ +public class ImageInputInfo { + private final String cleanBase64; + private final String format; + private final boolean isDataUri; + private final String mimeType; + + public ImageInputInfo(String cleanBase64, String format, boolean isDataUri, String mimeType) { + this.cleanBase64 = cleanBase64; + this.format = format; + this.isDataUri = isDataUri; + this.mimeType = mimeType; + } + + public String getCleanBase64() { + return cleanBase64; + } + + public String getFormat() { + return format; + } + + public boolean isDataUri() { + return isDataUri; + } + + public String getMimeType() { + return mimeType; + } +} \ No newline at end of file diff --git a/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java b/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java new file mode 100644 index 00000000..2ede8a54 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java @@ -0,0 +1,428 @@ +/** + * 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 org.apache.batik.anim.dom.SAXSVGDocumentFactory; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.ImageTranscoder; +import org.apache.batik.transcoder.image.JPEGTranscoder; +import org.apache.batik.transcoder.image.PNGTranscoder; +import org.apache.batik.util.XMLResourceDescriptor; +import org.w3c.dom.Document; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 智能图片缩略图生成工具类 + * 支持自动识别输入格式,输出保持相同格式 + */ +public class ImageThumbnailGenerator { + + // 支持的输出格式 + public static final String FORMAT_JPG = "jpg"; + public static final String FORMAT_JPEG = "jpeg"; + public static final String FORMAT_PNG = "png"; + public static final String FORMAT_GIF = "gif"; + public static final String FORMAT_BMP = "bmp"; + public static final String FORMAT_SVG = "svg"; + + // 数据URI模式匹配 + private static final Pattern DATA_URI_PATTERN = + Pattern.compile("^data:image/([a-zA-Z+]+);base64,(.*)$", Pattern.CASE_INSENSITIVE); + + private static final Map MIME_TYPES = new HashMap<>(); + private static final Map EXTENSION_TO_MIME = new HashMap<>(); + + static { + // MIME类型映射 + MIME_TYPES.put(FORMAT_JPG, "image/jpeg"); + MIME_TYPES.put(FORMAT_JPEG, "image/jpeg"); + MIME_TYPES.put(FORMAT_PNG, "image/png"); + MIME_TYPES.put(FORMAT_GIF, "image/gif"); + MIME_TYPES.put(FORMAT_BMP, "image/bmp"); + MIME_TYPES.put(FORMAT_SVG, "image/svg+xml"); + + // 扩展名到MIME的映射 + EXTENSION_TO_MIME.put("jpg", "image/jpeg"); + EXTENSION_TO_MIME.put("jpeg", "image/jpeg"); + EXTENSION_TO_MIME.put("png", "image/png"); + EXTENSION_TO_MIME.put("gif", "image/gif"); + EXTENSION_TO_MIME.put("bmp", "image/bmp"); + EXTENSION_TO_MIME.put("svg", "image/svg+xml"); + } + + /** + * 生成缩略图(自动保持输入格式) + */ + public static String createThumbnail(String imageInput, int maxWidth, int maxHeight) { + validateParameters(maxWidth, maxHeight); + + try { + ImageInputInfo inputInfo = parseImageInput(imageInput); + + String thumbnailBase64; + if (FORMAT_SVG.equalsIgnoreCase(inputInfo.getFormat())) { + thumbnailBase64 = createSVGThumbnail(inputInfo.getCleanBase64(), maxWidth, maxHeight, inputInfo.getFormat()); + } else { + thumbnailBase64 = createRasterThumbnail(inputInfo.getCleanBase64(), maxWidth, maxHeight, inputInfo.getFormat()); + } + + // 如果输入是数据URI格式,输出也保持数据URI格式 + if (inputInfo.isDataUri()) { + return createDataUri(thumbnailBase64, inputInfo.getFormat()); + } else { + return thumbnailBase64; + } + + } catch (Exception e) { + throw new RuntimeException("生成缩略图失败: " + e.getMessage(), e); + } + } + + /** + * 生成缩略图(指定输出格式) + */ + public static String createThumbnail(String imageInput, int maxWidth, int maxHeight, String outputFormat) { + validateParameters(maxWidth, maxHeight, outputFormat); + + try { + ImageInputInfo inputInfo = parseImageInput(imageInput); + + String thumbnailBase64; + if (FORMAT_SVG.equalsIgnoreCase(inputInfo.getFormat()) && !FORMAT_SVG.equalsIgnoreCase(outputFormat)) { + // SVG转位图 + thumbnailBase64 = createSVGThumbnail(inputInfo.getCleanBase64(), maxWidth, maxHeight, outputFormat); + } else if (FORMAT_SVG.equalsIgnoreCase(outputFormat)) { + // 位图转SVG(不支持,使用PNG代替) + thumbnailBase64 = createRasterThumbnail(inputInfo.getCleanBase64(), maxWidth, maxHeight, FORMAT_PNG); + } else { + // 位图到位图 + thumbnailBase64 = createRasterThumbnail(inputInfo.getCleanBase64(), maxWidth, maxHeight, outputFormat); + } + + // 如果输入是数据URI格式,输出也保持数据URI格式 + if (inputInfo.isDataUri()) { + return createDataUri(thumbnailBase64, outputFormat); + } else { + return thumbnailBase64; + } + + } catch (Exception e) { + throw new RuntimeException("生成缩略图失败: " + e.getMessage(), e); + } + } + + /** + * 解析图片输入信息 + */ + private static ImageInputInfo parseImageInput(String imageInput) { + if (imageInput == null || imageInput.trim().isEmpty()) { + throw new IllegalArgumentException("图片输入不能为空"); + } + + String trimmed = imageInput.trim(); + + // 检查是否为数据URI格式 + Matcher matcher = DATA_URI_PATTERN.matcher(trimmed); + if (matcher.matches()) { + String mimeType = matcher.group(1).toLowerCase(); + String cleanBase64 = matcher.group(2); + String format = mimeTypeToFormat(mimeType); + + return new ImageInputInfo(cleanBase64, format, true, mimeType); + } + + // 如果不是数据URI,尝试检测格式 + String cleanBase64 = trimmed; + String detectedFormat = detectFormatFromBase64(trimmed); + + return new ImageInputInfo(cleanBase64, detectedFormat, false, null); + } + + /** + * 从Base64检测图片格式 + */ + public static String detectFormatFromBase64(String base64Input) { + try { + String cleanBase64 = extractCleanBase64(base64Input); + byte[] imageBytes = Base64.getDecoder().decode(cleanBase64); + + // 先检查是否为SVG + String content = new String(imageBytes, 0, Math.min(1000, imageBytes.length)).trim().toLowerCase(); + if (content.contains(" T decodeBase64ToObject(String encodedString, Class clazz) throws Exception { + // 解码 Base64 字符串 + byte[] decodedBytes = Base64.getDecoder().decode(encodedString); + String jsonString = new String(decodedBytes, StandardCharsets.UTF_8); + + return JsonUtils.decode(jsonString, clazz); + } + + /** + * base64编码转map + * + * @param encodedString + * @return map + */ + public static Map decodeBase64ToMap(String encodedString) throws Exception { + return decodeBase64ToObject(encodedString, Map.class); + } } diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java new file mode 100644 index 00000000..d842a4c5 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -0,0 +1,269 @@ +/** + * 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.controller; + +import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.log.SystemControllerLog; +import com.tinyengine.it.common.utils.ImageThumbnailGenerator; +import com.tinyengine.it.common.utils.Utils; +import com.tinyengine.it.model.dto.ResourceRequestDto; +import com.tinyengine.it.model.entity.Resource; +import com.tinyengine.it.service.material.ResourceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +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.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.io.OutputStream; +import java.util.Base64; +import java.util.List; + +/** + * 资源 + * + * @since 2025-09-03 + */ +@Validated +@RestController +@RequestMapping("/material-center/api") +@Tag(name = "资源") +public class ResourceController { + /** + * The Resource service. + */ + @Autowired + private ResourceService resourceService; + + /** + * 查询表Resource信息 + * + * @return Resource信息 all resource + */ + @Operation(summary = "查询表Resource信息", description = "查询表Resource信息", + responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "查询表Resource信息") + @GetMapping("/resource/list") + public Result> getAllResource() { + List appList = resourceService.queryAllResource(); + return Result.success(appList); + } + + /** + * 根据id查询表Resource信息 + * + * @param id the id + * @return Resource信息 app by id + */ + @Operation(summary = "根据id查询表Resource信息", description = "根据id查询表Resource信息", + parameters = { + @Parameter(name = "id", description = "Resource主键id") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "根据id查询表Resource信息") + @GetMapping("/resource/{id}") + public Result getResourceById(@PathVariable Integer id) { + return resourceService.queryResourceById(id); + } + + /** + * 根据分组id和创建人查询表t_resource信息 + * + * @param resourceGroupId the resourceGroupId + * @return the list + */ + @Operation(summary = "根据分组id和创建人查询表t_resource信息", description = "根据分组id和创建人查询表t_resource信息", + parameters = { + @Parameter(name = "resourceGroupId", description = "ResourceGroup主键id") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "根据分组id和创建人查询表t_resource信息") + @GetMapping("/resource/find/{resourceGroupId}") + public Result> getResourceByResourceGroupId(@PathVariable Integer resourceGroupId) { + List resourceList = resourceService.queryResourceByResourceGroupId(resourceGroupId); + return Result.success(resourceList); + } + /** + * 模糊查询表Resource信息 + * + * @param nameCn the nameCn + * @param nameEn the nameEn + * @param des the des + * @return Resource信息列表 + */ + @Operation(summary = "模糊查询表Resource信息列表", description = "模糊查询表Resource信息列表", + parameters = { + @Parameter(name = "nameCn", description = "中文名"), + @Parameter(name = "nameEn", description = "英文名"), + @Parameter(name = "des", description = "描述") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "模糊查询表Resource信息列表") + @GetMapping("/resource/like") + public Result> getResourceById(@PathVariable String nameCn, + @PathVariable String nameEn, + @PathVariable String des) { + List resourceList = resourceService.queryResourcesByNameAndDes(nameCn, nameEn, des); + return Result.success(resourceList); + } + + + /** + * 创建Resource + * + * @param resource the resource + * @return Resource信息 result + */ + @Operation(summary = "创建resource", description = "创建resource", + parameters = { + @Parameter(name = "resource", description = "Resource入参对象") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "创建resource") + @PostMapping("/resource/create") + public Result createResource(@Valid @RequestBody Resource resource) throws Exception { + return resourceService.createResource(resource); + } + + /** + * 修改Resource信息 + * + * @param id the id + * @param resource the resource + * @return Resource信息 result + */ + @Operation(summary = "修改单个Resource信息", description = "修改单个Resource信息", + parameters = { + @Parameter(name = "id", description = "appId"), + @Parameter(name = "Resource", description = "入参对象")}, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "修改单个Resource信息") + @PutMapping("/resource/update/{id}") + public Result updateResource(@PathVariable Integer id, @RequestBody Resource resource) { + resource.setId(id); + return resourceService.updateResourceById(resource); + } + + /** + * 删除Resource信息 + * + * @param id the id + * @return resource信息 result + */ + @Operation(summary = "删除resource信息", description = "删除resource信息", + parameters = { + @Parameter(name = "id", description = "Resource主键id") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "删除resource信息") + @DeleteMapping("/resource/delete/{id}") + public Result deleteResource(@PathVariable Integer id) { + return resourceService.deleteResourceById(id); + } + + /** + * 获取resource信息详情 + * + * @param id the id + * @return the result + */ + @Operation(summary = "获取resource信息详情", description = "获取resource信息详情", + parameters = { + @Parameter(name = "id", description = "appId")}, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "获取resource信息详情") + @GetMapping("/resource/detail/{id}") + public Result detail(@PathVariable Integer id) { + return resourceService.queryResourceById(id); + } + + /** + * 获取资源 + * + * @param data the data + * @return the result + */ + @Operation(summary = "获取资源", description = "获取资源", + parameters = { + @Parameter(name = "id", description = "appId")}, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "获取资源") + @GetMapping("/resource/download") + public void getResource(@RequestParam("data") String data, HttpServletResponse response) throws Exception { + ResourceRequestDto param = Utils.decodeBase64ToObject(data, ResourceRequestDto.class); + Resource resource = resourceService.queryResourceByData(param); + + boolean useOriginal = param.isResource(); + String base64Data = useOriginal ? resource.getResourceData() : resource.getThumbnailData(); + String cleanBase64 = ImageThumbnailGenerator.extractCleanBase64(base64Data); + byte[] imageBytes = Base64.getDecoder().decode(cleanBase64); + + String detectedType = ImageThumbnailGenerator.detectFormatFromBase64(base64Data); + String fileExtension = detectedType.equals("jpeg") ? "jpg" : detectedType; + + String fileName = useOriginal ? + resource.getNameEn() + "." + fileExtension : + resource.getNameEn() + "_thumbnail." + fileExtension; + + + response.setContentType("image/" + detectedType); + response.setHeader("Content-Disposition", + "inline; filename=\"" + fileName + "\"; filename*=UTF-8''" + fileName); + + try (OutputStream out = response.getOutputStream()) { + out.write(imageBytes); + } + } +} diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceGroupController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceGroupController.java new file mode 100644 index 00000000..4c429aa7 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceGroupController.java @@ -0,0 +1,175 @@ +/** + * 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.controller; + +import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.log.SystemControllerLog; +import com.tinyengine.it.model.entity.ResourceGroup; +import com.tinyengine.it.service.material.ResourceGroupService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 资源分组 + * + * @since 2025-09-03 + */ +@Validated +@RestController +@RequestMapping("/material-center/api") +@Tag(name = "资源分组") +public class ResourceGroupController { + /** + * The ResourceGroup service. + */ + @Autowired + private ResourceGroupService resourceGroupService; + /** + * 查询表ResourceGroup信息 + * + * @return ResourceGroup信息 all resource + */ + @Operation(summary = "查询表ResourceGroup信息", description = "查询表ResourceGroup信息", + responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResourceGroup.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "查询表ResourceGroup信息") + @GetMapping("/resource-group/list") + public Result> queryAllResourceGroupAndResource() { + List resourceGroupList = resourceGroupService.queryAllResourceGroupAndResource(); + return Result.success(resourceGroupList); + } + + /** + * 根据id查询表ResourceGroup信息 + * + * @param appId the appId + * @return ResourceGroup信息 app by id + */ + @Operation(summary = "根据id查询表ResourceGroup信息", description = "根据id查询表ResourceGroup信息", + parameters = { + @Parameter(name = "appId", description = "ResourceGroup主键id") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResourceGroup.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "根据id查询表ResourceGroup信息") + @GetMapping("/resource-group/{appId}") + public Result queryResourceGroupByAppId(@PathVariable Integer appId) { + return resourceGroupService.queryResourceGroupByAppId(appId); + } + + /** + * 创建ResourceGroup + * + * @param resource the resource + * @return ResourceGroup信息 result + */ + @Operation(summary = "创建resource", description = "创建resource", + parameters = { + @Parameter(name = "resource", description = "ResourceGroup入参对象") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResourceGroup.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "创建resource") + @PostMapping("/resource-group/create") + public Result createResourceGroup(@Valid @RequestBody ResourceGroup resource) { + return resourceGroupService.createResourceGroup(resource); + } + + /** + * 修改ResourceGroup信息 + * + * @param id the id + * @param resource the resource + * @return ResourceGroup信息 result + */ + @Operation(summary = "修改单个ResourceGroup信息", description = "修改单个ResourceGroup信息", + parameters = { + @Parameter(name = "id", description = "appId"), + @Parameter(name = "ResourceGroup", description = "入参对象") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResourceGroup.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "修改单个ResourceGroup信息") + @PutMapping("/resource-group/update/{id}") + public Result updateResourceGroup(@PathVariable Integer id, @RequestBody ResourceGroup resource) { + resource.setId(id); + return resourceGroupService.updateResourceGroupById(resource); + } + + /** + * 删除ResourceGroup信息 + * + * @param id the id + * @return resource信息 result + */ + @Operation(summary = "删除resource信息", + description = "删除resource信息", + parameters = { + @Parameter(name = "id", description = "ResourceGroup主键id") + }, + responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ResourceGroup.class))), + @ApiResponse(responseCode = "400", description = "请求失败")} + ) + @SystemControllerLog(description = "删除resource信息") + @DeleteMapping("/resource-group/delete/{id}") + public Result deleteResourceGroup(@PathVariable Integer id) { + return resourceGroupService.deleteResourceGroupById(id); + } + + /** + * 获取resource信息详情 + * + * @param id the id + * @return the result + */ + @Operation(summary = "获取resource信息详情", description = "获取resource信息详情", parameters = { + @Parameter(name = "id", description = "appId")}, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = ResourceGroup.class))), + @ApiResponse(responseCode = "400", description = "请求失败")}) + @SystemControllerLog(description = "获取resource信息详情") + @GetMapping("/resource-group/detail/{id}") + public Result detail(@PathVariable Integer id) { + return resourceGroupService.queryResourceGroupById(id); + } +} diff --git a/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupMapper.java b/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupMapper.java new file mode 100644 index 00000000..9cd5a322 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupMapper.java @@ -0,0 +1,77 @@ +/** + * 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.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.tinyengine.it.model.entity.ResourceGroup; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ResourceGroupMapper extends BaseMapper { + /** + * 查询表t_resource_group所有信息 + * + * @return the list + */ + List queryAllResourceGroupAndResource(String groupCreatedBy); + + /** + * 根据appId查询表t_resource_group数据 + * + * @param appId the appId + * @return the resourceGroup + */ + ResourceGroup queryResourceGroupByAppId(Integer appId); + + /** + * 根据主键id查询表t_resource_group数据 + * + * @param id the id + * @param groupCreatedBy the groupCreatedBy + * @return the resourceGroup + */ + ResourceGroup queryResourceGroupById(Integer id, String groupCreatedBy); + + /** + * 根据条件查询表t_resource_group数据 + * + * @param resourceGroup the resourceGroup + * @return the list + */ + List queryResourceGroupByCondition(ResourceGroup resourceGroup); + + /** + * 根据主键id删除表t_resource_group数据 + * + * @param id the id + * @return the integer + */ + Integer deleteResourceGroupById(@Param("id") Integer id); + + /** + * 根据主键id更新表t_resource_group数据 + * + * @param resourceGroup the resourceGroup + * @return the integer + */ + Integer updateResourceGroupById(ResourceGroup resourceGroup); + + /** + * 新增表t_resource_group数据 + * + * @param resourceGroup the resourceGroup + * @return the integer + */ + Integer createResourceGroup(ResourceGroup resourceGroup); +} diff --git a/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupResourceMapper.java b/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupResourceMapper.java new file mode 100644 index 00000000..534badaa --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupResourceMapper.java @@ -0,0 +1,63 @@ +/** + * 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.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.tinyengine.it.model.entity.ResourceGroupResource; +import org.apache.ibatis.annotations.Delete; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + * The resource group resource mapper. + * + * @since 2024-10-20 + */ +public interface ResourceGroupResourceMapper extends BaseMapper { + /** + * 新增表r_resource_group_resource数据 + * + * @param resourceGroupResource the resourceGroupResource + * @return the integer + */ + Integer createResourceGroupResource(ResourceGroupResource resourceGroupResource); + + /** + * 通过资源分组id查询分组下区块 + * + * @param resourceGroupId the resource group id + * @return the list + */ + @Select("select * from r_resource_group_resource where resource_group_id = #{resourceGroupId}") + List findResourceGroupResourceByResourceGroupId(Integer resourceGroupId); + + /** + * 通过资源分组id删除分组下资源 + * + * @param resourceGroupId the resource group id + * @return the int + */ + @Delete("DELETE FROM r_resource_group_resource WHERE resource_group_id = #{resourceGroupId}") + int deleteResourceGroupResourceByGroupId(Integer resourceGroupId); + + /** + * 通过资源分组id查询分组下区块 + * + * @param resourceGroupId the resource group id + * @param resourceId the resource group id + * @return ResourceGroupResource the ResourceGroupResource + */ + @Select("select * from r_resource_group_resource where resource_group_id = #{resourceGroupId} and resource_id = #{resourceId}") + ResourceGroupResource findResourceGroupResourceByResourceGroupIdAndResourceId(Integer resourceGroupId, Integer resourceId); +} diff --git a/base/src/main/java/com/tinyengine/it/mapper/ResourceMapper.java b/base/src/main/java/com/tinyengine/it/mapper/ResourceMapper.java new file mode 100644 index 00000000..50d19803 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/mapper/ResourceMapper.java @@ -0,0 +1,92 @@ +/** + * 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.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.tinyengine.it.model.entity.Resource; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * The interface Resource mapper. + * + * @since 2024-10-20 + */ +public interface ResourceMapper extends BaseMapper { + /** + * 查询表t_resource所有信息 + * + * @return the list + */ + List queryAllResource(); + + /** + * 模糊查询表t_resource数据 + * + * @param nameCn the nameCn + * @param nameEn the nameEn + * @param des the des + * @return the resource + */ + List findResourcesByNameAndDes(String nameCn, String nameEn, String des); + + /** + * 根据主键id查询表t_resource数据 + * + * @param id the id + * @return the resource + */ + Resource queryResourceById(@Param("id") Integer id); + + /** + * 根据分组id和创建人查询表t_resource信息 + * + * @param resourceGroupId the resourceGroupId + * @param createBy the createBy + * @return the list + */ + List findResourceByResourceGroupId(Integer resourceGroupId, String createBy); + + /** + * 根据条件查询表t_resource数据 + * + * @param resource the resource + * @return the list + */ + List queryResourceByCondition(Resource resource); + + /** + * 根据主键id删除表t_resource数据 + * + * @param id the id + * @return the integer + */ + Integer deleteResourceById(@Param("id") Integer id); + + /** + * 根据主键id更新表t_resource数据 + * + * @param resource the resource + * @return the integer + */ + Integer updateResourceById(Resource resource); + + /** + * 新增表t_resource数据 + * + * @param resource the resource + * @return the integer + */ + Integer createResource(Resource resource); +} diff --git a/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java b/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java new file mode 100644 index 00000000..b5fefb9c --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java @@ -0,0 +1,27 @@ +/** + * 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.model.dto; + +import lombok.Data; + +/** + * The type ResourceRequest dto. + * + * @since 2025-09-04 + */ +@Data +public class ResourceRequestDto { + private String nameCn; + private String nameEn; + private boolean isResource; +} diff --git a/base/src/main/java/com/tinyengine/it/model/entity/Resource.java b/base/src/main/java/com/tinyengine/it/model/entity/Resource.java new file mode 100644 index 00000000..0d50373b --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/model/entity/Resource.java @@ -0,0 +1,67 @@ +/** + * 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.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.tinyengine.it.common.base.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +/** + * + * 资源表 + * + * @since 2025-09-03 + */ +@Getter +@Setter +@TableName("t_resource") +@Schema(name = "Resource", description = "资源表") +public class Resource extends BaseEntity { + @Schema(name = "appId", description = "关联appId") + private Integer appId; + + @Schema(name = "platformId", description = "关联设计器id") + private Integer platformId; + + @Schema(name = "nameCn", description = "中文名称") + private String nameCn; + + @Schema(name = "nameEn", description = "英文名称") + private String nameEn; + + @Schema(name = "resourceUrl", description = "资源url") + private String resourceUrl; + + @Schema(name = "thumbnailUrl", description = "缩略图url") + private String thumbnailUrl; + + @Schema(name = "category", description = "分类") + private String category; + + @Schema(name = "description", description = "描述") + private String description; + + @Schema(name = "thumbnailData", description = "缩略图数据") + private String thumbnailData; + + @Schema(name = "resourceData", description = "资源数据") + private String resourceData; + + @Schema(name = "publicStatus", description = "公开状态:0,1,2") + private Integer publicStatus; + + @Schema(name = "isDefault", description = "是否是默认") + private Boolean isDefault; +} diff --git a/base/src/main/java/com/tinyengine/it/model/entity/ResourceGroup.java b/base/src/main/java/com/tinyengine/it/model/entity/ResourceGroup.java new file mode 100644 index 00000000..c3f4ecd1 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/model/entity/ResourceGroup.java @@ -0,0 +1,51 @@ +/** + * 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.model.entity; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.tinyengine.it.common.base.BaseEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * 资源分组 + * + * @since 2025-09-03 + */ +@Getter +@Setter +@TableName("t_resource_group") +@Schema(name = "ResourceGroup", description = "资源分组") +public class ResourceGroup extends BaseEntity { + @Schema(name = "name", description = "分组名称") + private String name; + + @Schema(name = "appId", description = "创建分组所在app") + private Integer appId; + + @Schema(name = "platformId", description = "设计器id") + private Integer platformId; + + @Schema(name = "description", description = "分组描述") + private String description; + + @TableField(exist = false) + @Schema(name = "resources", description = "资源") + private List resources = new ArrayList<>(); +} diff --git a/base/src/main/java/com/tinyengine/it/model/entity/ResourceGroupResource.java b/base/src/main/java/com/tinyengine/it/model/entity/ResourceGroupResource.java new file mode 100644 index 00000000..250b0be2 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/model/entity/ResourceGroupResource.java @@ -0,0 +1,42 @@ +/** + * 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.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +/** + * + * 资源分组与资源关系 + * + * + * @since 2025-09-04 + */ +@Getter +@Setter +@TableName("r_resource_group_resource") +public class ResourceGroupResource { + @Schema(name = "id", description = "主键id") + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + @Schema(name = "resourceId", description = "资源id") + private Integer resourceId; + + @Schema(name = "resourceGroupId", description = "资源分组id") + private Integer resourceGroupId; +} diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java index 8248fd3a..70a67c40 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java @@ -12,10 +12,10 @@ package com.tinyengine.it.service.app.impl.v1; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; 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.config.OpenAIConfig; import com.tinyengine.it.model.dto.ChatRequest; @@ -56,6 +56,7 @@ public class AiChatV1ServiceImpl implements AiChatV1Service { * @return Object the Object */ @Override + @SystemServiceLog(description = "chatCompletion") public Object chatCompletion(ChatRequest request) throws Exception { String requestBody = buildRequestBody(request); String apiKey = request.getApiKey() != null ? request.getApiKey() : config.getApiKey(); @@ -74,7 +75,7 @@ public Object chatCompletion(ChatRequest request) throws Exception { } } - private String buildRequestBody(ChatRequest request) throws JsonProcessingException { + private String buildRequestBody(ChatRequest request) { Map body = new HashMap<>(); body.put("model", request.getModel() != null ? request.getModel() : config.getDefaultModel()); body.put("messages", request.getMessages()); diff --git a/base/src/main/java/com/tinyengine/it/service/material/ResourceGroupService.java b/base/src/main/java/com/tinyengine/it/service/material/ResourceGroupService.java new file mode 100644 index 00000000..255f8acd --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/service/material/ResourceGroupService.java @@ -0,0 +1,64 @@ +package com.tinyengine.it.service.material; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.model.entity.ResourceGroup; + +import java.util.List; + +public interface ResourceGroupService extends IService { + /** + * 查询表t_resource_group所有信息 + * + * @return the list + */ + List queryAllResourceGroupAndResource(); + + /** + * 根据appId查询表t_resource_group信息 + * + * @param appId the appId + * @return the resourceGroup + */ + Result queryResourceGroupByAppId(Integer appId); + + /** + * 根据Id查询表t_resource_group信息 + * + * @param id the id + * @return the resourceGroup + */ + Result queryResourceGroupById(Integer id); + + /** + * 根据条件查询表t_resource_group信息 + * + * @param resourceGroup the resourceGroup + * @return the list + */ + List queryResourceGroupByCondition(ResourceGroup resourceGroup); + + /** + * 根据主键id删除t_resource_group数据 + * + * @param id the id + * @return the integer + */ + Result deleteResourceGroupById(Integer id); + + /** + * 根据主键id更新表t_resource_group信息 + * + * @param resourceGroup the resourceGroup + * @return the integer + */ + Result updateResourceGroupById(ResourceGroup resourceGroup); + + /** + * 新增表t_resource_group数据 + * + * @param resourceGroup the resourceGroup + * @return the integer + */ + Result createResourceGroup(ResourceGroup resourceGroup); +} diff --git a/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java b/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java new file mode 100644 index 00000000..ce494a0a --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java @@ -0,0 +1,88 @@ +/** + * 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.service.material; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.model.dto.ResourceRequestDto; +import com.tinyengine.it.model.entity.Resource; + +import java.io.IOException; +import java.util.List; + +public interface ResourceService extends IService { + /** + * 查询表t_resource所有信息 + * + * @return the list + */ + List queryAllResource(); + + /** + * 模糊查询表Resource信息 + * + * @param nameCn the nameCn + * @param nameEn the nameEn + * @param des the des + * @return Resource信息列表 + */ + List queryResourcesByNameAndDes(String nameCn, String nameEn, String des); + + /** + * 根据主键id查询表t_resource信息 + * + * @param id the id + * @return the resource + */ + Result queryResourceById(Integer id); + + /** + * 根据data查询表t_resource信息 + * + * @param data the data + * @return the resource + */ + Resource queryResourceByData(ResourceRequestDto data) throws Exception; + + /** + * 根据分组id和创建人查询表t_resource信息 + * + * @param resourceGroupId the resourceGroupId + * @return the list + */ + List queryResourceByResourceGroupId(Integer resourceGroupId); + + /** + * 根据主键id删除t_resource数据 + * + * @param id the id + * @return the integer + */ + Result deleteResourceById(Integer id); + + /** + * 根据主键id更新表t_resource信息 + * + * @param resource the resource + * @return the integer + */ + Result updateResourceById(Resource resource); + + /** + * 新增表t_resource数据 + * + * @param resource the resource + * @return the integer + */ + Result createResource(Resource resource) throws Exception; +} diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/BlockGroupServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/BlockGroupServiceImpl.java index e5ee1f94..1f9b8a15 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/BlockGroupServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/BlockGroupServiceImpl.java @@ -237,16 +237,16 @@ public List getBlockGroupByIdsOrAppId(List ids, Integer app * 根据参数处理区块分组与区块关系 * * @param groupBlockIds the groupBlockIds - * @param paramIds the paramIds + * @param blockIds the blockIds * @param groupId the groupId * @return the result */ - private Integer getBlockGroupIds(List groupBlockIds, List paramIds, Integer groupId) { + private Integer getBlockGroupIds(List groupBlockIds, List blockIds, Integer groupId) { int result = 0; - if (groupBlockIds.size() > paramIds.size()) { + if (groupBlockIds.size() > blockIds.size()) { Block block = new Block(); for (Integer blockId : groupBlockIds) { - if (!paramIds.contains(blockId)) { + if (!blockIds.contains(blockId)) { result = blockId; block.setId(blockId); // 找到多出的元素 break; @@ -264,7 +264,7 @@ private Integer getBlockGroupIds(List groupBlockIds, List para blockGroupBlockMapper.deleteBlockGroupBlockById(blockGroupBlockId); return result; } else { - for (int block : paramIds) { + for (int block : blockIds) { BlockGroupBlock blockGroupBlockParam = new BlockGroupBlock(); blockGroupBlockParam.setBlockId(block); blockGroupBlockParam.setBlockGroupId(groupId); diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceGroupServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceGroupServiceImpl.java new file mode 100644 index 00000000..77ef3cb9 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceGroupServiceImpl.java @@ -0,0 +1,186 @@ +package com.tinyengine.it.service.material.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +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.exception.ServiceException; +import com.tinyengine.it.common.log.SystemServiceLog; +import com.tinyengine.it.mapper.ResourceGroupMapper; +import com.tinyengine.it.mapper.ResourceGroupResourceMapper; +import com.tinyengine.it.model.entity.Block; +import com.tinyengine.it.model.entity.BlockCarriersRelation; +import com.tinyengine.it.model.entity.BlockGroupBlock; +import com.tinyengine.it.model.entity.Resource; +import com.tinyengine.it.model.entity.ResourceGroup; +import com.tinyengine.it.model.entity.ResourceGroupResource; +import com.tinyengine.it.service.material.ResourceGroupService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + + +@Service +public class ResourceGroupServiceImpl extends ServiceImpl implements ResourceGroupService { + /** + * The loginUserContext service. + */ + @Autowired + private LoginUserContext loginUserContext; + + @Autowired + private ResourceGroupResourceMapper resourceGroupResourceMapper; + /** + * 查询表t_resource_group所有信息 + * + * @return the list + */ + @Override + @SystemServiceLog(description = "查询表t_resource_group所有信息") + public List queryAllResourceGroupAndResource() { + return baseMapper.queryAllResourceGroupAndResource(loginUserContext.getLoginUserId()); + } + + /** + * 根据主键id查询表t_resource_group信息 + * + * @param appId the appId + * @return the resourceGroup + */ + @Override + @SystemServiceLog(description = "根据主键id查询表t_resource_group信息") + public Result queryResourceGroupByAppId(Integer appId) { + ResourceGroup resourceGroup = baseMapper.queryResourceGroupByAppId(appId); + return Result.success(resourceGroup); + } + + /** + * 根据Id查询表t_resource_group信息 + * + * @param id the id + * @return the resourceGroup + */ + @Override + public Result queryResourceGroupById(Integer id) { + ResourceGroup resourceGroup = this.baseMapper.selectById(id); + return Result.success(resourceGroup); + } + + /** + * 根据条件查询表t_resource_group信息 + * + * @param resourceGroup the resourceGroup + * @return the list + */ + @Override + @SystemServiceLog(description = "根据条件查询表t_resource_group信息") + public List queryResourceGroupByCondition(ResourceGroup resourceGroup) { + return null; + } + + /** + * 根据主键id删除t_resource_group数据 + * + * @param id the id + * @return the integer + */ + @Override + @SystemServiceLog(description = "根据主键id删除t_resource_group数据") + public Result deleteResourceGroupById(Integer id) { + int deleteResult = baseMapper.deleteResourceGroupById(id); + if (deleteResult != 1) { + return Result.failed(ExceptionEnum.CM008); + } + resourceGroupResourceMapper.deleteResourceGroupResourceByGroupId(id); + ResourceGroup resourceGroup = baseMapper.selectById(id); + return Result.success(resourceGroup); + } + + /** + * 根据主键id更新表t_resource_group信息 + * + * @param resourceGroup the resourceGroup + * @return the integer + */ + @Override + @SystemServiceLog(description = "根据主键id更新表t_resource_group信息") + public Result updateResourceGroupById(ResourceGroup resourceGroup) { + List resourceList = resourceGroup.getResources(); + List resourceGroupBlocks = resourceGroupResourceMapper.findResourceGroupResourceByResourceGroupId( + resourceGroup.getId()); + List groupResourceIds = resourceGroupBlocks.stream() + .map(ResourceGroupResource::getResourceId) + .collect(Collectors.toList()); + + if (resourceList.isEmpty()) { + resourceGroupResourceMapper.deleteResourceGroupResourceByGroupId(resourceGroup.getId()); + this.baseMapper.updateResourceGroupById(resourceGroup); + ResourceGroup result = this.baseMapper.queryResourceGroupById(resourceGroup.getId(), loginUserContext.getLoginUserId()); + return Result.success(result); + } + + List resourceIds = resourceList.stream().map(Resource::getId).collect(Collectors.toList()); + getResourceGroupIds(groupResourceIds, resourceIds, resourceGroup.getId()); + this.baseMapper.updateResourceGroupById(resourceGroup); + ResourceGroup result = this.baseMapper.queryResourceGroupById(resourceGroup.getId(), loginUserContext.getLoginUserId()); + return Result.success(result); + } + + /** + * 新增表t_resource_group数据 + * + * @param resourceGroup the resourceGroup + * @return the integer + */ + @Override + @SystemServiceLog(description = "新增表t_resource_group数据") + public Result createResourceGroup(ResourceGroup resourceGroup) { + int createResult = baseMapper.createResourceGroup(resourceGroup); + if (createResult != 1) { + return Result.failed(ExceptionEnum.CM008); + } + ResourceGroup result = baseMapper.selectById(resourceGroup.getId()); + return Result.success(result); + } + + /** + * 根据参数处理资源分组与资源关系 + * + * @param groupResourceIds the groupResourceIds + * @param resourceIds the resourceIds + * @param groupId the groupId + * @return the result + */ + private Integer getResourceGroupIds(List groupResourceIds, List resourceIds, Integer groupId) { + int result = 0; + if (groupResourceIds.size() > resourceIds.size()) { + Resource resource = new Resource(); + for (Integer resourceId : groupResourceIds) { + if (!resourceIds.contains(resourceId)) { + result = resourceId; + resource.setId(resourceId); // 找到多出的元素 + break; + } + } + ResourceGroupResource queryResult = resourceGroupResourceMapper.findResourceGroupResourceByResourceGroupIdAndResourceId(groupId, resource.getId()); + if (queryResult == null) { + return result; + } + + resourceGroupResourceMapper.deleteById(queryResult.getId()); + return result; + } else { + for (int resourceId : resourceIds) { + ResourceGroupResource resourceGroupResource = new ResourceGroupResource(); + resourceGroupResource.setResourceId(resourceId); + resourceGroupResource.setResourceGroupId(groupId); + resourceGroupResourceMapper.createResourceGroupResource(resourceGroupResource); + } + } + return result; + } +} diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java new file mode 100644 index 00000000..0c439090 --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java @@ -0,0 +1,197 @@ +/** + * 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.service.material.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.context.LoginUserContext; +import com.tinyengine.it.common.exception.ExceptionEnum; +import com.tinyengine.it.common.log.SystemServiceLog; +import com.tinyengine.it.common.utils.ImageThumbnailGenerator; +import com.tinyengine.it.common.utils.Utils; +import com.tinyengine.it.mapper.ResourceMapper; +import com.tinyengine.it.model.dto.ResourceRequestDto; +import com.tinyengine.it.model.entity.Resource; +import com.tinyengine.it.service.material.ResourceService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +@Service +public class ResourceServiceImpl extends ServiceImpl implements ResourceService { + /** + * The loginUserContext service. + */ + @Autowired + private LoginUserContext loginUserContext; + + /** + * 查询表t_resource所有信息 + * + * @return the list + */ + @Override + @SystemServiceLog(description = "查询表t_resource所有信息") + public List queryAllResource() { + return baseMapper.queryAllResource(); + } + + /** + * 模糊查询表Resource信息 + * + * @param nameCn the nameCn + * @param nameEn the nameEn + * @param des the des + * @return Resource信息列表 + */ + @Override + public List queryResourcesByNameAndDes(String nameCn, String nameEn, String des) { + return this.baseMapper.findResourcesByNameAndDes(nameCn, nameEn, des); + } + + /** + * 根据主键id查询表t_resource信息 + * + * @param id the id + * @return the resource + */ + @Override + @SystemServiceLog(description = "根据主键id查询表t_resource信息") + public Result queryResourceById(Integer id) { + Resource resource = baseMapper.queryResourceById(id); + return Result.success(resource); + } + + /** + * 根据data查询表t_resource信息 + * + * @param data the data + * @return the resource + */ + @Override + @SystemServiceLog(description = "根据data查询表t_resource信息") + public Resource queryResourceByData(ResourceRequestDto data) throws Exception { + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("name_cn", data.getNameCn()); + queryWrapper.eq("name_en", data.getNameEn()); + + return this.baseMapper.selectOne(queryWrapper); + } + + /** + * 根据分组id和创建人查询表t_resource信息 + * + * @param resourceGroupId the resourceGroupId + * @return the list + */ + @Override + @SystemServiceLog(description = "根据条件查询表t_resource信息") + public List queryResourceByResourceGroupId(Integer resourceGroupId) { + + return baseMapper.findResourceByResourceGroupId(resourceGroupId, loginUserContext.getLoginUserId()); + } + + /** + * 根据主键id删除t_resource数据 + * + * @param id the id + * @return the integer + */ + @Override + @SystemServiceLog(description = "根据主键id删除t_resource数据") + public Result deleteResourceById(Integer id) { + int deleteResult = baseMapper.deleteResourceById(id); + if (deleteResult != 1) { + return Result.failed(ExceptionEnum.CM008); + } + Resource resource = baseMapper.queryResourceById(id); + return Result.success(resource); + } + + /** + * 根据主键id更新表t_resource信息 + * + * @param resource the resource + * @return the integer + */ + @Override + @SystemServiceLog(description = "根据主键id更新表t_resource信息") + public Result updateResourceById(Resource resource) { + int updateResult = baseMapper.updateResourceById(resource); + if (updateResult != 1) { + return Result.failed(ExceptionEnum.CM008); + } + Resource result = baseMapper.queryResourceById(resource.getId()); + return Result.success(result); + } + + /** + * 新增表t_resource数据 + * + * @param resource the resource + * @return the integer + */ + @Override + @SystemServiceLog(description = "新增表t_resource数据") + public Result createResource(Resource resource) throws Exception { + + ResourceRequestDto resourceParam = new ResourceRequestDto(); + resourceParam.setNameCn(resource.getNameCn()); + resourceParam.setNameEn(resource.getNameEn()); + resourceParam.setResource(true); + + String encodedResourceParam = Utils.encodeObjectToBase64(resourceParam); + + ResourceRequestDto thumbnailParam = new ResourceRequestDto(); + thumbnailParam.setNameCn(resource.getNameCn()); + thumbnailParam.setNameEn(resource.getNameEn()); + thumbnailParam.setResource(false); + String encodedThumbnailParam = Utils.encodeObjectToBase64(thumbnailParam); + + String resourceData = resource.getResourceData(); + String tinyEngineUrl = "http://127.0.0.1:9090/material-center/api/resource/download"; // System.getenv("TINY_ENGINE_URL"); + + if(!StringUtils.isEmpty(resourceData)) { + resource.setResourceUrl(String.format("%s?data=%s", tinyEngineUrl, encodedResourceParam)); + resource.setThumbnailUrl(String.format("%s?data=%s", tinyEngineUrl, encodedThumbnailParam)); + resource.setThumbnailData(ImageThumbnailGenerator.createThumbnail(resource.getResourceData(), 200, 200)); + } + + int createResult = baseMapper.createResource(resource); + if (createResult != 1) { + return Result.failed(ExceptionEnum.CM008); + } + + Resource result = baseMapper.queryResourceById(resource.getId()); + return Result.success(result); + } + + + /** + * 批量新增表t_resource数据 + * @param entityList + * @param batchSize + * @return + */ + @Override + @SystemServiceLog(description = "批量新增表t_resource数据") + public boolean saveBatch(Collection entityList, int batchSize) { + return false; + } + +} diff --git a/base/src/main/resources/mappers/ResourceGroupMapper.xml b/base/src/main/resources/mappers/ResourceGroupMapper.xml new file mode 100644 index 00000000..6d6d7270 --- /dev/null +++ b/base/src/main/resources/mappers/ResourceGroupMapper.xml @@ -0,0 +1,327 @@ + + + + + + + + + id + , name, app_id, platform_id, description, created_by, last_updated_by, created_time, last_updated_time, + tenant_id, renter_id, site_id + + + + + + AND name = #{name} + + + AND app_id = #{appId} + + + AND platform_id = #{platformId} + + + AND description = #{description} + + + AND created_by = #{createdBy} + + + AND last_updated_by = #{lastUpdatedBy} + + + AND created_time = #{createdTime} + + + AND last_updated_time = #{lastUpdatedTime} + + + AND tenant_id = #{tenantId} + + + AND renter_id = #{renterId} + + + AND site_id = #{siteId} + + + + + + + name = #{name}, + + + app_id = #{appId}, + + + platform_id = #{platformId}, + + + description = #{description}, + + + created_by = #{createdBy}, + + + last_updated_by = #{lastUpdatedBy}, + + + created_time = #{createdTime}, + + + last_updated_time = #{lastUpdatedTime}, + + + tenant_id = #{tenantId}, + + + renter_id = #{renterId}, + + + site_id = #{siteId}, + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE + FROM t_resource_group + WHERE id = #{id} + + + + + UPDATE t_resource_group + + + + WHERE id = #{id} + + + + + INSERT INTO t_resource_group ( id + , app_id + , platform_id + , name + , description + , created_by + , last_updated_by + , created_time + , last_updated_time + , tenant_id + , renter_id + , site_id) + VALUES ( #{id} + , #{appId} + , #{platformId} + , #{name} + , #{description} + , #{createdBy} + , #{lastUpdatedBy} + , #{createdTime} + , #{lastUpdatedTime} + , #{tenantId} + , #{renterId} + , #{siteId}) + + diff --git a/base/src/main/resources/mappers/ResourceGroupResourceMapper.xml b/base/src/main/resources/mappers/ResourceGroupResourceMapper.xml new file mode 100644 index 00000000..6297e1b5 --- /dev/null +++ b/base/src/main/resources/mappers/ResourceGroupResourceMapper.xml @@ -0,0 +1,103 @@ + + + + + + + + + id + , resource_id, resource_group_id + + + + + + AND resource_id = #{resourceId} + + + AND resource_group_id = #{resourceGroupId} + + + + + + + resource_id = #{resourceId}, + + + resource_group_id = #{resourceGroupId}, + + + + + + + + + + + + + + + + + + + + + + + DELETE + FROM r_resource_group_resource + WHERE id = #{id} + + + + + DELETE + FROM r_resource_group_resource + WHERE resource_group_id = #{resourceGroupId} + + + + + UPDATE r_resource_group_resource + + + + WHERE + id=#{id} + + + + + INSERT INTO r_resource_group_resource ( id + , resource_id + , resource_group_id) + VALUES ( #{id} + , #{resourceId} + , #{resourceGroupId}) ON DUPLICATE KEY + UPDATE + resource_id = + VALUES (resource_id), resource_group_id = + VALUES (resource_group_id); + + diff --git a/base/src/main/resources/mappers/ResourceMapper.xml b/base/src/main/resources/mappers/ResourceMapper.xml new file mode 100644 index 00000000..f6849eba --- /dev/null +++ b/base/src/main/resources/mappers/ResourceMapper.xml @@ -0,0 +1,290 @@ + + + + + + + + + id + , app_id, platform_id, name_cn, name_en, resource_url, thumbnail_url, category, description, thumbnail_data, resource_data, + public_status, is_default, created_by, last_updated_by, created_time, last_updated_time, tenant_id, renter_id, site_id + + + + + + AND app_id = #{appId} + + + AND platform_id = #{platformId} + + + + AND name_cn = #{nameCn} + + + AND name_en = #{nameEn} + + + AND resource_url = #{resourceUrl} + + + AND thumbnail_url = #{thumbnailUrl} + + + AND category = #{category} + + + AND description = #{description} + + + AND thumbnail_data = #{thumbnailData} + + + AND resource_data = #{resourceData} + + + AND public_status = #{publicStatus} + + + AND is_default = #{isDefault} + + + AND created_by = #{createdBy} + + + AND last_updated_by = #{lastUpdatedBy} + + + AND created_time = #{createdTime} + + + AND last_updated_time = #{lastUpdatedTime} + + + AND tenant_id = #{tenantId} + + + AND renter_id = #{renterId} + + + AND site_id = #{siteId} + + + + + + + app_id = #{appId} + + + platform_id = #{platformId} + + + name_cn = #{nameCn} + + + name_en = #{nameEn} + + + resource_url = #{resourceUrl} + + + thumbnail_url = #{thumbnailUrl} + + + category = #{category} + + + description = #{description} + + + thumbnail_data = #{thumbnailData} + + + resource_data = #{resourceData} + + + public_status = #{publicStatus} + + + is_default = #{isDefault} + + + created_by = #{createdBy} + + + last_updated_by = #{lastUpdatedBy} + + + created_time = #{createdTime} + + + last_updated_time = #{lastUpdatedTime} + + + tenant_id = #{tenantId} + + + renter_id = #{renterId} + + + site_id = #{siteId} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE + FROM t_resource + WHERE id = #{id} + + + + + UPDATE t_resource + + + + WHERE + id=#{id} + + + + + INSERT INTO t_resource ( id + , app_id + , platform_id + , name_cn + , name_en + , resource_url + , thumbnail_url + , category + , description + , thumbnail_data + , resource_data + , public_status + , is_default + , created_by + , last_updated_by + , created_time + , last_updated_time + , tenant_id + , renter_id + , site_id) + VALUES ( #{id} + , #{appId} + , #{platformId} + , #{nameCn} + , #{nameEn} + , #{resourceUrl} + , #{thumbnailUrl} + , #{category} + , #{description} + , #{thumbnailData} + , #{resourceData} + , #{publicStatus} + , #{isDefault} + , #{createdBy} + , #{lastUpdatedBy} + , #{createdTime} + , #{lastUpdatedTime} + , #{tenantId} + , #{renterId} + , #{siteId}) + + diff --git a/pom.xml b/pom.xml index be68c86c..dcd77d62 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ 4.11.0 6.1.0 4.1.118.Final + 1.18 1.0-SNAPSHOT @@ -78,6 +79,17 @@ ${hutool.version} + + org.apache.xmlgraphics + batik-all + ${batik-version} + + + + org.apache.xmlgraphics + batik-transcoder + ${batik-version} + org.mariadb.jdbc mariadb-java-client @@ -130,6 +142,7 @@ test ${mockito-inline.version} + io.netty netty-buffer From df6ba91f91f3582dc63d4a2e79ee0fcfbaa23e9a Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Thu, 4 Sep 2025 20:18:53 -0700 Subject: [PATCH 02/31] fix: modify source API --- .../sql/h2/create_resources_ddl_2025_0902.sql | 5 +- .../mysql/create_resources_ddl_2025_0902.sql | 5 +- .../com/tinyengine/it/common/utils/Utils.java | 58 ++++++++++++------- .../it/controller/ResourceController.java | 53 ++++++++++++----- .../tinyengine/it/mapper/ResourceMapper.java | 5 +- .../it/model/dto/ResourceRequestDto.java | 4 +- .../tinyengine/it/model/entity/Resource.java | 7 +-- .../it/service/material/ResourceService.java | 16 +++-- .../material/impl/ResourceServiceImpl.java | 51 ++++++++-------- .../main/resources/mappers/ResourceMapper.xml | 32 ++++------ 10 files changed, 134 insertions(+), 102 deletions(-) diff --git a/app/src/main/resources/sql/h2/create_resources_ddl_2025_0902.sql b/app/src/main/resources/sql/h2/create_resources_ddl_2025_0902.sql index b7c3b663..1dc2874f 100644 --- a/app/src/main/resources/sql/h2/create_resources_ddl_2025_0902.sql +++ b/app/src/main/resources/sql/h2/create_resources_ddl_2025_0902.sql @@ -5,8 +5,7 @@ create table `t_resource` `id` int not null auto_increment comment '主键id', `app_id` int comment '关联appId', `platform_id` int comment '关联设计器id', - `name_cn` varchar(255) not null comment '中文名称', - `name_en` varchar(255) not null comment '英文名称', + `name` varchar(255) not null comment '名称', `resource_url` varchar(255) comment '资源url', `thumbnail_url` varchar(255) comment '缩略图url', `category` varchar(255) not null comment '分类', @@ -23,7 +22,7 @@ create table `t_resource` `renter_id` varchar(60) comment '业务租户id', `site_id` varchar(60) comment '站点id,设计预留字段', primary key (`id`) using btree, - unique index `u_idx_resource` (`name_en`,`name_cn`, `tenant_id`) using btree + unique index `u_idx_resource` (`category`,`name`, `tenant_id`) using btree ) engine = innodb comment = '资源表'; drop table if exists `t_resource_group`; diff --git a/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql b/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql index b7c3b663..1dc2874f 100644 --- a/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql +++ b/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql @@ -5,8 +5,7 @@ create table `t_resource` `id` int not null auto_increment comment '主键id', `app_id` int comment '关联appId', `platform_id` int comment '关联设计器id', - `name_cn` varchar(255) not null comment '中文名称', - `name_en` varchar(255) not null comment '英文名称', + `name` varchar(255) not null comment '名称', `resource_url` varchar(255) comment '资源url', `thumbnail_url` varchar(255) comment '缩略图url', `category` varchar(255) not null comment '分类', @@ -23,7 +22,7 @@ create table `t_resource` `renter_id` varchar(60) comment '业务租户id', `site_id` varchar(60) comment '站点id,设计预留字段', primary key (`id`) using btree, - unique index `u_idx_resource` (`name_en`,`name_cn`, `tenant_id`) using btree + unique index `u_idx_resource` (`category`,`name`, `tenant_id`) using btree ) engine = innodb comment = '资源表'; drop table if exists `t_resource_group`; 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 5813da5a..538273f9 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 @@ -425,23 +425,18 @@ public static void validateFileStream(MultipartFile file, String code, List T decodeBase64ToObject(String encodedString, Class clazz) throws Exception { - // 解码 Base64 字符串 - byte[] decodedBytes = Base64.getDecoder().decode(encodedString); - String jsonString = new String(decodedBytes, StandardCharsets.UTF_8); - - return JsonUtils.decode(jsonString, clazz); + public static T decodeBase64ToObject(String encodedString, Class clazz) { + // 处理URL安全的Base64编码 + String standardBase64 = fromUrlSafe(encodedString); + try { + byte[] decodedBytes = Base64.getDecoder().decode(standardBase64); + String jsonString = new String(decodedBytes, StandardCharsets.UTF_8); + return JsonUtils.decode(jsonString, clazz); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid Base64 string: " + encodedString, e); + } } /** - * base64编码转map - * - * @param encodedString - * @return map + * 将标准Base64转换为URL安全格式 + */ + private static String makeUrlSafe(String base64) { + return base64 + .replace('+', '-') + .replace('/', '_') + .replace("=", ""); + } + /** + * 将URL安全Base64转换回标准格式 */ - public static Map decodeBase64ToMap(String encodedString) throws Exception { - return decodeBase64ToObject(encodedString, Map.class); + private static String fromUrlSafe(String urlSafeBase64) { + // 先替换字符 + String standard = urlSafeBase64 + .replace('-', '+') + .replace('_', '/'); + + // 添加填充字符使长度成为4的倍数 + int padding = standard.length() % 4; + if (padding > 0) { + standard += "=".repeat(4 - padding); + } + + return standard; } } diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index d842a4c5..0130abc6 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -40,6 +40,8 @@ import org.springframework.web.bind.annotation.RestController; import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.List; @@ -120,15 +122,13 @@ public Result> getResourceByResourceGroupId(@PathVariable Integer /** * 模糊查询表Resource信息 * - * @param nameCn the nameCn - * @param nameEn the nameEn + * @param name the name * @param des the des * @return Resource信息列表 */ @Operation(summary = "模糊查询表Resource信息列表", description = "模糊查询表Resource信息列表", parameters = { - @Parameter(name = "nameCn", description = "中文名"), - @Parameter(name = "nameEn", description = "英文名"), + @Parameter(name = "name", description = "名称"), @Parameter(name = "des", description = "描述") }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", @@ -137,10 +137,8 @@ public Result> getResourceByResourceGroupId(@PathVariable Integer }) @SystemControllerLog(description = "模糊查询表Resource信息列表") @GetMapping("/resource/like") - public Result> getResourceById(@PathVariable String nameCn, - @PathVariable String nameEn, - @PathVariable String des) { - List resourceList = resourceService.queryResourcesByNameAndDes(nameCn, nameEn, des); + public Result> getResourceById(@PathVariable String name, @PathVariable String des) { + List resourceList = resourceService.queryResourcesByNameAndDes(name, des); return Result.success(resourceList); } @@ -162,7 +160,29 @@ public Result> getResourceById(@PathVariable String nameCn, @SystemControllerLog(description = "创建resource") @PostMapping("/resource/create") public Result createResource(@Valid @RequestBody Resource resource) throws Exception { - return resourceService.createResource(resource); + Resource result = resourceService.createResource(resource); + return Result.success(result); + } + + /** + * 批量创建Resource + * + * @param resources the resources + * @return Resource信息 result + */ + @Operation(summary = "批量创建Resource", description = "批量创建Resource", + parameters = { + @Parameter(name = "resources", description = "Resource入参对象") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "批量创建Resource") + @PostMapping("/resource/create/batch") + public Result> createResource(@Valid @RequestBody List resources) throws Exception { + List resourceList = resourceService.createBatchResource(resources); + return Result.success(resourceList); } /** @@ -254,16 +274,19 @@ public void getResource(@RequestParam("data") String data, HttpServletResponse r String fileExtension = detectedType.equals("jpeg") ? "jpg" : detectedType; String fileName = useOriginal ? - resource.getNameEn() + "." + fileExtension : - resource.getNameEn() + "_thumbnail." + fileExtension; - - + resource.getName() + "." + fileExtension : + resource.getName() + "_thumbnail." + fileExtension; + // URL编码文件名 + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()) + .replace("+", "%20"); response.setContentType("image/" + detectedType); - response.setHeader("Content-Disposition", - "inline; filename=\"" + fileName + "\"; filename*=UTF-8''" + fileName); + // 只使用 filename* 格式,避免中文字符直接出现在header中 + response.setHeader("Content-Disposition", + "inline; filename*=UTF-8''" + encodedFileName); try (OutputStream out = response.getOutputStream()) { out.write(imageBytes); } } + } diff --git a/base/src/main/java/com/tinyengine/it/mapper/ResourceMapper.java b/base/src/main/java/com/tinyengine/it/mapper/ResourceMapper.java index 50d19803..4275e35c 100644 --- a/base/src/main/java/com/tinyengine/it/mapper/ResourceMapper.java +++ b/base/src/main/java/com/tinyengine/it/mapper/ResourceMapper.java @@ -34,12 +34,11 @@ public interface ResourceMapper extends BaseMapper { /** * 模糊查询表t_resource数据 * - * @param nameCn the nameCn - * @param nameEn the nameEn + * @param name the name * @param des the des * @return the resource */ - List findResourcesByNameAndDes(String nameCn, String nameEn, String des); + List findResourcesByNameAndDes(String name, String des); /** * 根据主键id查询表t_resource数据 diff --git a/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java b/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java index b5fefb9c..44684a13 100644 --- a/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java +++ b/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java @@ -21,7 +21,7 @@ */ @Data public class ResourceRequestDto { - private String nameCn; - private String nameEn; + private String name; + private String category; private boolean isResource; } diff --git a/base/src/main/java/com/tinyengine/it/model/entity/Resource.java b/base/src/main/java/com/tinyengine/it/model/entity/Resource.java index 0d50373b..b3f3980c 100644 --- a/base/src/main/java/com/tinyengine/it/model/entity/Resource.java +++ b/base/src/main/java/com/tinyengine/it/model/entity/Resource.java @@ -35,11 +35,8 @@ public class Resource extends BaseEntity { @Schema(name = "platformId", description = "关联设计器id") private Integer platformId; - @Schema(name = "nameCn", description = "中文名称") - private String nameCn; - - @Schema(name = "nameEn", description = "英文名称") - private String nameEn; + @Schema(name = "name", description = "名称") + private String name; @Schema(name = "resourceUrl", description = "资源url") private String resourceUrl; diff --git a/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java b/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java index ce494a0a..9da16452 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java +++ b/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java @@ -17,7 +17,6 @@ import com.tinyengine.it.model.dto.ResourceRequestDto; import com.tinyengine.it.model.entity.Resource; -import java.io.IOException; import java.util.List; public interface ResourceService extends IService { @@ -31,12 +30,11 @@ public interface ResourceService extends IService { /** * 模糊查询表Resource信息 * - * @param nameCn the nameCn - * @param nameEn the nameEn + * @param name the name * @param des the des * @return Resource信息列表 */ - List queryResourcesByNameAndDes(String nameCn, String nameEn, String des); + List queryResourcesByNameAndDes(String name, String des); /** * 根据主键id查询表t_resource信息 @@ -84,5 +82,13 @@ public interface ResourceService extends IService { * @param resource the resource * @return the integer */ - Result createResource(Resource resource) throws Exception; + Resource createResource(Resource resource) throws Exception; + + /** + * 批量新增表t_resource数据 + * + * @param resources the resources + * @return the integer + */ + List createBatchResource(List resources) throws Exception; } diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java index 0c439090..2163b553 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java @@ -17,6 +17,7 @@ import com.tinyengine.it.common.base.Result; import com.tinyengine.it.common.context.LoginUserContext; 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.ImageThumbnailGenerator; import com.tinyengine.it.common.utils.Utils; @@ -28,7 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.Collection; +import java.util.ArrayList; import java.util.List; @Service @@ -53,14 +54,13 @@ public List queryAllResource() { /** * 模糊查询表Resource信息 * - * @param nameCn the nameCn - * @param nameEn the nameEn + * @param name the name * @param des the des * @return Resource信息列表 */ @Override - public List queryResourcesByNameAndDes(String nameCn, String nameEn, String des) { - return this.baseMapper.findResourcesByNameAndDes(nameCn, nameEn, des); + public List queryResourcesByNameAndDes(String name, String des) { + return this.baseMapper.findResourcesByNameAndDes(name, des); } /** @@ -84,11 +84,11 @@ public Result queryResourceById(Integer id) { */ @Override @SystemServiceLog(description = "根据data查询表t_resource信息") - public Resource queryResourceByData(ResourceRequestDto data) throws Exception { + public Resource queryResourceByData(ResourceRequestDto data) { QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("name_cn", data.getNameCn()); - queryWrapper.eq("name_en", data.getNameEn()); + queryWrapper.eq("name", data.getName()); + queryWrapper.eq("category", data.getCategory()); return this.baseMapper.selectOne(queryWrapper); } @@ -148,18 +148,18 @@ public Result updateResourceById(Resource resource) { */ @Override @SystemServiceLog(description = "新增表t_resource数据") - public Result createResource(Resource resource) throws Exception { + public Resource createResource(Resource resource) throws Exception { ResourceRequestDto resourceParam = new ResourceRequestDto(); - resourceParam.setNameCn(resource.getNameCn()); - resourceParam.setNameEn(resource.getNameEn()); + resourceParam.setName(resource.getName()); + resourceParam.setCategory(resource.getCategory()); resourceParam.setResource(true); String encodedResourceParam = Utils.encodeObjectToBase64(resourceParam); ResourceRequestDto thumbnailParam = new ResourceRequestDto(); - thumbnailParam.setNameCn(resource.getNameCn()); - thumbnailParam.setNameEn(resource.getNameEn()); + thumbnailParam.setName(resource.getName()); + thumbnailParam.setCategory(resource.getCategory()); thumbnailParam.setResource(false); String encodedThumbnailParam = Utils.encodeObjectToBase64(thumbnailParam); @@ -174,24 +174,29 @@ public Result createResource(Resource resource) throws Exception { int createResult = baseMapper.createResource(resource); if (createResult != 1) { - return Result.failed(ExceptionEnum.CM008); + throw new ServiceException(ExceptionEnum.CM008.getResultCode(), ExceptionEnum.CM008.getResultMsg()); } - Resource result = baseMapper.queryResourceById(resource.getId()); - return Result.success(result); + return baseMapper.queryResourceById(resource.getId()); } - /** * 批量新增表t_resource数据 - * @param entityList - * @param batchSize - * @return + * + * @param resources the resources + * @return the integer */ @Override - @SystemServiceLog(description = "批量新增表t_resource数据") - public boolean saveBatch(Collection entityList, int batchSize) { - return false; + public List createBatchResource(List resources) throws Exception { + List resourceList = new ArrayList<>(); + if(resources.isEmpty()){ + return resourceList; + } + for(Resource resource : resources) { + Resource result = this.createResource(resource); + resourceList.add(result); + } + return resourceList; } } diff --git a/base/src/main/resources/mappers/ResourceMapper.xml b/base/src/main/resources/mappers/ResourceMapper.xml index f6849eba..078fee80 100644 --- a/base/src/main/resources/mappers/ResourceMapper.xml +++ b/base/src/main/resources/mappers/ResourceMapper.xml @@ -7,7 +7,7 @@ id - , app_id, platform_id, name_cn, name_en, resource_url, thumbnail_url, category, description, thumbnail_data, resource_data, + , app_id, platform_id, `name`, resource_url, thumbnail_url, category, description, thumbnail_data, resource_data, public_status, is_default, created_by, last_updated_by, created_time, last_updated_time, tenant_id, renter_id, site_id @@ -20,11 +20,8 @@ AND platform_id = #{platformId} - - AND name_cn = #{nameCn} - - - AND name_en = #{nameEn} + + AND `name` = #{name} AND resource_url = #{resourceUrl} @@ -81,11 +78,8 @@ platform_id = #{platformId} - - name_cn = #{nameCn} - - - name_en = #{nameEn} + + `name` = #{name} resource_url = #{resourceUrl} @@ -140,8 +134,7 @@ - - + @@ -214,11 +207,8 @@ FROM t_resource - - name_cn like concat('%',#{nameCn},'%') - - - name_en like concat('%',#{nameEn},'%') + + name like concat('%',#{name},'%') or description like concat('%',#{description},'%') @@ -249,8 +239,7 @@ INSERT INTO t_resource ( id , app_id , platform_id - , name_cn - , name_en + , name , resource_url , thumbnail_url , category @@ -269,8 +258,7 @@ VALUES ( #{id} , #{appId} , #{platformId} - , #{nameCn} - , #{nameEn} + , #{name} , #{resourceUrl} , #{thumbnailUrl} , #{category} From 0fef229d8dea77ea04fa3faf074e310da046ada7 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Thu, 4 Sep 2025 20:27:07 -0700 Subject: [PATCH 03/31] fix: modify source API --- .../it/service/material/impl/ResourceServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java index 2163b553..bd281033 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java @@ -164,7 +164,7 @@ public Resource createResource(Resource resource) throws Exception { String encodedThumbnailParam = Utils.encodeObjectToBase64(thumbnailParam); String resourceData = resource.getResourceData(); - String tinyEngineUrl = "http://127.0.0.1:9090/material-center/api/resource/download"; // System.getenv("TINY_ENGINE_URL"); + String tinyEngineUrl = System.getenv("TINY_ENGINE_URL"); if(!StringUtils.isEmpty(resourceData)) { resource.setResourceUrl(String.format("%s?data=%s", tinyEngineUrl, encodedResourceParam)); From ab24c4055248547200c93e82f006e31f6de29863 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Fri, 5 Sep 2025 00:09:59 -0700 Subject: [PATCH 04/31] fix: modify source API --- .../sql/mysql/create_resources_ddl_2025_0902.sql | 8 ++++---- .../tinyengine/it/controller/ResourceGroupController.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql b/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql index 1dc2874f..f6d445ac 100644 --- a/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql +++ b/app/src/main/resources/sql/mysql/create_resources_ddl_2025_0902.sql @@ -3,8 +3,8 @@ drop table if exists `t_resource`; create table `t_resource` ( `id` int not null auto_increment comment '主键id', - `app_id` int comment '关联appId', - `platform_id` int comment '关联设计器id', + `app_id` int not null comment '关联appId', + `platform_id` int not null comment '关联设计器id', `name` varchar(255) not null comment '名称', `resource_url` varchar(255) comment '资源url', `thumbnail_url` varchar(255) comment '缩略图url', @@ -31,8 +31,8 @@ create table `t_resource_group` ( `id` int not null auto_increment comment '主键id', `name` varchar(255) not null comment '中文名称', - `app_id` int comment '关联appId', - `platform_id` int comment '关联设计器id', + `app_id` int not null comment '关联appId', + `platform_id` int not null comment '关联设计器id', `description` varchar(2000) comment '描述', `created_by` varchar(60) not null comment '创建人', `created_time` timestamp not null default current_timestamp comment '创建时间', diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceGroupController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceGroupController.java index 4c429aa7..6d7466cb 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceGroupController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceGroupController.java @@ -75,7 +75,7 @@ public Result> queryAllResourceGroupAndResource() { * @param appId the appId * @return ResourceGroup信息 app by id */ - @Operation(summary = "根据id查询表ResourceGroup信息", description = "根据id查询表ResourceGroup信息", + @Operation(summary = "根据appId查询表ResourceGroup信息", description = "根据appId查询表ResourceGroup信息", parameters = { @Parameter(name = "appId", description = "ResourceGroup主键id") }, responses = { @@ -83,7 +83,7 @@ public Result> queryAllResourceGroupAndResource() { content = @Content(mediaType = "application/json", schema = @Schema(implementation = ResourceGroup.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) - @SystemControllerLog(description = "根据id查询表ResourceGroup信息") + @SystemControllerLog(description = "根据appId查询表ResourceGroup信息") @GetMapping("/resource-group/{appId}") public Result queryResourceGroupByAppId(@PathVariable Integer appId) { return resourceGroupService.queryResourceGroupByAppId(appId); From 787d1d161f4f33ef5395940f723d1052bf2860b3 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Fri, 5 Sep 2025 00:49:52 -0700 Subject: [PATCH 05/31] fix: modify source API --- .../main/resources/mappers/ResourceGroupMapper.xml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/base/src/main/resources/mappers/ResourceGroupMapper.xml b/base/src/main/resources/mappers/ResourceGroupMapper.xml index 6d6d7270..279960a5 100644 --- a/base/src/main/resources/mappers/ResourceGroupMapper.xml +++ b/base/src/main/resources/mappers/ResourceGroupMapper.xml @@ -104,8 +104,7 @@ - - + @@ -142,8 +141,7 @@ r.id as resource_id, r.app_id as resource_app_id, r.platform_id as resource_platform_id, - r.name_cn, - r.name_en, + r.name, r.resource_url, r.thumbnail_url, r.category, @@ -190,8 +188,7 @@ r.id as resource_id, r.app_id as resource_app_id, r.platform_id as resource_platform_id, - r.name_cn, - r.name_en, + r.name, r.resource_url, r.thumbnail_url, r.category, @@ -240,8 +237,7 @@ r.id as resource_id, r.app_id as resource_app_id, r.platform_id as resource_platform_id, - r.name_cn, - r.name_en, + r.name, r.resource_url, r.thumbnail_url, r.category, From 5fed78f103864a6e7a052f6ad268efe5dd772573 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Fri, 5 Sep 2025 01:20:04 -0700 Subject: [PATCH 06/31] fix: modify source API --- .../tinyengine/it/mapper/ResourceGroupMapper.java | 3 ++- .../material/impl/ResourceGroupServiceImpl.java | 6 +++--- .../resources/mappers/ResourceGroupMapper.xml | 15 ++++++++------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupMapper.java b/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupMapper.java index 9cd5a322..7a95680e 100644 --- a/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupMapper.java +++ b/base/src/main/java/com/tinyengine/it/mapper/ResourceGroupMapper.java @@ -30,9 +30,10 @@ public interface ResourceGroupMapper extends BaseMapper { * 根据appId查询表t_resource_group数据 * * @param appId the appId + * @param groupCreatedBy the groupCreatedBy * @return the resourceGroup */ - ResourceGroup queryResourceGroupByAppId(Integer appId); + ResourceGroup queryResourceGroupByAppId(Integer appId, String groupCreatedBy); /** * 根据主键id查询表t_resource_group数据 diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceGroupServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceGroupServiceImpl.java index 77ef3cb9..79fe0cb0 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceGroupServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceGroupServiceImpl.java @@ -46,15 +46,15 @@ public List queryAllResourceGroupAndResource() { } /** - * 根据主键id查询表t_resource_group信息 + * 根据appId查询表t_resource_group信息 * * @param appId the appId * @return the resourceGroup */ @Override - @SystemServiceLog(description = "根据主键id查询表t_resource_group信息") + @SystemServiceLog(description = "根据主键appId查询表t_resource_group信息") public Result queryResourceGroupByAppId(Integer appId) { - ResourceGroup resourceGroup = baseMapper.queryResourceGroupByAppId(appId); + ResourceGroup resourceGroup = baseMapper.queryResourceGroupByAppId(appId, loginUserContext.getLoginUserId()); return Result.success(resourceGroup); } diff --git a/base/src/main/resources/mappers/ResourceGroupMapper.xml b/base/src/main/resources/mappers/ResourceGroupMapper.xml index 279960a5..8a8c1d04 100644 --- a/base/src/main/resources/mappers/ResourceGroupMapper.xml +++ b/base/src/main/resources/mappers/ResourceGroupMapper.xml @@ -104,7 +104,7 @@ - + @@ -141,7 +141,7 @@ r.id as resource_id, r.app_id as resource_app_id, r.platform_id as resource_platform_id, - r.name, + r.name as resource_name, r.resource_url, r.thumbnail_url, r.category, @@ -161,7 +161,7 @@ LEFT JOIN r_resource_group_resource rgr ON rgr.resource_group_id = rg.id LEFT JOIN - t_resource r ON r.id = rgr.resource_id + t_resource r ON r.id = rgr.resource_id AND r.id IS NOT NULL @@ -188,7 +188,7 @@ r.id as resource_id, r.app_id as resource_app_id, r.platform_id as resource_platform_id, - r.name, + r.name as resource_name, r.resource_url, r.thumbnail_url, r.category, @@ -208,7 +208,7 @@ LEFT JOIN r_resource_group_resource rgr ON rgr.resource_group_id = rg.id LEFT JOIN - t_resource r ON r.id = rgr.resource_id + t_resource r ON r.id = rgr.resource_id AND r.id IS NOT NULL @@ -219,6 +219,7 @@ + SELECT r.* FROM t_resource r - left JOIN r_resource_group_resource rrgr ON b.id = rrgr.resource_id - left JOIN t_resource_group rg ON rrgr.resource_group_id = rg.id + left JOIN r_resource_group_resource rgr ON b.id = rrgr.resource_id + left JOIN t_resource_group rg ON rgr.resource_group_id = rg.id WHERE rg.app_id = #{appId} GROUP BY r.id SELECT r.* FROM t_resource r - left JOIN r_resource_group_resource rgr ON b.id = rrgr.resource_id + left JOIN r_resource_group_resource rgr ON r.id = rgr.resource_id left JOIN t_resource_group rg ON rgr.resource_group_id = rg.id WHERE rg.app_id = #{appId} GROUP BY r.id @@ -211,7 +211,13 @@ name like concat('%',#{name},'%') - or description like concat('%',#{description},'%') + + + or description like concat('%',#{description},'%') + + + description like concat('%',#{description},'%') + @@ -237,24 +243,24 @@ INSERT INTO t_resource ( id - , app_id - , platform_id - , name - , resource_url - , thumbnail_url - , category - , description - , thumbnail_data - , resource_data - , public_status - , is_default - , created_by - , last_updated_by - , created_time - , last_updated_time - , tenant_id - , renter_id - , site_id) + , app_id + , platform_id + , name + , resource_url + , thumbnail_url + , category + , description + , thumbnail_data + , resource_data + , public_status + , is_default + , created_by + , last_updated_by + , created_time + , last_updated_time + , tenant_id + , renter_id + , site_id) VALUES ( #{id} , #{appId} , #{platformId} diff --git a/docker-deploy-data/mysql/init/create_resources_ddl_2025_0902.sql b/docker-deploy-data/mysql/init/create_resources_ddl_2025_0902.sql index b5c38e3a..4dda7424 100644 --- a/docker-deploy-data/mysql/init/create_resources_ddl_2025_0902.sql +++ b/docker-deploy-data/mysql/init/create_resources_ddl_2025_0902.sql @@ -53,5 +53,5 @@ CREATE TABLE `r_resource_group_resource` `resource_id` int NOT NULL COMMENT '资源id', `resource_group_id` int NOT NULL COMMENT '资源分组id', PRIMARY KEY (`id`) USING BTREE, - UNIQUE KEY `u_idx_block_group_block` (`resource_id`,`resource_group_id`) USING BTREE + UNIQUE KEY `u_idx_resource_group_resource` (`resource_id`,`resource_group_id`) USING BTREE ) engine = innodb comment = '资源及资源分组关系表'; \ No newline at end of file diff --git a/pom.xml b/pom.xml index dcd77d62..b9458b29 100644 --- a/pom.xml +++ b/pom.xml @@ -85,11 +85,6 @@ ${batik-version} - - org.apache.xmlgraphics - batik-transcoder - ${batik-version} - org.mariadb.jdbc mariadb-java-client From 3e2e61629d001f324645224e0e0961ec4ea03a70 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Tue, 9 Sep 2025 23:55:07 -0700 Subject: [PATCH 16/31] fix: modify resource download --- .../common/utils/ImageThumbnailGenerator.java | 20 +++++++++++++++++++ .../it/controller/ResourceController.java | 5 ++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java b/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java index 2ede8a54..4b653ef0 100644 --- a/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java +++ b/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java @@ -215,6 +215,26 @@ public static String extractCleanBase64(String input) { return trimmed; } + /** + * 从 Base64 数据中提取 MIME 类型 + * @param base64Data 完整的 Base64 数据(包含 data:image/png;base64, 前缀) + * @return 提取到的 MIME 类型,如 "image/png", "image/svg+xml" + */ + public static String extractContentType(String base64Data) { + if (base64Data == null || !base64Data.startsWith("data:")) { + throw new IllegalArgumentException("Invalid Base64 data format"); + } + + Pattern pattern = Pattern.compile("^data:([^;]+);"); + Matcher matcher = pattern.matcher(base64Data); + + if (matcher.find()) { + return matcher.group(1); + } + + throw new IllegalArgumentException("Cannot extract content type from Base64 data"); + } + /** * MIME类型转格式 */ diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index 6802edaa..8d37cd53 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -270,14 +270,13 @@ public void getResource(@RequestParam("data") String data, HttpServletResponse r String cleanBase64 = ImageThumbnailGenerator.extractCleanBase64(base64Data); byte[] imageBytes = Base64.getDecoder().decode(cleanBase64); - String detectedType = ImageThumbnailGenerator.detectFormatFromBase64(base64Data); - String fileExtension = detectedType.equals("jpeg") ? "jpg" : detectedType; + String detectedType = ImageThumbnailGenerator.extractContentType(base64Data); String fileName = useOriginal ? resource.getName() : "thumbnail_" + resource.getName(); // URL编码文件名 String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8) .replace("+", "%20"); - response.setContentType("image/" + detectedType); + response.setContentType(detectedType); // 只使用 filename* 格式,避免中文字符直接出现在header中 response.setHeader("Content-Disposition", From 603f65d39e740fcbafbc8c3466c0b23ad67b4dd4 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Sun, 14 Sep 2025 23:56:39 -0700 Subject: [PATCH 17/31] fix: modify block group --- .../it/service/material/impl/BlockServiceImpl.java | 5 ++++- base/src/main/resources/mappers/BlockGroupMapper.xml | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) 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 7a3867b0..24161939 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 @@ -605,7 +605,10 @@ public IPage findBlocksByConditionPagetion(Map request) { @Override public List getUsers(List blocksList) { Set userSet = new HashSet<>(); - + List users = new ArrayList<>(); + if(blocksList.isEmpty()) { + return users; + } // 提取 createdBy 列表中的唯一值 blocksList.forEach(item -> { if (item.getCreatedBy() != null && !userSet.contains(item.getCreatedBy())) { diff --git a/base/src/main/resources/mappers/BlockGroupMapper.xml b/base/src/main/resources/mappers/BlockGroupMapper.xml index a4c254ff..a10bc8ec 100644 --- a/base/src/main/resources/mappers/BlockGroupMapper.xml +++ b/base/src/main/resources/mappers/BlockGroupMapper.xml @@ -182,7 +182,7 @@ LEFT JOIN r_block_group_block rbg ON rbg.block_group_id = bg.id LEFT JOIN - t_block b ON b.id = rbg.block_id + t_block b ON b.id = rbg.block_id AND b.id IS NOT NULL AND b.created_by = #{blockCreatedBy} @@ -247,7 +247,7 @@ LEFT JOIN r_block_group_block rbg ON rbg.block_group_id = bg.id LEFT JOIN - t_block b ON b.id = rbg.block_id + t_block b ON b.id = rbg.block_id AND b.id IS NOT NULL AND b.created_by = #{blockCreatedBy} @@ -314,7 +314,7 @@ LEFT JOIN r_block_group_block rbg ON rbg.block_group_id = bg.id LEFT JOIN - t_block b ON b.id = rbg.block_id + t_block b ON b.id = rbg.block_id AND b.id IS NOT NULL AND b.created_by = #{blockCreatedBy} From f8d37c070903c2b0f452d123f4fd22813f81ab10 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Mon, 15 Sep 2025 23:57:37 -0700 Subject: [PATCH 18/31] fix: modify AI chat --- .../it/controller/AiChatController.java | 76 +++-- .../tinyengine/it/model/dto/ChatRequest.java | 3 +- .../com/tinyengine/it/model/dto/NodeDto.java | 27 ++ .../app/impl/v1/AiChatV1ServiceImpl.java | 308 ++++++++++++++---- .../it/service/app/v1/AiChatV1Service.java | 12 + .../it/controller/AiChatControllerTest.java | 22 +- pom.xml | 7 +- 7 files changed, 351 insertions(+), 104 deletions(-) create mode 100644 base/src/main/java/com/tinyengine/it/model/dto/NodeDto.java diff --git a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java index 69a066fb..b806f6f9 100644 --- a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java +++ b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java @@ -14,9 +14,8 @@ import com.tinyengine.it.common.base.Result; import com.tinyengine.it.common.log.SystemControllerLog; -import com.tinyengine.it.model.dto.AiParam; import com.tinyengine.it.model.dto.ChatRequest; -import com.tinyengine.it.service.app.AiChatService; +import com.tinyengine.it.model.dto.NodeDto; import com.tinyengine.it.service.app.v1.AiChatV1Service; import io.swagger.v3.oas.annotations.Operation; @@ -37,7 +36,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; -import java.util.Map; +import java.util.List; /** * The type Ai chat controller. @@ -49,12 +48,6 @@ @RequestMapping("/app-center/api") @Tag(name = "AIChat") public class AiChatController { - /** - * The Ai chat service. - */ - @Autowired - private AiChatService aiChatService; - /** * The Ai chat v1 service. */ @@ -64,18 +57,34 @@ public class AiChatController { /** * AI api * - * @param aiParam the AI param + * @param request the AI param * @return ai回答信息 result */ - @Operation(summary = "获取ai回答信息", description = "获取ai回答信息", parameters = { - @Parameter(name = "AiParam", description = "入参对象")}, responses = { + @Operation(summary = "获取ai回答信息", description = "获取ai回答信息", + parameters = { + @Parameter(name = "ChatRequest", description = "入参对象") + }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败")}) + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") + }) @SystemControllerLog(description = "AI api") @PostMapping("/ai/chat") - public Result> aiChat(@RequestBody AiParam aiParam) { - return aiChatService.getAnswerFromAi(aiParam); + public ResponseEntity aiChat(@RequestBody ChatRequest request) { + try { + Object response = aiChatV1Service.chatCompletion(request); + + if (request.isStream()) { + return ResponseEntity.ok() + .contentType(MediaType.TEXT_EVENT_STREAM) + .body((StreamingResponseBody) response); + } else { + return ResponseEntity.ok(response); + } + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(e.getMessage()); + } } /** @@ -84,11 +93,14 @@ public Result> aiChat(@RequestBody AiParam aiParam) { * @param request the AI param * @return ai回答信息 result */ - @Operation(summary = "获取ai回答信息", description = "获取ai回答信息", parameters = { - @Parameter(name = "ChatRequest", description = "入参对象")}, responses = { + @Operation(summary = "获取ai回答信息", description = "获取ai回答信息", + parameters = { + @Parameter(name = "ChatRequest", description = "入参对象") + }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", content = @Content(mediaType = "application/json", schema = @Schema())), - @ApiResponse(responseCode = "400", description = "请求失败")}) + @ApiResponse(responseCode = "400", description = "请求失败") + }) @SystemControllerLog(description = "AI api v1") @PostMapping("/chat/completions") public ResponseEntity chat(@RequestBody ChatRequest request) { @@ -97,14 +109,34 @@ public ResponseEntity chat(@RequestBody ChatRequest request) { if (request.isStream()) { return ResponseEntity.ok() - .contentType(MediaType.TEXT_EVENT_STREAM) - .body((StreamingResponseBody) response); + .contentType(MediaType.TEXT_EVENT_STREAM) + .body((StreamingResponseBody) response); } else { return ResponseEntity.ok(response); } } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) - .body(e.getMessage()); + .body(e.getMessage()); } } + + /** + * AI search api + * + * @param content the AI search param + * @return ai回答信息 result + */ + @Operation(summary = "搜索知识库", description = "搜索知识库", + parameters = { + @Parameter(name = "content", description = "入参对象") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema())), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "AI serarch api") + @PostMapping("/ai/search") + public Result> search(@RequestBody String content) throws Exception { + return aiChatV1Service.chatSearch(content); + } } diff --git a/base/src/main/java/com/tinyengine/it/model/dto/ChatRequest.java b/base/src/main/java/com/tinyengine/it/model/dto/ChatRequest.java index f237fc37..ef3c6d40 100644 --- a/base/src/main/java/com/tinyengine/it/model/dto/ChatRequest.java +++ b/base/src/main/java/com/tinyengine/it/model/dto/ChatRequest.java @@ -27,5 +27,6 @@ public class ChatRequest { private Object messages; private Object tools; private Double temperature = 0.7; - private boolean stream = false; // 流式开关 + private boolean stream = false; + private Integer maxTokens; } diff --git a/base/src/main/java/com/tinyengine/it/model/dto/NodeDto.java b/base/src/main/java/com/tinyengine/it/model/dto/NodeDto.java new file mode 100644 index 00000000..4e77fb7d --- /dev/null +++ b/base/src/main/java/com/tinyengine/it/model/dto/NodeDto.java @@ -0,0 +1,27 @@ +/** + * 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.model.dto; + +import lombok.Data; + +/** + * Node dto + * + * @since 2025-09-16 + */ +@Data +public class NodeDto { + private Double score; + private String docName; + private String content; +} diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java index 70a67c40..7b369d32 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java @@ -12,29 +12,37 @@ package com.tinyengine.it.service.app.impl.v1; +import com.aliyun.bailian20231229.Client; +import com.aliyun.tea.TeaException; +import com.aliyun.teaopenapi.models.Config; +import com.aliyun.teaopenapi.models.OpenApiRequest; +import com.aliyun.teaopenapi.models.Params; +import com.aliyun.teautil.models.RuntimeOptions; import com.fasterxml.jackson.databind.JsonNode; -import com.tinyengine.it.common.exception.ExceptionEnum; -import com.tinyengine.it.common.exception.ServiceException; +import com.tinyengine.it.common.base.Result; import com.tinyengine.it.common.log.SystemServiceLog; import com.tinyengine.it.common.utils.JsonUtils; import com.tinyengine.it.config.OpenAIConfig; import com.tinyengine.it.model.dto.ChatRequest; +import com.tinyengine.it.model.dto.NodeDto; import com.tinyengine.it.service.app.v1.AiChatV1Service; -import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import java.io.IOException; -import java.io.OutputStream; +import java.io.InputStream; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.time.Duration; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.stream.Stream; /** * The type AiChat v1 service. @@ -42,12 +50,17 @@ * @since 2025-08-06 */ @Service -@Slf4j public class AiChatV1ServiceImpl implements AiChatV1Service { + private static final String ACCESS_KEY_ID = System.getenv("ACCESS_KEY_ID"); + private static final String ACCESS_KEY_SECRET = System.getenv("ACCESS_KEY_SECRET"); + private static final String ENDPOINT = "bailian.cn-beijing.aliyuncs.com"; + private static final String INDEX_ID = System.getenv("INDEX_ID"); + private static final String WORK_SPACE_ID = System.getenv("WORK_SPACE_ID"); private final OpenAIConfig config = new OpenAIConfig(); + private static final Logger log = LoggerFactory.getLogger(AiChatV1ServiceImpl.class); private HttpClient httpClient = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(config.getTimeoutSeconds())) - .build(); + .connectTimeout(Duration.ofSeconds(config.getTimeoutSeconds())) + .build(); /** * chatCompletion. @@ -60,13 +73,16 @@ public class AiChatV1ServiceImpl implements AiChatV1Service { public Object chatCompletion(ChatRequest request) throws Exception { String requestBody = buildRequestBody(request); String apiKey = request.getApiKey() != null ? request.getApiKey() : config.getApiKey(); - String baseUrl = request.getBaseUrl() != null ? request.getBaseUrl() : config.getBaseUrl(); + String baseUrl = request.getBaseUrl(); + + // 规范化URL处理 + String normalizedUrl = normalizeApiUrl(baseUrl); + HttpRequest.Builder requestBuilder = HttpRequest.newBuilder() - .uri(URI.create(baseUrl)) + .uri(URI.create(normalizedUrl)) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + apiKey) .POST(HttpRequest.BodyPublishers.ofString(requestBody)); - if (request.isStream()) { requestBuilder.header("Accept", "text/event-stream"); return processStreamResponse(requestBuilder); @@ -75,14 +91,204 @@ public Object chatCompletion(ChatRequest request) throws Exception { } } + /** + * 规范化API URL,兼容不同厂商 + */ + private String normalizeApiUrl(String baseUrl) { + if (baseUrl == null || baseUrl.trim().isEmpty()) { + baseUrl = config.getBaseUrl(); + } + baseUrl = baseUrl.trim(); + + if (baseUrl.contains("/chat/completions") || baseUrl.contains("/v1/chat/completions")) { + return ensureUrlProtocol(baseUrl); + } + + if (baseUrl.contains("v1")) { + return ensureUrlProtocol(baseUrl) + "/chat/completions"; + } else { + return ensureUrlProtocol(baseUrl) + "/v1/chat/completions"; + } + } + + /** + * 确保URL有正确的协议前缀 + */ + private String ensureUrlProtocol(String url) { + if (url.startsWith("http://") || url.startsWith("https://")) { + return url; + } + // 默认使用https + return "https://" + url; + } + + /** + * 创建客户端 + */ + private Client createClient() throws Exception { + return new Client(new Config() + .setAccessKeyId(ACCESS_KEY_ID) + .setAccessKeySecret(ACCESS_KEY_SECRET) + .setEndpoint(ENDPOINT) + .setEndpointType("access_key")); + } + + /** + * 创建API信息 + */ + private Params createApiInfo(String WorkspaceId) throws Exception { + Params params = new Params() + // 接口名称 + .setAction("Retrieve") + // 接口版本 + .setVersion("2023-12-29") + // 接口协议 + .setProtocol("HTTPS") + // 接口 HTTP 方法 + .setMethod("POST") + .setAuthType("AK") + .setStyle("ROA") + // 接口 PATH + .setPathname("/" + com.aliyun.openapiutil.Client.getEncodeParam(WorkspaceId) + "/index/retrieve") + // 接口请求体内容格式 + .setReqBodyType("json") + // 接口响应体内容格式 + .setBodyType("json"); + return params; + } + + /** + * 安全类型转换工具方法 + */ + private T safeCast(Object obj, Class clazz, T defaultValue) { + if (obj == null) { + return defaultValue; + } + try { + return clazz.cast(obj); + } catch (ClassCastException e) { + log.warn("类型转换失败: {} 无法转换为 {}", obj.getClass().getName(), clazz.getName()); + return defaultValue; + } + } + + private String safeCastToString(Object obj) { + return safeCast(obj, String.class, ""); + } + + private Double safeCastToDouble(Object obj) { + return safeCast(obj, Double.class, 0.0); + } + + private Long safeCastToLong(Object obj) { + return safeCast(obj, Long.class, 0L); + } + + /** + * chatSearch. + * + * @param content the content + * @return String the String + */ + public Result> chatSearch(String content) { + try { + Client client = createClient(); + Params params = createApiInfo(WORK_SPACE_ID); + + Map queries = new HashMap<>(); + queries.put("IndexId", INDEX_ID); + queries.put("Query", content); + queries.put("EnableRewrite", "true"); + + RuntimeOptions runtime = new RuntimeOptions(); + OpenApiRequest request = new OpenApiRequest() + .setQuery(com.aliyun.openapiutil.Client.query(queries)); + + Map> response = (Map>) client.callApi(params, request, runtime); + Map body = response.get("body"); + + if (body == null) { + return Result.failed("响应体为空"); + } + + Long status = safeCastToLong(body.get("Status")); + if (status != 200) { + String message = safeCastToString(body.get("Message")); + log.error("搜索失败: status={}, message={}", status, message); + return Result.failed("搜索失败: " + message); + } + + Map data = safeCast(body.get("Data"), Map.class, new HashMap<>()); + if (data == null || data.isEmpty()) { + return Result.success(new ArrayList<>()); + } + + List> nodes = safeCast(data.get("Nodes"), List.class, new ArrayList<>()); + if (nodes.isEmpty()) { + return Result.success(new ArrayList<>()); + } + + List nodeDtos = convertToNodeDtos(nodes); + return Result.success(nodeDtos); + + } catch (TeaException e) { + log.error("阿里云Tea异常: {}", e.getMessage(), e); + return Result.failed("阿里云服务异常: " + e.getMessage()); + } catch (Exception e) { + log.error("搜索异常: {}", e.getMessage(), e); + return Result.failed("系统异常: " + e.getMessage()); + } + } + + /** + * 转换节点数据 + */ + private List convertToNodeDtos(List> nodes) { + List nodeDtos = new ArrayList<>(); + + for (Map node : nodes) { + try { + NodeDto nodeDto = new NodeDto(); + + // 安全获取文本内容 + nodeDto.setContent(safeCastToString(node.get("Text"))); + + // 安全获取分数 + Object scoreObj = node.get("Score"); + if (scoreObj instanceof Number) { + nodeDto.setScore(((Number) scoreObj).doubleValue()); + } else { + nodeDto.setScore(safeCastToDouble(scoreObj)); + } + + // 安全获取元数据 + Map metadata = safeCast(node.get("Metadata"), Map.class, new HashMap<>()); + if (metadata != null) { + nodeDto.setDocName(safeCastToString(metadata.get("doc_name"))); + } + + nodeDtos.add(nodeDto); + + } catch (Exception e) { + log.warn("节点数据转换失败: {}", e.getMessage()); + } + } + + return nodeDtos; + } + private String buildRequestBody(ChatRequest request) { Map body = new HashMap<>(); body.put("model", request.getModel() != null ? request.getModel() : config.getDefaultModel()); body.put("messages", request.getMessages()); - body.put("temperature", request.getTemperature()); body.put("stream", request.isStream()); body.put("tools", request.getTools()); - + if (request.getMaxTokens() != null) { + body.put("max_tokens", request.getMaxTokens()); + } + if (request.getTemperature() != null) { + body.put("temperature", request.getTemperature()); + } return JsonUtils.encode(body); } @@ -96,55 +302,39 @@ private JsonNode processStandardResponse(HttpRequest.Builder requestBuilder) private StreamingResponseBody processStreamResponse(HttpRequest.Builder requestBuilder) { return outputStream -> { try { - HttpResponse> response = httpClient.send( - requestBuilder.build(), HttpResponse.BodyHandlers.ofLines()); - processLines(response.body(), outputStream); + HttpClient client = HttpClient.newHttpClient(); + HttpResponse response = client.send( + requestBuilder.build(), + HttpResponse.BodyHandlers.ofInputStream() + ); + if (response.statusCode() != 200) { + String errorBody = new String(response.body().readAllBytes(), StandardCharsets.UTF_8); + throw new IOException("API请求失败: " + response.statusCode() + " - " + errorBody); + } + try (InputStream inputStream = response.body()) { + byte[] buffer = new byte[8192]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + outputStream.flush(); + } + } } catch (Exception e) { - handleError(e, outputStream); - } finally { - closeStream(outputStream); - } - }; - } + // 简单的错误处理:如果是客户端断开连接,忽略错误 + String errorMsg = e.getMessage(); + if (errorMsg != null && + (errorMsg.contains("Broken pipe") || errorMsg.contains("Connection reset"))) { + return; + } - private void processLines(Stream lines, OutputStream outputStream) { - try (Stream filteredLines = lines.filter(line -> !line.isEmpty())) { - filteredLines.forEach(line -> writeLine(line, outputStream)); - } - } + try { + String errorEvent = "data: {\"error\": \"" + e.getMessage() + "\"}\n\n"; + outputStream.write(errorEvent.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + } catch (IOException ignored) { - private void writeLine(String line, OutputStream outputStream) { - try { - if (!line.startsWith("data:")) { - line = "data: " + line; - } - if (!line.endsWith("\n\n")) { - line = line + "\n\n"; + } } - outputStream.write(line.getBytes(StandardCharsets.UTF_8)); - outputStream.flush(); - } catch (IOException e) { - throw new ServiceException(ExceptionEnum.CM326.getResultCode(), - ExceptionEnum.CM326.getResultMsg()); - } - } - - private void handleError(Exception e, OutputStream outputStream) { - try { - String errorEvent = "data: " + JsonUtils.encode(Map.of("error", e.getMessage())) + "\n\n"; - outputStream.write(errorEvent.getBytes(StandardCharsets.UTF_8)); - outputStream.flush(); - } catch (IOException ioException) { - throw new ServiceException(ExceptionEnum.CM326.getResultCode(), ExceptionEnum.CM326.getResultMsg()); - } - } - - private void closeStream(OutputStream outputStream) { - try { - outputStream.close(); - } catch (IOException e) { - // 忽略关闭异常 - } + }; } - } diff --git a/base/src/main/java/com/tinyengine/it/service/app/v1/AiChatV1Service.java b/base/src/main/java/com/tinyengine/it/service/app/v1/AiChatV1Service.java index d2bda179..4a05d05b 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/v1/AiChatV1Service.java +++ b/base/src/main/java/com/tinyengine/it/service/app/v1/AiChatV1Service.java @@ -12,7 +12,11 @@ package com.tinyengine.it.service.app.v1; +import com.tinyengine.it.common.base.Result; import com.tinyengine.it.model.dto.ChatRequest; +import com.tinyengine.it.model.dto.NodeDto; + +import java.util.List; /** * The interface AIChat v 1 service. @@ -27,4 +31,12 @@ public interface AiChatV1Service { * @return Object the Object */ public Object chatCompletion(ChatRequest request) throws Exception; + + /** + * chatSearch. + * + * @param content the content + * @return String the String + */ + public Result> chatSearch(String content) throws Exception; } diff --git a/base/src/test/java/com/tinyengine/it/controller/AiChatControllerTest.java b/base/src/test/java/com/tinyengine/it/controller/AiChatControllerTest.java index 165aba00..fde49572 100644 --- a/base/src/test/java/com/tinyengine/it/controller/AiChatControllerTest.java +++ b/base/src/test/java/com/tinyengine/it/controller/AiChatControllerTest.java @@ -16,6 +16,7 @@ import com.tinyengine.it.common.base.Result; import com.tinyengine.it.model.dto.AiParam; +import com.tinyengine.it.model.dto.ChatRequest; import com.tinyengine.it.service.app.AiChatService; import org.junit.jupiter.api.Assertions; @@ -34,30 +35,9 @@ * @since 2024-10-29 */ class AiChatControllerTest { - @Mock - private AiChatService aiChatService; - @InjectMocks - private AiChatController aiChatController; - @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); } - - @Test - void testAiChat() { - AiParam aiParam = new AiParam(); - HashMap mockData = new HashMap() { - { - put("key", "parameter"); - } - }; - Result> mapResult = Result.success(mockData); - - when(aiChatService.getAnswerFromAi(aiParam)).thenReturn(mapResult); - - Result> result = aiChatController.aiChat(aiParam); - Assertions.assertEquals("parameter", result.getData().get("key")); - } } diff --git a/pom.xml b/pom.xml index b9458b29..c70e77d2 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,12 @@ fastjson ${fastjson.version} + + + com.aliyun + bailian20231229 + 2.4.1 + com.baomidou @@ -72,7 +78,6 @@ mybatis-plus-generator ${mybatis-plus.version} - cn.hutool hutool-all From 6c4f3eaf9ef6ee8a637d2d006dea572f1f9c246f Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Tue, 16 Sep 2025 00:10:02 -0700 Subject: [PATCH 19/31] fix: modify AI chat --- Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 562e7fee..177a9e5f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,11 @@ FROM eclipse-temurin:17-jdk-jammy WORKDIR /app COPY --from=build /app/app/target/tiny-engine-app-*.jar /app/tiny-engine-app.jar COPY --from=build /app/base/target/tiny-engine-base-*.jar /app/tiny-engine-base.jar - +# 设置环境变量 +ENV ACCESS_KEY_ID=" " +ENV ACCESS_KEY_SECRET = " " +ENV INDEX_ID = " " +ENV WORK_SPACE_ID = "" ENTRYPOINT ["java", "-jar", "tiny-engine-app.jar", "--spring.profiles.active=alpha"] EXPOSE 9090 From 35a0b4561558d5a81a0ec208121af00832ea5f9b Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Tue, 16 Sep 2025 00:18:32 -0700 Subject: [PATCH 20/31] fix: modify AI chat --- .../app/impl/v1/AiChatV1ServiceImpl.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java index 7b369d32..c5cf93d2 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java @@ -137,7 +137,7 @@ private Client createClient() throws Exception { * 创建API信息 */ private Params createApiInfo(String WorkspaceId) throws Exception { - Params params = new Params() + return new Params() // 接口名称 .setAction("Retrieve") // 接口版本 @@ -154,7 +154,6 @@ private Params createApiInfo(String WorkspaceId) throws Exception { .setReqBodyType("json") // 接口响应体内容格式 .setBodyType("json"); - return params; } /** @@ -190,7 +189,7 @@ private Long safeCastToLong(Object obj) { * @param content the content * @return String the String */ - public Result> chatSearch(String content) { + public Result chatSearch(String content) { try { Client client = createClient(); Params params = createApiInfo(WORK_SPACE_ID); @@ -204,31 +203,31 @@ public Result> chatSearch(String content) { OpenApiRequest request = new OpenApiRequest() .setQuery(com.aliyun.openapiutil.Client.query(queries)); - Map> response = (Map>) client.callApi(params, request, runtime); - Map body = response.get("body"); + Map response = client.callApi(params, request, runtime); + Map body = (Map) response.get("body"); if (body == null) { return Result.failed("响应体为空"); } - Long status = safeCastToLong(body.get("Status")); - if (status != 200) { + long status = safeCastToLong(body.get("Status")); + if (status != 200L) { String message = safeCastToString(body.get("Message")); log.error("搜索失败: status={}, message={}", status, message); return Result.failed("搜索失败: " + message); } - Map data = safeCast(body.get("Data"), Map.class, new HashMap<>()); + Map data = safeCast(body.get("Data"), Map.class, new HashMap<>()); if (data == null || data.isEmpty()) { return Result.success(new ArrayList<>()); } - List> nodes = safeCast(data.get("Nodes"), List.class, new ArrayList<>()); + List nodes = safeCast(data.get("Nodes"), List.class, new ArrayList<>()); if (nodes.isEmpty()) { return Result.success(new ArrayList<>()); } - List nodeDtos = convertToNodeDtos(nodes); + List nodeDtos = convertToNodeDtos(nodes); return Result.success(nodeDtos); } catch (TeaException e) { @@ -262,7 +261,7 @@ private List convertToNodeDtos(List> nodes) { } // 安全获取元数据 - Map metadata = safeCast(node.get("Metadata"), Map.class, new HashMap<>()); + Map metadata = safeCast(node.get("Metadata"), Map.class, new HashMap<>()); if (metadata != null) { nodeDto.setDocName(safeCastToString(metadata.get("doc_name"))); } From 4d7848bdc7c7b3271f231369e65cc8c8d98c95ae Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Tue, 16 Sep 2025 02:54:19 -0700 Subject: [PATCH 21/31] fix: modify AI chat --- .../it/controller/AiChatController.java | 8 ++++++- .../it/controller/ResourceController.java | 21 +++++++++++++++++ .../app/impl/v1/AiChatV1ServiceImpl.java | 6 ----- .../it/service/material/ResourceService.java | 8 +++++++ .../material/impl/ResourceServiceImpl.java | 23 +++++++++++++++---- 5 files changed, 54 insertions(+), 12 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java index b806f6f9..0ebf4934 100644 --- a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java +++ b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java @@ -32,6 +32,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; @@ -103,7 +104,12 @@ public ResponseEntity aiChat(@RequestBody ChatRequest request) { }) @SystemControllerLog(description = "AI api v1") @PostMapping("/chat/completions") - public ResponseEntity chat(@RequestBody ChatRequest request) { + public ResponseEntity chat(@RequestBody ChatRequest request, + @RequestHeader("Authorization") String authorization) { + if (authorization != null && authorization.startsWith("Bearer ")) { + String token = authorization.replace("Bearer ", ""); + request.setApiKey(token); + } try { Object response = aiChatV1Service.chatCompletion(request); diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index 8d37cd53..60419240 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -164,6 +164,27 @@ public Result createResource(@Valid @RequestBody Resource resource) th return Result.success(result); } + /** + * 上传图片 + * + * @param resource the resource + * @return Resource信息 result + */ + @Operation(summary = "创建resource", description = "创建resource", + parameters = { + @Parameter(name = "resource", description = "Resource入参对象") + }, responses = { + @ApiResponse(responseCode = "200", description = "返回信息", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + @ApiResponse(responseCode = "400", description = "请求失败") + }) + @SystemControllerLog(description = "创建resource") + @PostMapping("/resource/uoload") + public Result resourceUoload(@Valid @RequestBody Resource resource) throws Exception { + Resource result = resourceService.resourceUpload(resource); + return Result.success(result); + } + /** * 批量创建Resource * diff --git a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java index c5cf93d2..e0cc3e39 100644 --- a/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/app/impl/v1/AiChatV1ServiceImpl.java @@ -319,12 +319,6 @@ private StreamingResponseBody processStreamResponse(HttpRequest.Builder requestB } } } catch (Exception e) { - // 简单的错误处理:如果是客户端断开连接,忽略错误 - String errorMsg = e.getMessage(); - if (errorMsg != null && - (errorMsg.contains("Broken pipe") || errorMsg.contains("Connection reset"))) { - return; - } try { String errorEvent = "data: {\"error\": \"" + e.getMessage() + "\"}\n\n"; diff --git a/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java b/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java index 9da16452..5857e856 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java +++ b/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java @@ -84,6 +84,14 @@ public interface ResourceService extends IService { */ Resource createResource(Resource resource) throws Exception; + /** + * 图片上传 + * + * @param resource the resource + * @return the integer + */ + Resource resourceUpload(Resource resource) throws Exception; + /** * 批量新增表t_resource数据 * diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java index b79609d5..3c0d4f75 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java @@ -155,6 +155,23 @@ public Result updateResourceById(Resource resource) { @SystemServiceLog(description = "新增表t_resource数据") public Resource createResource(Resource resource) throws Exception { + Resource res = this.resourceUpload(resource); + ResourceGroupResource resourceGroupResource = new ResourceGroupResource(); + resourceGroupResource.setResourceId(res.getId()); + resourceGroupResource.setResourceGroupId(resource.getResourceGroupId()); + resourceGroupResourceMapper.createResourceGroupResource(resourceGroupResource); + + return res; + } + + /** + * 图片上传 + * + * @param resource the resource + * @return the integer + */ + @Override + public Resource resourceUpload(Resource resource) throws Exception { ResourceRequestDto resourceParam = new ResourceRequestDto(); resourceParam.setName(resource.getName()); resourceParam.setCategory(resource.getCategory()); @@ -179,7 +196,7 @@ public Resource createResource(Resource resource) throws Exception { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", resource.getName()); queryWrapper.eq("category", resource.getCategory()); - // 接入组合系统需添加租户id查询 + // 接入租户系统需添加租户id查询 Resource resourceResult = this.baseMapper.selectOne(queryWrapper); if (resourceResult != null) { throw new ServiceException(ExceptionEnum.CM003.getResultCode(), ExceptionEnum.CM003.getResultMsg()); @@ -188,10 +205,6 @@ public Resource createResource(Resource resource) throws Exception { if (createResult != 1) { throw new ServiceException(ExceptionEnum.CM002.getResultCode(), ExceptionEnum.CM002.getResultMsg()); } - ResourceGroupResource resourceGroupResource = new ResourceGroupResource(); - resourceGroupResource.setResourceId(resource.getId()); - resourceGroupResource.setResourceGroupId(resource.getResourceGroupId()); - resourceGroupResourceMapper.createResourceGroupResource(resourceGroupResource); return this.baseMapper.queryResourceById(resource.getId()); } From ad807d9a68b28782478c54a2b727cf1a0ceb4562 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Tue, 16 Sep 2025 02:56:49 -0700 Subject: [PATCH 22/31] fix: modify AI chat --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 177a9e5f..c7f57dcf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,7 @@ ENV ACCESS_KEY_ID=" " ENV ACCESS_KEY_SECRET = " " ENV INDEX_ID = " " ENV WORK_SPACE_ID = "" +ENV TINY_ENGINE_URL=" " ENTRYPOINT ["java", "-jar", "tiny-engine-app.jar", "--spring.profiles.active=alpha"] EXPOSE 9090 From ca4ff1ba9b2a728b22f58dc291156bf2fa170393 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Tue, 16 Sep 2025 03:16:19 -0700 Subject: [PATCH 23/31] fix: modify AI chat --- .../java/com/tinyengine/it/controller/ResourceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index 60419240..5b8433d8 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -170,7 +170,7 @@ public Result createResource(@Valid @RequestBody Resource resource) th * @param resource the resource * @return Resource信息 result */ - @Operation(summary = "创建resource", description = "创建resource", + @Operation(summary = "上传图片", description = "上传图片", parameters = { @Parameter(name = "resource", description = "Resource入参对象") }, responses = { From bff65fe6a30758d6c6b58ea1c84714d3b4336fc3 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Tue, 16 Sep 2025 03:25:54 -0700 Subject: [PATCH 24/31] fix: modify AI chat --- Dockerfile | 6 ++---- .../com/tinyengine/it/controller/ResourceController.java | 2 +- .../it/service/material/impl/ResourceServiceImpl.java | 1 + 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index ef16e3e1..367fcd86 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,10 +19,8 @@ ENV ACCESS_KEY_ID=" " ENV ACCESS_KEY_SECRET = " " ENV INDEX_ID = " " ENV WORK_SPACE_ID = "" -<<<<<<< HEAD -ENV TINY_ENGINE_URL=" " -======= ->>>>>>> 380d9549bd9d2f481bf65f78ebb4c77a1d82c7b4 +# 替换为自己的域名接口路径 +ENV TINY_ENGINE_URL="https://agent.opentiny.design/material-center/api/resource/download" ENTRYPOINT ["java", "-jar", "tiny-engine-app.jar", "--spring.profiles.active=alpha"] EXPOSE 9090 diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index 5b8433d8..d445f35c 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -178,7 +178,7 @@ public Result createResource(@Valid @RequestBody Resource resource) th content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) - @SystemControllerLog(description = "创建resource") + @SystemControllerLog(description = "上传图片") @PostMapping("/resource/uoload") public Result resourceUoload(@Valid @RequestBody Resource resource) throws Exception { Resource result = resourceService.resourceUpload(resource); diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java index 3c0d4f75..258cc04d 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java @@ -171,6 +171,7 @@ public Resource createResource(Resource resource) throws Exception { * @return the integer */ @Override + @SystemServiceLog(description = "图片上传") public Resource resourceUpload(Resource resource) throws Exception { ResourceRequestDto resourceParam = new ResourceRequestDto(); resourceParam.setName(resource.getName()); From 4eaee7aa474e26d08e57a541e1b6f60ad1b0b384 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Tue, 16 Sep 2025 20:24:32 -0700 Subject: [PATCH 25/31] fix: modify resource upload --- .../common/utils/ImageThumbnailGenerator.java | 25 +++++++++ .../it/controller/AiChatController.java | 18 +++---- .../it/controller/ResourceController.java | 53 +++++++++++++------ 3 files changed, 71 insertions(+), 25 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java b/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java index 4b653ef0..3d8cbf16 100644 --- a/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java +++ b/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java @@ -19,6 +19,7 @@ import org.apache.batik.transcoder.image.JPEGTranscoder; import org.apache.batik.transcoder.image.PNGTranscoder; import org.apache.batik.util.XMLResourceDescriptor; +import org.springframework.web.multipart.MultipartFile; import org.w3c.dom.Document; import javax.imageio.ImageIO; @@ -26,6 +27,7 @@ import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -235,6 +237,29 @@ public static String extractContentType(String base64Data) { throw new IllegalArgumentException("Cannot extract content type from Base64 data"); } + /** + * 检查文件是否为图片类型 + */ + public static boolean validateByImageIO(MultipartFile file) { + try { + BufferedImage image = ImageIO.read(new ByteArrayInputStream(file.getBytes())); + return image != null; // 如果是图片,返回true + } catch (IOException e) { + return false; + } + } + + /** + * 将MultipartFile转换为Base64字符串 + */ + public static String convertToBase64(MultipartFile file) throws IOException { + String mimeType = file.getContentType(); + byte[] fileBytes = file.getBytes(); + String base64 = Base64.getEncoder().encodeToString(fileBytes); + + return "data:" + mimeType + ";base64," + base64; + } + /** * MIME类型转格式 */ diff --git a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java index 0ebf4934..8e5b7a42 100644 --- a/base/src/main/java/com/tinyengine/it/controller/AiChatController.java +++ b/base/src/main/java/com/tinyengine/it/controller/AiChatController.java @@ -66,10 +66,10 @@ public class AiChatController { @Parameter(name = "ChatRequest", description = "入参对象") }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), + content = @Content(mediaType = "application/json", schema = @Schema())), @ApiResponse(responseCode = "400", description = "请求失败") }) - @SystemControllerLog(description = "AI api") + @SystemControllerLog(description = "AI chat") @PostMapping("/ai/chat") public ResponseEntity aiChat(@RequestBody ChatRequest request) { try { @@ -99,12 +99,12 @@ public ResponseEntity aiChat(@RequestBody ChatRequest request) { @Parameter(name = "ChatRequest", description = "入参对象") }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), + content = @Content(mediaType = "application/json", schema = @Schema())), @ApiResponse(responseCode = "400", description = "请求失败") }) - @SystemControllerLog(description = "AI api v1") + @SystemControllerLog(description = "AI completions") @PostMapping("/chat/completions") - public ResponseEntity chat(@RequestBody ChatRequest request, + public ResponseEntity completions(@RequestBody ChatRequest request, @RequestHeader("Authorization") String authorization) { if (authorization != null && authorization.startsWith("Bearer ")) { String token = authorization.replace("Bearer ", ""); @@ -133,11 +133,11 @@ public ResponseEntity chat(@RequestBody ChatRequest request, * @return ai回答信息 result */ @Operation(summary = "搜索知识库", description = "搜索知识库", - parameters = { - @Parameter(name = "content", description = "入参对象") - }, responses = { + parameters = { + @Parameter(name = "content", description = "入参对象") + }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema())), + content = @Content(mediaType = "application/json", schema = @Schema())), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "AI serarch api") diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index d445f35c..91e91598 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -13,6 +13,8 @@ package com.tinyengine.it.controller; import com.tinyengine.it.common.base.Result; +import com.tinyengine.it.common.context.LoginUserContext; +import com.tinyengine.it.common.exception.ExceptionEnum; import com.tinyengine.it.common.log.SystemControllerLog; import com.tinyengine.it.common.utils.ImageThumbnailGenerator; import com.tinyengine.it.common.utils.Utils; @@ -38,6 +40,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; import java.io.OutputStream; import java.net.URLEncoder; @@ -61,6 +64,9 @@ public class ResourceController { @Autowired private ResourceService resourceService; + @Autowired + private LoginUserContext loginUserContext; + /** * 查询表Resource信息 * @@ -69,7 +75,7 @@ public class ResourceController { @Operation(summary = "查询表Resource信息", description = "查询表Resource信息", responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "查询表Resource信息") @@ -90,7 +96,7 @@ public Result> getAllResource() { @Parameter(name = "id", description = "Resource主键id") }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "根据id查询表Resource信息") @@ -110,7 +116,7 @@ public Result getResourceById(@PathVariable Integer id) { @Parameter(name = "resourceGroupId", description = "ResourceGroup主键id") }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "根据分组id和创建人查询表t_resource信息") @@ -132,7 +138,7 @@ public Result> getResourceByResourceGroupId(@PathVariable Integer @Parameter(name = "des", description = "描述") }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "模糊查询表Resource信息列表") @@ -154,7 +160,7 @@ public Result> getResourceById(@RequestParam String name, @Reques @Parameter(name = "resource", description = "Resource入参对象") }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "创建resource") @@ -167,20 +173,35 @@ public Result createResource(@Valid @RequestBody Resource resource) th /** * 上传图片 * - * @param resource the resource + * @param file the file * @return Resource信息 result */ @Operation(summary = "上传图片", description = "上传图片", - parameters = { - @Parameter(name = "resource", description = "Resource入参对象") - }, responses = { + parameters = { + @Parameter(name = "file", description = "图片") + }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "上传图片") - @PostMapping("/resource/uoload") - public Result resourceUoload(@Valid @RequestBody Resource resource) throws Exception { + @PostMapping("/resource/upload") + public Result resourceUpload(@RequestParam MultipartFile file) throws Exception { + // 获取文件的原始名称 + String fileName = file.getOriginalFilename(); + if (file.isEmpty()) { + return Result.failed(ExceptionEnum.CM009); + } + if(!ImageThumbnailGenerator.validateByImageIO(file)){ + return Result.failed(ExceptionEnum.CM325); + } + // 将文件转为 Base64 + String base64 = ImageThumbnailGenerator.convertToBase64(file); + Resource resource = new Resource(); + resource.setName(fileName); + resource.setResourceData(base64); + resource.setAppId(loginUserContext.getAppId()); + resource.setCategory("image"); Resource result = resourceService.resourceUpload(resource); return Result.success(result); } @@ -196,7 +217,7 @@ public Result resourceUoload(@Valid @RequestBody Resource resource) th @Parameter(name = "resources", description = "Resource入参对象") }, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "批量创建Resource") @@ -218,7 +239,7 @@ public Result> createResource(@Valid @RequestBody List @Parameter(name = "id", description = "id"), @Parameter(name = "Resource", description = "入参对象")}, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "修改单个Resource信息") @@ -258,7 +279,7 @@ public Result deleteResource(@PathVariable Integer id) { parameters = { @Parameter(name = "id", description = "id")}, responses = { @ApiResponse(responseCode = "200", description = "返回信息", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "获取resource信息详情") @@ -277,7 +298,7 @@ public Result detail(@PathVariable Integer id) { parameters = { @Parameter(name = "data", description = "base64编码数据")}, responses = { @ApiResponse(responseCode = "200", description = "图片流数据", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "获取资源") From d211902c2a3720257ba545be56049a36cdb08948 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Tue, 16 Sep 2025 20:44:12 -0700 Subject: [PATCH 26/31] fix: modify resource upload --- .../common/utils/ImageThumbnailGenerator.java | 50 +++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java b/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java index 3d8cbf16..5678ce12 100644 --- a/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java +++ b/base/src/main/java/com/tinyengine/it/common/utils/ImageThumbnailGenerator.java @@ -28,6 +28,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -238,25 +239,66 @@ public static String extractContentType(String base64Data) { } /** - * 检查文件是否为图片类型 + * 图片验证 */ public static boolean validateByImageIO(MultipartFile file) { try { + String filename = file.getOriginalFilename(); + if (filename == null) { + return false; + } + // 获取文件扩展名 + String extension = getFileExtension(filename).toLowerCase(); + + if ("svg".equals(extension)) { + return isSvgFile(file); + } + BufferedImage image = ImageIO.read(new ByteArrayInputStream(file.getBytes())); - return image != null; // 如果是图片,返回true + return image != null; } catch (IOException e) { return false; } } /** - * 将MultipartFile转换为Base64字符串 + * SVG文件验证 + */ + private static boolean isSvgFile(MultipartFile file) { + try { + byte[] bytes = file.getBytes(); + String content = new String(bytes, StandardCharsets.UTF_8); + + // 简单检查:包含 Date: Tue, 16 Sep 2025 20:50:58 -0700 Subject: [PATCH 27/31] fix: modify resource upload --- .../tinyengine/it/controller/ResourceController.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index 91e91598..05d13f76 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -30,6 +30,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -188,13 +189,14 @@ public Result createResource(@Valid @RequestBody Resource resource) th @PostMapping("/resource/upload") public Result resourceUpload(@RequestParam MultipartFile file) throws Exception { // 获取文件的原始名称 - String fileName = file.getOriginalFilename(); - if (file.isEmpty()) { - return Result.failed(ExceptionEnum.CM009); - } + String fileName = StringUtils.cleanPath(java.util.Optional.ofNullable(file.getOriginalFilename()).orElse("image")); + if(!ImageThumbnailGenerator.validateByImageIO(file)){ return Result.failed(ExceptionEnum.CM325); } + if(fileName.contains("..")) { + return Result.failed(ExceptionEnum.CM325); + } // 将文件转为 Base64 String base64 = ImageThumbnailGenerator.convertToBase64(file); Resource resource = new Resource(); From a93100a739a3d02d4596e45357d6e48c6691dabf Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Wed, 17 Sep 2025 02:27:32 -0700 Subject: [PATCH 28/31] fix: modify resource upload --- .../it/controller/ResourceController.java | 27 ++++++++----- .../it/model/dto/ResourceRequestDto.java | 1 - .../it/service/material/ResourceService.java | 7 ++-- .../material/impl/ResourceServiceImpl.java | 40 ++++++++----------- 4 files changed, 35 insertions(+), 40 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index 05d13f76..e149dcd8 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -15,10 +15,9 @@ import com.tinyengine.it.common.base.Result; import com.tinyengine.it.common.context.LoginUserContext; import com.tinyengine.it.common.exception.ExceptionEnum; +import com.tinyengine.it.common.exception.ServiceException; import com.tinyengine.it.common.log.SystemControllerLog; import com.tinyengine.it.common.utils.ImageThumbnailGenerator; -import com.tinyengine.it.common.utils.Utils; -import com.tinyengine.it.model.dto.ResourceRequestDto; import com.tinyengine.it.model.entity.Resource; import com.tinyengine.it.service.material.ResourceService; import io.swagger.v3.oas.annotations.Operation; @@ -44,6 +43,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.OutputStream; +import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -293,30 +293,35 @@ public Result detail(@PathVariable Integer id) { /** * 获取资源 * - * @param data the data + * @param name the name + * @param isResource the isResource * @return the result */ @Operation(summary = "获取资源", description = "获取资源", parameters = { - @Parameter(name = "data", description = "base64编码数据")}, responses = { + @Parameter(name = "name", description = "名称"), + @Parameter(name = "isResource", description = "isResource"), + }, responses = { @ApiResponse(responseCode = "200", description = "图片流数据", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "获取资源") @GetMapping("/resource/download") - public void getResource(@RequestParam("data") String data, HttpServletResponse response) throws Exception { - ResourceRequestDto param = Utils.decodeBase64ToObject(data, ResourceRequestDto.class); - Resource resource = resourceService.queryResourceByData(param); - - boolean useOriginal = param.isResource(); - String base64Data = useOriginal ? resource.getResourceData() : resource.getThumbnailData(); + public void getResource(@RequestParam String name, @RequestParam boolean isResource, + HttpServletResponse response) throws Exception { + String imageName = URLDecoder.decode(name); + Resource resource = resourceService.queryResourceByName(imageName); + if(resource == null) { + throw new ServiceException(ExceptionEnum.CM009.getResultCode(),ExceptionEnum.CM009.getResultMsg()); + } + String base64Data = isResource ? resource.getResourceData() : resource.getThumbnailData(); String cleanBase64 = ImageThumbnailGenerator.extractCleanBase64(base64Data); byte[] imageBytes = Base64.getDecoder().decode(cleanBase64); String detectedType = ImageThumbnailGenerator.extractContentType(base64Data); - String fileName = useOriginal ? resource.getName() : "thumbnail_" + resource.getName(); + String fileName = isResource ? resource.getName() : "thumbnail_" + resource.getName(); // URL编码文件名 String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8) .replace("+", "%20"); diff --git a/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java b/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java index 44684a13..88420334 100644 --- a/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java +++ b/base/src/main/java/com/tinyengine/it/model/dto/ResourceRequestDto.java @@ -22,6 +22,5 @@ @Data public class ResourceRequestDto { private String name; - private String category; private boolean isResource; } diff --git a/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java b/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java index 5857e856..8a9e5f65 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java +++ b/base/src/main/java/com/tinyengine/it/service/material/ResourceService.java @@ -14,7 +14,6 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.tinyengine.it.common.base.Result; -import com.tinyengine.it.model.dto.ResourceRequestDto; import com.tinyengine.it.model.entity.Resource; import java.util.List; @@ -45,12 +44,12 @@ public interface ResourceService extends IService { Result queryResourceById(Integer id); /** - * 根据data查询表t_resource信息 + * 根据name查询表t_resource信息 * - * @param data the data + * @param name the name * @return the resource */ - Resource queryResourceByData(ResourceRequestDto data) throws Exception; + Resource queryResourceByName(String name) throws Exception; /** * 根据分组id和创建人查询表t_resource信息 diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java index 258cc04d..df4077d9 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java @@ -20,10 +20,8 @@ import com.tinyengine.it.common.exception.ServiceException; import com.tinyengine.it.common.log.SystemServiceLog; import com.tinyengine.it.common.utils.ImageThumbnailGenerator; -import com.tinyengine.it.common.utils.Utils; import com.tinyengine.it.mapper.ResourceGroupResourceMapper; import com.tinyengine.it.mapper.ResourceMapper; -import com.tinyengine.it.model.dto.ResourceRequestDto; import com.tinyengine.it.model.entity.Resource; import com.tinyengine.it.model.entity.ResourceGroupResource; import com.tinyengine.it.service.material.ResourceService; @@ -31,6 +29,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -82,18 +83,17 @@ public Result queryResourceById(Integer id) { } /** - * 根据data查询表t_resource信息 + * 根据name查询表t_resource信息 * - * @param data the data + * @param name the name * @return the resource */ @Override - @SystemServiceLog(description = "根据data查询表t_resource信息") - public Resource queryResourceByData(ResourceRequestDto data) { + @SystemServiceLog(description = "根据name查询表t_resource信息") + public Resource queryResourceByName(String name) { QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("name", data.getName()); - queryWrapper.eq("category", data.getCategory()); + queryWrapper.eq("name", name); return this.baseMapper.selectOne(queryWrapper); } @@ -172,26 +172,18 @@ public Resource createResource(Resource resource) throws Exception { */ @Override @SystemServiceLog(description = "图片上传") - public Resource resourceUpload(Resource resource) throws Exception { - ResourceRequestDto resourceParam = new ResourceRequestDto(); - resourceParam.setName(resource.getName()); - resourceParam.setCategory(resource.getCategory()); - resourceParam.setResource(true); - - String encodedResourceParam = Utils.encodeObjectToBase64(resourceParam); - - ResourceRequestDto thumbnailParam = new ResourceRequestDto(); - thumbnailParam.setName(resource.getName()); - thumbnailParam.setCategory(resource.getCategory()); - thumbnailParam.setResource(false); - String encodedThumbnailParam = Utils.encodeObjectToBase64(thumbnailParam); - + public Resource resourceUpload(Resource resource) { + String imageName = Instant.now().toEpochMilli()+resource.getName(); + resource.setName(imageName); String resourceData = resource.getResourceData(); String tinyEngineUrl = System.getenv("TINY_ENGINE_URL"); if (!StringUtils.isEmpty(resourceData)) { - resource.setResourceUrl(String.format("%s?data=%s", tinyEngineUrl, encodedResourceParam)); - resource.setThumbnailUrl(String.format("%s?data=%s", tinyEngineUrl, encodedThumbnailParam)); + String encodedName = URLEncoder.encode(imageName, StandardCharsets.UTF_8); + String resourceUrl = tinyEngineUrl + "?name=" + encodedName + "&isResource=" + true; + String thumbnailUrl = tinyEngineUrl + "?name=" + encodedName + "&isResource=" + false; + resource.setResourceUrl(resourceUrl); + resource.setThumbnailUrl(thumbnailUrl); resource.setThumbnailData(ImageThumbnailGenerator.createThumbnail(resource.getResourceData(), 200, 200)); } QueryWrapper queryWrapper = new QueryWrapper<>(); From 68193ce7b947fb25dfa57b2ce1f4fa3dc6bd5d49 Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Wed, 17 Sep 2025 02:51:18 -0700 Subject: [PATCH 29/31] fix: modify resource upload --- .../java/com/tinyengine/it/controller/ResourceController.java | 3 +-- .../it/service/material/impl/ResourceServiceImpl.java | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index e149dcd8..ce66f59f 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -310,8 +310,7 @@ public Result detail(@PathVariable Integer id) { @GetMapping("/resource/download") public void getResource(@RequestParam String name, @RequestParam boolean isResource, HttpServletResponse response) throws Exception { - String imageName = URLDecoder.decode(name); - Resource resource = resourceService.queryResourceByName(imageName); + Resource resource = resourceService.queryResourceByName(name); if(resource == null) { throw new ServiceException(ExceptionEnum.CM009.getResultCode(),ExceptionEnum.CM009.getResultMsg()); } diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java index df4077d9..53a398f4 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java @@ -94,6 +94,7 @@ public Resource queryResourceByName(String name) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", name); + queryWrapper.eq("app_id", loginUserContext.getAppId()); return this.baseMapper.selectOne(queryWrapper); } From 64a73975328b1cd7185da8e1553badc0afe487cf Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Wed, 17 Sep 2025 02:53:40 -0700 Subject: [PATCH 30/31] fix: modify resource upload --- .../java/com/tinyengine/it/controller/ResourceController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index ce66f59f..c3095335 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -43,7 +43,6 @@ import org.springframework.web.multipart.MultipartFile; import java.io.OutputStream; -import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Base64; @@ -303,7 +302,7 @@ public Result detail(@PathVariable Integer id) { @Parameter(name = "isResource", description = "isResource"), }, responses = { @ApiResponse(responseCode = "200", description = "图片流数据", - content = @Content(mediaType = "application/json", schema = @Schema(implementation = Resource.class))), + content = @Content(mediaType = "image/*", schema = @Schema(implementation = Resource.class))), @ApiResponse(responseCode = "400", description = "请求失败") }) @SystemControllerLog(description = "获取资源") From 363f72859c7e21f0c3b4d9e2d36c0e0c2d0f6c6a Mon Sep 17 00:00:00 2001 From: lu17301156525 Date: Wed, 17 Sep 2025 05:00:58 -0700 Subject: [PATCH 31/31] fix: modify resource upload --- .../it/controller/ResourceController.java | 9 +++++---- .../material/impl/ResourceServiceImpl.java | 15 ++++++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java index c3095335..aa46b321 100644 --- a/base/src/main/java/com/tinyengine/it/controller/ResourceController.java +++ b/base/src/main/java/com/tinyengine/it/controller/ResourceController.java @@ -202,7 +202,6 @@ public Result resourceUpload(@RequestParam MultipartFile file) throws resource.setName(fileName); resource.setResourceData(base64); resource.setAppId(loginUserContext.getAppId()); - resource.setCategory("image"); Resource result = resourceService.resourceUpload(resource); return Result.success(result); } @@ -308,7 +307,7 @@ public Result detail(@PathVariable Integer id) { @SystemControllerLog(description = "获取资源") @GetMapping("/resource/download") public void getResource(@RequestParam String name, @RequestParam boolean isResource, - HttpServletResponse response) throws Exception { + @RequestParam(required = false) boolean isChat, HttpServletResponse response) throws Exception { Resource resource = resourceService.queryResourceByName(name); if(resource == null) { throw new ServiceException(ExceptionEnum.CM009.getResultCode(),ExceptionEnum.CM009.getResultMsg()); @@ -326,8 +325,10 @@ public void getResource(@RequestParam String name, @RequestParam boolean isResou response.setContentType(detectedType); // 只使用 filename* 格式,避免中文字符直接出现在header中 - response.setHeader("Content-Disposition", - "inline; filename*=UTF-8''" + encodedFileName); + response.setHeader("Content-Disposition", "inline; filename*=UTF-8''" + encodedFileName); + if(isChat){ + response.setHeader("Content-Disposition", "attachment ; filename*=UTF-8''" + encodedFileName); + } try (OutputStream out = response.getOutputStream()) { out.write(imageBytes); } diff --git a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java index 53a398f4..0a53cde1 100644 --- a/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java +++ b/base/src/main/java/com/tinyengine/it/service/material/impl/ResourceServiceImpl.java @@ -174,15 +174,20 @@ public Resource createResource(Resource resource) throws Exception { @Override @SystemServiceLog(description = "图片上传") public Resource resourceUpload(Resource resource) { - String imageName = Instant.now().toEpochMilli()+resource.getName(); + + String imageName = Instant.now().toEpochMilli() + resource.getName(); resource.setName(imageName); String resourceData = resource.getResourceData(); String tinyEngineUrl = System.getenv("TINY_ENGINE_URL"); - + String encodedName = URLEncoder.encode(imageName, StandardCharsets.UTF_8); + String resourceUrl = tinyEngineUrl + "?name=" + encodedName + "&isResource=" + true; + String thumbnailUrl = tinyEngineUrl + "?name=" + encodedName + "&isResource=" + false; + if (resource.getCategory() == null) { + resourceUrl = tinyEngineUrl + "?name=" + encodedName + "&isResource=" + true + "&isChat=" + true; + thumbnailUrl = tinyEngineUrl + "?name=" + encodedName + "&isResource=" + false + "&isChat=" + true; + resource.setCategory("image"); + } if (!StringUtils.isEmpty(resourceData)) { - String encodedName = URLEncoder.encode(imageName, StandardCharsets.UTF_8); - String resourceUrl = tinyEngineUrl + "?name=" + encodedName + "&isResource=" + true; - String thumbnailUrl = tinyEngineUrl + "?name=" + encodedName + "&isResource=" + false; resource.setResourceUrl(resourceUrl); resource.setThumbnailUrl(thumbnailUrl); resource.setThumbnailData(ImageThumbnailGenerator.createThumbnail(resource.getResourceData(), 200, 200));