feat: 增加附件表结构与本地上传接口
This commit is contained in:
45
script/sql/attachment.sql
Normal file
45
script/sql/attachment.sql
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
DROP TABLE IF EXISTS sys_attachment;
|
||||||
|
|
||||||
|
CREATE TABLE sys_attachment (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
source_type VARCHAR(100) NOT NULL,
|
||||||
|
source_id BIGINT,
|
||||||
|
original_name VARCHAR(255) NOT NULL,
|
||||||
|
file_name VARCHAR(255) NOT NULL,
|
||||||
|
file_suffix VARCHAR(50),
|
||||||
|
content_type VARCHAR(100),
|
||||||
|
file_size BIGINT NOT NULL DEFAULT 0,
|
||||||
|
storage_type VARCHAR(50) NOT NULL,
|
||||||
|
file_path VARCHAR(500) NOT NULL,
|
||||||
|
file_url VARCHAR(500),
|
||||||
|
version INTEGER NOT NULL DEFAULT 1,
|
||||||
|
create_time TIMESTAMP,
|
||||||
|
update_time TIMESTAMP,
|
||||||
|
remark VARCHAR(500) DEFAULT '',
|
||||||
|
create_by VARCHAR(64),
|
||||||
|
update_by VARCHAR(64)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_sys_attachment_source_type ON sys_attachment (source_type);
|
||||||
|
CREATE INDEX idx_sys_attachment_source_id ON sys_attachment (source_id);
|
||||||
|
CREATE INDEX idx_sys_attachment_storage_type ON sys_attachment (storage_type);
|
||||||
|
CREATE INDEX idx_sys_attachment_create_time ON sys_attachment (create_time);
|
||||||
|
|
||||||
|
COMMENT ON TABLE sys_attachment IS '系统附件表';
|
||||||
|
COMMENT ON COLUMN sys_attachment.id IS 'ID';
|
||||||
|
COMMENT ON COLUMN sys_attachment.source_type IS '来源业务类型';
|
||||||
|
COMMENT ON COLUMN sys_attachment.source_id IS '来源业务ID';
|
||||||
|
COMMENT ON COLUMN sys_attachment.original_name IS '原始文件名';
|
||||||
|
COMMENT ON COLUMN sys_attachment.file_name IS '存储文件名';
|
||||||
|
COMMENT ON COLUMN sys_attachment.file_suffix IS '文件后缀';
|
||||||
|
COMMENT ON COLUMN sys_attachment.content_type IS '文件MIME类型';
|
||||||
|
COMMENT ON COLUMN sys_attachment.file_size IS '文件大小(字节)';
|
||||||
|
COMMENT ON COLUMN sys_attachment.storage_type IS '存储类型';
|
||||||
|
COMMENT ON COLUMN sys_attachment.file_path IS '文件存储路径';
|
||||||
|
COMMENT ON COLUMN sys_attachment.file_url IS '文件访问地址';
|
||||||
|
COMMENT ON COLUMN sys_attachment.version IS '版本';
|
||||||
|
COMMENT ON COLUMN sys_attachment.create_time IS '创建时间';
|
||||||
|
COMMENT ON COLUMN sys_attachment.update_time IS '更新时间';
|
||||||
|
COMMENT ON COLUMN sys_attachment.remark IS '备注';
|
||||||
|
COMMENT ON COLUMN sys_attachment.create_by IS '创建者';
|
||||||
|
COMMENT ON COLUMN sys_attachment.update_by IS '更新者';
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.bruce.common.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "common.attachment")
|
||||||
|
public class AttachmentProperties {
|
||||||
|
|
||||||
|
private String basePath = "data/attachments";
|
||||||
|
|
||||||
|
public String getBasePath() {
|
||||||
|
return basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBasePath(String basePath) {
|
||||||
|
this.basePath = basePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.bruce.common.controller;
|
||||||
|
|
||||||
|
import com.bruce.common.entity.SysAttachment;
|
||||||
|
import com.bruce.common.service.ISysAttachmentService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@Tag(name = "系统附件管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/attachments")
|
||||||
|
public class SysAttachmentController {
|
||||||
|
|
||||||
|
private final ISysAttachmentService sysAttachmentService;
|
||||||
|
|
||||||
|
public SysAttachmentController(ISysAttachmentService sysAttachmentService) {
|
||||||
|
this.sysAttachmentService = sysAttachmentService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "上传附件")
|
||||||
|
@PostMapping("/upload")
|
||||||
|
public SysAttachment upload(@RequestParam("file") MultipartFile file,
|
||||||
|
@RequestParam String sourceType,
|
||||||
|
@RequestParam(required = false) Long sourceId) {
|
||||||
|
return sysAttachmentService.upload(file, sourceType, sourceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/main/java/com/bruce/common/entity/SysAttachment.java
Normal file
59
src/main/java/com/bruce/common/entity/SysAttachment.java
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package com.bruce.common.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("sys_attachment")
|
||||||
|
@Schema(description = "系统附件")
|
||||||
|
public class SysAttachment extends BaseEntity {
|
||||||
|
|
||||||
|
@Schema(description = "来源业务类型")
|
||||||
|
@TableField("source_type")
|
||||||
|
private String sourceType;
|
||||||
|
|
||||||
|
@Schema(description = "来源业务ID")
|
||||||
|
@TableField("source_id")
|
||||||
|
private Long sourceId;
|
||||||
|
|
||||||
|
@Schema(description = "原始文件名")
|
||||||
|
@TableField("original_name")
|
||||||
|
private String originalName;
|
||||||
|
|
||||||
|
@Schema(description = "存储文件名")
|
||||||
|
@TableField("file_name")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@Schema(description = "文件后缀")
|
||||||
|
@TableField("file_suffix")
|
||||||
|
private String fileSuffix;
|
||||||
|
|
||||||
|
@Schema(description = "文件MIME类型")
|
||||||
|
@TableField("content_type")
|
||||||
|
private String contentType;
|
||||||
|
|
||||||
|
@Schema(description = "文件大小(字节)")
|
||||||
|
@TableField("file_size")
|
||||||
|
private Long fileSize;
|
||||||
|
|
||||||
|
@Schema(description = "存储类型")
|
||||||
|
@TableField("storage_type")
|
||||||
|
private String storageType;
|
||||||
|
|
||||||
|
@Schema(description = "文件存储路径")
|
||||||
|
@TableField("file_path")
|
||||||
|
private String filePath;
|
||||||
|
|
||||||
|
@Schema(description = "文件访问地址")
|
||||||
|
@TableField("file_url")
|
||||||
|
private String fileUrl;
|
||||||
|
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.bruce.common.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.bruce.common.entity.SysAttachment;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface SysAttachmentMapper extends BaseMapper<SysAttachment> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.bruce.common.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.bruce.common.entity.SysAttachment;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
public interface ISysAttachmentService extends IService<SysAttachment> {
|
||||||
|
|
||||||
|
SysAttachment upload(MultipartFile file, String sourceType, Long sourceId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.bruce.common.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.bruce.common.config.AttachmentProperties;
|
||||||
|
import com.bruce.common.entity.SysAttachment;
|
||||||
|
import com.bruce.common.mapper.SysAttachmentMapper;
|
||||||
|
import com.bruce.common.service.ISysAttachmentService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class SysAttachmentServiceImpl extends ServiceImpl<SysAttachmentMapper, SysAttachment> implements ISysAttachmentService {
|
||||||
|
|
||||||
|
private static final String STORAGE_TYPE_LOCAL = "LOCAL";
|
||||||
|
|
||||||
|
private final AttachmentProperties attachmentProperties;
|
||||||
|
|
||||||
|
public SysAttachmentServiceImpl(AttachmentProperties attachmentProperties) {
|
||||||
|
this.attachmentProperties = attachmentProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SysAttachment upload(MultipartFile file, String sourceType, Long sourceId) {
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("上传文件不能为空");
|
||||||
|
}
|
||||||
|
if (!StringUtils.hasText(sourceType)) {
|
||||||
|
throw new IllegalArgumentException("sourceType不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
String originalName = file.getOriginalFilename();
|
||||||
|
String suffix = StringUtils.getFilenameExtension(originalName);
|
||||||
|
String storedFileName = UUID.randomUUID() + (StringUtils.hasText(suffix) ? "." + suffix : "");
|
||||||
|
String dateDirectory = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
|
||||||
|
Path rootPath = Paths.get(attachmentProperties.getBasePath()).toAbsolutePath().normalize();
|
||||||
|
Path targetDirectory = rootPath.resolve(dateDirectory);
|
||||||
|
Path targetPath = targetDirectory.resolve(storedFileName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.createDirectories(targetDirectory);
|
||||||
|
file.transferTo(targetPath);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("文件上传失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
SysAttachment attachment = new SysAttachment();
|
||||||
|
attachment.setSourceType(sourceType);
|
||||||
|
attachment.setSourceId(sourceId);
|
||||||
|
attachment.setOriginalName(originalName);
|
||||||
|
attachment.setFileName(storedFileName);
|
||||||
|
attachment.setFileSuffix(suffix);
|
||||||
|
attachment.setContentType(file.getContentType());
|
||||||
|
attachment.setFileSize(file.getSize());
|
||||||
|
attachment.setStorageType(STORAGE_TYPE_LOCAL);
|
||||||
|
attachment.setFilePath(dateDirectory + "/" + storedFileName);
|
||||||
|
attachment.setFileUrl(null);
|
||||||
|
attachment.setVersion(1);
|
||||||
|
save(attachment);
|
||||||
|
return attachment;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,3 +11,7 @@ mybatis-plus:
|
|||||||
mapper-locations: classpath*:/mapper/**/*.xml
|
mapper-locations: classpath*:/mapper/**/*.xml
|
||||||
configuration:
|
configuration:
|
||||||
map-underscore-to-camel-case: true
|
map-underscore-to-camel-case: true
|
||||||
|
|
||||||
|
common:
|
||||||
|
attachment:
|
||||||
|
base-path: /data/common-agent/attachments
|
||||||
|
|||||||
@@ -4,3 +4,6 @@ spring:
|
|||||||
profiles:
|
profiles:
|
||||||
active: dev
|
active: dev
|
||||||
|
|
||||||
|
common:
|
||||||
|
attachment:
|
||||||
|
base-path: data/attachments
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.bruce.common.attachment;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.bruce.common.controller.SysAttachmentController;
|
||||||
|
import com.bruce.common.entity.SysAttachment;
|
||||||
|
import com.bruce.common.mapper.SysAttachmentMapper;
|
||||||
|
import com.bruce.common.service.ISysAttachmentService;
|
||||||
|
import com.bruce.common.service.impl.SysAttachmentServiceImpl;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class SysAttachmentComponentStructureTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sysAttachmentComponentsShouldReuseMybatisPlusBaseTypes() {
|
||||||
|
assertTrue(BaseMapper.class.isAssignableFrom(SysAttachmentMapper.class));
|
||||||
|
assertTrue(IService.class.isAssignableFrom(ISysAttachmentService.class));
|
||||||
|
assertTrue(ServiceImpl.class.isAssignableFrom(SysAttachmentServiceImpl.class));
|
||||||
|
assertTrue(ISysAttachmentService.class.isAssignableFrom(SysAttachmentServiceImpl.class));
|
||||||
|
assertTrue(SysAttachment.class.isAssignableFrom(SysAttachment.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void sysAttachmentShouldExposeUploadMethods() throws NoSuchMethodException {
|
||||||
|
Method serviceMethod = ISysAttachmentService.class.getMethod(
|
||||||
|
"upload",
|
||||||
|
MultipartFile.class,
|
||||||
|
String.class,
|
||||||
|
Long.class
|
||||||
|
);
|
||||||
|
Method controllerMethod = SysAttachmentController.class.getMethod(
|
||||||
|
"upload",
|
||||||
|
MultipartFile.class,
|
||||||
|
String.class,
|
||||||
|
Long.class
|
||||||
|
);
|
||||||
|
|
||||||
|
assertNotNull(serviceMethod);
|
||||||
|
assertNotNull(controllerMethod);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user