1. 实现集中式的附件统一管理,统一上传统一预览(代码生成器,公共组件,公共附件元数据定义)

2. 实现统一的 DB 字段数据库定义(代码生成器,共用规范检查)

(cherry picked from commit c2195ee3cf)
This commit is contained in:
chenbowen
2025-08-01 08:47:13 +08:00
committed by chenbowen
parent f9dc200d26
commit 014bd716de
63 changed files with 1674 additions and 351 deletions

View File

@@ -37,6 +37,18 @@
<artifactId>yudao-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-rpc</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,49 @@
package cn.iocoder.yudao.framework.business.annotation;
import java.lang.annotation.*;
/**
* @author chenbowen
*
* 附件上传 Controller 注解,
* 1. 标记附件列表上传在 requestBody 中的 Key 值,
* 2. 标记当前 controller 中业务创建请求后返回的业务主键 Key 值,
* 2. 标记会开启业务提交时,前置校验操作用户唯一部门
* 3. 标记当前 controller 所属的业务来源
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface FileUploadController {
/**
* 附件列表上传在 requestBody 中的 Key 值
*/
String filesKey() default "files";
/**
* 附件名称在 requestBody 中的 Key 值
*/
String fileNameKey() default "name";
/**
* 附件 ID 在 requestBody 中的 Key 值
*/
String fileIdKey() default "id";
/**
* 业务来源
* 例如bpm、oa、hr 等
*/
String source() default "default";
/**
* 业务创建请求后返回的业务主键 Key 值
*/
String primaryKey() default "data.id";
/**
* 业务创建请求后返回的业务编码 Key 值
*/
String codeKey() default "data.code";
}

View File

@@ -1,8 +1,13 @@
package cn.iocoder.yudao.framework.business.config;
import cn.iocoder.yudao.framework.business.filter.FileUploadFilter;
import cn.iocoder.yudao.framework.business.interceptor.BusinessHeaderInterceptor;
import cn.iocoder.yudao.framework.business.interceptor.FileUploadHeaderInterceptor;
import cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@@ -10,11 +15,19 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
* @author chenbowen
*/
@AutoConfiguration(after = YudaoWebAutoConfiguration.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class YudaoBusinessAutoConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 只拦截增删改和 set 相关的 url
registry.addInterceptor(new BusinessHeaderInterceptor())
.addPathPatterns("/**/add**", "/**/create**", "/**/update**", "/**/edit**", "/**/set**");
registry.addInterceptor(new FileUploadHeaderInterceptor())
.addPathPatterns("/**/add**", "/**/create**", "/**/update**", "/**/edit**", "/**/set**");
}
@Bean
public FilterRegistrationBean<FileUploadFilter> businessHeaderFilter() {
return new FilterRegistrationBean<>(new FileUploadFilter());
}
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.yudao.framework.business.controller;
import cn.iocoder.yudao.framework.business.annotation.FileUploadController;
import cn.iocoder.yudao.framework.business.vo.FileUploadInfoVO;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.web.bind.annotation.GetMapping;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
* @author chenbowen
*/
public abstract class AbstractFileUploadController {
protected static String FILE_UPLOAD_SOURCE = "";
@GetMapping("/upload-info")
@Operation(summary = "获取文件上传 source 配置值")
public CommonResult<FileUploadInfoVO> getFileUploadSource() {
FileUploadInfoVO vo = new FileUploadInfoVO();
vo.setSource(FILE_UPLOAD_SOURCE);
return success(vo);
}
protected static void setFileUploadInfo(FileUploadController annotation) {
FILE_UPLOAD_SOURCE = annotation.source();
}
}

View File

@@ -0,0 +1,19 @@
package cn.iocoder.yudao.framework.business.enums;
/**
* @author chenbowen
*/
public interface FileUploadConstants {
// 是否业务请求
String IS_UPLOAD_REQUEST = "isUploadRequest";
// request 中附件列表的 Key 值,支持多层级引用 如 data.files
String FILES_KEY = "fileKey";
String FILE_NAME_KEY = "fileNameKey";
String FILE_ID_KEY = "fileIdKey";
// request 中业务来源的
String FILE_SOURCE = "fileSource";
// request 中业务创建请求后返回的业务主键 Key 值,支持多层级引用 如 data.businessPrimaryKey
String FILE_REL_PRIMARY_KEY = "fileRelPrimaryKey";
String FILE_REL_CODE_KEY = "fileRelCode";
}

