feat(oss): 实现文件上传功能并集成Feign调用
- 新增文件上传接口,支持multipart/form-data格式 - 配置Spring Servlet multipart参数,设置文件大小限制 - 添加Feign客户端配置,支持表单提交 - 实现Feign请求拦截器,传递用户上下文信息 - 创建OSS服务API接口,用于文件上传 - 在用户服务中集成OSS RPC调用,实现头像和背景图上传 - 添加上传失败的业务异常处理 - 更新pom.xml依赖,引入OpenFeign、负载均衡及Feign表单相关组件 - 定义API常量和服务名称 - 启用Feign客户端扫描,支持跨服务调用
This commit is contained in:
@@ -20,5 +20,24 @@
|
||||
<groupId>com.hanserwei</groupId>
|
||||
<artifactId>hanserwei-common</artifactId>
|
||||
</dependency>
|
||||
<!-- OpenFeign -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<!-- 负载均衡 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
<!-- Feign 表单提交 -->
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign.form</groupId>
|
||||
<artifactId>feign-form-spring</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign.form</groupId>
|
||||
<artifactId>feign-form</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.hanserwei.hannote.oss.api;
|
||||
|
||||
import com.hanserwei.framework.common.response.Response;
|
||||
import com.hanserwei.hannote.oss.config.FeignFormConfig;
|
||||
import com.hanserwei.hannote.oss.constant.ApiConstants;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@FeignClient(name = ApiConstants.SERVICE_NAME, configuration = FeignFormConfig.class)
|
||||
public interface FileFeignApi {
|
||||
|
||||
String PREFIX = "/file";
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
*
|
||||
* @param file 文件
|
||||
* @return 文件上传结果
|
||||
*/
|
||||
@PostMapping(value = PREFIX + "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
Response<?> uploadFile(@RequestPart(value = "file") MultipartFile file);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.hanserwei.hannote.oss.config;
|
||||
|
||||
import feign.codec.Encoder;
|
||||
import feign.form.spring.SpringFormEncoder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
@Configuration
|
||||
public class FeignFormConfig {
|
||||
|
||||
@Bean
|
||||
public Encoder feignFormEncoder() {
|
||||
return new SpringFormEncoder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.hanserwei.hannote.oss.constant;
|
||||
|
||||
public interface ApiConstants {
|
||||
|
||||
/**
|
||||
* 服务名称
|
||||
*/
|
||||
String SERVICE_NAME = "han-note-oss";
|
||||
}
|
||||
@@ -75,6 +75,23 @@
|
||||
<groupId>com.qcloud</groupId>
|
||||
<artifactId>cos_api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hanserwei</groupId>
|
||||
<artifactId>hanserwei-spring-boot-starter-biz-operationlog</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hanserwei</groupId>
|
||||
<artifactId>hanserwei-spring-boot-starter-jackson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hanserwei</groupId>
|
||||
<artifactId>han-note-oss-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hanserwei</groupId>
|
||||
<artifactId>hanserwei-spring-boot-starter-biz-context</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.hanserwei.hannote.oss.controller;
|
||||
|
||||
import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder;
|
||||
import com.hanserwei.framework.common.response.Response;
|
||||
import com.hanserwei.hannote.oss.service.FileService;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -21,6 +22,7 @@ public class FileController {
|
||||
|
||||
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
public Response<?> uploadFile(@RequestPart(value = "file") MultipartFile file) {
|
||||
log.info("当前用户 ID: {}", LoginUserContextHolder.getUserId());
|
||||
return fileService.uploadFile(file);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@ server:
|
||||
spring:
|
||||
profiles:
|
||||
active: dev # 默认激活 dev 本地开发环境
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 20MB # 单个文件最大大小
|
||||
max-request-size: 100MB # 单次请求最大大小(包含多个文件)
|
||||
|
||||
storage:
|
||||
type: rustfs # 对象存储类型
|
||||
@@ -66,6 +66,10 @@
|
||||
<groupId>com.hanserwei</groupId>
|
||||
<artifactId>hanserwei-spring-boot-starter-biz-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hanserwei</groupId>
|
||||
<artifactId>han-note-oss-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.hanserwei.hannote.user.biz;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.hanserwei.hannote.user.biz.domain.mapper")
|
||||
@EnableFeignClients(basePackages = "com.hanserwei.hannote")
|
||||
public class HannoteUserBizApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(HannoteUserBizApplication.class, args);
|
||||
|
||||
@@ -17,7 +17,8 @@ public enum ResponseCodeEnum implements BaseExceptionInterface {
|
||||
HAN_NOTE_ID_VALID_FAIL("USER-20002", "小憨书号请设置6-15个字符,仅可使用英文(必须)、数字、下划线"),
|
||||
SEX_VALID_FAIL("USER-20003", "性别错误"),
|
||||
INTRODUCTION_VALID_FAIL("USER-20004", "个人简介请设置1-100个字符"),
|
||||
;
|
||||
UPLOAD_AVATAR_FAIL("USER-20005", "头像上传失败"),
|
||||
UPLOAD_BACKGROUND_IMG_FAIL("USER-20006", "背景图上传失败"),
|
||||
;
|
||||
|
||||
// 异常码
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.hanserwei.hannote.user.biz.rpc;
|
||||
|
||||
import com.hanserwei.framework.common.response.Response;
|
||||
import com.hanserwei.hannote.oss.api.FileFeignApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@Component
|
||||
public class OssRpcService {
|
||||
|
||||
@Resource
|
||||
private FileFeignApi fileFeignApi;
|
||||
|
||||
public String uploadFile(MultipartFile file) {
|
||||
// 调用对象存储服务上传文件
|
||||
Response<?> response = fileFeignApi.uploadFile(file);
|
||||
|
||||
if (!response.isSuccess()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 返回图片访问链接
|
||||
return (String) response.getData();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.hanserwei.hannote.user.biz.service.impl;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder;
|
||||
import com.hanserwei.framework.common.exception.ApiException;
|
||||
import com.hanserwei.framework.common.response.Response;
|
||||
import com.hanserwei.framework.common.utils.ParamUtils;
|
||||
import com.hanserwei.hannote.user.biz.domain.dataobject.UserDO;
|
||||
@@ -10,7 +11,9 @@ import com.hanserwei.hannote.user.biz.domain.mapper.UserDOMapper;
|
||||
import com.hanserwei.hannote.user.biz.enums.ResponseCodeEnum;
|
||||
import com.hanserwei.hannote.user.biz.enums.SexEnum;
|
||||
import com.hanserwei.hannote.user.biz.model.vo.UpdateUserInfoReqVO;
|
||||
import com.hanserwei.hannote.user.biz.rpc.OssRpcService;
|
||||
import com.hanserwei.hannote.user.biz.service.UserService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -23,6 +26,10 @@ import java.util.Objects;
|
||||
@Service
|
||||
@Slf4j
|
||||
public class UserServiceImpl extends ServiceImpl<UserDOMapper, UserDO> implements UserService {
|
||||
|
||||
@Resource
|
||||
private OssRpcService ossRpcService;
|
||||
|
||||
@Override
|
||||
public Response<?> updateUserInfo(UpdateUserInfoReqVO updateUserInfoReqVO) {
|
||||
UserDO userDO = new UserDO();
|
||||
@@ -35,7 +42,16 @@ public class UserServiceImpl extends ServiceImpl<UserDOMapper, UserDO> implement
|
||||
MultipartFile avatar = updateUserInfoReqVO.getAvatar();
|
||||
|
||||
if (Objects.nonNull(avatar)) {
|
||||
// TODO: 上传头像,调用服务
|
||||
String avatarUrl = ossRpcService.uploadFile(avatar);
|
||||
log.info("==> 调用 oss 服务成功,上传头像,url:{}", avatarUrl);
|
||||
|
||||
// 若上传头像失败,则抛出业务异常
|
||||
if (StringUtils.isBlank(avatarUrl)) {
|
||||
throw new ApiException(ResponseCodeEnum.UPLOAD_AVATAR_FAIL);
|
||||
}
|
||||
|
||||
userDO.setAvatar(avatarUrl);
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
// 昵称
|
||||
@@ -80,7 +96,16 @@ public class UserServiceImpl extends ServiceImpl<UserDOMapper, UserDO> implement
|
||||
// 背景图片
|
||||
MultipartFile backgroundImg = updateUserInfoReqVO.getBackgroundImg();
|
||||
if (Objects.nonNull(backgroundImg)) {
|
||||
// TODO: 上传背景图片,调用服务
|
||||
String backgroundImgUrl = ossRpcService.uploadFile(backgroundImg);
|
||||
log.info("==> 调用 oss 服务成功,上传背景图,url:{}", backgroundImg);
|
||||
|
||||
// 若上传背景图失败,则抛出业务异常
|
||||
if (StringUtils.isBlank(backgroundImgUrl)) {
|
||||
throw new ApiException(ResponseCodeEnum.UPLOAD_BACKGROUND_IMG_FAIL);
|
||||
}
|
||||
|
||||
userDO.setBackgroundImg(backgroundImgUrl);
|
||||
needUpdate = true;
|
||||
}
|
||||
|
||||
if (needUpdate) {
|
||||
|
||||
@@ -4,6 +4,10 @@ server:
|
||||
spring:
|
||||
profiles:
|
||||
active: dev # 默认激活 dev 本地开发环境
|
||||
servlet:
|
||||
multipart:
|
||||
max-file-size: 20MB # 单个文件最大大小
|
||||
max-request-size: 100MB # 单次请求最大大小(包含多个文件)
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
<artifactId>feign-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.hanserwei.framework.biz.context.config;
|
||||
|
||||
import com.hanserwei.framework.biz.context.interceptor.FeignRequestInterceptor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@AutoConfiguration
|
||||
public class FeignContextAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public FeignRequestInterceptor feignRequestInterceptor() {
|
||||
return new FeignRequestInterceptor();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.hanserwei.framework.biz.context.interceptor;
|
||||
|
||||
import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder;
|
||||
import com.hanserwei.framework.common.constant.GlobalConstants;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
public class FeignRequestInterceptor implements RequestInterceptor {
|
||||
|
||||
@Override
|
||||
public void apply(RequestTemplate requestTemplate) {
|
||||
// 获取当前上下文中的用户 ID
|
||||
Long userId = LoginUserContextHolder.getUserId();
|
||||
|
||||
// 若不为空,则添加到请求头中
|
||||
if (Objects.nonNull(userId)) {
|
||||
requestTemplate.header(GlobalConstants.USER_ID, String.valueOf(userId));
|
||||
log.info("########## feign 请求设置请求头 userId: {}", userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
com.hanserwei.framework.biz.context.config.ContextAutoConfiguration
|
||||
com.hanserwei.framework.biz.context.config.FeignContextAutoConfiguration
|
||||
12
pom.xml
12
pom.xml
@@ -50,6 +50,7 @@
|
||||
<activation.version>1.1.1</activation.version>
|
||||
<jaxb-runtime.version>2.3.3</jaxb-runtime.version>
|
||||
<cos-api.version>5.6.227</cos-api.version>
|
||||
<feign-form.version>3.8.0</feign-form.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -203,6 +204,17 @@
|
||||
<artifactId>cos_api</artifactId>
|
||||
<version>${cos-api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hanserwei</groupId>
|
||||
<artifactId>han-note-oss-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- Feign 表单提交 -->
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign.form</groupId>
|
||||
<artifactId>feign-form</artifactId>
|
||||
<version>${feign-form.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user