自媒体素材管理与文章管理
一.后台搭建
1.1 搭建自媒体网关
导入网关模块>>>在网关模块的pom.xml文件中添加该子模块>>>刷新maven
<modules>
<module>heima-leadnews-app-gateway</module>
<!--新增-->
<module>heima-leadnews-wemedia-gateway</module>
</modules>
添加nacos配置
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" #跨域处理 允许所有的域
allowedMethods: # 支持的方法
- GET
- POST
- PUT
- DELETE
routes:
# 平台管理
- id: wemedia
uri: lb://leadnews-wemedia
predicates:
- Path=/wemedia/**
filters:
- StripPrefix= 1
1.2 搭建自媒体微服务
导入自媒体微服务模块>>>微服务模块的pom.xml文件中添加该子模块>>>刷新maven
<modules>
<module>heima-leadnews-user</module>
<module>heima-leadnews-article</module>
<module>heima-leadnews-wemedia</module>
</modules>
添加nacos配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/leadnews_wemedia?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: root
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.heima.model.wemedia.pojos
创建数据库leadnews_wemedia>>>导入sql脚本
1.3 导入实体类
找到xxx-model模块>>>在com.heima.model下导入wemedia包
二.前台搭建
通过nginx的虚拟主机功能,使用同一个nginx访问多个项目
2.1 解压前端工程到指定目录
我的前端工程和nginx都放到了E:\JavaCode\heima-leadnews-front目录下
2.2 新增wemedia前端工程配置文件,并在外围配置文件nginx.conf中引入该配置文件
配置文件
upstream heima-wemedia-gateway{
server localhost:51602; # 自媒体模块网关端口
}
server {
listen 8802; # 前端访问端口
location / {
root E:/JavaCode/heima-leadnews-front/wemedia-web/;
index index.html;
}
location ~/wemedia/MEDIA/(.*) { # 请求映射
proxy_pass http://heima-wemedia-gateway/$1;
proxy_set_header HOST $host; # 不改变源请求头的值
proxy_pass_request_body on; #开启获取请求体
proxy_pass_request_headers on; #开启获取请求头
proxy_set_header X-Real-IP $remote_addr; # 记录真实发出请求的客户端IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #记录代理信息
}
}
2.3 启动nginx,启动自媒体微服务和对应网关
三.自媒体素材管理
3.1 自媒体素材管理&图片上传
3.1.1 添加自媒体素材对应的实体类
直接拷贝到对应的包下即可
3.1.2 上传素材实现思路
(1).前端发送上传图片请求,类型为MultipartFile
(2).网关进行token解析后,把解析后的用户信息存储到header中
需要在网关的全局认证过滤器中添加代码,用于解析用户信息到header中;
//5.判断token是否有效
try {
Claims claimsBody = AppJwtUtil.getClaimsBody(token);
//是否是过期
int result = AppJwtUtil.verifyToken(claimsBody);
if (result == 1 || result == 2) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//获得token解析后中的用户信息
Object userId = claimsBody.get("id");
//----------------------------新增部分------------------------\\
//在header中添加新的信息
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
httpHeaders.add("userId", userId + "");
}).build();
//重置header
exchange.mutate().request(serverHttpRequest).build();
//----------------------------新增部分------------------------\\
} catch (Exception e) {
e.printStackTrace();
}
(3).自媒体微服务使用拦截器获取到header中的的用户信息,并放入到threadlocal中
3.1.3 新增ThreadLocal工具类WmThreadLocalUtil
WmThreadLocalUtil工具类会便于操作ThreadLocalUtil对象;
public class WmThreadLocalUtil {
private final static ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();
/**
* 添加用户
* @param wmUser
*/
public static void setUser(WmUser wmUser){
WM_USER_THREAD_LOCAL.set(wmUser);
}
/**
* 获取用户
*/
public static WmUser getUser(){
return WM_USER_THREAD_LOCAL.get();
}
/**
* 清理用户
*/
public static void clear(){
WM_USER_THREAD_LOCAL.remove();
}
}
3.1.4 在heima-leadnews-wemedia中新增拦截器
拦截器的作用,从header中取出登录用户ID存入当前线程;在 com.heima.wemedia.interceptor 包下创建拦截器WmTokenInterceptor
@Slf4j
public class WmTokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//得到header中的信息
String userId = request.getHeader("userId");
Optional<String> optional = Optional.ofNullable(userId);
if(optional.isPresent()){
//把用户id存入threadloacl中
WmUser wmUser = new WmUser();
wmUser.setId(Integer.valueOf(userId));
WmThreadLocalUtil.setUser(wmUser);
log.info("wmTokenFilter设置用户信息到threadlocal中...");
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("清理threadlocal...");
WmThreadLocalUtil.clear();
}
}
3.1.5 通过配置类WebMvcConfig配置拦截器生效
拦截所有请求
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**");
}
}
3.1.6 自媒体微服务集成heima-file-starter
由于要用到上传minio的功能,因此需要集成heima-file-starter(该模块中提供了上传文件(图片,html文件),下载,删除文件的功能。
在自媒体微服务中导入依赖
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-file-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
自媒体微服务的配置中心添加以下配置
minio:
accessKey: minio
secretKey: minio123
bucket: leadnews
endpoint: http://47.108.218.130:9000
readPath: http://47.108.218.130:9000
3.1.7 具体实现
三步走,mapper>>>service>>>controller;
问题一
中间遇到了一个问题:上传文件到minio时失败
// 报错日志
minio put file error.
The request signature we calculated does not match the signature you provided.
Check your key and signing method.
报错日志大意如下:
我们计算的请求签名与您提供的签名不匹配。
检查您的密钥和签名方法。
推测大概可能是配置文件中的minio的秘钥与创建minio容器时指定的不一致,我在创建时使用的秘钥是11位,而在配置文件中使用的是8位因此报错,修改后重启。该报错消失返回了fileId;
问题二
在解决了上述错误之后,又遇到了报错信息:
catch exception:null
java.lang.NullPointerException
at com.heima.wemedia.service.impl.WmMaterialServiceImpl.uploadPicture
(WmMaterialServiceImpl.java:56)
这个就不翻译了,推测应该是在业务层中出现了内容为空的情况,根据报错信息定位到对应的代码,经过检查发现并不是上传文件返回结果的问题,文件已经被上传到了minio,并且返回了相应的地址;问题是拦截器无法直接从请求的请求头中获得"userId",那么说明网关在过滤请求的时候确实是从请求中解析了token,但是没有把解析出来的内容userId直接添加到请求头中,因此要在heima-leadnews-wemedia-gateway模块的包com.heima.wemedia.gateway.filter下的AuthorizeFilter中步骤5中添加如下代码:
//5.判断token是否有效
try {
Claims claimsBody = AppJwtUtil.getClaimsBody(token);
//是否是过期
int result = AppJwtUtil.verifyToken(claimsBody);
if (result == 1 || result == 2) {
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//-------------------添加部分--------------------//
//获得token解析后中的用户信息
Object userId = claimsBody.get("id");
//在header中添加新的信息
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
httpHeaders.add("userId", userId + "");
}).build();
//重置header
exchange.mutate().request(serverHttpRequest).build();
//-------------------添加部分--------------------//
} catch (Exception e) {
e.printStackTrace();
}
此时图片上传成功,并且可以正常回显,数据库中的数据已经更新:
3.2 自媒体素材管理&素材列表查询
在xxx-model模块对应包下新增WmMaterialDto
@Data
public class WmMaterialDto extends PageRequestDto {
/**
* 1 收藏
* 0 未收藏
*/
private Short isCollection;
}
三步走mapper>>>service>>>controller
另外,由于用到分页查询,需要在引导类中添加mp的分页拦截器
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
四.自媒体文章管理
4.1 文章列表查询
在xxx-model模块对应包下新增WmMaterialDto
@Data
public class WmNewsPageReqDto extends PageRequestDto {
/**
* 状态
*/
private Short status;
/**
* 开始时间
*/
private Date beginPubDate;
/**
* 结束时间
*/
private Date endPubDate;
/**
* 所属频道ID
*/
private Integer channelId;
/**
* 关键字
*/
private String keyword;
}
对应如下:
三步走mapper>>>service>>>controller
主要来说一下业务层逻辑:
该查询是一个多条件分页查询
- 1.检查查询条件
- 2.分页参数检查
- 3.获取当前登录人的信息
- 4.使用lambdaQueryWrapper封装多个条件用于查询
- 5.返回结果
针对一些核心代码进行说明:
//2.分页条件查询
IPage page = new Page(dto.getPage(),dto.getSize());
//创建了LambdaQueryWrapper 对象,并指定了泛型参数为 WmNews,这表示要查询的实体类型为 WmNews
LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//状态精确查询
if(dto.getStatus() != null){
//表示该查询条件为:数据库中字段为"status"且值等于dto.getStatus()的值的WmNews
lambdaQueryWrapper.eq(WmNews::getStatus,dto.getStatus());
}