feat(项目管理): 文件管理的模块树功能开发

This commit is contained in:
song-tianyang 2023-09-07 15:43:58 +08:00 committed by 刘瑞斌
parent 9e3e2c471e
commit 39f2f6c888
80 changed files with 3833 additions and 475 deletions

View File

@ -82,4 +82,6 @@ spring.freemarker.check-template-location=false
spring.groovy.template.check-template-location=false
# swagger docs group
springdoc.api-docs.groups.enabled=true
springdoc.api-docs.groups.enabled=true
#批量文件下载的最大值
metersphere.file.batch-download-max=600MB

View File

@ -1,78 +1,82 @@
package io.metersphere.project.domain;
import io.metersphere.validation.groups.*;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Data;
@Data
public class FileMetadata implements Serializable {
@Schema(description = "文件ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "文件ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_metadata.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{file_metadata.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "文件名", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "文件名", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_metadata.name.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{file_metadata.name.length_range}", groups = {Created.class, Updated.class})
private String name;
@Schema(description = "文件类型")
@Schema(description = "文件类型")
private String type;
@Schema(description = "文件大小", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "文件大小", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{file_metadata.size.not_blank}", groups = {Created.class})
private Long size;
@Schema(description = "创建时间")
@Schema(description = "创建时间")
private Long createTime;
@Schema(description = "更新时间")
@Schema(description = "更新时间")
private Long updateTime;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_metadata.project_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{file_metadata.project_id.length_range}", groups = {Created.class, Updated.class})
private String projectId;
@Schema(description = "文件存储方式")
@Schema(description = "文件存储方式", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_metadata.storage.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{file_metadata.storage.length_range}", groups = {Created.class, Updated.class})
private String storage;
@Schema(description = "创建人")
@Schema(description = "创建人")
private String createUser;
@Schema(description = "修改人")
@Schema(description = "修改人")
private String updateUser;
@Schema(description = "标签")
@Schema(description = "标签")
private String tags;
@Schema(description = "描述")
@Schema(description = "描述")
private String description;
@Schema(description = "文件所属模块")
@Schema(description = "文件所属模块")
private String moduleId;
@Schema(description = "是否加载jar开启后用于接口测试执行时使用")
private Boolean loadJar;
@Schema(description = "文件存储路径")
@Schema(description = "文件存储路径")
private String path;
@Schema(description = "资源作用范围主要兼容2.1版本前的历史数据,后续版本不再产生数据")
private String resourceType;
@Schema(description = "是否是最新版")
@Schema(description = "是否是最新版", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{file_metadata.latest.not_blank}", groups = {Created.class})
private Boolean latest;
@Schema(description = "同版本数据关联的ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "同版本数据关联的ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_metadata.ref_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{file_metadata.ref_id.length_range}", groups = {Created.class, Updated.class})
private String refId;
@Schema(description = "文件版本号")
private String fileVersion;
private static final long serialVersionUID = 1L;
public enum Column {
@ -89,11 +93,10 @@ public class FileMetadata implements Serializable {
tags("tags", "tags", "VARCHAR", false),
description("description", "description", "VARCHAR", false),
moduleId("module_id", "moduleId", "VARCHAR", false),
loadJar("load_jar", "loadJar", "BIT", false),
path("path", "path", "VARCHAR", true),
resourceType("resource_type", "resourceType", "VARCHAR", false),
latest("latest", "latest", "BIT", false),
refId("ref_id", "refId", "VARCHAR", false);
refId("ref_id", "refId", "VARCHAR", false),
fileVersion("file_version", "fileVersion", "VARCHAR", false);
private static final String BEGINNING_DELIMITER = "`";

View File

@ -984,66 +984,6 @@ public class FileMetadataExample {
return (Criteria) this;
}
public Criteria andLoadJarIsNull() {
addCriterion("load_jar is null");
return (Criteria) this;
}
public Criteria andLoadJarIsNotNull() {
addCriterion("load_jar is not null");
return (Criteria) this;
}
public Criteria andLoadJarEqualTo(Boolean value) {
addCriterion("load_jar =", value, "loadJar");
return (Criteria) this;
}
public Criteria andLoadJarNotEqualTo(Boolean value) {
addCriterion("load_jar <>", value, "loadJar");
return (Criteria) this;
}
public Criteria andLoadJarGreaterThan(Boolean value) {
addCriterion("load_jar >", value, "loadJar");
return (Criteria) this;
}
public Criteria andLoadJarGreaterThanOrEqualTo(Boolean value) {
addCriterion("load_jar >=", value, "loadJar");
return (Criteria) this;
}
public Criteria andLoadJarLessThan(Boolean value) {
addCriterion("load_jar <", value, "loadJar");
return (Criteria) this;
}
public Criteria andLoadJarLessThanOrEqualTo(Boolean value) {
addCriterion("load_jar <=", value, "loadJar");
return (Criteria) this;
}
public Criteria andLoadJarIn(List<Boolean> values) {
addCriterion("load_jar in", values, "loadJar");
return (Criteria) this;
}
public Criteria andLoadJarNotIn(List<Boolean> values) {
addCriterion("load_jar not in", values, "loadJar");
return (Criteria) this;
}
public Criteria andLoadJarBetween(Boolean value1, Boolean value2) {
addCriterion("load_jar between", value1, value2, "loadJar");
return (Criteria) this;
}
public Criteria andLoadJarNotBetween(Boolean value1, Boolean value2) {
addCriterion("load_jar not between", value1, value2, "loadJar");
return (Criteria) this;
}
public Criteria andPathIsNull() {
addCriterion("`path` is null");
return (Criteria) this;
@ -1114,76 +1054,6 @@ public class FileMetadataExample {
return (Criteria) this;
}
public Criteria andResourceTypeIsNull() {
addCriterion("resource_type is null");
return (Criteria) this;
}
public Criteria andResourceTypeIsNotNull() {
addCriterion("resource_type is not null");
return (Criteria) this;
}
public Criteria andResourceTypeEqualTo(String value) {
addCriterion("resource_type =", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeNotEqualTo(String value) {
addCriterion("resource_type <>", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeGreaterThan(String value) {
addCriterion("resource_type >", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeGreaterThanOrEqualTo(String value) {
addCriterion("resource_type >=", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeLessThan(String value) {
addCriterion("resource_type <", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeLessThanOrEqualTo(String value) {
addCriterion("resource_type <=", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeLike(String value) {
addCriterion("resource_type like", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeNotLike(String value) {
addCriterion("resource_type not like", value, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeIn(List<String> values) {
addCriterion("resource_type in", values, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeNotIn(List<String> values) {
addCriterion("resource_type not in", values, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeBetween(String value1, String value2) {
addCriterion("resource_type between", value1, value2, "resourceType");
return (Criteria) this;
}
public Criteria andResourceTypeNotBetween(String value1, String value2) {
addCriterion("resource_type not between", value1, value2, "resourceType");
return (Criteria) this;
}
public Criteria andLatestIsNull() {
addCriterion("latest is null");
return (Criteria) this;
@ -1313,6 +1183,76 @@ public class FileMetadataExample {
addCriterion("ref_id not between", value1, value2, "refId");
return (Criteria) this;
}
public Criteria andFileVersionIsNull() {
addCriterion("file_version is null");
return (Criteria) this;
}
public Criteria andFileVersionIsNotNull() {
addCriterion("file_version is not null");
return (Criteria) this;
}
public Criteria andFileVersionEqualTo(String value) {
addCriterion("file_version =", value, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionNotEqualTo(String value) {
addCriterion("file_version <>", value, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionGreaterThan(String value) {
addCriterion("file_version >", value, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionGreaterThanOrEqualTo(String value) {
addCriterion("file_version >=", value, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionLessThan(String value) {
addCriterion("file_version <", value, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionLessThanOrEqualTo(String value) {
addCriterion("file_version <=", value, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionLike(String value) {
addCriterion("file_version like", value, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionNotLike(String value) {
addCriterion("file_version not like", value, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionIn(List<String> values) {
addCriterion("file_version in", values, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionNotIn(List<String> values) {
addCriterion("file_version not in", values, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionBetween(String value1, String value2) {
addCriterion("file_version between", value1, value2, "fileVersion");
return (Criteria) this;
}
public Criteria andFileVersionNotBetween(String value1, String value2) {
addCriterion("file_version not between", value1, value2, "fileVersion");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -9,13 +9,13 @@ import java.util.Arrays;
import lombok.Data;
@Data
public class FileMetadataBlob implements Serializable {
@Schema(description = "文件ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_metadata_blob.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{file_metadata_blob.id.length_range}", groups = {Created.class, Updated.class})
public class FileMetadataRepository implements Serializable {
@Schema(title = "文件ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_metadata_repository.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{file_metadata_repository.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "储存库")
@Schema(title = "储存库")
private byte[] gitInfo;
private static final long serialVersionUID = 1L;

View File

@ -3,14 +3,14 @@ package io.metersphere.project.domain;
import java.util.ArrayList;
import java.util.List;
public class FileMetadataBlobExample {
public class FileMetadataRepositoryExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public FileMetadataBlobExample() {
public FileMetadataRepositoryExample() {
oredCriteria = new ArrayList<Criteria>();
}

View File

@ -1,49 +1,52 @@
package io.metersphere.project.domain;
import io.metersphere.validation.groups.*;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Data;
@Data
public class FileModule implements Serializable {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_module.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{file_module.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_module.project_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{file_module.project_id.length_range}", groups = {Created.class, Updated.class})
private String projectId;
@Schema(description = "模块名称", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "模块名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_module.name.not_blank}", groups = {Created.class})
@Size(min = 1, max = 64, message = "{file_module.name.length_range}", groups = {Created.class, Updated.class})
private String name;
@Schema(description = "父级ID")
@Schema(description = "父级ID")
private String parentId;
@Schema(description = "层数")
private Integer level;
@Schema(description = "创建时间")
@Schema(description = "创建时间")
private Long createTime;
@Schema(description = "更新时间")
@Schema(description = "更新时间")
private Long updateTime;
@Schema(description = "排序用的标识")
private Double pos;
@Schema(description = "排序用的标识")
private Integer pos;
@Schema(description = "创建人")
@Schema(description = "修改人")
private String updateUser;
@Schema(description = "创建人")
private String createUser;
@Schema(description = "模块类型: module/repository")
@Schema(description = "模块类型: module/repository")
private String moduleType;
private static final long serialVersionUID = 1L;
@ -53,10 +56,10 @@ public class FileModule implements Serializable {
projectId("project_id", "projectId", "VARCHAR", false),
name("name", "name", "VARCHAR", true),
parentId("parent_id", "parentId", "VARCHAR", false),
level("level", "level", "INTEGER", true),
createTime("create_time", "createTime", "BIGINT", false),
updateTime("update_time", "updateTime", "BIGINT", false),
pos("pos", "pos", "DOUBLE", false),
pos("pos", "pos", "INTEGER", false),
updateUser("update_user", "updateUser", "VARCHAR", false),
createUser("create_user", "createUser", "VARCHAR", false),
moduleType("module_type", "moduleType", "VARCHAR", false);

View File

@ -384,66 +384,6 @@ public class FileModuleExample {
return (Criteria) this;
}
public Criteria andLevelIsNull() {
addCriterion("`level` is null");
return (Criteria) this;
}
public Criteria andLevelIsNotNull() {
addCriterion("`level` is not null");
return (Criteria) this;
}
public Criteria andLevelEqualTo(Integer value) {
addCriterion("`level` =", value, "level");
return (Criteria) this;
}
public Criteria andLevelNotEqualTo(Integer value) {
addCriterion("`level` <>", value, "level");
return (Criteria) this;
}
public Criteria andLevelGreaterThan(Integer value) {
addCriterion("`level` >", value, "level");
return (Criteria) this;
}
public Criteria andLevelGreaterThanOrEqualTo(Integer value) {
addCriterion("`level` >=", value, "level");
return (Criteria) this;
}
public Criteria andLevelLessThan(Integer value) {
addCriterion("`level` <", value, "level");
return (Criteria) this;
}
public Criteria andLevelLessThanOrEqualTo(Integer value) {
addCriterion("`level` <=", value, "level");
return (Criteria) this;
}
public Criteria andLevelIn(List<Integer> values) {
addCriterion("`level` in", values, "level");
return (Criteria) this;
}
public Criteria andLevelNotIn(List<Integer> values) {
addCriterion("`level` not in", values, "level");
return (Criteria) this;
}
public Criteria andLevelBetween(Integer value1, Integer value2) {
addCriterion("`level` between", value1, value2, "level");
return (Criteria) this;
}
public Criteria andLevelNotBetween(Integer value1, Integer value2) {
addCriterion("`level` not between", value1, value2, "level");
return (Criteria) this;
}
public Criteria andCreateTimeIsNull() {
addCriterion("create_time is null");
return (Criteria) this;
@ -574,56 +514,126 @@ public class FileModuleExample {
return (Criteria) this;
}
public Criteria andPosEqualTo(Double value) {
public Criteria andPosEqualTo(Integer value) {
addCriterion("pos =", value, "pos");
return (Criteria) this;
}
public Criteria andPosNotEqualTo(Double value) {
public Criteria andPosNotEqualTo(Integer value) {
addCriterion("pos <>", value, "pos");
return (Criteria) this;
}
public Criteria andPosGreaterThan(Double value) {
public Criteria andPosGreaterThan(Integer value) {
addCriterion("pos >", value, "pos");
return (Criteria) this;
}
public Criteria andPosGreaterThanOrEqualTo(Double value) {
public Criteria andPosGreaterThanOrEqualTo(Integer value) {
addCriterion("pos >=", value, "pos");
return (Criteria) this;
}
public Criteria andPosLessThan(Double value) {
public Criteria andPosLessThan(Integer value) {
addCriterion("pos <", value, "pos");
return (Criteria) this;
}
public Criteria andPosLessThanOrEqualTo(Double value) {
public Criteria andPosLessThanOrEqualTo(Integer value) {
addCriterion("pos <=", value, "pos");
return (Criteria) this;
}
public Criteria andPosIn(List<Double> values) {
public Criteria andPosIn(List<Integer> values) {
addCriterion("pos in", values, "pos");
return (Criteria) this;
}
public Criteria andPosNotIn(List<Double> values) {
public Criteria andPosNotIn(List<Integer> values) {
addCriterion("pos not in", values, "pos");
return (Criteria) this;
}
public Criteria andPosBetween(Double value1, Double value2) {
public Criteria andPosBetween(Integer value1, Integer value2) {
addCriterion("pos between", value1, value2, "pos");
return (Criteria) this;
}
public Criteria andPosNotBetween(Double value1, Double value2) {
public Criteria andPosNotBetween(Integer value1, Integer value2) {
addCriterion("pos not between", value1, value2, "pos");
return (Criteria) this;
}
public Criteria andUpdateUserIsNull() {
addCriterion("update_user is null");
return (Criteria) this;
}
public Criteria andUpdateUserIsNotNull() {
addCriterion("update_user is not null");
return (Criteria) this;
}
public Criteria andUpdateUserEqualTo(String value) {
addCriterion("update_user =", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotEqualTo(String value) {
addCriterion("update_user <>", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserGreaterThan(String value) {
addCriterion("update_user >", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserGreaterThanOrEqualTo(String value) {
addCriterion("update_user >=", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserLessThan(String value) {
addCriterion("update_user <", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserLessThanOrEqualTo(String value) {
addCriterion("update_user <=", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserLike(String value) {
addCriterion("update_user like", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotLike(String value) {
addCriterion("update_user not like", value, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserIn(List<String> values) {
addCriterion("update_user in", values, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotIn(List<String> values) {
addCriterion("update_user not in", values, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserBetween(String value1, String value2) {
addCriterion("update_user between", value1, value2, "updateUser");
return (Criteria) this;
}
public Criteria andUpdateUserNotBetween(String value1, String value2) {
addCriterion("update_user not between", value1, value2, "updateUser");
return (Criteria) this;
}
public Criteria andCreateUserIsNull() {
addCriterion("create_user is null");
return (Criteria) this;

View File

@ -1,30 +1,33 @@
package io.metersphere.project.domain;
import io.metersphere.validation.groups.*;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Data;
@Data
public class FileModuleRepository implements Serializable {
@Schema(title = "file_module_id", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "file_module_id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_module_repository.file_module_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{file_module_repository.file_module_id.length_range}", groups = {Created.class, Updated.class})
private String fileModuleId;
@Schema(title = "所属平台;GitHub/Gitlab/Gitee")
@Schema(description = "所属平台;GitHub/Gitlab/Gitee")
private String platform;
@Schema(title = "存储库地址")
@Schema(description = "存储库地址")
private String repositoryPath;
@Schema(title = "存储库Token;platform为Gitee时必填")
@Schema(description = "存储库Token;platform为Gitee时必填")
private String repositoryUserName;
@Schema(title = "存储库Token")
@Schema(description = "存储库Token")
private String repositoryToken;
private static final long serialVersionUID = 1L;

View File

@ -1,38 +0,0 @@
package io.metersphere.project.mapper;
import io.metersphere.project.domain.FileMetadataBlob;
import io.metersphere.project.domain.FileMetadataBlobExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface FileMetadataBlobMapper {
long countByExample(FileMetadataBlobExample example);
int deleteByExample(FileMetadataBlobExample example);
int deleteByPrimaryKey(String id);
int insert(FileMetadataBlob record);
int insertSelective(FileMetadataBlob record);
List<FileMetadataBlob> selectByExampleWithBLOBs(FileMetadataBlobExample example);
List<FileMetadataBlob> selectByExample(FileMetadataBlobExample example);
FileMetadataBlob selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") FileMetadataBlob record, @Param("example") FileMetadataBlobExample example);
int updateByExampleWithBLOBs(@Param("record") FileMetadataBlob record, @Param("example") FileMetadataBlobExample example);
int updateByExample(@Param("record") FileMetadataBlob record, @Param("example") FileMetadataBlobExample example);
int updateByPrimaryKeySelective(FileMetadataBlob record);
int updateByPrimaryKeyWithBLOBs(FileMetadataBlob record);
int batchInsert(@Param("list") List<FileMetadataBlob> list);
int batchInsertSelective(@Param("list") List<FileMetadataBlob> list, @Param("selective") FileMetadataBlob.Column ... selective);
}

View File

@ -15,11 +15,10 @@
<result column="tags" jdbcType="VARCHAR" property="tags" />
<result column="description" jdbcType="VARCHAR" property="description" />
<result column="module_id" jdbcType="VARCHAR" property="moduleId" />
<result column="load_jar" jdbcType="BIT" property="loadJar" />
<result column="path" jdbcType="VARCHAR" property="path" />
<result column="resource_type" jdbcType="VARCHAR" property="resourceType" />
<result column="latest" jdbcType="BIT" property="latest" />
<result column="ref_id" jdbcType="VARCHAR" property="refId" />
<result column="file_version" jdbcType="VARCHAR" property="fileVersion" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -81,8 +80,7 @@
</sql>
<sql id="Base_Column_List">
id, `name`, `type`, `size`, create_time, update_time, project_id, `storage`, create_user,
update_user, tags, description, module_id, load_jar, `path`, resource_type, latest,
ref_id
update_user, tags, description, module_id, `path`, latest, ref_id, file_version
</sql>
<select id="selectByExample" parameterType="io.metersphere.project.domain.FileMetadataExample" resultMap="BaseResultMap">
select
@ -119,16 +117,14 @@
`size`, create_time, update_time,
project_id, `storage`, create_user,
update_user, tags, description,
module_id, load_jar, `path`,
resource_type, latest, ref_id
)
module_id, `path`, latest,
ref_id, file_version)
values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR},
#{size,jdbcType=BIGINT}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{projectId,jdbcType=VARCHAR}, #{storage,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR},
#{updateUser,jdbcType=VARCHAR}, #{tags,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR},
#{moduleId,jdbcType=VARCHAR}, #{loadJar,jdbcType=BIT}, #{path,jdbcType=VARCHAR},
#{resourceType,jdbcType=VARCHAR}, #{latest,jdbcType=BIT}, #{refId,jdbcType=VARCHAR}
)
#{moduleId,jdbcType=VARCHAR}, #{path,jdbcType=VARCHAR}, #{latest,jdbcType=BIT},
#{refId,jdbcType=VARCHAR}, #{fileVersion,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.project.domain.FileMetadata">
insert into file_metadata
@ -172,21 +168,18 @@
<if test="moduleId != null">
module_id,
</if>
<if test="loadJar != null">
load_jar,
</if>
<if test="path != null">
`path`,
</if>
<if test="resourceType != null">
resource_type,
</if>
<if test="latest != null">
latest,
</if>
<if test="refId != null">
ref_id,
</if>
<if test="fileVersion != null">
file_version,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -228,21 +221,18 @@
<if test="moduleId != null">
#{moduleId,jdbcType=VARCHAR},
</if>
<if test="loadJar != null">
#{loadJar,jdbcType=BIT},
</if>
<if test="path != null">
#{path,jdbcType=VARCHAR},
</if>
<if test="resourceType != null">
#{resourceType,jdbcType=VARCHAR},
</if>
<if test="latest != null">
#{latest,jdbcType=BIT},
</if>
<if test="refId != null">
#{refId,jdbcType=VARCHAR},
</if>
<if test="fileVersion != null">
#{fileVersion,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.project.domain.FileMetadataExample" resultType="java.lang.Long">
@ -293,21 +283,18 @@
<if test="record.moduleId != null">
module_id = #{record.moduleId,jdbcType=VARCHAR},
</if>
<if test="record.loadJar != null">
load_jar = #{record.loadJar,jdbcType=BIT},
</if>
<if test="record.path != null">
`path` = #{record.path,jdbcType=VARCHAR},
</if>
<if test="record.resourceType != null">
resource_type = #{record.resourceType,jdbcType=VARCHAR},
</if>
<if test="record.latest != null">
latest = #{record.latest,jdbcType=BIT},
</if>
<if test="record.refId != null">
ref_id = #{record.refId,jdbcType=VARCHAR},
</if>
<if test="record.fileVersion != null">
file_version = #{record.fileVersion,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -328,11 +315,10 @@
tags = #{record.tags,jdbcType=VARCHAR},
description = #{record.description,jdbcType=VARCHAR},
module_id = #{record.moduleId,jdbcType=VARCHAR},
load_jar = #{record.loadJar,jdbcType=BIT},
`path` = #{record.path,jdbcType=VARCHAR},
resource_type = #{record.resourceType,jdbcType=VARCHAR},
latest = #{record.latest,jdbcType=BIT},
ref_id = #{record.refId,jdbcType=VARCHAR}
ref_id = #{record.refId,jdbcType=VARCHAR},
file_version = #{record.fileVersion,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -376,21 +362,18 @@
<if test="moduleId != null">
module_id = #{moduleId,jdbcType=VARCHAR},
</if>
<if test="loadJar != null">
load_jar = #{loadJar,jdbcType=BIT},
</if>
<if test="path != null">
`path` = #{path,jdbcType=VARCHAR},
</if>
<if test="resourceType != null">
resource_type = #{resourceType,jdbcType=VARCHAR},
</if>
<if test="latest != null">
latest = #{latest,jdbcType=BIT},
</if>
<if test="refId != null">
ref_id = #{refId,jdbcType=VARCHAR},
</if>
<if test="fileVersion != null">
file_version = #{fileVersion,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -408,27 +391,25 @@
tags = #{tags,jdbcType=VARCHAR},
description = #{description,jdbcType=VARCHAR},
module_id = #{moduleId,jdbcType=VARCHAR},
load_jar = #{loadJar,jdbcType=BIT},
`path` = #{path,jdbcType=VARCHAR},
resource_type = #{resourceType,jdbcType=VARCHAR},
latest = #{latest,jdbcType=BIT},
ref_id = #{refId,jdbcType=VARCHAR}
ref_id = #{refId,jdbcType=VARCHAR},
file_version = #{fileVersion,jdbcType=VARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into file_metadata
(id, `name`, `type`, `size`, create_time, update_time, project_id, `storage`, create_user,
update_user, tags, description, module_id, load_jar, `path`, resource_type, latest,
ref_id)
update_user, tags, description, module_id, `path`, latest, ref_id, file_version
)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR}, #{item.type,jdbcType=VARCHAR},
#{item.size,jdbcType=BIGINT}, #{item.createTime,jdbcType=BIGINT}, #{item.updateTime,jdbcType=BIGINT},
#{item.projectId,jdbcType=VARCHAR}, #{item.storage,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR},
#{item.updateUser,jdbcType=VARCHAR}, #{item.tags,jdbcType=VARCHAR}, #{item.description,jdbcType=VARCHAR},
#{item.moduleId,jdbcType=VARCHAR}, #{item.loadJar,jdbcType=BIT}, #{item.path,jdbcType=VARCHAR},
#{item.resourceType,jdbcType=VARCHAR}, #{item.latest,jdbcType=BIT}, #{item.refId,jdbcType=VARCHAR}
)
#{item.moduleId,jdbcType=VARCHAR}, #{item.path,jdbcType=VARCHAR}, #{item.latest,jdbcType=BIT},
#{item.refId,jdbcType=VARCHAR}, #{item.fileVersion,jdbcType=VARCHAR})
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
@ -480,21 +461,18 @@
<if test="'module_id'.toString() == column.value">
#{item.moduleId,jdbcType=VARCHAR}
</if>
<if test="'load_jar'.toString() == column.value">
#{item.loadJar,jdbcType=BIT}
</if>
<if test="'path'.toString() == column.value">
#{item.path,jdbcType=VARCHAR}
</if>
<if test="'resource_type'.toString() == column.value">
#{item.resourceType,jdbcType=VARCHAR}
</if>
<if test="'latest'.toString() == column.value">
#{item.latest,jdbcType=BIT}
</if>
<if test="'ref_id'.toString() == column.value">
#{item.refId,jdbcType=VARCHAR}
</if>
<if test="'file_version'.toString() == column.value">
#{item.fileVersion,jdbcType=VARCHAR}
</if>
</foreach>
)
</foreach>

View File

@ -0,0 +1,38 @@
package io.metersphere.project.mapper;
import io.metersphere.project.domain.FileMetadataRepository;
import io.metersphere.project.domain.FileMetadataRepositoryExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface FileMetadataRepositoryMapper {
long countByExample(FileMetadataRepositoryExample example);
int deleteByExample(FileMetadataRepositoryExample example);
int deleteByPrimaryKey(String id);
int insert(FileMetadataRepository record);
int insertSelective(FileMetadataRepository record);
List<FileMetadataRepository> selectByExampleWithBLOBs(FileMetadataRepositoryExample example);
List<FileMetadataRepository> selectByExample(FileMetadataRepositoryExample example);
FileMetadataRepository selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") FileMetadataRepository record, @Param("example") FileMetadataRepositoryExample example);
int updateByExampleWithBLOBs(@Param("record") FileMetadataRepository record, @Param("example") FileMetadataRepositoryExample example);
int updateByExample(@Param("record") FileMetadataRepository record, @Param("example") FileMetadataRepositoryExample example);
int updateByPrimaryKeySelective(FileMetadataRepository record);
int updateByPrimaryKeyWithBLOBs(FileMetadataRepository record);
int batchInsert(@Param("list") List<FileMetadataRepository> list);
int batchInsertSelective(@Param("list") List<FileMetadataRepository> list, @Param("selective") FileMetadataRepository.Column ... selective);
}

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.project.mapper.FileMetadataBlobMapper">
<resultMap id="BaseResultMap" type="io.metersphere.project.domain.FileMetadataBlob">
<mapper namespace="io.metersphere.project.mapper.FileMetadataRepositoryMapper">
<resultMap id="BaseResultMap" type="io.metersphere.project.domain.FileMetadataRepository">
<id column="id" jdbcType="VARCHAR" property="id" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.project.domain.FileMetadataBlob">
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.project.domain.FileMetadataRepository">
<result column="git_info" jdbcType="LONGVARBINARY" property="gitInfo" />
</resultMap>
<sql id="Example_Where_Clause">
@ -71,7 +71,7 @@
<sql id="Blob_Column_List">
git_info
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.project.domain.FileMetadataBlobExample" resultMap="ResultMapWithBLOBs">
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.project.domain.FileMetadataRepositoryExample" resultMap="ResultMapWithBLOBs">
select
<if test="distinct">
distinct
@ -79,7 +79,7 @@
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from file_metadata_blob
from file_metadata_repository
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
@ -87,13 +87,13 @@
order by ${orderByClause}
</if>
</select>
<select id="selectByExample" parameterType="io.metersphere.project.domain.FileMetadataBlobExample" resultMap="BaseResultMap">
<select id="selectByExample" parameterType="io.metersphere.project.domain.FileMetadataRepositoryExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from file_metadata_blob
from file_metadata_repository
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
@ -106,25 +106,25 @@
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from file_metadata_blob
from file_metadata_repository
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from file_metadata_blob
delete from file_metadata_repository
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.project.domain.FileMetadataBlobExample">
delete from file_metadata_blob
<delete id="deleteByExample" parameterType="io.metersphere.project.domain.FileMetadataRepositoryExample">
delete from file_metadata_repository
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.project.domain.FileMetadataBlob">
insert into file_metadata_blob (id, git_info)
<insert id="insert" parameterType="io.metersphere.project.domain.FileMetadataRepository">
insert into file_metadata_repository (id, git_info)
values (#{id,jdbcType=VARCHAR}, #{gitInfo,jdbcType=LONGVARBINARY})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.project.domain.FileMetadataBlob">
insert into file_metadata_blob
<insert id="insertSelective" parameterType="io.metersphere.project.domain.FileMetadataRepository">
insert into file_metadata_repository
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
@ -142,14 +142,14 @@
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.project.domain.FileMetadataBlobExample" resultType="java.lang.Long">
select count(*) from file_metadata_blob
<select id="countByExample" parameterType="io.metersphere.project.domain.FileMetadataRepositoryExample" resultType="java.lang.Long">
select count(*) from file_metadata_repository
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update file_metadata_blob
update file_metadata_repository
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
@ -163,7 +163,7 @@
</if>
</update>
<update id="updateByExampleWithBLOBs" parameterType="map">
update file_metadata_blob
update file_metadata_repository
set id = #{record.id,jdbcType=VARCHAR},
git_info = #{record.gitInfo,jdbcType=LONGVARBINARY}
<if test="_parameter != null">
@ -171,14 +171,14 @@
</if>
</update>
<update id="updateByExample" parameterType="map">
update file_metadata_blob
update file_metadata_repository
set id = #{record.id,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.project.domain.FileMetadataBlob">
update file_metadata_blob
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.project.domain.FileMetadataRepository">
update file_metadata_repository
<set>
<if test="gitInfo != null">
git_info = #{gitInfo,jdbcType=LONGVARBINARY},
@ -186,13 +186,13 @@
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.project.domain.FileMetadataBlob">
update file_metadata_blob
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.project.domain.FileMetadataRepository">
update file_metadata_repository
set git_info = #{gitInfo,jdbcType=LONGVARBINARY}
where id = #{id,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into file_metadata_blob
insert into file_metadata_repository
(id, git_info)
values
<foreach collection="list" item="item" separator=",">
@ -200,7 +200,7 @@
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
insert into file_metadata_blob (
insert into file_metadata_repository (
<foreach collection="selective" item="column" separator=",">
${column.escapedColumnName}
</foreach>

View File

@ -6,10 +6,10 @@
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="parent_id" jdbcType="VARCHAR" property="parentId" />
<result column="level" jdbcType="INTEGER" property="level" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
<result column="pos" jdbcType="DOUBLE" property="pos" />
<result column="pos" jdbcType="INTEGER" property="pos" />
<result column="update_user" jdbcType="VARCHAR" property="updateUser" />
<result column="create_user" jdbcType="VARCHAR" property="createUser" />
<result column="module_type" jdbcType="VARCHAR" property="moduleType" />
</resultMap>
@ -72,7 +72,7 @@
</where>
</sql>
<sql id="Base_Column_List">
id, project_id, `name`, parent_id, `level`, create_time, update_time, pos, create_user,
id, project_id, `name`, parent_id, create_time, update_time, pos, update_user, create_user,
module_type
</sql>
<select id="selectByExample" parameterType="io.metersphere.project.domain.FileModuleExample" resultMap="BaseResultMap">
@ -107,12 +107,12 @@
</delete>
<insert id="insert" parameterType="io.metersphere.project.domain.FileModule">
insert into file_module (id, project_id, `name`,
parent_id, `level`, create_time,
update_time, pos, create_user,
parent_id, create_time, update_time,
pos, update_user, create_user,
module_type)
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{parentId,jdbcType=VARCHAR}, #{level,jdbcType=INTEGER}, #{createTime,jdbcType=BIGINT},
#{updateTime,jdbcType=BIGINT}, #{pos,jdbcType=DOUBLE}, #{createUser,jdbcType=VARCHAR},
#{parentId,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{pos,jdbcType=INTEGER}, #{updateUser,jdbcType=VARCHAR}, #{createUser,jdbcType=VARCHAR},
#{moduleType,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.project.domain.FileModule">
@ -130,9 +130,6 @@
<if test="parentId != null">
parent_id,
</if>
<if test="level != null">
`level`,
</if>
<if test="createTime != null">
create_time,
</if>
@ -142,6 +139,9 @@
<if test="pos != null">
pos,
</if>
<if test="updateUser != null">
update_user,
</if>
<if test="createUser != null">
create_user,
</if>
@ -162,9 +162,6 @@
<if test="parentId != null">
#{parentId,jdbcType=VARCHAR},
</if>
<if test="level != null">
#{level,jdbcType=INTEGER},
</if>
<if test="createTime != null">
#{createTime,jdbcType=BIGINT},
</if>
@ -172,7 +169,10 @@
#{updateTime,jdbcType=BIGINT},
</if>
<if test="pos != null">
#{pos,jdbcType=DOUBLE},
#{pos,jdbcType=INTEGER},
</if>
<if test="updateUser != null">
#{updateUser,jdbcType=VARCHAR},
</if>
<if test="createUser != null">
#{createUser,jdbcType=VARCHAR},
@ -203,9 +203,6 @@
<if test="record.parentId != null">
parent_id = #{record.parentId,jdbcType=VARCHAR},
</if>
<if test="record.level != null">
`level` = #{record.level,jdbcType=INTEGER},
</if>
<if test="record.createTime != null">
create_time = #{record.createTime,jdbcType=BIGINT},
</if>
@ -213,7 +210,10 @@
update_time = #{record.updateTime,jdbcType=BIGINT},
</if>
<if test="record.pos != null">
pos = #{record.pos,jdbcType=DOUBLE},
pos = #{record.pos,jdbcType=INTEGER},
</if>
<if test="record.updateUser != null">
update_user = #{record.updateUser,jdbcType=VARCHAR},
</if>
<if test="record.createUser != null">
create_user = #{record.createUser,jdbcType=VARCHAR},
@ -232,10 +232,10 @@
project_id = #{record.projectId,jdbcType=VARCHAR},
`name` = #{record.name,jdbcType=VARCHAR},
parent_id = #{record.parentId,jdbcType=VARCHAR},
`level` = #{record.level,jdbcType=INTEGER},
create_time = #{record.createTime,jdbcType=BIGINT},
update_time = #{record.updateTime,jdbcType=BIGINT},
pos = #{record.pos,jdbcType=DOUBLE},
pos = #{record.pos,jdbcType=INTEGER},
update_user = #{record.updateUser,jdbcType=VARCHAR},
create_user = #{record.createUser,jdbcType=VARCHAR},
module_type = #{record.moduleType,jdbcType=VARCHAR}
<if test="_parameter != null">
@ -254,9 +254,6 @@
<if test="parentId != null">
parent_id = #{parentId,jdbcType=VARCHAR},
</if>
<if test="level != null">
`level` = #{level,jdbcType=INTEGER},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=BIGINT},
</if>
@ -264,7 +261,10 @@
update_time = #{updateTime,jdbcType=BIGINT},
</if>
<if test="pos != null">
pos = #{pos,jdbcType=DOUBLE},
pos = #{pos,jdbcType=INTEGER},
</if>
<if test="updateUser != null">
update_user = #{updateUser,jdbcType=VARCHAR},
</if>
<if test="createUser != null">
create_user = #{createUser,jdbcType=VARCHAR},
@ -280,23 +280,23 @@
set project_id = #{projectId,jdbcType=VARCHAR},
`name` = #{name,jdbcType=VARCHAR},
parent_id = #{parentId,jdbcType=VARCHAR},
`level` = #{level,jdbcType=INTEGER},
create_time = #{createTime,jdbcType=BIGINT},
update_time = #{updateTime,jdbcType=BIGINT},
pos = #{pos,jdbcType=DOUBLE},
pos = #{pos,jdbcType=INTEGER},
update_user = #{updateUser,jdbcType=VARCHAR},
create_user = #{createUser,jdbcType=VARCHAR},
module_type = #{moduleType,jdbcType=VARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into file_module
(id, project_id, `name`, parent_id, `level`, create_time, update_time, pos, create_user,
(id, project_id, `name`, parent_id, create_time, update_time, pos, update_user, create_user,
module_type)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=VARCHAR}, #{item.projectId,jdbcType=VARCHAR}, #{item.name,jdbcType=VARCHAR},
#{item.parentId,jdbcType=VARCHAR}, #{item.level,jdbcType=INTEGER}, #{item.createTime,jdbcType=BIGINT},
#{item.updateTime,jdbcType=BIGINT}, #{item.pos,jdbcType=DOUBLE}, #{item.createUser,jdbcType=VARCHAR},
#{item.parentId,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT}, #{item.updateTime,jdbcType=BIGINT},
#{item.pos,jdbcType=INTEGER}, #{item.updateUser,jdbcType=VARCHAR}, #{item.createUser,jdbcType=VARCHAR},
#{item.moduleType,jdbcType=VARCHAR})
</foreach>
</insert>
@ -322,9 +322,6 @@
<if test="'parent_id'.toString() == column.value">
#{item.parentId,jdbcType=VARCHAR}
</if>
<if test="'level'.toString() == column.value">
#{item.level,jdbcType=INTEGER}
</if>
<if test="'create_time'.toString() == column.value">
#{item.createTime,jdbcType=BIGINT}
</if>
@ -332,7 +329,10 @@
#{item.updateTime,jdbcType=BIGINT}
</if>
<if test="'pos'.toString() == column.value">
#{item.pos,jdbcType=DOUBLE}
#{item.pos,jdbcType=INTEGER}
</if>
<if test="'update_user'.toString() == column.value">
#{item.updateUser,jdbcType=VARCHAR}
</if>
<if test="'create_user'.toString() == column.value">
#{item.createUser,jdbcType=VARCHAR}

View File

@ -1,35 +1,39 @@
package io.metersphere.system.domain;
import io.metersphere.validation.groups.*;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Data;
@Data
public class UserInvite implements Serializable {
@Schema(title = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{user_invite.id.not_blank}", groups = {Updated.class})
@Size(min = 1, max = 50, message = "{user_invite.id.length_range}", groups = {Created.class, Updated.class})
private String id;
@Schema(title = "邀请邮箱", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "邀请邮箱", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{user_invite.email.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{user_invite.email.length_range}", groups = {Created.class, Updated.class})
private String email;
@Schema(title = "邀请用户", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "邀请用户", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{user_invite.invite_user.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{user_invite.invite_user.length_range}", groups = {Created.class, Updated.class})
private String inviteUser;
@Schema(title = "邀请时间", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "邀请时间", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "{user_invite.invite_time.not_blank}", groups = {Created.class})
private Long inviteTime;
@Schema(title = "所属权限")
@Schema(description = "所属权限")
private String roles;
private static final long serialVersionUID = 1L;

View File

@ -62,30 +62,41 @@ CREATE INDEX idx_file_metadata_id ON file_association (file_metadata_id);
CREATE INDEX idx_project_id ON file_association (project_id);
CREATE INDEX idx_source_id ON file_association (source_id);
CREATE TABLE IF NOT EXISTS file_metadata_repository
(
`id` VARCHAR(50) NOT NULL COMMENT '文件ID',
`git_info` LONGBLOB COMMENT '储存库',
PRIMARY KEY (id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
COMMENT = '文件基础信息大字段';
CREATE TABLE IF NOT EXISTS file_metadata
(
`id` VARCHAR(50) NOT NULL COMMENT '文件ID',
`name` VARCHAR(255) NOT NULL COMMENT '文件名',
`type` VARCHAR(64) COMMENT '文件类型',
`size` BIGINT NOT NULL COMMENT '文件大小',
`create_time` BIGINT NOT NULL COMMENT '创建时间',
`update_time` BIGINT NOT NULL COMMENT '更新时间',
`project_id` VARCHAR(50) NOT NULL COMMENT '项目ID',
`storage` VARCHAR(50) NOT NULL DEFAULT 'MINIO' COMMENT '文件存储方式',
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人',
`update_user` VARCHAR(50) NOT NULL COMMENT '修改人',
`tags` VARCHAR(1000) COMMENT '标签',
`description` VARCHAR(500) COMMENT '描述',
`module_id` VARCHAR(50) COMMENT '文件所属模块',
`load_jar` BIT DEFAULT 0 COMMENT '是否加载jar开启后用于接口测试执行时使用',
`path` VARCHAR(1000) COMMENT '文件存储路径',
`resource_type` VARCHAR(50) COMMENT '资源作用范围主要兼容2.1版本前的历史数据,后续版本不再产生数据',
`latest` BIT NOT NULL DEFAULT 1 COMMENT '是否是最新版',
`ref_id` VARCHAR(50) NOT NULL COMMENT '同版本数据关联的ID',
`id` VARCHAR(50) NOT NULL COMMENT '文件ID',
`name` VARCHAR(255) NOT NULL COMMENT '文件名',
`type` VARCHAR(64) COMMENT '文件类型',
`size` BIGINT NOT NULL COMMENT '文件大小',
`create_time` BIGINT NOT NULL COMMENT '创建时间',
`update_time` BIGINT NOT NULL COMMENT '更新时间',
`project_id` VARCHAR(50) NOT NULL COMMENT '项目ID',
`storage` VARCHAR(50) NOT NULL DEFAULT 'MINIO' COMMENT '文件存储方式',
`create_user` VARCHAR(50) NOT NULL COMMENT '创建人',
`update_user` VARCHAR(50) NOT NULL COMMENT '修改人',
`tags` VARCHAR(1000) COMMENT '标签',
`description` VARCHAR(500) COMMENT '描述',
`module_id` VARCHAR(50) COMMENT '文件所属模块',
`path` VARCHAR(1000) COMMENT '文件存储路径',
`latest` BIT NOT NULL DEFAULT 1 COMMENT '是否是最新版',
`ref_id` VARCHAR(50) NOT NULL COMMENT '同版本数据关联的ID',
`file_version` VARCHAR(50) COMMENT '文件版本号',
PRIMARY KEY (id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '文件基础信息';
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
COMMENT = '文件基础信息';
CREATE INDEX idx_file_name ON file_metadata (name);
@ -95,22 +106,24 @@ CREATE INDEX idx_storage ON file_metadata (storage);
CREATE INDEX idx_module_id ON file_metadata (module_id);
CREATE INDEX idx_project_id ON file_metadata (project_id);
CREATE TABLE IF NOT EXISTS file_module
(
`id` VARCHAR(50) NOT NULL COMMENT 'ID',
`project_id` VARCHAR(50) NOT NULL COMMENT '项目ID',
`name` VARCHAR(64) NOT NULL COMMENT '模块名称',
`parent_id` VARCHAR(50) COMMENT '父级ID',
`level` INT DEFAULT 1 COMMENT '层数',
`create_time` BIGINT NOT NULL COMMENT '创建时间',
`update_time` BIGINT NOT NULL COMMENT '更新时间',
`pos` DOUBLE COMMENT '排序用的标识',
`pos` INT NOT NULL DEFAULT 0 COMMENT '排序用的标识',
`update_user` VARCHAR(50) COMMENT '修改人',
`create_user` VARCHAR(50) COMMENT '创建人',
`module_type` VARCHAR(20) DEFAULT 'module' COMMENT '模块类型: module/repository',
PRIMARY KEY (id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '文件管理模块';
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
COMMENT = '文件管理模块';
CREATE INDEX idx_project_id ON file_module (project_id);
@ -120,6 +133,7 @@ CREATE INDEX idx_update_timed ON file_module (update_time);
CREATE INDEX idx_pos ON file_module (pos);
CREATE INDEX idx_create_user ON file_module (create_user);
CREATE TABLE IF NOT EXISTS project
(
`id` VARCHAR(50) NOT NULL COMMENT '项目ID',
@ -214,15 +228,6 @@ CREATE TABLE IF NOT EXISTS custom_function_blob
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '自定义函数-代码片段大字段';
CREATE TABLE IF NOT EXISTS file_metadata_blob
(
`id` VARCHAR(50) NOT NULL COMMENT '文件ID',
`git_info` LONGBLOB COMMENT '储存库',
PRIMARY KEY (id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '文件基础信息大字段';
CREATE TABLE IF NOT EXISTS message_task
(
`id` VARCHAR(50) NOT NULL COMMENT '' ,

View File

@ -90,6 +90,11 @@ INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_ENVIRONMENT:READ+UPDATE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_ENVIRONMENT:READ+DELETE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_FILE:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_FILE_MANAGEMENT:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_FILE_MANAGEMENT:READ+ADD');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_FILE_MANAGEMENT:READ+UPDATE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_FILE_MANAGEMENT:READ+DOWNLOAD');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_FILE_MANAGEMENT:READ+DELETE');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_TEMPLATE:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_MESSAGE:READ');
INSERT INTO user_role_permission (id, role_id, permission_id) VALUES (UUID_SHORT(), 'project_admin', 'PROJECT_MESSAGE:READ+ADD');

View File

@ -0,0 +1,16 @@
package io.metersphere.sdk.constants;
public class ModuleConstants {
//未规划节点的ID
public static final String DEFAULT_NODE_ID = "root";
//没有父类的节点parent_id为none
public static final String ROOT_NODE_PARENT_ID = "none";
//默认节点类型
public static final String NODE_TYPE_DEFAULT = "module";
//GitHub节点类型
public static final String NODE_TYPE_GITHUB = "GitHub";
//Gitee节点类型
public static final String NODE_TYPE_GITEE = "Gitee";
//GitLab节点类型
public static final String NODE_TYPE_GITLAB = "GitLab";
}

View File

@ -185,4 +185,20 @@ public class PermissionConstants {
public static final String PROJECT_VERSION_READ_UPDATE = "PROJECT_VERSION:READ+UPDATE";
public static final String PROJECT_VERSION_READ_DELETE = "PROJECT_VERSION:READ+DELETE";
/*------ end: PROJECT_VERSION ------*/
/**
* 文件模块树
* 查看文件 项目管理-文件管理-查询权限
* 添加文件 项目管理-文件管理-创建权限
* 编辑文件 项目管理-文件管理-编辑权限
* 下载文件 项目管理-文件管理-下载权限
* 删除文件 项目管理-文件管理-删除权限
*/
public static final String PROJECT_FILE_MANAGEMENT_READ = "PROJECT_FILE_MANAGEMENT:READ";
public static final String PROJECT_FILE_MANAGEMENT_READ_ADD = "PROJECT_FILE_MANAGEMENT:READ+ADD";
public static final String PROJECT_FILE_MANAGEMENT_READ_UPDATE = "PROJECT_FILE_MANAGEMENT:READ+UPDATE";
public static final String PROJECT_FILE_MANAGEMENT_READ_DOWNLOAD = "PROJECT_FILE_MANAGEMENT:READ+DOWNLOAD";
public static final String PROJECT_FILE_MANAGEMENT_READ_DELETE = "PROJECT_FILE_MANAGEMENT:READ+DELETE";
}

View File

@ -0,0 +1,27 @@
package io.metersphere.sdk.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseModule {
@Schema(description = "节点id")
private String id;
@Schema(description = "节点名称")
private String name;
@Schema(description = "排序单位")
private int pos;
@Schema(description = "项目ID")
private String projectId;
@Schema(description = "父节点id")
private String parentId;
}

View File

@ -21,18 +21,27 @@ public class BaseTreeNode {
private String type;
@Schema(description = "是否是叶子节点")
private boolean leafNode;
private boolean leafNode = false;
// @Schema(description = "排序单位")
// private int pos;
@Schema(description = "子节点")
private List<BaseTreeNode> children = new ArrayList<>();
public BaseTreeNode(String id, String name, String type, boolean isLeafNode) {
public BaseTreeNode(String id, String name, String type) {
this.id = id;
this.name = name;
this.type = type;
this.leafNode = isLeafNode;
}
// public BaseTreeNode(String id, String name, String type, int pos) {
// this.id = id;
// this.name = name;
// this.type = type;
// this.pos = pos;
// }
public void addChild(BaseTreeNode node) {
this.leafNode = false;
children.add(node);

View File

@ -0,0 +1,27 @@
package io.metersphere.sdk.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class RemoteFileAttachInfo implements Serializable {
private String repositoryPath;
private String userName;
private String token;
private String branch;
private String commitId;
private String filePath;
private String commitMessage;
private long size;
public String getRepositoryInfo() {
return repositoryPath + "-" + userName + "-" + token;
}
}

View File

@ -0,0 +1,24 @@
package io.metersphere.sdk.dto.request;
import io.metersphere.sdk.constants.ModuleConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
@Data
public class NodeMoveRequest {
@Schema(description = "模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{node.not_blank}")
private String nodeId;
@Schema(description = "父模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{parent.node.not_blank}")
private String parentId = ModuleConstants.ROOT_NODE_PARENT_ID;
@Schema(description = "前一个节点ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String previousNodeId;
@Schema(description = "后一个节点ID", requiredMode = Schema.RequiredMode.REQUIRED)
private String nextNodeId;
}

View File

@ -17,6 +17,16 @@ public class FileCenter {
return fileRepository == null ? getDefaultRepository() : fileRepository;
}
public static FileRepository getRepository(String storage) {
Map<String, StorageType> storageTypeMap = new HashMap<>() {{
put(StorageType.MINIO.name(), StorageType.MINIO);
put(StorageType.LOCAL.name(), StorageType.LOCAL);
put(StorageType.GIT.name(), StorageType.GIT);
}};
return getRepository(storageTypeMap.get(storage.toUpperCase()));
}
public static FileRepository getDefaultRepository() {
return CommonBeanFactory.getBean(MinioRepository.class);
}

View File

@ -8,7 +8,10 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@ -38,7 +41,7 @@ public class MinioRepository implements FileRepository {
.object(filePath)
.stream(file.getInputStream(), file.getSize(), -1) // 文件内容
.build());
return request.getFileName();
return filePath;
}
@Override

View File

@ -0,0 +1,9 @@
package io.metersphere.sdk.mapper;
import org.apache.ibatis.annotations.Param;
public interface BaseModuleMapper {
long addResourceCount(@Param("tableName") String tableName, @Param("primaryKey") String primaryKey, @Param("count") int count);
long subResourceCount(@Param("tableName") String tableName, @Param("primaryKey") String primaryKey, @Param("count") int count);
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.sdk.mapper.BaseModuleMapper">
<update id="addResourceCount">
UPDATE #{tableName}
SET resource_count = resource_count + #{count}
WHERE id = #{primaryKey}
</update>
<update id="subResourceCount">
UPDATE #{tableName}
SET resource_count = resource_count - #{count}
WHERE id = #{primaryKey}
</update>
</mapper>

View File

@ -0,0 +1,21 @@
package io.metersphere.sdk.service;
import io.metersphere.sdk.file.FileCenter;
import io.metersphere.sdk.file.FileRequest;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@Service
public class FileService {
public String upload(MultipartFile file, FileRequest request) throws Exception {
return FileCenter.getRepository(request.getStorage()).saveFile(file, request);
}
public byte[] download(FileRequest request) throws Exception {
return FileCenter.getRepository(request.getStorage()).getFile(request);
}
public void deleteFile(FileRequest request) throws Exception {
FileCenter.getRepository(request.getStorage()).delete(request);
}
}

View File

@ -0,0 +1,74 @@
package io.metersphere.sdk.service;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.dto.BaseModule;
import io.metersphere.sdk.dto.BaseTreeNode;
import io.metersphere.sdk.dto.request.NodeMoveRequest;
import io.metersphere.sdk.mapper.BaseModuleMapper;
import io.metersphere.sdk.util.Translator;
import org.apache.commons.lang3.StringUtils;
public abstract class ModuleTreeService {
protected static final int LIMIT_POS = 64;
public BaseTreeNode getDefaultModule() {
//默认模块下不允许创建子模块 它本身也就是叶子节点
return new BaseTreeNode(ModuleConstants.DEFAULT_NODE_ID, Translator.get("default.module"), ModuleConstants.NODE_TYPE_DEFAULT);
}
public long changeResourceCount(String tableName, String primaryKey, int count, boolean isAdd, BaseModuleMapper baseModuleMapper) {
if (isAdd) {
return baseModuleMapper.addResourceCount(tableName, primaryKey, count);
} else {
return baseModuleMapper.subResourceCount(tableName, primaryKey, count);
}
}
/**
* 模块树排序
*/
public void sort(NodeMoveRequest nodeMoveRequest) {
if (StringUtils.isAllBlank(nodeMoveRequest.getPreviousNodeId(), nodeMoveRequest.getNextNodeId())) {
// 没有相邻节点pos为0
updatePos(nodeMoveRequest.getNodeId(), 0);
} else {
BaseModule previousNode = null;
BaseModule nextNode = null;
// 获取相邻节点
if (StringUtils.isNotBlank(nodeMoveRequest.getPreviousNodeId())) {
previousNode = getNode(nodeMoveRequest.getPreviousNodeId());
}
if (StringUtils.isNotBlank(nodeMoveRequest.getNextNodeId())) {
nextNode = getNode(nodeMoveRequest.getNextNodeId());
}
boolean refreshPos = false;
int pos;
if (nextNode == null) {
pos = previousNode.getPos() + LIMIT_POS;
} else if (previousNode == null) {
pos = nextNode.getPos() / 2;
if (pos < 2) {
refreshPos = true;
}
} else {
int quantityDifference = (nextNode.getPos() - previousNode.getPos()) / 2;
if (quantityDifference <= 2) {
refreshPos = true;
}
pos = previousNode.getPos() + quantityDifference;
}
updatePos(nodeMoveRequest.getNodeId(), pos);
if (refreshPos) {
refreshPos(nodeMoveRequest.getParentId());
}
}
}
public abstract BaseModule getNode(String id);
public abstract void updatePos(String id, int pos);
public abstract void refreshPos(String parentId);
}

View File

@ -0,0 +1,67 @@
package io.metersphere.sdk.util;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
public class FilePreviewUtil {
private static final String BASE_FILE_FOLDER = File.separator + "opt" + File.separator + "metersphere" + File.separator + "data" + File.separator + "file" + File.separator + "preview" + File.separator;
public static boolean isImage(String type) {
return StringUtils.equalsAnyIgnoreCase(type, "jpg", "jpeg", "png", "gif", "bmp", "svg", "ico");
}
//生成执行文件的绝对路径
public static String getFileAbsolutePath(String fileName) {
return BASE_FILE_FOLDER + fileName;
}
public static File getFile(String fileName) {
File file = new File(getFileAbsolutePath(fileName));
if (file.exists()) {
return file;
} else {
return null;
}
}
public static String catchFileIfNotExists(String fileName, byte[] fileBytes) {
if (getFile(fileName) == null && fileBytes != null) {
createFile(getFileAbsolutePath(fileName), fileBytes);
}
return getFileAbsolutePath(fileName);
}
public static void deleteFile(String deleteFileName) {
File file = new File(getFileAbsolutePath(deleteFileName));
if (file.exists()) {
file.delete();
}
}
private static void createFile(String filePath, byte[] fileBytes) {
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
try {
File dir = file.getParentFile();
if (!dir.exists()) {
dir.mkdirs();
}
file.createNewFile();
} catch (Exception e) {
LogUtils.error(e);
}
try (InputStream in = new ByteArrayInputStream(fileBytes); OutputStream out = new FileOutputStream(file)) {
final int MAX = 4096;
byte[] buf = new byte[MAX];
for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
out.write(buf, 0, bytesRead);
}
} catch (IOException e) {
LogUtils.error(e);
}
}
}

View File

@ -33,7 +33,6 @@ public class JSON {
// 设置JSON处理字符长度限制
objectMapper.getFactory()
.setStreamReadConstraints(StreamReadConstraints.builder().maxStringLength(DEFAULT_MAX_STRING_LEN).build());
// 处理时间格式
objectMapper.registerModule(new JavaTimeModule());
}

View File

@ -444,4 +444,8 @@ template_scene_illegal_error=使用场景不合法
# 内置的模板或字段
custom_field.functional_priority=优先级
template.functional_default=默认模板
template.functional_default=默认模板
parent.node.not_blank=父节点不能为空
node.not_blank=节点不能为空
node.name.repeat=节点名称重复
project.cannot.match.parent=和父节点的项目无法匹配

View File

@ -453,3 +453,8 @@ global_parameters_already_exist=Global parameters already exist
global_parameters_is_not_exist=Global parameters is not exist
api_test_environment_not_exist=Environment is not exist
parent.node.not_blank=Parent node can not blank
node.not_blank=Node can not blank
node.name.repeat=Name repeat
project.cannot.match.parent=Project can not match parent

View File

@ -449,4 +449,8 @@ template.functional_default=默认模板
global_parameters_already_exist=全局参数已存在
global_parameters_is_not_exist=全局参数不存在
parent.node.not_blank=父节点不能为空
node.not_blank=节点不能为空
node.name.repeat=节点名称重复
project.cannot.match.parent=和父节点的项目无法匹配
api_test_environment_not_exist=环境不存在

View File

@ -448,4 +448,9 @@ template.functional_default=默認模板
global_parameters_already_exist=全局參數已存在
global_parameters_is_not_exist=全局參數不存在
parent.node.not_blank=父節點不能為空
node.not_blank=節點不能為空
node.name.repeat=節點名稱重複
project.cannot.match.parent=和父節點的項目無法匹配
api_test_environment_not_exist=環境不存在

View File

@ -114,3 +114,18 @@ permission.project_application_ui.read=UI测试-查询
permission.project_application_ui.update=UI测试-编辑
permission.project_base_info.name=基本信息
permission.project_log.name=日志
#file management
file_module.not.exist=文件模块不存在
upload.file.error=上传文件失败
file.not.exist=文件不存在
old.file.not.exist=旧文件不存在
file.name.exist=文件名已存在
file.log.delete_module=模块下的所有数据全部被删除
file.module.root=根目录
file.log.move_to=移动到
file.log.next=之后
file.log.previous=之前
file.log.upload=上传
file.log.re-upload=重新上传
file.name.cannot.be.empty=文件名称不能为空
#file management over

View File

@ -115,6 +115,7 @@ permission.project_user.name=User
permission.project_group.name=User group
permission.project_environment.name=Environment config
permission.project_file.name=File management
permission.project_file.download=Download file
permission.project_template.name=Template management
permission.project_message.name=Message management
permission.project_version.name=Version management
@ -146,3 +147,19 @@ environment_datasource.driverId.not_blank=Driver ID is required
environment_datasource.dbUrl.not_blank=Db Url is required
environment_name_is_null=Environment name is required
environment_config_is_null=Environment config is required
#file management
file_module.not.exist=File module does not exist
upload.file.error=Upload file error
file.not.exist=File does not exist
old.file.not.exist=Old file does not exist
file.name.exist=File name already exists
file.log.delete_module=has be deleted
file.module.root=root module
file.log.move_to=move to
file.log.next=next
file.log.previous=behind
file.log.upload=upload
file.log.re-upload=re-upload
file.name.cannot.be.empty=File name cannot be empty
#file management over

View File

@ -115,6 +115,7 @@ permission.project_user.name=用户
permission.project_group.name=用户组
permission.project_environment.name=环境配置
permission.project_file.name=文件管理
permission.project_file.download=文件下载
permission.project_template.name=模版管理
permission.project_message.name=消息管理
permission.project_version.name=版本管理
@ -146,3 +147,19 @@ environment_datasource.driverId.not_blank=驱动ID不能为空
environment_datasource.dbUrl.not_blank=数据库连接不能为空
environment_name_is_null=环境名称不能为空
environment_config_is_null=环境配置不能为空
#file management
file_module.not.exist=文件模块不存在
upload.file.error=上传文件失败
file.not.exist=文件不存在
old.file.not.exist=旧文件不存在
file.name.exist=文件名已存在
file.log.delete_module=模块下的所有数据全部被删除
file.module.root=根目录
file.log.move_to=移动到
file.log.next=之后
file.log.previous=之前
file.log.upload=上传
file.log.re-upload=重新上传
file.name.cannot.be.empty=文件名称不能为空
#file management over

View File

@ -115,6 +115,7 @@ permission.project_user.name=用戶
permission.project_group.name=用戶組
permission.project_environment.name=環境配置
permission.project_file.name=文件管理
permission.project_file.download=文件下載
permission.project_template.name=模版管理
permission.project_message.name=消息管理
permission.project_version.name=版本管理
@ -145,4 +146,19 @@ environment_datasource.driver.not_blank=驅動不能為空
environment_datasource.driverId.not_blank=驅動ID不能為空
environment_datasource.dbUrl.not_blank=數據庫地址不能為空
environment_name_is_null=環境名稱不能為空
environment_config_is_null=環境配置不能為空
environment_config_is_null=環境配置不能為空
#file management
file_module.not.exist=文件模塊不存在
upload.file.error=上傳文件失敗
file.not.exist=文件不存在
old.file.not.exist=舊文件不存在
file.name.exist=文件名已存在
file.log.delete_module=模塊下的所有數據全部被刪除
file.module.root=根目錄
file.log.move_to=移動到
file.log.next=之後
file.log.previous=之前
file.log.upload=上傳
file.log.re-upload=重新上傳
file.name.cannot.be.empty=文件名稱不能為空
#file management over

View File

@ -4,6 +4,7 @@ role.not.global.system=Role is not global system role
role.not.contains.member=Role not contains member
user.not.login=User not login
user.not.exist=User not exist
default.module=Default module
user.not.empty=User can not empty
auth_source.id.not_blank=Auth source id must not be blank
auth_source.status.length_range=Auth source status must be between {min} and {max} characters long

View File

@ -5,6 +5,7 @@ role.not.contains.member=角色不包含系统成员角色
user.not.login=未获取到登录用户
user.not.empty=用户不呢为空
user.not.exist=用户不存在
default.module=默认模块
auth_source.id.not_blank=认证源ID不能为空
auth_source.status.length_range=认证源状态长度必须在{min}和{max}之间
auth_source.status.not_blank=认证源状态不能为空

View File

@ -5,6 +5,7 @@ role.not.contains.member=角色不包含系統成員角色
user.not.login=未獲取到登錄用戶
user.not.empty=用戶不呢為空
user.not.exist=用戶不存在
default.module=默認模塊
auth_source.id.not_blank=認證源ID不能為空
auth_source.status.length_range=認證源狀態長度必須在{min}和{max}之間
auth_source.status.not_blank=認證源狀態不能為空

View File

@ -0,0 +1,87 @@
package io.metersphere.project.controller;
import io.metersphere.project.dto.FileTableResult;
import io.metersphere.project.request.filemanagement.*;
import io.metersphere.project.service.FileManagementService;
import io.metersphere.project.service.FileMetadataService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.utils.SessionUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@Tag(name = "项目管理-文件管理-文件")
@RestController
@RequestMapping("/project/file")
public class FileManagementController {
@Resource
private FileMetadataService fileMetadataService;
@Resource
private FileManagementService fileManagementService;
@PostMapping("/page")
@Operation(summary = "项目管理-文件管理-表格分页查询文件")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ)
public FileTableResult page(@Validated @RequestBody FileMetadataTableRequest request) {
return fileMetadataService.page(request);
}
@PostMapping("/upload")
@Operation(summary = "项目管理-文件管理-上传文件")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_ADD)
public String upload(@Validated @RequestPart("request") FileUploadRequest request, @RequestPart(value = "file", required = false) MultipartFile uploadFile) {
try {
return fileMetadataService.upload(request, SessionUtils.getUserId(), uploadFile);
} catch (Exception e) {
throw new MSException(Translator.get("upload.file.error"), e);
}
}
@PostMapping("/re-upload")
@Operation(summary = "项目管理-文件管理-重新上传文件")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_UPDATE)
public String reUpload(@Validated @RequestPart("request") FileReUploadRequest request, @RequestPart(value = "file", required = false) MultipartFile uploadFile) {
try {
return fileMetadataService.reUpload(request, SessionUtils.getUserId(), uploadFile);
} catch (Exception e) {
throw new MSException(Translator.get("upload.file.error"), e);
}
}
@GetMapping(value = "/download/{id}")
@Operation(summary = "项目管理-文件管理-下载文件")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_DOWNLOAD)
public ResponseEntity<byte[]> download(@PathVariable String id) throws Exception {
return fileMetadataService.downloadById(id);
}
@PostMapping(value = "/delete")
@Operation(summary = "项目管理-文件管理-删除文件")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_DELETE)
public void delete(@Validated @RequestBody FileBatchProcessDTO request) throws Exception {
fileManagementService.delete(request, SessionUtils.getUserId());
}
@PostMapping(value = "/update")
@Operation(summary = "项目管理-文件管理-修改文件")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_UPDATE)
public void update(@Validated @RequestBody FileUpdateRequest request) throws Exception {
fileMetadataService.update(request, SessionUtils.getUserId());
}
@PostMapping(value = "/batch-download")
@Operation(summary = "项目管理-文件管理-批量下载文件")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_DOWNLOAD)
public ResponseEntity<byte[]> downloadBodyFiles(@Validated @RequestBody FileBatchProcessDTO request) {
return fileMetadataService.batchDownload(request);
}
}

View File

@ -0,0 +1,71 @@
package io.metersphere.project.controller;
import io.metersphere.project.request.filemanagement.FileModuleCreateRequest;
import io.metersphere.project.request.filemanagement.FileModuleUpdateRequest;
import io.metersphere.project.service.FileModuleLogService;
import io.metersphere.project.service.FileModuleService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.dto.BaseTreeNode;
import io.metersphere.sdk.dto.request.NodeMoveRequest;
import io.metersphere.system.log.annotation.Log;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.utils.SessionUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "项目管理-文件管理-模块")
@RestController
@RequestMapping("/project/file-module")
public class FileModuleController {
@Resource
private FileModuleService fileModuleService;
@GetMapping("/tree/{projectId}")
@Operation(summary = "项目管理-文件管理-模块-查找模块")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ)
public List<BaseTreeNode> getTree(@PathVariable String projectId) {
return fileModuleService.getTree(projectId);
}
@PostMapping("/add")
@Operation(summary = "项目管理-文件管理-模块-添加模块")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_ADD)
public String add(@RequestBody @Validated FileModuleCreateRequest request) {
return fileModuleService.add(request, SessionUtils.getUserId());
}
@PostMapping("/update")
@Operation(summary = "项目管理-文件管理-模块-修改模块")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_UPDATE)
public boolean list(@RequestBody @Validated FileModuleUpdateRequest request) {
fileModuleService.update(request, SessionUtils.getUserId());
return true;
}
@GetMapping("/delete/{deleteId}")
@Operation(summary = "项目管理-文件管理-模块-删除模块")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#deleteId)", msClass = FileModuleLogService.class)
public void deleteNode(@PathVariable String deleteId) {
fileModuleService.deleteModule(deleteId);
}
@PostMapping("/move")
@Operation(summary = "项目管理-文件管理-模块-移动模块")
@RequiresPermissions(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_UPDATE)
public void moveNode(@RequestBody NodeMoveRequest request) {
/**
* 拖拽操作 两种同级移动 跨级移动
* 1.判断移动后的parentID判断是否是移动到其余的目录下
* 2.拖拽后的前后ID 用于排序
*/
fileModuleService.moveNode(request);
}
}

View File

@ -0,0 +1,54 @@
package io.metersphere.project.dto;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.sdk.util.JSON;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@Data
@NoArgsConstructor
public class FileInformationDTO {
@Schema(description = "ID")
private String id;
@Schema(description = "文件名称")
private String name;
@Schema(description = "文件类型")
private String fileType;
@Schema(description = "标签")
private List<String> tags;
@Schema(description = "描述")
private String description;
@Schema(description = "更新人")
private String updateUser;
@Schema(description = "更新时间")
private long updateTime;
@Schema(description = "预览路径")
private String previewSrc;
@Schema(description = "文件大小")
private long size;
public FileInformationDTO(FileMetadata fileMetadata) {
this.id = fileMetadata.getId();
this.name = fileMetadata.getName();
this.fileType = fileMetadata.getType();
this.size = fileMetadata.getSize();
if (StringUtils.isNotBlank(fileMetadata.getTags())) {
tags = JSON.parseArray(fileMetadata.getTags(), String.class);
}
this.description = fileMetadata.getDescription();
this.updateUser = fileMetadata.getUpdateUser();
this.updateTime = fileMetadata.getUpdateTime();
}
}

View File

@ -0,0 +1,18 @@
package io.metersphere.project.dto;
import io.metersphere.sdk.util.Pager;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Data
public class FileTableResult {
@Schema(description = "表格数据")
Pager<List<FileInformationDTO>> tableData;
@Schema(description = "模块统计")
Map<String, Long> moduleCount = new HashMap<>();
}

View File

@ -0,0 +1,9 @@
package io.metersphere.project.dto;
import lombok.Data;
@Data
public class ModuleCountDTO {
private String moduleId;
private long dataCount;
}

View File

@ -0,0 +1,23 @@
package io.metersphere.project.mapper;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.project.dto.ModuleCountDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtFileMetadataMapper {
List<FileMetadata> selectByKeywordAndFileType(@Param("projectId") String projectId, @Param("keyword") String keyword, @Param("moduleIds") List<String> moduleIds, @Param("fileTypes") List<String> fileTypes, @Param("isRefId") boolean isRefId);
List<ModuleCountDTO> countModuleIdByKeywordAndFileType(@Param("projectId") String projectId, @Param("keyword") String keyword, @Param("moduleIds") List<String> moduleIds, @Param("fileTypes") List<String> fileTypes);
List<String> selectIdByRefIdList(@Param("refIdList") List<String> refIdList);
List<FileMetadata> selectDeleteFileInfoByIds(@Param("ids") List<String> ids);
List<FileMetadata> selectDeleteFileInfoByRefIdList(@Param("refIdList") List<String> refIdList);
List<FileMetadata> selectRefIdByIds(@Param("fileIds") List<String> processIds);
List<FileMetadata> selectRefIdByModuleIds(@Param("moduleIds") List<String> moduleIds);
}

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.project.mapper.ExtFileMetadataMapper">
<select id="selectByKeywordAndFileType" resultType="io.metersphere.project.domain.FileMetadata">
SELECT
f.id,
<if test="isRefId">
f.ref_id
</if>
<if test="!isRefId">
f.name,
f.type,
f.tags,
f.description,
u.name as update_user,
f.update_time,
f.path,
f.project_id,
f.size,
f.storage
</if>
FROM file_metadata f
INNER JOIN user u ON f.update_user = u.id
WHERE latest = true
AND f.project_id = #{projectId}
<if test="keyword != null and keyword != ''">
AND f.name = #{keyword}
</if>
<if test="moduleIds != null and moduleIds.size() != 0">
AND f.module_id IN
<foreach collection="moduleIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="fileTypes != null and fileTypes.size() != 0 ">
AND f.type IN
<foreach collection="fileTypes" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</select>
<select id="selectIdByRefIdList" resultType="java.lang.String">
SELECT id FROM file_metadata WHERE ref_id IN
<foreach collection="refIdList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<select id="selectDeleteFileInfoByIds" resultType="io.metersphere.project.domain.FileMetadata">
SELECT
f.id,
f.project_id,
f.storage
FROM file_metadata f WHERE f.id IN
<foreach collection="ids" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<select id="countModuleIdByKeywordAndFileType" resultType="io.metersphere.project.dto.ModuleCountDTO">
SELECT f.module_id AS moduleId, count(f.id) AS dataCount
FROM file_metadata f
INNER JOIN user u ON f.update_user = u.id
WHERE latest = true
AND f.project_id = #{projectId}
<if test="keyword != null and keyword != ''">
AND f.name = #{keyword}
</if>
<if test="moduleIds != null and moduleIds.size() != 0">
AND f.module_id IN
<foreach collection="moduleIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="fileTypes != null and fileTypes.size() != 0 ">
AND f.type IN
<foreach collection="fileTypes" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
GROUP BY f.module_id
</select>
<select id="selectRefIdByIds" resultType="io.metersphere.project.domain.FileMetadata">
SELECT DISTINCT f.ref_id FROM file_metadata f
WHERE
f.id IN
<foreach collection="fileIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<select id="selectDeleteFileInfoByRefIdList" resultType="io.metersphere.project.domain.FileMetadata">
SELECT
f.id,
f.project_id,
f.storage
FROM file_metadata f WHERE f.ref_id IN
<foreach collection="refIdList" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<select id="selectRefIdByModuleIds" resultType="io.metersphere.project.domain.FileMetadata">
SELECT
f.ref_id
FROM file_metadata f
WHERE f.module_id IN
<foreach collection="moduleIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
</mapper>

View File

@ -0,0 +1,20 @@
package io.metersphere.project.mapper;
import io.metersphere.project.domain.FileModule;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtFileModuleMapper {
List<FileModule> selectBaseByProjectId(String projectId);
List<String> selectChildrenIdsByParentIds(@Param("ids") List<String> deleteIds);
List<String> selectChildrenIdsSortByPos(String parentId);
void deleteByIds(@Param("ids") List<String> deleteId);
Integer getMaxPosByParentId(String parentId);
List<String> selectIdsByProjectId(String projectId);
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.project.mapper.ExtFileModuleMapper">
<select id="selectBaseByProjectId" resultType="io.metersphere.project.domain.FileModule">
SELECT id, name, parent_id, pos
FROM file_module
WHERE project_id = #{0}
ORDER BY pos
</select>
<select id="selectIdsByProjectId" resultType="java.lang.String">
SELECT id
FROM file_module
WHERE project_id = #{0}
</select>
<select id="selectChildrenIdsByParentIds" resultType="java.lang.String">
SELECT id FROM file_module WHERE parent_id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<delete id="deleteByIds">
DELETE FROM file_module WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
<select id="getMaxPosByParentId" resultType="java.lang.Integer">
SELECT max(pos) FROM file_module
WHERE parent_id = #{0}
</select>
<select id="selectChildrenIdsSortByPos" resultType="java.lang.String">
SELECT id FROM file_module WHERE parent_id = #{0}
ORDER BY pos ASC
</select>
</mapper>

View File

@ -0,0 +1,38 @@
package io.metersphere.project.request.filemanagement;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class FileBatchProcessDTO {
@Schema(description = "不处理的ID")
List<String> excludeIds;
@Schema(description = "项目ID")
@NotBlank(message = "{id must not be blank}")
private String projectId;
@Schema(description = "选择的ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Valid
private List<
@NotBlank(message = "{id must not be blank}")
String
> selectIds = new ArrayList<>();
@Schema(description = "是否选择所有数据")
private boolean selectAll;
@Schema(description = "文件类型")
private List<String> fileTypes;
@Schema(description = "关键字")
private String keyword;
@Schema(description = "模块ID")
private List<String> moduleIds;
}

View File

@ -0,0 +1,21 @@
package io.metersphere.project.request.filemanagement;
import io.metersphere.sdk.dto.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.List;
@Data
public class FileMetadataTableRequest extends BasePageRequest {
@Schema(description = "模块ID(根据模块树查询时要把当前节点以及子节点都放在这里。)")
private List<String> moduleIds;
@Schema(description = "文件类型")
private List<String> fileTypes;
@Schema(description = "项目ID")
@NotBlank(message = "{id must not be blank}")
private String projectId;
}

View File

@ -0,0 +1,23 @@
package io.metersphere.project.request.filemanagement;
import io.metersphere.sdk.constants.ModuleConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
@Data
public class FileModuleCreateRequest {
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{project.id.not_blank}")
private String projectId;
@Schema(description = "模块名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{file_module.name.not_blank}")
private String name;
@Schema(description = "父模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{parent.node.not_blank}")
private String parentId = ModuleConstants.ROOT_NODE_PARENT_ID;
}

View File

@ -0,0 +1,18 @@
package io.metersphere.project.request.filemanagement;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
@Data
public class FileModuleUpdateRequest {
@Schema(description = "模块ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{file_module.id.not_blank}")
private String id;
@Schema(description = "模块名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "{file_module.name.not_blank}")
private String name;
}

View File

@ -0,0 +1,12 @@
package io.metersphere.project.request.filemanagement;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class FileReUploadRequest {
@Schema(description = "文件Id")
@NotBlank(message = "{file_metadata.id.not_blank}")
private String fileId;
}

View File

@ -0,0 +1,28 @@
package io.metersphere.project.request.filemanagement;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.List;
@Data
public class FileUpdateRequest {
@Schema(description = "文件Id")
@NotBlank(message = "{file_metadata.id.not_blank}")
private String id;
@Schema(description = "文件名称")
private String name;
@Schema(description = "标签")
private List<
@NotBlank
String> tags;
@Schema(description = "文件描述")
private String description;
@Schema(description = "模块ID")
private String moduleId;
}

View File

@ -0,0 +1,17 @@
package io.metersphere.project.request.filemanagement;
import io.metersphere.sdk.constants.ModuleConstants;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class FileUploadRequest {
@Schema(description = "项目Id")
@NotBlank(message = "{project.id.not_blank}")
private String projectId;
@Schema(description = "模块Id")
@NotBlank(message = "{file_module.id.not_blank}")
private String moduleId = ModuleConstants.DEFAULT_NODE_ID;
}

View File

@ -0,0 +1,139 @@
package io.metersphere.project.service;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.project.domain.FileMetadataExample;
import io.metersphere.project.domain.FileModuleExample;
import io.metersphere.project.mapper.ExtFileMetadataMapper;
import io.metersphere.project.mapper.FileMetadataMapper;
import io.metersphere.project.mapper.FileModuleMapper;
import io.metersphere.project.request.filemanagement.FileBatchProcessDTO;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.service.FileService;
import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class FileManagementService {
@Resource
private FileMetadataMapper fileMetadataMapper;
@Resource
private FileModuleMapper fileModuleMapper;
@Resource
private FileService fileService;
@Resource
private FileMetadataLogService fileMetadataLogService;
@Resource
private ExtFileMetadataMapper extFileMetadataMapper;
public void checkModule(String moduleId, String nodeTypeDefault) {
if (!StringUtils.equals(moduleId, ModuleConstants.DEFAULT_NODE_ID)) {
FileModuleExample example = new FileModuleExample();
example.createCriteria().andIdEqualTo(moduleId).andModuleTypeEqualTo(nodeTypeDefault);
if (fileModuleMapper.countByExample(example) == 0) {
throw new MSException("file_module.not.exist");
}
}
}
public void delete(FileBatchProcessDTO request, String operator) {
List<FileMetadata> deleteList = this.getDeleteList(request);
List<String> deleteIds = deleteList.stream().map(FileMetadata::getId).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(deleteIds)) {
FileMetadataExample example = new FileMetadataExample();
example.createCriteria().andIdIn(deleteIds);
fileMetadataMapper.deleteByExample(example);
//记录日志
fileMetadataLogService.saveDeleteLog(deleteList, request.getProjectId(), operator);
deleteList.forEach(fileMetadata -> {
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(fileMetadata.getId());
fileRequest.setProjectId(fileMetadata.getProjectId());
fileRequest.setStorage(fileMetadata.getStorage());
try {
fileService.deleteFile(fileRequest);
} catch (Exception e) {
LogUtils.error("删除文件失败", e);
}
});
}
}
public List<FileMetadata> getDeleteList(FileBatchProcessDTO request) {
List<String> processIds = request.getSelectIds();
List<FileMetadata> refFileList = new ArrayList<>();
if (request.isSelectAll()) {
refFileList = extFileMetadataMapper.selectByKeywordAndFileType(request.getProjectId(), request.getKeyword(), request.getModuleIds(), request.getFileTypes(), true);
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {
refFileList = refFileList.stream().filter(fileMetadata -> !request.getExcludeIds().contains(fileMetadata.getId())).collect(Collectors.toList());
}
} else if (CollectionUtils.isNotEmpty(processIds)) {
refFileList = extFileMetadataMapper.selectRefIdByIds(processIds);
}
List<String> refIdList = refFileList.stream().map(FileMetadata::getRefId).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(refIdList)) {
processIds = extFileMetadataMapper.selectIdByRefIdList(refIdList);
return extFileMetadataMapper.selectDeleteFileInfoByIds(processIds);
} else {
return new ArrayList<>();
}
}
public List<FileMetadata> getProcessList(FileBatchProcessDTO request) {
List<String> processIds = request.getSelectIds();
List<FileMetadata> refFileList = new ArrayList<>();
if (request.isSelectAll()) {
refFileList = extFileMetadataMapper.selectByKeywordAndFileType(request.getProjectId(), request.getKeyword(), request.getModuleIds(), request.getFileTypes(), false);
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {
refFileList = refFileList.stream().filter(fileMetadata -> !request.getExcludeIds().contains(fileMetadata.getId())).collect(Collectors.toList());
}
} else if (CollectionUtils.isNotEmpty(processIds)) {
refFileList = extFileMetadataMapper.selectRefIdByIds(processIds);
}
return refFileList;
}
public void deleteByModuleIds(List<String> deleteModuleIds) {
//获取要删除的文件引用ID
List<FileMetadata> refFileList = extFileMetadataMapper.selectRefIdByModuleIds(deleteModuleIds);
List<String> refIdList = refFileList.stream().map(FileMetadata::getRefId).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(refIdList)) {
//获取要删除的所有文件ID
List<FileMetadata> deleteList = extFileMetadataMapper.selectDeleteFileInfoByRefIdList(refIdList);
if (CollectionUtils.isNotEmpty(deleteList)) {
FileMetadataExample example = new FileMetadataExample();
example.createCriteria().andIdIn(
deleteList.stream().map(FileMetadata::getId).collect(Collectors.toList()));
fileMetadataMapper.deleteByExample(example);
deleteList.forEach(fileMetadata -> {
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(fileMetadata.getId());
fileRequest.setProjectId(fileMetadata.getProjectId());
fileRequest.setStorage(fileMetadata.getStorage());
try {
fileService.deleteFile(fileRequest);
} catch (Exception e) {
LogUtils.error("删除文件失败", e);
}
});
}
}
}
}

View File

@ -0,0 +1,104 @@
package io.metersphere.project.service;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.FileMetadataMapper;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.dto.LogDTO;
import io.metersphere.sdk.dto.builder.LogDTOBuilder;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.service.OperationLogService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional(rollbackFor = Exception.class)
public class FileMetadataLogService {
@Resource
private FileMetadataMapper fileMetadataMapper;
@Resource
private ProjectMapper projectMapper;
@Resource
private OperationLogService operationLogService;
public void saveUploadLog(FileMetadata module, String operator) {
Project project = projectMapper.selectByPrimaryKey(module.getProjectId());
LogDTO dto = LogDTOBuilder.builder()
.projectId(module.getProjectId())
.organizationId(project.getOrganizationId())
.type(OperationLogType.ADD.name())
.module(OperationLogModule.PROJECT_FILE_MANAGEMENT)
.method(HttpMethodConstants.POST.name())
.path("/project/file/upload")
.sourceId(module.getId())
.content(Translator.get("file.log.upload") + " " + module.getName())
.originalValue(JSON.toJSONBytes(module))
.createUser(operator)
.build().getLogDTO();
operationLogService.add(dto);
}
public void saveReUploadLog(FileMetadata module, String operator) {
Project project = projectMapper.selectByPrimaryKey(module.getProjectId());
LogDTO dto = LogDTOBuilder.builder()
.projectId(module.getProjectId())
.organizationId(project.getOrganizationId())
.type(OperationLogType.UPDATE.name())
.module(OperationLogModule.PROJECT_FILE_MANAGEMENT)
.method(HttpMethodConstants.POST.name())
.path("/project/file/re-upload")
.sourceId(module.getId())
.content(Translator.get("file.log.re-upload") + " " + module.getName())
.originalValue(JSON.toJSONBytes(module))
.createUser(operator)
.build().getLogDTO();
operationLogService.add(dto);
}
public void saveUpdateLog(FileMetadata module, String projectId, String operator) {
Project project = projectMapper.selectByPrimaryKey(projectId);
LogDTO dto = LogDTOBuilder.builder()
.projectId(projectId)
.organizationId(project.getOrganizationId())
.type(OperationLogType.UPDATE.name())
.module(OperationLogModule.PROJECT_FILE_MANAGEMENT)
.method(HttpMethodConstants.POST.name())
.path("/project/file/update")
.sourceId(module.getId())
.content(module.getName())
.originalValue(JSON.toJSONBytes(module))
.createUser(operator)
.build().getLogDTO();
operationLogService.add(dto);
}
public void saveDeleteLog(List<FileMetadata> deleteList, String projectId, String operator) {
Project project = projectMapper.selectByPrimaryKey(projectId);
List<LogDTO> list = new ArrayList<>();
for (FileMetadata fileMetadata : deleteList) {
LogDTO dto = LogDTOBuilder.builder()
.projectId(projectId)
.organizationId(project.getOrganizationId())
.type(OperationLogType.DELETE.name())
.module(OperationLogModule.PROJECT_FILE_MANAGEMENT)
.method(HttpMethodConstants.POST.name())
.path("/project/file/delete")
.sourceId(fileMetadata.getId())
.content(fileMetadata.getName())
.originalValue(JSON.toJSONBytes(fileMetadata))
.createUser(operator)
.build().getLogDTO();
list.add(dto);
}
operationLogService.batchAdd(list);
}
}

View File

@ -0,0 +1,297 @@
package io.metersphere.project.service;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.project.domain.FileMetadata;
import io.metersphere.project.domain.FileMetadataExample;
import io.metersphere.project.dto.FileInformationDTO;
import io.metersphere.project.dto.FileTableResult;
import io.metersphere.project.dto.ModuleCountDTO;
import io.metersphere.project.mapper.ExtFileMetadataMapper;
import io.metersphere.project.mapper.FileMetadataMapper;
import io.metersphere.project.request.filemanagement.*;
import io.metersphere.project.utils.FileDownloadUtils;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.constants.StorageType;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.service.FileService;
import io.metersphere.sdk.util.*;
import io.metersphere.system.uid.UUID;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.unit.DataSize;
import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class FileMetadataService {
@Resource
private FileMetadataMapper fileMetadataMapper;
@Resource
private ExtFileMetadataMapper extFileMetadataMapper;
@Resource
private FileMetadataLogService fileMetadataLogService;
@Resource
private FileManagementService fileManagementService;
@Resource
private FileService fileService;
@Value("${metersphere.file.batch-download-max:600MB}")
private DataSize maxFileSize;
public List<FileInformationDTO> list(FileMetadataTableRequest request) {
List<FileInformationDTO> returnList = new ArrayList<>();
List<FileMetadata> fileMetadataList = extFileMetadataMapper.selectByKeywordAndFileType(request.getProjectId(), request.getKeyword(), request.getModuleIds(), request.getFileTypes(), false);
fileMetadataList.forEach(fileMetadata -> {
FileInformationDTO fileInformationDTO = new FileInformationDTO(fileMetadata);
if (FilePreviewUtil.isImage(fileMetadata.getType())) {
fileInformationDTO.setPreviewSrc(FilePreviewUtil.catchFileIfNotExists(fileMetadata.getPath() + "." + fileMetadata.getType(), this.getFile(fileMetadata)));
}
returnList.add(fileInformationDTO);
});
return returnList;
}
public String upload(FileUploadRequest request, String operator, MultipartFile uploadFile) throws Exception {
//检查模块的合法性
fileManagementService.checkModule(request.getModuleId(), ModuleConstants.NODE_TYPE_DEFAULT);
String fileName = StringUtils.trim(uploadFile.getOriginalFilename());
FileMetadata fileMetadata = new FileMetadata();
if (StringUtils.contains(fileName, ".")) {
fileMetadata.setName(StringUtils.substring(fileName, 0, fileName.lastIndexOf(".")));
fileMetadata.setType(StringUtils.substring(fileName, fileName.lastIndexOf(".") + 1));
} else {
fileMetadata.setName(fileName);
fileMetadata.setType(StringUtils.EMPTY);
}
//检查处理后的用户名合法性
this.checkFileName(null, fileMetadata.getName(), request.getProjectId());
fileMetadata.setId(UUID.randomUUID().toString());
fileMetadata.setStorage(StorageType.MINIO.name());
fileMetadata.setProjectId(request.getProjectId());
fileMetadata.setModuleId(request.getModuleId());
long operationTime = System.currentTimeMillis();
fileMetadata.setCreateTime(operationTime);
fileMetadata.setCreateUser(operator);
fileMetadata.setUpdateTime(operationTime);
fileMetadata.setUpdateUser(operator);
fileMetadata.setSize(uploadFile.getSize());
fileMetadata.setLatest(true);
fileMetadata.setRefId(fileMetadata.getId());
fileMetadataMapper.insert(fileMetadata);
//记录日志
fileMetadataLogService.saveUploadLog(fileMetadata, operator);
// 上传文件
String filePath = this.uploadFile(fileMetadata, uploadFile);
FileMetadata updateFileMetadata = new FileMetadata();
updateFileMetadata.setId(fileMetadata.getId());
updateFileMetadata.setPath(filePath);
updateFileMetadata.setFileVersion(fileMetadata.getId());
fileMetadataMapper.updateByPrimaryKeySelective(updateFileMetadata);
return fileMetadata.getId();
}
private void checkFileName(String id, String fileName, String projectId) {
if (StringUtils.isBlank(fileName)) {
throw new MSException(Translator.get("file.name.cannot.be.empty"));
}
FileMetadataExample example = new FileMetadataExample();
if (StringUtils.isBlank(id)) {
example.createCriteria().andNameEqualTo(fileName).andProjectIdEqualTo(projectId);
} else {
example.createCriteria().andNameEqualTo(fileName).andProjectIdEqualTo(projectId).andIdNotEqualTo(id);
}
if (fileMetadataMapper.countByExample(example) > 0) {
throw new MSException(Translator.get("file.name.exist"));
}
}
/**
* 重新上传
*/
public String reUpload(FileReUploadRequest request, String operator, MultipartFile uploadFile) throws Exception {
//检查模块的合法性
FileMetadata oldFile = fileMetadataMapper.selectByPrimaryKey(request.getFileId());
if (oldFile == null) {
throw new MSException(Translator.get("old.file.not.exist"));
}
oldFile.setLatest(false);
fileMetadataMapper.updateByPrimaryKeySelective(oldFile);
long operationTime = System.currentTimeMillis();
FileMetadata fileMetadata = new FileMetadata();
fileMetadata.setId(UUID.randomUUID().toString());
fileMetadata.setStorage(oldFile.getStorage());
fileMetadata.setProjectId(oldFile.getProjectId());
fileMetadata.setModuleId(oldFile.getModuleId());
fileMetadata.setName(oldFile.getName());
fileMetadata.setType(oldFile.getType());
fileMetadata.setCreateTime(operationTime);
fileMetadata.setCreateUser(operator);
fileMetadata.setUpdateTime(operationTime);
fileMetadata.setUpdateUser(operator);
fileMetadata.setSize(uploadFile.getSize());
fileMetadata.setRefId(oldFile.getRefId());
fileMetadata.setLatest(true);
fileMetadataMapper.insert(fileMetadata);
//记录日志
fileMetadataLogService.saveReUploadLog(fileMetadata, operator);
// 上传文件
String filePath = this.uploadFile(fileMetadata, uploadFile);
FileMetadata updateFileMetadata = new FileMetadata();
updateFileMetadata.setId(fileMetadata.getId());
updateFileMetadata.setPath(filePath);
updateFileMetadata.setFileVersion(fileMetadata.getId());
fileMetadataMapper.updateByPrimaryKeySelective(updateFileMetadata);
return fileMetadata.getId();
}
private String uploadFile(FileMetadata fileMetadata, MultipartFile file) throws Exception {
FileRequest uploadFileRequest = new FileRequest();
uploadFileRequest.setFileName(fileMetadata.getId());
uploadFileRequest.setProjectId(fileMetadata.getProjectId());
uploadFileRequest.setStorage(StorageType.MINIO.name());
return fileService.upload(file, uploadFileRequest);
}
public ResponseEntity<byte[]> downloadById(String id) {
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(id);
byte[] bytes = this.getFile(fileMetadata);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + this.getFileName(fileMetadata.getName(), fileMetadata.getType()) + "\"")
.body(bytes);
}
private String getFileName(String fileName, String type) {
if (StringUtils.isBlank(type)) {
return fileName;
}
return fileName + "." + type;
}
private byte[] getFile(FileMetadata fileMetadata) {
if (fileMetadata == null) {
throw new MSException(Translator.get("file.not.exist"));
}
FileRequest fileRequest = new FileRequest();
fileRequest.setFileName(fileMetadata.getId());
fileRequest.setProjectId(fileMetadata.getProjectId());
fileRequest.setStorage(fileMetadata.getStorage());
try {
return fileService.download(fileRequest);
} catch (Exception e) {
LogUtils.error("获取文件失败", e);
}
return null;
}
public void update(FileUpdateRequest request, String operator) {
//检查模块的合法性
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(request.getId());
if (fileMetadata == null) {
throw new MSException(Translator.get("file.not.exist"));
}
//检查是否是空参数
if (!StringUtils.isAllBlank(request.getName(), request.getDescription(), request.getModuleId()) && CollectionUtils.isNotEmpty(request.getTags())) {
FileMetadata updateExample = new FileMetadata();
updateExample.setId(request.getId());
updateExample.setDescription(request.getDescription());
updateExample.setModuleId(request.getModuleId());
if (StringUtils.isNotBlank(request.getName())) {
this.checkFileName(request.getId(), request.getName(), fileMetadata.getProjectId());
updateExample.setName(request.getName());
}
if (CollectionUtils.isNotEmpty(request.getTags())) {
updateExample.setTags(JSON.toJSONString(request.getTags()));
}
updateExample.setUpdateUser(operator);
updateExample.setUpdateTime(System.currentTimeMillis());
fileMetadataMapper.updateByPrimaryKeySelective(updateExample);
//记录日志
fileMetadataLogService.saveUpdateLog(fileMetadata, fileMetadata.getProjectId(), operator);
}
}
public FileTableResult page(FileMetadataTableRequest request) {
FileTableResult dto = new FileTableResult();
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString()) ? request.getSortString() : "update_time desc");
dto.setTableData(PageUtils.setPageInfo(page, this.list(request)));
//获取模块统计
List<ModuleCountDTO> moduleCountDTOList = extFileMetadataMapper.countModuleIdByKeywordAndFileType(request.getProjectId(), request.getKeyword(), request.getModuleIds(), request.getFileTypes());
Map<String, Long> moduleCountMap = moduleCountDTOList.stream().collect(Collectors.toMap(ModuleCountDTO::getModuleId, ModuleCountDTO::getDataCount));
dto.setModuleCount(moduleCountMap);
return dto;
}
public ResponseEntity<byte[]> batchDownload(FileBatchProcessDTO request) {
List<FileMetadata> fileMetadataList = fileManagementService.getProcessList(request);
this.checkDownloadSize(fileMetadataList);
try {
byte[] bytes = this.batchDownload(fileMetadataList);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + "files.zip")
.body(bytes);
} catch (Exception e) {
return ResponseEntity.status(509).body(e.getMessage().getBytes());
}
}
public byte[] batchDownload(List<FileMetadata> fileMetadataList) {
Map<String, byte[]> files = new LinkedHashMap<>();
fileMetadataList.forEach(fileMetadata -> {
byte[] bytes = this.getFile(fileMetadata);
if (bytes != null) {
files.put(this.getFileName(fileMetadata.getName(), fileMetadata.getType()), bytes);
}
});
return FileDownloadUtils.listBytesToZip(files);
}
//检查下载的文件的大小
private void checkDownloadSize(List<FileMetadata> fileMetadataList) {
AtomicLong fileSize = new AtomicLong();
if (CollectionUtils.isNotEmpty(fileMetadataList)) {
fileMetadataList.forEach(item -> fileSize.addAndGet(item.getSize()));
}
if (fileSize.get() == 0) {
throw new MSException(Translator.get("file.size.is.zero"));
}
DataSize dataSize = DataSize.ofBytes(fileSize.get());
if (maxFileSize.compareTo(dataSize) < 0) {
throw new MSException(Translator.get("file.size.is.too.large"));
}
}
}

View File

@ -0,0 +1,127 @@
package io.metersphere.project.service;
import io.metersphere.project.domain.FileModule;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.FileModuleMapper;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.dto.LogDTO;
import io.metersphere.sdk.dto.builder.LogDTOBuilder;
import io.metersphere.sdk.dto.request.NodeMoveRequest;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.service.OperationLogService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional(rollbackFor = Exception.class)
public class FileModuleLogService {
@Resource
private FileModuleMapper fileModuleMapper;
@Resource
private ProjectMapper projectMapper;
@Resource
private OperationLogService operationLogService;
public void saveAddLog(FileModule module, String operator) {
Project project = projectMapper.selectByPrimaryKey(module.getProjectId());
LogDTO dto = LogDTOBuilder.builder()
.projectId(module.getProjectId())
.organizationId(project.getOrganizationId())
.type(OperationLogType.ADD.name())
.module(OperationLogModule.PROJECT_FILE_MANAGEMENT)
.method(HttpMethodConstants.POST.name())
.path("/project/file-module/add")
.sourceId(module.getId())
.content(module.getName())
.originalValue(JSON.toJSONBytes(module))
.createUser(operator)
.build().getLogDTO();
operationLogService.add(dto);
}
public void saveUpdateLog(FileModule module, String projectId, String operator) {
Project project = projectMapper.selectByPrimaryKey(projectId);
LogDTO dto = LogDTOBuilder.builder()
.projectId(projectId)
.organizationId(project.getOrganizationId())
.type(OperationLogType.UPDATE.name())
.module(OperationLogModule.PROJECT_FILE_MANAGEMENT)
.method(HttpMethodConstants.POST.name())
.path("/project/file-module/update")
.sourceId(module.getId())
.content(module.getName())
.originalValue(JSON.toJSONBytes(module))
.createUser(operator)
.build().getLogDTO();
operationLogService.add(dto);
}
public void saveDeleteLog(FileModule deleteModule, String operator) {
Project project = projectMapper.selectByPrimaryKey(deleteModule.getProjectId());
LogDTO dto = LogDTOBuilder.builder()
.projectId(deleteModule.getProjectId())
.organizationId(project.getOrganizationId())
.type(OperationLogType.DELETE.name())
.module(OperationLogModule.PROJECT_FILE_MANAGEMENT)
.method(HttpMethodConstants.GET.name())
.path("/project/file-module/delete/%s")
.sourceId(deleteModule.getId())
.content(deleteModule.getName() + " " + Translator.get("file.log.delete_module"))
.originalValue(JSON.toJSONBytes(deleteModule))
.createUser(operator)
.build().getLogDTO();
operationLogService.add(dto);
}
public void saveMoveLog(NodeMoveRequest request, String operator) {
FileModule moveNode;
FileModule previousNode = null;
FileModule nextNode = null;
FileModule parentModule;
moveNode = fileModuleMapper.selectByPrimaryKey(request.getNodeId());
if (request.getPreviousNodeId() != null) {
previousNode = fileModuleMapper.selectByPrimaryKey(request.getPreviousNodeId());
}
if (request.getNextNodeId() != null) {
nextNode = fileModuleMapper.selectByPrimaryKey(request.getNextNodeId());
}
parentModule = fileModuleMapper.selectByPrimaryKey(request.getParentId());
if (parentModule == null) {
parentModule = new FileModule();
parentModule.setName(Translator.get("file.module.root"));
}
Project project = projectMapper.selectByPrimaryKey(moveNode.getProjectId());
String logContent;
if (nextNode == null && previousNode == null) {
logContent = moveNode.getName() + " " + Translator.get("file.log.move_to") + parentModule.getName();
} else if (nextNode == null) {
logContent = moveNode.getName() + " " + Translator.get("file.log.move_to") + parentModule.getName() + " " + previousNode.getName() + Translator.get("file.log.next");
} else if (previousNode == null) {
logContent = moveNode.getName() + " " + Translator.get("file.log.move_to") + parentModule.getName() + " " + nextNode.getName() + Translator.get("file.log.previous");
} else {
logContent = moveNode.getName() + " " + Translator.get("file.log.move_to") + parentModule.getName() + " " +
previousNode.getName() + Translator.get("file.log.next") + " " + nextNode.getName() + Translator.get("file.log.previous");
}
LogDTO dto = LogDTOBuilder.builder()
.projectId(moveNode.getProjectId())
.organizationId(project.getOrganizationId())
.type(OperationLogType.UPDATE.name())
.module(OperationLogModule.PROJECT_FILE_MANAGEMENT)
.method(HttpMethodConstants.POST.name())
.path("/project/file-module/move")
.sourceId(moveNode.getId())
.content(logContent)
.originalValue(JSON.toJSONBytes(moveNode))
.createUser(operator)
.build().getLogDTO();
operationLogService.add(dto);
}
}

View File

@ -0,0 +1,234 @@
package io.metersphere.project.service;
import io.metersphere.project.domain.FileModule;
import io.metersphere.project.domain.FileModuleExample;
import io.metersphere.project.mapper.ExtFileModuleMapper;
import io.metersphere.project.mapper.FileModuleMapper;
import io.metersphere.project.request.filemanagement.FileModuleCreateRequest;
import io.metersphere.project.request.filemanagement.FileModuleUpdateRequest;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.dto.BaseModule;
import io.metersphere.sdk.dto.BaseTreeNode;
import io.metersphere.sdk.dto.request.NodeMoveRequest;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.service.ModuleTreeService;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.service.CleanupProjectResourceService;
import io.metersphere.system.uid.UUID;
import io.metersphere.system.utils.SessionUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@Service
@Transactional(rollbackFor = Exception.class)
public class FileModuleService extends ModuleTreeService implements CleanupProjectResourceService {
@Resource
private FileModuleLogService fileModuleLogService;
@Resource
private FileModuleMapper fileModuleMapper;
@Resource
private ExtFileModuleMapper extFileModuleMapper;
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource
private FileManagementService fileManagementService;
public List<BaseTreeNode> getTree(String projectId) {
BaseTreeNode defaultNode = this.getDefaultModule();
List<BaseTreeNode> baseTreeNodeList = new ArrayList<>();
baseTreeNodeList.add(defaultNode);
List<FileModule> fileModuleList = extFileModuleMapper.selectBaseByProjectId(projectId);
int lastSize = 0;
Map<String, BaseTreeNode> baseTreeNodeMap = new HashMap<>();
while (CollectionUtils.isNotEmpty(fileModuleList) && fileModuleList.size() != lastSize) {
List<FileModule> notMatchedList = new ArrayList<>();
for (FileModule fileModule : fileModuleList) {
if (StringUtils.equals(fileModule.getParentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) {
BaseTreeNode node = new BaseTreeNode(fileModule.getId(), fileModule.getName(), ModuleConstants.NODE_TYPE_DEFAULT);
baseTreeNodeList.add(node);
baseTreeNodeMap.put(fileModule.getId(), node);
} else {
if (baseTreeNodeMap.containsKey(fileModule.getParentId())) {
BaseTreeNode node = new BaseTreeNode(fileModule.getId(), fileModule.getName(), ModuleConstants.NODE_TYPE_DEFAULT);
baseTreeNodeMap.get(fileModule.getParentId()).addChild(node);
baseTreeNodeMap.put(fileModule.getId(), node);
} else {
notMatchedList.add(fileModule);
}
}
}
fileModuleList = notMatchedList;
}
return baseTreeNodeList;
}
public String add(FileModuleCreateRequest request, String operator) {
FileModule fileModule = new FileModule();
fileModule.setId(UUID.randomUUID().toString());
fileModule.setName(request.getName());
fileModule.setParentId(request.getParentId());
fileModule.setProjectId(request.getProjectId());
this.checkDataValidity(fileModule);
fileModule.setCreateTime(System.currentTimeMillis());
fileModule.setUpdateTime(fileModule.getCreateTime());
fileModule.setPos(this.countPos(request.getParentId()));
fileModule.setCreateUser(operator);
fileModule.setUpdateUser(operator);
fileModule.setModuleType(ModuleConstants.NODE_TYPE_DEFAULT);
fileModuleMapper.insert(fileModule);
//记录日志
fileModuleLogService.saveAddLog(fileModule, operator);
return fileModule.getId();
}
private Integer countPos(String parentId) {
Integer maxPos = extFileModuleMapper.getMaxPosByParentId(parentId);
if (maxPos == null) {
return LIMIT_POS;
} else {
return maxPos + LIMIT_POS;
}
}
/**
* 检查数据的合法性
*/
private void checkDataValidity(FileModule fileModule) {
FileModuleExample example = new FileModuleExample();
if (!StringUtils.equals(fileModule.getParentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) {
//检查父ID是否存在
example.createCriteria().andIdEqualTo(fileModule.getParentId());
if (fileModuleMapper.countByExample(example) == 0) {
throw new MSException(Translator.get("parent.node.not_blank"));
}
example.clear();
if (StringUtils.isNotBlank(fileModule.getProjectId())) {
//检查项目ID是否和父节点ID一致
example.createCriteria().andProjectIdEqualTo(fileModule.getProjectId()).andIdEqualTo(fileModule.getParentId());
if (fileModuleMapper.countByExample(example) == 0) {
throw new MSException(Translator.get("project.cannot.match.parent"));
}
example.clear();
}
}
example.createCriteria().andParentIdEqualTo(fileModule.getParentId()).andNameEqualTo(fileModule.getName()).andIdNotEqualTo(fileModule.getId());
if (fileModuleMapper.countByExample(example) > 0) {
throw new MSException(Translator.get("node.name.repeat"));
}
example.clear();
}
public void update(FileModuleUpdateRequest request, String userId) {
FileModule module = fileModuleMapper.selectByPrimaryKey(request.getId());
if (module == null) {
throw new MSException("file_module.not.exist");
}
FileModule updateModule = new FileModule();
updateModule.setId(request.getId());
updateModule.setName(request.getName());
updateModule.setParentId(module.getParentId());
this.checkDataValidity(updateModule);
updateModule.setUpdateTime(System.currentTimeMillis());
updateModule.setUpdateUser(userId);
fileModuleMapper.updateByPrimaryKeySelective(updateModule);
//记录日志
fileModuleLogService.saveUpdateLog(updateModule, module.getProjectId(), userId);
}
public void deleteModule(String deleteId) {
FileModule deleteModule = fileModuleMapper.selectByPrimaryKey(deleteId);
if (deleteModule != null) {
this.deleteModule(Collections.singletonList(deleteId));
//记录日志
fileModuleLogService.saveDeleteLog(deleteModule, SessionUtils.getUserId());
}
}
public void deleteModule(List<String> deleteIds) {
if (CollectionUtils.isEmpty(deleteIds)) {
return;
}
extFileModuleMapper.deleteByIds(deleteIds);
fileManagementService.deleteByModuleIds(deleteIds);
List<String> childrenIds = extFileModuleMapper.selectChildrenIdsByParentIds(deleteIds);
if (CollectionUtils.isNotEmpty(childrenIds)) {
deleteModule(childrenIds);
}
}
public void moveNode(NodeMoveRequest request) {
FileModuleExample example = new FileModuleExample();
example.createCriteria().andParentIdEqualTo(request.getParentId()).andIdEqualTo(request.getNodeId());
if (fileModuleMapper.countByExample(example) == 0) {
//节点换到了别的节点下
FileModule fileModule = new FileModule();
fileModule.setId(request.getNodeId());
fileModule.setParentId(request.getParentId());
fileModuleMapper.updateByPrimaryKeySelective(fileModule);
}
this.sort(request);
//记录日志
fileModuleLogService.saveMoveLog(request, SessionUtils.getUserId());
}
@Override
public BaseModule getNode(String id) {
FileModule module = fileModuleMapper.selectByPrimaryKey(id);
if (module == null) {
return null;
} else {
return new BaseModule(module.getId(), module.getName(), module.getPos(), module.getProjectId(), module.getParentId());
}
}
@Override
public void updatePos(String id, int pos) {
FileModule updateModule = new FileModule();
updateModule.setPos(pos);
updateModule.setId(id);
fileModuleMapper.updateByPrimaryKeySelective(updateModule);
}
@Override
public void refreshPos(String parentId) {
List<String> childrenIdSortByPos = extFileModuleMapper.selectChildrenIdsSortByPos(parentId);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
FileModuleMapper batchUpdateMapper = sqlSession.getMapper(FileModuleMapper.class);
for (int i = 0; i < childrenIdSortByPos.size(); i++) {
String nodeId = childrenIdSortByPos.get(i);
FileModule updateModule = new FileModule();
updateModule.setId(nodeId);
updateModule.setPos((i + 1) * LIMIT_POS);
batchUpdateMapper.updateByPrimaryKeySelective(updateModule);
}
sqlSession.flushStatements();
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
@Override
public void deleteResources(String projectId) {
List<String> fileModuleIdList = extFileModuleMapper.selectIdsByProjectId(projectId);
if (CollectionUtils.isNotEmpty(fileModuleIdList)) {
this.deleteModule(fileModuleIdList);
}
}
@Override
public void cleanReportResources(String projectId) {
}
}

View File

@ -0,0 +1,29 @@
package io.metersphere.project.utils;
import java.io.ByteArrayOutputStream;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class FileDownloadUtils {
public static byte[] listBytesToZip(Map<String, byte[]> mapReport) {
try {
if (!mapReport.isEmpty()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
for (Map.Entry<String, byte[]> report : mapReport.entrySet()) {
ZipEntry entry = new ZipEntry(report.getKey());
entry.setSize(report.getValue().length);
zos.putNextEntry(entry);
zos.write(report.getValue());
}
zos.closeEntry();
zos.close();
return baos.toByteArray();
}
} catch (Exception e) {
return new byte[10];
}
return new byte[10];
}
}

View File

@ -137,6 +137,28 @@
}
]
},
{
"id": "PROJECT_FILE_MANAGEMENT",
"name": "permission.project_file.name",
"permissions": [
{
"id": "PROJECT_FILE_MANAGEMENT:READ"
},
{
"id": "PROJECT_FILE_MANAGEMENT:READ+ADD"
},
{
"id": "PROJECT_FILE_MANAGEMENT:READ+UPDATE"
},
{
"id": "PROJECT_FILE_MANAGEMENT:READ+DOWNLOAD",
"name": "permission.project_file.download"
},
{
"id": "PROJECT_FILE_MANAGEMENT:READ+DELETE"
}
]
},
{
"id": "PROJECT_FAKE_ERROR",
"name": "permission.project_fake_error.name",

View File

@ -0,0 +1,137 @@
package io.metersphere.project.controller.filemanagement;
import io.metersphere.project.request.filemanagement.*;
import io.metersphere.project.utils.FileManagementBaseUtils;
import io.metersphere.project.utils.FileManagementRequestUtils;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.dto.request.NodeMoveRequest;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.util.ArrayList;
import java.util.Objects;
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@AutoConfigureMockMvc
public class FileManagementPermissionControllerTests extends BaseTest {
private static final String TEST_ID = "test";
@Test
@Order(1)
public void addModuleTestSuccess() throws Exception {
FileModuleCreateRequest request = new FileModuleCreateRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setName("a1");
this.requestPostPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_ADD, FileManagementRequestUtils.URL_MODULE_ADD, request);
}
@Test
@Order(2)
public void updateModuleTestSuccess() throws Exception {
FileModuleUpdateRequest updateRequest = new FileModuleUpdateRequest();
updateRequest.setId(TEST_ID);
updateRequest.setName("a1-a1");
this.requestPostPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_UPDATE, FileManagementRequestUtils.URL_MODULE_UPDATE, updateRequest);
}
@Test
@Order(3)
public void fileUploadTestSuccess() throws Exception {
//这个权限无法校验
FileUploadRequest fileUploadRequest = new FileUploadRequest();
fileUploadRequest.setProjectId(DEFAULT_PROJECT_ID);
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/file_upload.JPG")).getPath();
MockMultipartFile file = new MockMultipartFile("file", "file_upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileManagementBaseUtils.getFileBytes(filePath));
MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("file", file);
paramMap.add("request", JSON.toJSONString(fileUploadRequest));
this.requestMultipartPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_ADD, FileManagementRequestUtils.URL_FILE_UPLOAD, paramMap);
}
@Test
@Order(4)
public void fileReUploadTestSuccess() throws Exception {
FileUploadRequest fileUploadRequest = new FileUploadRequest();
fileUploadRequest.setProjectId(DEFAULT_PROJECT_ID);
//重新上传并修改文件版本
FileReUploadRequest fileReUploadRequest = new FileReUploadRequest();
fileReUploadRequest.setFileId(TEST_ID);
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/file_re-upload.JPG")).getPath();
MockMultipartFile file = new MockMultipartFile("file", "file_re-upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileManagementBaseUtils.getFileBytes(filePath));
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
paramMap.add("file", file);
paramMap.add("request", JSON.toJSONString(fileReUploadRequest));
this.requestMultipartPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_UPDATE, FileManagementRequestUtils.URL_FILE_RE_UPLOAD, paramMap);
}
@Test
@Order(5)
public void filePageTestSuccess() throws Exception {
FileMetadataTableRequest request = new FileMetadataTableRequest() {{
this.setCurrent(1);
this.setPageSize(10);
this.setProjectId(DEFAULT_PROJECT_ID);
}};
this.requestPostPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ, FileManagementRequestUtils.URL_FILE_PAGE, request);
}
@Test
public void fileDeleteSuccess() throws Exception {
FileBatchProcessDTO fileBatchProcessDTO = new FileBatchProcessDTO();
fileBatchProcessDTO.setProjectId(DEFAULT_PROJECT_ID);
fileBatchProcessDTO.setSelectIds(new ArrayList<>() {{
this.add(TEST_ID);
}});
this.requestPostPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_DELETE, FileManagementRequestUtils.URL_FILE_DELETE, fileBatchProcessDTO);
}
@Test
public void fileUpdateSuccess() throws Exception {
FileUpdateRequest updateRequest = new FileUpdateRequest();
updateRequest.setId(TEST_ID);
updateRequest.setName("update_" + TEST_ID);
updateRequest.setTags(new ArrayList<>() {{
this.add("tag1");
}});
this.requestPostPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_UPDATE, FileManagementRequestUtils.URL_FILE_UPDATE, updateRequest);
}
@Test
public void moveTest() throws Exception {
{
NodeMoveRequest request = new NodeMoveRequest();
request.setNodeId(TEST_ID);
request.setParentId(ModuleConstants.ROOT_NODE_PARENT_ID);
this.requestPostPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_UPDATE, FileManagementRequestUtils.URL_MODULE_MOVE, request);
}
}
@Test
@Order(90)
public void deleteModuleTestSuccess() throws Exception {
this.requestGetPermissionTest(PermissionConstants.PROJECT_FILE_MANAGEMENT_READ_DELETE, String.format(FileManagementRequestUtils.URL_MODULE_DELETE, TEST_ID));
}
}

View File

@ -0,0 +1,127 @@
package io.metersphere.project.utils;
import io.metersphere.project.dto.FileInformationDTO;
import io.metersphere.project.dto.FileTableResult;
import io.metersphere.project.request.filemanagement.FileMetadataTableRequest;
import io.metersphere.sdk.dto.BaseTreeNode;
import io.metersphere.sdk.util.FilePreviewUtil;
import io.metersphere.sdk.util.JSON;
import org.apache.commons.collections4.CollectionUtils;
import org.junit.jupiter.api.Assertions;
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.List;
public class FileManagementBaseUtils {
public static BaseTreeNode getNodeByName(List<BaseTreeNode> preliminaryTreeNodes, String nodeName) {
for (BaseTreeNode firstLevelNode : preliminaryTreeNodes) {
if (StringUtils.equals(firstLevelNode.getName(), nodeName)) {
return firstLevelNode;
}
if (CollectionUtils.isNotEmpty(firstLevelNode.getChildren())) {
for (BaseTreeNode secondLevelNode : firstLevelNode.getChildren()) {
if (StringUtils.equals(secondLevelNode.getName(), nodeName)) {
return secondLevelNode;
}
if (CollectionUtils.isNotEmpty(secondLevelNode.getChildren())) {
for (BaseTreeNode thirdLevelNode : secondLevelNode.getChildren()) {
if (StringUtils.equals(thirdLevelNode.getName(), nodeName)) {
return thirdLevelNode;
}
}
}
}
}
}
return null;
}
public static byte[] getFileBytes(String filePath) {
File file = new File(filePath);
byte[] buffer = new byte[0];
try (FileInputStream fi = new FileInputStream(file)) {
buffer = new byte[(int) file.length()];
int offset = 0;
int numRead;
while (offset < buffer.length
&& (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {
offset += numRead;
}
} catch (Exception ignore) {
}
return buffer;
}
public static String getFileMD5(File file) {
if (!file.isFile()) {
return null;
}
MessageDigest digest = null;
FileInputStream in = null;
byte buffer[] = new byte[8192];
int len;
try {
digest = MessageDigest.getInstance("MD5");
in = new FileInputStream(file);
while ((len = in.read(buffer)) != -1) {
digest.update(buffer, 0, len);
}
BigInteger bigInt = new BigInteger(1, digest.digest());
return bigInt.toString(16);
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static String getFileMD5(byte[] bytes) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(bytes, 0, bytes.length);
BigInteger bigInt = new BigInteger(1, digest.digest());
return bigInt.toString(16);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void checkFilePage(FileTableResult result, FileMetadataTableRequest request, boolean hasData) {
//返回值的页码和当前页码相同
Assertions.assertEquals(result.getTableData().getCurrent(), request.getCurrent());
//返回的数据量不超过规定要返回的数据量相同
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(result.getTableData().getList())).size() <= request.getPageSize());
List<FileInformationDTO> fileInformationDTOList = JSON.parseArray(JSON.toJSONString(result.getTableData().getList()), FileInformationDTO.class);
for (FileInformationDTO fileInformationDTO : fileInformationDTOList) {
if (FilePreviewUtil.isImage(fileInformationDTO.getFileType())) {
//检查是否有预览文件
String previewPath = fileInformationDTO.getPreviewSrc();
File file = new File(previewPath);
Assertions.assertTrue(file.exists());
}
}
//判断返回的节点统计总量是否和表格总量匹配
long allResult = 0;
for (Long countByModuleId : result.getModuleCount().values()) {
allResult += countByModuleId;
}
Assertions.assertEquals(allResult, result.getTableData().getTotal());
Assertions.assertEquals(request.getPageSize(), result.getTableData().getPageSize());
if (hasData) {
Assertions.assertTrue(allResult > 0);
} else {
Assertions.assertTrue(allResult == 0);
}
}
}

View File

@ -0,0 +1,30 @@
package io.metersphere.project.utils;
public class FileManagementRequestUtils {
//文件模块树查询
public static final String URL_MODULE_TREE = "/project/file-module/tree/%s";
//添加文件模块
public static final String URL_MODULE_ADD = "/project/file-module/add";
//修改文件模块
public static final String URL_MODULE_UPDATE = "/project/file-module/update";
//删除文件模块
public static final String URL_MODULE_DELETE = "/project/file-module/delete/%s";
//移动文件模块
public static final String URL_MODULE_MOVE = "/project/file-module/move";
//文件上传
public static final String URL_FILE_UPLOAD = "/project/file/upload";
//文件列表查询
public static final String URL_FILE_PAGE = "/project/file/page";
//文件重传
public static final String URL_FILE_RE_UPLOAD = "/project/file/re-upload";
//文件下载
public static final String URL_FILE_DOWNLOAD = "/project/file/download/%s";
//文件批量下载
public static final String URL_FILE_BATCH_DOWNLOAD = "/project/file/batch-download";
//文件批量删除
public static final String URL_FILE_DELETE = "/project/file/delete";
//文件信息修改
public static final String URL_FILE_UPDATE = "/project/file/update";
}

View File

@ -83,4 +83,5 @@ minio.secret-key=${embedded.minio.secretKey}
logging.level.org.springframework.jdbc.core=info
logging.level.io.metersphere.sdk.mapper=info
logging.level.io.metersphere.project.mapper=info
logging.level.io.metersphere.project.mapper=info
metersphere.file.batch-download-max=600MB

View File

@ -0,0 +1 @@
这是用来做反例测试的

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -0,0 +1 @@
这是证明文件没后缀的

View File

@ -0,0 +1 @@
普通txt文件

View File

@ -1,7 +1,7 @@
package io.metersphere.system.config.interceptor;
import io.metersphere.project.domain.CustomFunctionBlob;
import io.metersphere.project.domain.FileMetadataBlob;
import io.metersphere.project.domain.FileMetadataRepository;
import io.metersphere.sdk.util.CompressUtils;
import io.metersphere.sdk.util.MybatisInterceptorConfig;
import org.springframework.context.annotation.Bean;
@ -16,7 +16,7 @@ public class ProjectInterceptor {
public List<MybatisInterceptorConfig> projectCompressConfigs() {
List<MybatisInterceptorConfig> configList = new ArrayList<>();
configList.add(new MybatisInterceptorConfig(FileMetadataBlob.class, "gitInfo", CompressUtils.class, "zip", "unzip"));
configList.add(new MybatisInterceptorConfig(FileMetadataRepository.class, "gitInfo", CompressUtils.class, "zip", "unzip"));
configList.add(new MybatisInterceptorConfig(CustomFunctionBlob.class, "script", CompressUtils.class, "zip", "unzip"));
configList.add(new MybatisInterceptorConfig(CustomFunctionBlob.class, "result", CompressUtils.class, "zip", "unzip"));
configList.add(new MybatisInterceptorConfig(CustomFunctionBlob.class, "params", CompressUtils.class, "zip", "unzip"));

View File

@ -4,8 +4,8 @@ package io.metersphere.system.service;
* 清理项目资源
*/
public interface CleanupProjectResourceService {
void deleteResources(String projectId);
void deleteResources(String projectId);
/**
* 清理报告资源

View File

@ -8,12 +8,12 @@ import io.metersphere.sdk.constants.OperationLogConstants;
import io.metersphere.sdk.dto.LogDTO;
import io.metersphere.sdk.dto.TableBatchProcessDTO;
import io.metersphere.sdk.dto.builder.LogDTOBuilder;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.service.OperationLogService;
import io.metersphere.sdk.util.JSON;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.domain.*;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.log.service.OperationLogService;
import io.metersphere.system.mapper.OrganizationMapper;
import io.metersphere.system.mapper.UserMapper;
import io.metersphere.system.mapper.UserRoleMapper;
@ -56,7 +56,7 @@ public class UserLogService {
.type(OperationLogType.ADD.name())
.module(OperationLogModule.SETTING_SYSTEM_USER_SINGLE)
.method(HttpMethodConstants.POST.name())
.path("/addUser")
.path("/system/user/addUser")
.sourceId(user.getId())
.content(user.getName() + "(" + user.getEmail() + ")")
.originalValue(JSON.toJSONBytes(user))
@ -76,7 +76,7 @@ public class UserLogService {
.type(OperationLogType.UPDATE.name())
.module(OperationLogModule.SETTING_SYSTEM_USER_SINGLE)
.method(HttpMethodConstants.POST.name())
.path("/update")
.path("/system/user/update")
.sourceId(request.getId())
.content(user.getName())
.originalValue(JSON.toJSONBytes(user))
@ -98,7 +98,7 @@ public class UserLogService {
.type(OperationLogType.UPDATE.name())
.module(OperationLogModule.SETTING_SYSTEM_USER_SINGLE)
.method(HttpMethodConstants.POST.name())
.path("/update/enable")
.path("/system/user/update/enable")
.sourceId(user.getId())
.content((request.isEnable() ? Translator.get("user.enable") : Translator.get("user.disable")) + ":" + user.getName())
.originalValue(JSON.toJSONBytes(user))
@ -122,7 +122,7 @@ public class UserLogService {
.type(OperationLogType.UPDATE.name())
.module(OperationLogModule.SETTING_SYSTEM_USER_SINGLE)
.method(HttpMethodConstants.POST.name())
.path("/reset/password")
.path("/system/user/reset/password")
.sourceId(user.getId())
.content(Translator.get("user.reset.password") + " : " + user.getName())
.originalValue(JSON.toJSONBytes(user))
@ -144,7 +144,7 @@ public class UserLogService {
.type(OperationLogType.DELETE.name())
.module(OperationLogModule.SETTING_SYSTEM_USER_SINGLE)
.method(HttpMethodConstants.POST.name())
.path("/delete")
.path("/system/user/delete")
.sourceId(user.getId())
.content(Translator.get("user.delete") + " : " + user.getName())
.originalValue(JSON.toJSONBytes(user))

View File

@ -15,11 +15,11 @@ public class TreeNodeParseUtils {
Organization organization = entry.getKey();
List<Project> projects = entry.getValue();
BaseTreeNode orgNode = new BaseTreeNode(organization.getId(), organization.getName(), Organization.class.getName(), true);
BaseTreeNode orgNode = new BaseTreeNode(organization.getId(), organization.getName(), Organization.class.getName());
returnList.add(orgNode);
for (Project project : projects) {
BaseTreeNode projectNode = new BaseTreeNode(project.getId(), project.getName(), Project.class.getName(), true);
BaseTreeNode projectNode = new BaseTreeNode(project.getId(), project.getName(), Project.class.getName());
orgNode.addChild(projectNode);
}
}

View File

@ -224,11 +224,12 @@ public abstract class BaseTest {
File file = (File) o;
multipartFile = new MockMultipartFile(key, file.getName(),
MediaType.APPLICATION_OCTET_STREAM_VALUE, Files.readAllBytes(file.toPath()));
} else if (o instanceof MockMultipartFile) {
multipartFile = (MockMultipartFile) o;
} else {
multipartFile = new MockMultipartFile(key, null,
MediaType.APPLICATION_JSON_VALUE, o.toString().getBytes());
}
requestBuilder.file(multipartFile);
} catch (IOException e) {
throw new RuntimeException(e);

View File

@ -226,6 +226,7 @@ public class UserControllerTests extends BaseTest {
Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同
Assertions.assertEquals(returnPager.getCurrent(), basePageRequest.getCurrent());
Assertions.assertEquals(returnPager.getPageSize(), basePageRequest.getPageSize());
//返回的数据量不超过规定要返回的数据量相同
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(returnPager.getList())).size() <= basePageRequest.getPageSize());
List<UserTableResponse> userList = JSON.parseArray(JSON.toJSONString(returnPager.getList()), UserTableResponse.class);
@ -244,6 +245,7 @@ public class UserControllerTests extends BaseTest {
Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同
Assertions.assertEquals(returnPager.getCurrent(), basePageRequest.getCurrent());
Assertions.assertEquals(returnPager.getPageSize(), basePageRequest.getPageSize());
//返回的数据量不超过规定要返回的数据量相同
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(returnPager.getList())).size() <= basePageRequest.getPageSize());
//用户组不存在非全局用户组
@ -282,6 +284,7 @@ public class UserControllerTests extends BaseTest {
Assertions.assertNotNull(returnPager);
//返回值的页码和当前页码相同
Assertions.assertEquals(returnPager.getTotal(), 0);
Assertions.assertEquals(returnPager.getPageSize(), basePageRequest.getPageSize());
//返回的数据量不超过规定要返回的数据量相同
Assertions.assertEquals(0, JSON.parseArray(JSON.toJSONString(returnPager.getList())).size());
}