View File

@@ -0,0 +1,13 @@
package cn.iocoder.yudao.framework.business.framework.rpc;
import cn.iocoder.yudao.module.infra.api.businessfile.BusinessFileApi;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author chenbowen
*/
@AutoConfiguration
@EnableFeignClients(clients = BusinessFileApi.class)
public class YudaoBusinessRpcAutoConfiguration {
}

View File

@@ -0,0 +1,69 @@
package cn.iocoder.yudao.framework.business.interceptor;
import cn.iocoder.yudao.framework.business.annotation.FileUploadController;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResultCodeEnum;
import cn.iocoder.yudao.framework.common.pojo.CompanyDeptInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.lang.NonNull;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.Set;
import static cn.iocoder.yudao.framework.business.core.util.BusinessDeptHandleUtil.getBelongCompanyAndDept;
import static cn.iocoder.yudao.framework.business.enums.FileUploadConstants.*;
/**
* @author chenbowen
*/
@RequiredArgsConstructor
public class FileUploadHeaderInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {
if (!(handler instanceof HandlerMethod handlerMethod)) {
return true;
}
Object bean = handlerMethod.getBean();
FileUploadController annotation = bean.getClass().getAnnotation(FileUploadController.class);
if (annotation == null) {
return true;
}
// 设置请求属性,标记当前请求为业务控制器
request.setAttribute(IS_UPLOAD_REQUEST, true);
// 从注解中获取所有的 value 值,如果没有被 注解,获取注解默认值
// 将这些值设置到 request
request.setAttribute(FILES_KEY, annotation.filesKey());
request.setAttribute(FILE_NAME_KEY, annotation.fileNameKey());
request.setAttribute(FILE_ID_KEY, annotation.fileIdKey());
request.setAttribute(FILE_SOURCE, annotation.source());
request.setAttribute(FILE_REL_PRIMARY_KEY, annotation.primaryKey());
request.setAttribute(FILE_REL_CODE_KEY, annotation.codeKey());
ObjectMapper objectMapper = new ObjectMapper();
Set<CompanyDeptInfo> companyDeptInfos = getBelongCompanyAndDept(request, response);
// 无法获取到有效的用户归属公司与部门信息,提示错误
if (companyDeptInfos == null) {
return writeResponse(response, HttpStatus.BAD_REQUEST.value(), CommonResult.customize(null, CommonResultCodeEnum.ERROR.getCode(), "当前用户匹配部门不属于此公司"), objectMapper);
}
// 获取到了有效的一组或多组公司与部门信息,需要返回前端进行补充请求头后的二次请求
if (!companyDeptInfos.isEmpty()) {
return writeResponse(response, HttpStatus.OK.value(), CommonResult.customize(companyDeptInfos, CommonResultCodeEnum.NEED_ADJUST), objectMapper);
}
else{
return true;
}
}
private boolean writeResponse(HttpServletResponse response, int status, CommonResult<?> result, ObjectMapper objectMapper) throws Exception {
response.setStatus(status);
response.getWriter().write(objectMapper.writeValueAsString(result));
return false;
}
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.yudao.framework.business.vo;
import lombok.Data;
/**
* 文件上传信息的值对象
* 用于封装文件上传的相关信息
*
* @author chenbowen
*/
@Data
public class FileUploadInfoVO {
// 文件来源标识
private String source;
}

View File

@@ -1,2 +1,3 @@
cn.iocoder.yudao.framework.business.config.YudaoBusinessAutoConfiguration
cn.iocoder.yudao.framework.business.framework.BusinessDataPermissionConfiguration
cn.iocoder.yudao.framework.business.framework.BusinessDataPermissionConfiguration
cn.iocoder.yudao.framework.business.framework.rpc.YudaoBusinessRpcAutoConfiguration