mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 22:57:17 +08:00 
			
		
		
		
	feat: 新增部分存储库管理后端 API
This commit is contained in:
		| @@ -82,6 +82,12 @@ | ||||
|             <artifactId>sms4j-spring-boot-starter</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- X File Storage(一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿里云 OSS、华为云 OBS...等其它兼容 S3 协议的存储平台) --> | ||||
|         <dependency> | ||||
|             <groupId>org.dromara.x-file-storage</groupId> | ||||
|             <artifactId>x-file-storage-spring</artifactId> | ||||
|         </dependency> | ||||
|  | ||||
|         <!-- FreeMarker(模板引擎) --> | ||||
|         <dependency> | ||||
|             <groupId>org.freemarker</groupId> | ||||
|   | ||||
| @@ -61,11 +61,11 @@ public class MyBatisPlusMetaObjectHandler implements MetaObjectHandler { | ||||
|             Long createUser = LoginHelper.getUserId(); | ||||
|             LocalDateTime createTime = LocalDateTime.now(); | ||||
|             if (metaObject.getOriginalObject() instanceof BaseDO baseDO) { | ||||
|                 // 继承了 BaseDO 的类,填充创建信息 | ||||
|                 // 继承了 BaseDO 的类,填充创建信息字段 | ||||
|                 baseDO.setCreateUser(ObjectUtil.defaultIfNull(baseDO.getCreateUser(), createUser)); | ||||
|                 baseDO.setCreateTime(ObjectUtil.defaultIfNull(baseDO.getCreateTime(), createTime)); | ||||
|             } else { | ||||
|                 // 未继承 BaseDO 的类,如存在创建信息则进行填充 | ||||
|                 // 未继承 BaseDO 的类,如存在创建信息字段则进行填充 | ||||
|                 this.fillFieldValue(metaObject, CREATE_USER, createUser, false); | ||||
|                 this.fillFieldValue(metaObject, CREATE_TIME, createTime, false); | ||||
|             } | ||||
| @@ -94,7 +94,7 @@ public class MyBatisPlusMetaObjectHandler implements MetaObjectHandler { | ||||
|                 baseDO.setUpdateUser(updateUser); | ||||
|                 baseDO.setUpdateTime(updateTime); | ||||
|             } else { | ||||
|                 // 未继承 BaseDO 的类,根据类中拥有的修改信息进行填充,不存在修改信息不进行填充 | ||||
|                 // 未继承 BaseDO 的类,根据类中拥有的修改信息字段进行填充,不存在修改信息字段不进行填充 | ||||
|                 this.fillFieldValue(metaObject, UPDATE_USER, updateUser, true); | ||||
|                 this.fillFieldValue(metaObject, UPDATE_TIME, updateTime, true); | ||||
|             } | ||||
|   | ||||
| @@ -0,0 +1,82 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.config; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import org.dromara.x.file.storage.core.FileInfo; | ||||
| import org.dromara.x.file.storage.core.recorder.FileRecorder; | ||||
| import org.springframework.stereotype.Component; | ||||
|  | ||||
| import cn.hutool.core.date.DateUtil; | ||||
| import cn.hutool.core.util.EscapeUtil; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.util.helper.LoginHelper; | ||||
| import top.charles7c.continew.admin.system.enums.FileTypeEnum; | ||||
| import top.charles7c.continew.admin.system.mapper.FileMapper; | ||||
| import top.charles7c.continew.admin.system.mapper.StorageMapper; | ||||
| import top.charles7c.continew.admin.system.model.entity.FileDO; | ||||
| import top.charles7c.continew.admin.system.model.entity.StorageDO; | ||||
|  | ||||
| /** | ||||
|  * 文件记录实现类 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/24 22:31 | ||||
|  */ | ||||
| @Slf4j | ||||
| @Component | ||||
| @RequiredArgsConstructor | ||||
| public class FileRecorderImpl implements FileRecorder { | ||||
|  | ||||
|     private final FileMapper fileMapper; | ||||
|     private final StorageMapper storageMapper; | ||||
|  | ||||
|     @Override | ||||
|     public boolean save(FileInfo fileInfo) { | ||||
|         FileDO file = new FileDO(); | ||||
|         file.setName(EscapeUtil.unescape(fileInfo.getOriginalFilename())); | ||||
|         file.setSize(fileInfo.getSize()); | ||||
|         file.setUrl(fileInfo.getUrl()); | ||||
|         file.setExtension(fileInfo.getExt()); | ||||
|         file.setType(FileTypeEnum.getByExtension(file.getExtension())); | ||||
|         StorageDO storage = storageMapper.lambdaQuery().eq(StorageDO::getCode, fileInfo.getPlatform()).one(); | ||||
|         file.setStorageId(storage.getId()); | ||||
|         file.setCreateTime(DateUtil.toLocalDateTime(fileInfo.getCreateTime())); | ||||
|         file.setUpdateUser(LoginHelper.getUserId()); | ||||
|         file.setUpdateTime(file.getCreateTime()); | ||||
|         fileMapper.insert(file); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public FileInfo getByUrl(String url) { | ||||
|         FileDO file = fileMapper.lambdaQuery().eq(FileDO::getUrl, url).one(); | ||||
|         FileInfo fileInfo = new FileInfo(); | ||||
|         fileInfo.setOriginalFilename(file.getName()); | ||||
|         fileInfo.setSize(file.getSize()); | ||||
|         fileInfo.setUrl(file.getUrl()); | ||||
|         fileInfo.setExt(file.getExtension()); | ||||
|         return fileInfo; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean delete(String url) { | ||||
|         return fileMapper.lambdaUpdate().eq(FileDO::getUrl, url).remove(); | ||||
|     } | ||||
| } | ||||
| @@ -16,12 +16,15 @@ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.enums; | ||||
|  | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
| import lombok.Getter; | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import cn.hutool.core.util.StrUtil; | ||||
|  | ||||
| import top.charles7c.continew.starter.data.mybatis.plus.base.IBaseEnum; | ||||
|  | ||||
| /** | ||||
| @@ -62,4 +65,17 @@ public enum FileTypeEnum implements IBaseEnum<Integer> { | ||||
|     private final Integer value; | ||||
|     private final String description; | ||||
|     private final List<String> extensions; | ||||
|  | ||||
|     /** | ||||
|      * 根据扩展名查询 | ||||
|      * | ||||
|      * @param extension | ||||
|      *            扩展名 | ||||
|      * @return 文件类型 | ||||
|      */ | ||||
|     public static FileTypeEnum getByExtension(String extension) { | ||||
|         return Arrays.stream(FileTypeEnum.values()) | ||||
|             .filter(t -> t.getExtensions().contains(StrUtil.emptyIfNull(extension).toLowerCase())).findFirst() | ||||
|             .orElse(FileTypeEnum.UNKNOWN); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.mapper; | ||||
|  | ||||
| import top.charles7c.continew.admin.system.model.entity.StorageDO; | ||||
| import top.charles7c.continew.starter.data.mybatis.plus.base.BaseMapper; | ||||
|  | ||||
| /** | ||||
|  * 存储库 Mapper | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/26 22:09 | ||||
|  */ | ||||
| public interface StorageMapper extends BaseMapper<StorageDO> {} | ||||
| @@ -20,7 +20,7 @@ import java.io.Serial; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.TableName; | ||||
| import com.baomidou.mybatisplus.annotation.*; | ||||
|  | ||||
| import top.charles7c.continew.admin.system.enums.FileTypeEnum; | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseDO; | ||||
| @@ -38,6 +38,9 @@ public class FileDO extends BaseDO { | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     @TableId(type = IdType.ASSIGN_ID) | ||||
|     private Long id; | ||||
|  | ||||
|     /** | ||||
|      * 名称 | ||||
|      */ | ||||
| @@ -58,18 +61,13 @@ public class FileDO extends BaseDO { | ||||
|      */ | ||||
|     private String extension; | ||||
|  | ||||
|     /** | ||||
|      * MIME类型 | ||||
|      */ | ||||
|     private String mimeType; | ||||
|  | ||||
|     /** | ||||
|      * 类型 | ||||
|      */ | ||||
|     private FileTypeEnum type; | ||||
|  | ||||
|     /** | ||||
|      * 存储库ID | ||||
|      * 存储库 ID | ||||
|      */ | ||||
|     private Long storageId; | ||||
| } | ||||
| @@ -0,0 +1,94 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.model.entity; | ||||
|  | ||||
| import java.io.Serial; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import com.baomidou.mybatisplus.annotation.TableName; | ||||
|  | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseDO; | ||||
|  | ||||
| /** | ||||
|  * 存储库实体 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/26 22:09 | ||||
|  */ | ||||
| @Data | ||||
| @TableName("sys_storage") | ||||
| public class StorageDO extends BaseDO { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 名称 | ||||
|      */ | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * 编码 | ||||
|      */ | ||||
|     private String code; | ||||
|  | ||||
|     /** | ||||
|      * Access Key | ||||
|      */ | ||||
|     private String accessKey; | ||||
|  | ||||
|     /** | ||||
|      * Secret Key | ||||
|      */ | ||||
|     private String secretKey; | ||||
|  | ||||
|     /** | ||||
|      * Endpoint | ||||
|      */ | ||||
|     private String endpoint; | ||||
|  | ||||
|     /** | ||||
|      * 桶名称 | ||||
|      */ | ||||
|     private String bucketName; | ||||
|  | ||||
|     /** | ||||
|      * 自定义域名 | ||||
|      */ | ||||
|     private String domain; | ||||
|  | ||||
|     /** | ||||
|      * 描述 | ||||
|      */ | ||||
|     private String description; | ||||
|  | ||||
|     /** | ||||
|      * 是否为默认存储 | ||||
|      */ | ||||
|     private Boolean isDefault; | ||||
|  | ||||
|     /** | ||||
|      * 排序 | ||||
|      */ | ||||
|     private Integer sort; | ||||
|  | ||||
|     /** | ||||
|      * 状态 | ||||
|      */ | ||||
|     private Integer status; | ||||
| } | ||||
| @@ -0,0 +1,55 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.model.query; | ||||
|  | ||||
| import java.io.Serial; | ||||
| import java.io.Serializable; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import top.charles7c.continew.starter.data.mybatis.plus.query.Query; | ||||
| import top.charles7c.continew.starter.data.mybatis.plus.query.QueryType; | ||||
|  | ||||
| /** | ||||
|  * 存储库查询条件 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/26 22:09 | ||||
|  */ | ||||
| @Data | ||||
| @Schema(description = "存储库查询条件") | ||||
| public class StorageQuery implements Serializable { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 名称 | ||||
|      */ | ||||
|     @Schema(description = "名称") | ||||
|     @Query(type = QueryType.INNER_LIKE) | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * 状态 | ||||
|      */ | ||||
|     @Schema(description = "状态") | ||||
|     @Query(type = QueryType.EQUAL) | ||||
|     private Integer status; | ||||
| } | ||||
| @@ -0,0 +1,111 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.model.req; | ||||
|  | ||||
| import java.io.Serial; | ||||
|  | ||||
| import jakarta.validation.constraints.*; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.enums.DisEnableStatusEnum; | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseReq; | ||||
|  | ||||
| /** | ||||
|  * 创建或修改存储库信息 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/26 22:09 | ||||
|  */ | ||||
| @Data | ||||
| @Schema(description = "创建或修改存储库信息") | ||||
| public class StorageReq extends BaseReq { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 名称 | ||||
|      */ | ||||
|     @Schema(description = "名称") | ||||
|     @NotBlank(message = "名称不能为空") | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * 编码 | ||||
|      */ | ||||
|     @Schema(description = "编码") | ||||
|     @NotBlank(message = "编码不能为空") | ||||
|     private String code; | ||||
|  | ||||
|     /** | ||||
|      * Access Key | ||||
|      */ | ||||
|     @Schema(description = "Access Key") | ||||
|     private String accessKey; | ||||
|  | ||||
|     /** | ||||
|      * Secret Key | ||||
|      */ | ||||
|     @Schema(description = "Secret Key") | ||||
|     private String secretKey; | ||||
|  | ||||
|     /** | ||||
|      * Endpoint | ||||
|      */ | ||||
|     @Schema(description = "Endpoint") | ||||
|     private String endpoint; | ||||
|  | ||||
|     /** | ||||
|      * 桶名称 | ||||
|      */ | ||||
|     @Schema(description = "桶名称") | ||||
|     private String bucketName; | ||||
|  | ||||
|     /** | ||||
|      * 自定义域名 | ||||
|      */ | ||||
|     @Schema(description = "自定义域名") | ||||
|     private String domain; | ||||
|  | ||||
|     /** | ||||
|      * 描述 | ||||
|      */ | ||||
|     @Schema(description = "描述") | ||||
|     private String description; | ||||
|  | ||||
|     /** | ||||
|      * 是否为默认存储 | ||||
|      */ | ||||
|     @Schema(description = "是否为默认存储") | ||||
|     @NotNull(message = "是否为默认存储不能为空") | ||||
|     private Boolean isDefault; | ||||
|  | ||||
|     /** | ||||
|      * 排序 | ||||
|      */ | ||||
|     @Schema(description = "排序") | ||||
|     private Integer sort; | ||||
|  | ||||
|     /** | ||||
|      * 状态 | ||||
|      */ | ||||
|     @Schema(description = "状态(1:启用;2:禁用)", type = "Integer", allowableValues = {"1", "2"}, example = "1") | ||||
|     private DisEnableStatusEnum status; | ||||
| } | ||||
| @@ -0,0 +1,120 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.model.resp; | ||||
|  | ||||
| import java.io.Serial; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | ||||
| import com.alibaba.excel.annotation.ExcelProperty; | ||||
|  | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseDetailResp; | ||||
|  | ||||
| /** | ||||
|  * 存储库详情信息 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/26 22:09 | ||||
|  */ | ||||
| @Data | ||||
| @ExcelIgnoreUnannotated | ||||
| @Schema(description = "存储库详情信息") | ||||
| public class StorageDetailResp extends BaseDetailResp { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 名称 | ||||
|      */ | ||||
|     @Schema(description = "名称") | ||||
|     @ExcelProperty(value = "名称") | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * 编码 | ||||
|      */ | ||||
|     @Schema(description = "编码") | ||||
|     @ExcelProperty(value = "编码") | ||||
|     private String code; | ||||
|  | ||||
|     /** | ||||
|      * Access Key | ||||
|      */ | ||||
|     @Schema(description = "Access Key") | ||||
|     @ExcelProperty(value = "Access Key") | ||||
|     private String accessKey; | ||||
|  | ||||
|     /** | ||||
|      * Secret Key | ||||
|      */ | ||||
|     @Schema(description = "Secret Key") | ||||
|     @ExcelProperty(value = "Secret Key") | ||||
|     private String secretKey; | ||||
|  | ||||
|     /** | ||||
|      * Endpoint | ||||
|      */ | ||||
|     @Schema(description = "Endpoint") | ||||
|     @ExcelProperty(value = "Endpoint") | ||||
|     private String endpoint; | ||||
|  | ||||
|     /** | ||||
|      * 桶名称 | ||||
|      */ | ||||
|     @Schema(description = "桶名称") | ||||
|     @ExcelProperty(value = "桶名称") | ||||
|     private String bucketName; | ||||
|  | ||||
|     /** | ||||
|      * 自定义域名 | ||||
|      */ | ||||
|     @Schema(description = "自定义域名") | ||||
|     @ExcelProperty(value = "自定义域名") | ||||
|     private String domain; | ||||
|  | ||||
|     /** | ||||
|      * 描述 | ||||
|      */ | ||||
|     @Schema(description = "描述") | ||||
|     @ExcelProperty(value = "描述") | ||||
|     private String description; | ||||
|  | ||||
|     /** | ||||
|      * 是否为默认存储 | ||||
|      */ | ||||
|     @Schema(description = "是否为默认存储") | ||||
|     @ExcelProperty(value = "是否为默认存储") | ||||
|     private Boolean isDefault; | ||||
|  | ||||
|     /** | ||||
|      * 排序 | ||||
|      */ | ||||
|     @Schema(description = "排序") | ||||
|     @ExcelProperty(value = "排序") | ||||
|     private Integer sort; | ||||
|  | ||||
|     /** | ||||
|      * 状态 | ||||
|      */ | ||||
|     @Schema(description = "状态") | ||||
|     @ExcelProperty(value = "状态") | ||||
|     private Integer status; | ||||
| } | ||||
| @@ -0,0 +1,105 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.model.resp; | ||||
|  | ||||
| import java.io.Serial; | ||||
|  | ||||
| import lombok.Data; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||
|  | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseResp; | ||||
|  | ||||
| /** | ||||
|  * 存储库信息 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/26 22:09 | ||||
|  */ | ||||
| @Data | ||||
| @Schema(description = "存储库信息") | ||||
| public class StorageResp extends BaseResp { | ||||
|  | ||||
|     @Serial | ||||
|     private static final long serialVersionUID = 1L; | ||||
|  | ||||
|     /** | ||||
|      * 名称 | ||||
|      */ | ||||
|     @Schema(description = "名称") | ||||
|     private String name; | ||||
|  | ||||
|     /** | ||||
|      * 编码 | ||||
|      */ | ||||
|     @Schema(description = "编码") | ||||
|     private String code; | ||||
|  | ||||
|     /** | ||||
|      * Access Key | ||||
|      */ | ||||
|     @Schema(description = "Access Key") | ||||
|     private String accessKey; | ||||
|  | ||||
|     /** | ||||
|      * Secret Key | ||||
|      */ | ||||
|     @Schema(description = "Secret Key") | ||||
|     private String secretKey; | ||||
|  | ||||
|     /** | ||||
|      * Endpoint | ||||
|      */ | ||||
|     @Schema(description = "Endpoint") | ||||
|     private String endpoint; | ||||
|  | ||||
|     /** | ||||
|      * 桶名称 | ||||
|      */ | ||||
|     @Schema(description = "桶名称") | ||||
|     private String bucketName; | ||||
|  | ||||
|     /** | ||||
|      * 自定义域名 | ||||
|      */ | ||||
|     @Schema(description = "自定义域名") | ||||
|     private String domain; | ||||
|  | ||||
|     /** | ||||
|      * 描述 | ||||
|      */ | ||||
|     @Schema(description = "描述") | ||||
|     private String description; | ||||
|  | ||||
|     /** | ||||
|      * 是否为默认存储 | ||||
|      */ | ||||
|     @Schema(description = "是否为默认存储") | ||||
|     private Boolean isDefault; | ||||
|  | ||||
|     /** | ||||
|      * 排序 | ||||
|      */ | ||||
|     @Schema(description = "排序") | ||||
|     private Integer sort; | ||||
|  | ||||
|     /** | ||||
|      * 状态 | ||||
|      */ | ||||
|     @Schema(description = "状态") | ||||
|     private Integer status; | ||||
| } | ||||
| @@ -16,6 +16,10 @@ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.service; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import top.charles7c.continew.admin.system.model.query.FileQuery; | ||||
| import top.charles7c.continew.admin.system.model.req.FileReq; | ||||
| import top.charles7c.continew.admin.system.model.resp.FileDetailResp; | ||||
| @@ -28,4 +32,24 @@ import top.charles7c.continew.starter.extension.crud.base.BaseService; | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/23 10:38 | ||||
|  */ | ||||
| public interface FileService extends BaseService<FileResp, FileDetailResp, FileQuery, FileReq> {} | ||||
| public interface FileService extends BaseService<FileResp, FileDetailResp, FileQuery, FileReq> { | ||||
|  | ||||
|     /** | ||||
|      * 上传 | ||||
|      * | ||||
|      * @param file | ||||
|      *            文件信息 | ||||
|      * @param storageCode | ||||
|      *            存储库编码 | ||||
|      */ | ||||
|     void upload(MultipartFile file, String storageCode); | ||||
|  | ||||
|     /** | ||||
|      * 根据存储库 ID 列表查询 | ||||
|      * | ||||
|      * @param storageIds | ||||
|      *            存储库 ID 列表 | ||||
|      * @return 文件数量 | ||||
|      */ | ||||
|     Long countByStorageIds(List<Long> storageIds); | ||||
| } | ||||
| @@ -0,0 +1,49 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.service; | ||||
|  | ||||
| import top.charles7c.continew.admin.system.model.entity.StorageDO; | ||||
| import top.charles7c.continew.admin.system.model.query.StorageQuery; | ||||
| import top.charles7c.continew.admin.system.model.req.StorageReq; | ||||
| import top.charles7c.continew.admin.system.model.resp.StorageDetailResp; | ||||
| import top.charles7c.continew.admin.system.model.resp.StorageResp; | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseService; | ||||
|  | ||||
| /** | ||||
|  * 存储库业务接口 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/26 22:09 | ||||
|  */ | ||||
| public interface StorageService extends BaseService<StorageResp, StorageDetailResp, StorageQuery, StorageReq> { | ||||
|  | ||||
|     /** | ||||
|      * 查询默认存储库 | ||||
|      * | ||||
|      * @return 存储库信息 | ||||
|      */ | ||||
|     StorageDO getDefaultStorage(); | ||||
|  | ||||
|     /** | ||||
|      * 根据编码查询 | ||||
|      * | ||||
|      * @param code | ||||
|      *            编码 | ||||
|      * @return 存储库信息 | ||||
|      */ | ||||
|     StorageDO getByCode(String code); | ||||
| } | ||||
| @@ -16,17 +16,29 @@ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.service.impl; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import java.util.List; | ||||
|  | ||||
| import jakarta.annotation.Resource; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
| import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import org.dromara.x.file.storage.core.*; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.web.multipart.MultipartFile; | ||||
|  | ||||
| import cn.hutool.core.util.StrUtil; | ||||
|  | ||||
| import top.charles7c.continew.admin.system.mapper.FileMapper; | ||||
| import top.charles7c.continew.admin.system.model.entity.FileDO; | ||||
| import top.charles7c.continew.admin.system.model.entity.StorageDO; | ||||
| import top.charles7c.continew.admin.system.model.query.FileQuery; | ||||
| import top.charles7c.continew.admin.system.model.req.FileReq; | ||||
| import top.charles7c.continew.admin.system.model.resp.FileDetailResp; | ||||
| import top.charles7c.continew.admin.system.model.resp.FileResp; | ||||
| import top.charles7c.continew.admin.system.service.FileService; | ||||
| import top.charles7c.continew.admin.system.service.StorageService; | ||||
| import top.charles7c.continew.starter.core.util.validate.CheckUtils; | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseServiceImpl; | ||||
|  | ||||
| /** | ||||
| @@ -35,7 +47,45 @@ import top.charles7c.continew.starter.extension.crud.base.BaseServiceImpl; | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/23 10:38 | ||||
|  */ | ||||
| @Slf4j | ||||
| @Service | ||||
| @RequiredArgsConstructor | ||||
| public class FileServiceImpl extends BaseServiceImpl<FileMapper, FileDO, FileResp, FileDetailResp, FileQuery, FileReq> | ||||
|     implements FileService {} | ||||
|     implements FileService { | ||||
|  | ||||
|     @Resource | ||||
|     private StorageService storageService; | ||||
|     private final FileStorageService fileStorageService; | ||||
|  | ||||
|     @Override | ||||
|     public void upload(MultipartFile file, String storageCode) { | ||||
|         if (StrUtil.isBlank(storageCode)) { | ||||
|             StorageDO storage = storageService.getDefaultStorage(); | ||||
|             CheckUtils.throwIfNull(storage, "请先指定默认存储库"); | ||||
|             storageCode = storage.getCode(); | ||||
|         } | ||||
|         UploadPretreatment uploadPretreatment = fileStorageService.of(file).setPlatform(storageCode); | ||||
|         uploadPretreatment.setProgressMonitor(new ProgressListener() { | ||||
|             @Override | ||||
|             public void start() { | ||||
|                 log.info("开始上传"); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void progress(long progressSize, long allSize) { | ||||
|                 log.info("已上传 [{}],总大小 [{}]", progressSize, allSize); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void finish() { | ||||
|                 log.info("上传结束"); | ||||
|             } | ||||
|         }); | ||||
|         uploadPretreatment.upload(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Long countByStorageIds(List<Long> storageIds) { | ||||
|         return baseMapper.lambdaQuery().in(FileDO::getStorageId, storageIds).count(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,185 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.system.service.impl; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.CopyOnWriteArrayList; | ||||
|  | ||||
| import jakarta.annotation.Resource; | ||||
| import jakarta.servlet.ServletContext; | ||||
|  | ||||
| import lombok.RequiredArgsConstructor; | ||||
|  | ||||
| import org.dromara.x.file.storage.core.FileStorageProperties; | ||||
| import org.dromara.x.file.storage.core.FileStorageService; | ||||
| import org.dromara.x.file.storage.core.FileStorageServiceBuilder; | ||||
| import org.dromara.x.file.storage.core.platform.FileStorage; | ||||
| import org.springframework.context.ApplicationContext; | ||||
| import org.springframework.stereotype.Service; | ||||
| import org.springframework.web.accept.ContentNegotiationManager; | ||||
| import org.springframework.web.servlet.HandlerMapping; | ||||
| import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; | ||||
| import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; | ||||
| import org.springframework.web.util.UrlPathHelper; | ||||
|  | ||||
| import cn.hutool.core.util.ReflectUtil; | ||||
| import cn.hutool.core.util.StrUtil; | ||||
|  | ||||
| import top.charles7c.continew.admin.common.enums.DisEnableStatusEnum; | ||||
| import top.charles7c.continew.admin.system.mapper.StorageMapper; | ||||
| import top.charles7c.continew.admin.system.model.entity.StorageDO; | ||||
| import top.charles7c.continew.admin.system.model.query.StorageQuery; | ||||
| import top.charles7c.continew.admin.system.model.req.StorageReq; | ||||
| import top.charles7c.continew.admin.system.model.resp.StorageDetailResp; | ||||
| import top.charles7c.continew.admin.system.model.resp.StorageResp; | ||||
| import top.charles7c.continew.admin.system.service.FileService; | ||||
| import top.charles7c.continew.admin.system.service.StorageService; | ||||
| import top.charles7c.continew.starter.core.util.validate.CheckUtils; | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseServiceImpl; | ||||
|  | ||||
| /** | ||||
|  * 存储库业务实现 | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/26 22:09 | ||||
|  */ | ||||
| @Service | ||||
| @RequiredArgsConstructor | ||||
| public class StorageServiceImpl | ||||
|     extends BaseServiceImpl<StorageMapper, StorageDO, StorageResp, StorageDetailResp, StorageQuery, StorageReq> | ||||
|     implements StorageService { | ||||
|  | ||||
|     private final ApplicationContext applicationContext; | ||||
|     private final FileStorageService fileStorageService; | ||||
|     @Resource | ||||
|     private final FileService fileService; | ||||
|  | ||||
|     @Override | ||||
|     public Long add(StorageReq req) { | ||||
|         String code = req.getCode(); | ||||
|         CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code); | ||||
|         req.setStatus(DisEnableStatusEnum.ENABLE); | ||||
|         this.loadFileStorage(req); | ||||
|         return super.add(req); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void update(StorageReq req, Long id) { | ||||
|         String code = req.getCode(); | ||||
|         CheckUtils.throwIf(this.isCodeExists(code, id), "修改失败,[{}] 已存在", code); | ||||
|         StorageDO oldStorage = super.getById(id); | ||||
|         CheckUtils.throwIf( | ||||
|             Boolean.TRUE.equals(oldStorage.getIsDefault()) && DisEnableStatusEnum.DISABLE.equals(req.getStatus()), | ||||
|             "[{}] 是默认存储库,不允许禁用", oldStorage.getName()); | ||||
|         this.loadFileStorage(req); | ||||
|         super.update(req, id); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void delete(List<Long> ids) { | ||||
|         CheckUtils.throwIf(fileService.countByStorageIds(ids) > 0, "所选存储库存在文件关联,请删除文件后重试"); | ||||
|         List<StorageDO> storageList = baseMapper.lambdaQuery().in(StorageDO::getId, ids).list(); | ||||
|         storageList.forEach(s -> this.removeFileStorage(s.getCode())); | ||||
|         super.delete(ids); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public StorageDO getDefaultStorage() { | ||||
|         return baseMapper.lambdaQuery().eq(StorageDO::getIsDefault, true).one(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public StorageDO getByCode(String code) { | ||||
|         return baseMapper.lambdaQuery().eq(StorageDO::getCode, code).one(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 加载存储配置 | ||||
|      * | ||||
|      * @param req | ||||
|      *            存储配置信息 | ||||
|      */ | ||||
|     private void loadFileStorage(StorageReq req) { | ||||
|         CopyOnWriteArrayList<FileStorage> fileStorageList = fileStorageService.getFileStorageList(); | ||||
|         FileStorageProperties.LocalPlusConfig localPlusConfig = new FileStorageProperties.LocalPlusConfig(); | ||||
|         localPlusConfig.setPlatform(req.getCode()); | ||||
|         localPlusConfig.setStoragePath(req.getBucketName()); | ||||
|         localPlusConfig.setDomain(req.getDomain()); | ||||
|         fileStorageList | ||||
|             .addAll(FileStorageServiceBuilder.buildLocalPlusFileStorage(Collections.singletonList(localPlusConfig))); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 移除存储配置 | ||||
|      * | ||||
|      * @param code | ||||
|      *            存储配置编码 | ||||
|      */ | ||||
|     private void removeFileStorage(String code) { | ||||
|         CopyOnWriteArrayList<FileStorage> fileStorageList = fileStorageService.getFileStorageList(); | ||||
|         FileStorage fileStorage = fileStorageService.getFileStorage(code); | ||||
|         fileStorageList.remove(fileStorage); | ||||
|         fileStorage.close(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 编码是否存在 | ||||
|      * | ||||
|      * @param code | ||||
|      *            编码 | ||||
|      * @param id | ||||
|      *            ID | ||||
|      * @return 是否存在 | ||||
|      */ | ||||
|     private boolean isCodeExists(String code, Long id) { | ||||
|         return baseMapper.lambdaQuery().eq(StorageDO::getCode, code).ne(null != id, StorageDO::getId, id).exists(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 注册静态资源映射 | ||||
|      * | ||||
|      * @param registerMapping | ||||
|      *            静态资源映射列表 | ||||
|      */ | ||||
|     private void registerResource(Map<String, String> registerMapping) { | ||||
|         final UrlPathHelper urlPathHelper = applicationContext.getBean("mvcUrlPathHelper", UrlPathHelper.class); | ||||
|         final ContentNegotiationManager contentNegotiationManager = | ||||
|             applicationContext.getBean("mvcContentNegotiationManager", ContentNegotiationManager.class); | ||||
|         final ServletContext servletContext = applicationContext.getBean(ServletContext.class); | ||||
|         final HandlerMapping resourceHandlerMapping = | ||||
|             applicationContext.getBean("resourceHandlerMapping", HandlerMapping.class); | ||||
|         // 已经注册的静态资源映射 | ||||
|         final Map<String, Object> handlerMap = | ||||
|             (Map<String, Object>)ReflectUtil.getFieldValue(resourceHandlerMapping, "handlerMap"); | ||||
|         final ResourceHandlerRegistry resourceHandlerRegistry = | ||||
|             new ResourceHandlerRegistry(applicationContext, servletContext, contentNegotiationManager, urlPathHelper); | ||||
|         // 重新注册静态资源映射 | ||||
|         for (Map.Entry<String, String> entry : registerMapping.entrySet()) { | ||||
|             String pathPattern = StrUtil.appendIfMissing(entry.getKey(), "/**"); | ||||
|             String resourceLocations = StrUtil.appendIfMissing(entry.getValue(), "/"); | ||||
|             // 移除之前注册过的 | ||||
|             handlerMap.remove(pathPattern); | ||||
|             // 重新注册 | ||||
|             resourceHandlerRegistry.addResourceHandler(pathPattern).addResourceLocations("file:" + resourceLocations); | ||||
|         } | ||||
|         final Map<String, ?> additionalUrlMap = | ||||
|             ReflectUtil.<SimpleUrlHandlerMapping>invoke(resourceHandlerRegistry, "getHandlerMapping").getUrlMap(); | ||||
|         ReflectUtil.<Void>invoke(resourceHandlerMapping, "registerHandlers", additionalUrlMap); | ||||
|     } | ||||
| } | ||||
| @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.Hidden; | ||||
|  | ||||
| import org.dromara.x.file.storage.spring.EnableFileStorage; | ||||
| import org.springframework.boot.ApplicationArguments; | ||||
| import org.springframework.boot.ApplicationRunner; | ||||
| import org.springframework.boot.SpringApplication; | ||||
| @@ -44,6 +45,7 @@ import top.charles7c.continew.starter.core.autoconfigure.project.ProjectProperti | ||||
|  * @since 2022/12/8 23:15 | ||||
|  */ | ||||
| @Slf4j | ||||
| @EnableFileStorage | ||||
| @RestController | ||||
| @SpringBootApplication | ||||
| @RequiredArgsConstructor | ||||
|   | ||||
| @@ -0,0 +1,41 @@ | ||||
| /* | ||||
|  * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| package top.charles7c.continew.admin.webapi.system; | ||||
|  | ||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||
|  | ||||
| import org.springframework.web.bind.annotation.*; | ||||
|  | ||||
| import top.charles7c.continew.admin.system.model.query.StorageQuery; | ||||
| import top.charles7c.continew.admin.system.model.req.StorageReq; | ||||
| import top.charles7c.continew.admin.system.model.resp.StorageDetailResp; | ||||
| import top.charles7c.continew.admin.system.model.resp.StorageResp; | ||||
| import top.charles7c.continew.admin.system.service.StorageService; | ||||
| import top.charles7c.continew.starter.extension.crud.annotation.CrudRequestMapping; | ||||
| import top.charles7c.continew.starter.extension.crud.base.BaseController; | ||||
|  | ||||
| /** | ||||
|  * 存储库管理 API | ||||
|  * | ||||
|  * @author Charles7c | ||||
|  * @since 2023/12/26 22:09 | ||||
|  */ | ||||
| @Tag(name = "存储库管理 API") | ||||
| @RestController | ||||
| @CrudRequestMapping("/system/storage") | ||||
| public class StorageController | ||||
|     extends BaseController<StorageService, StorageResp, StorageDetailResp, StorageQuery, StorageReq> {} | ||||
| @@ -12,8 +12,8 @@ CREATE TABLE IF NOT EXISTS `sys_file` ( | ||||
|     `storage_id`    bigint(20)   NOT NULL                    COMMENT '存储库ID', | ||||
|     `create_user`   bigint(20)   NOT NULL                    COMMENT '创建人', | ||||
|     `create_time`   datetime     NOT NULL                    COMMENT '创建时间', | ||||
|     `update_user`   bigint(20)   DEFAULT NULL                COMMENT '修改人', | ||||
|     `update_time`   datetime     DEFAULT NULL                COMMENT '修改时间', | ||||
|     `update_user`   bigint(20)   NOT NULL                    COMMENT '修改人', | ||||
|     `update_time`   datetime     NOT NULL                    COMMENT '修改时间', | ||||
|     PRIMARY KEY (`id`) USING BTREE, | ||||
|     INDEX `idx_type`(`type`) USING BTREE, | ||||
|     INDEX `idx_create_user`(`create_user`) USING BTREE, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user