day3学习内容
1 自媒体后台搭建
1.1 总体架构
1.2 搭建步骤
1.2.1 基础环境与数据准备
导入sql文件
1.2.2 导入实体类WmLoginDto、WmUser
将自媒体对应的实体类导入heima-leadnews-model模块下的com.heima.model.wemedia包下
@Data
public class WmLoginDto {
/**
* 用户名
*/
private String name;
/**
* 密码
*/
private String password;
}
package com.heima.model.wemedia.pojos;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
* 自媒体用户信息表
* </p>
*
* @author itheima
*/
@Data
@TableName("wm_user")
public class WmUser implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField("ap_user_id")
private Integer apUserId;
@TableField("ap_author_id")
private Integer apAuthorId;
/**
* 登录用户名
*/
@TableField("name")
private String name;
/**
* 登录密码
*/
@TableField("password")
private String password;
/**
* 盐
*/
@TableField("salt")
private String salt;
/**
* 昵称
*/
@TableField("nickname")
private String nickname;
/**
* 头像
*/
@TableField("image")
private String image;
/**
* 归属地
*/
@TableField("location")
private String location;
/**
* 手机号
*/
@TableField("phone")
private String phone;
/**
* 状态
0 暂时不可用
1 永久不可用
9 正常可用
*/
@TableField("status")
private Integer status;
/**
* 邮箱
*/
@TableField("email")
private String email;
/**
* 账号类型
0 个人
1 企业
2 子账号
*/
@TableField("type")
private Integer type;
/**
* 运营评分
*/
@TableField("score")
private Integer score;
/**
* 最后一次登录时间
*/
@TableField("login_time")
private Date loginTime;
/**
* 创建时间
*/
@TableField("created_time")
private Date createdTime;
}
1.2.3 导入自媒体微服务模块
将heima-leadnews-wemedia模块导入heima-leadnews-service模块下,并修改heima-leadnews-service的pom文件的依赖,添加wemedia依赖
<modules>
<module>heima-leadnews-user</module>
<module>heima-leadnews-article</module>
<module>heima-leadnews-wemedia</module>
</modules>
并且修改bootstrap.yml
server:
port: 51803
spring:
application:
name: leadnews-wemedia
cloud:
nacos:
discovery:
server-addr: 192.168.204.129:8848
config:
server-addr: 192.168.204.129:8848
file-extension: yml
1.2.4 将自媒体微服务配置注册到Nacos
将heima-leadnews-wemedia微服务注册到Nacos
微服务名:leadnews-wemedia
注册到Nacos
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/leadnews_wemedia?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
username: root
password: 123sjbsjb
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.heima.model.media.pojos
1.2.5 导入自媒体网关模块
将heima-leadnews-wemedia-gateway模块导入heima-leadnews-gateway模块下
并且修改heima-leadnews-gateway的pom文件时期添加对heima-leadnews-wemedia-gateway的依赖
并且修改bootstrap.yml
server:
port: 51602
spring:
application:
name: leadnews-wemedia-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.204.129:8848
config:
server-addr: 192.168.204.129:8848
file-extension: yml
1.2.6 将自媒体网关配置注册到Nacos
微服务名:leadnews-wemedia-gateway
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.7 启动自媒体及其网关服务
2 自媒体前端的搭建
2.1 解压wemeadia-web静态资源
将wemeadia-web解压后与app-web放入同一目录
2.2 为wemeadia-web配置Nginx
在Nginx的conf目录下的leadnews.conf下创建heima-leadnews-wemedia.conf
upstream heima-wemedia-gateway{
server localhost:51602;
}
server {
listen 8802;
location / {
root D:/Code/JavaCode/HeimaToutiao/web/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; #记录代理信息
}
}
因为heima-app-gateway网关监听51602,Nginx要将8802端口转发到51602再通过Nacos转发到自媒体服务端口51603
location ~/wemedia/MEDIA/(.*)
是因为前端发送的请求的url是这,所以要与前端保持一致
proxy_pass
到http://heima-wemedia-gateway/$1
也就是heima-wemedia-gateway
的localhost:51602
(.*)
最后会替换$1
2.3 重新启动Nginx
nginx
访问http://localhost:8802/#/login
成功进入
3 自媒体素材管理
3.1 表结构
3.1.1 拷贝实体类WmMaterial
拷贝WmMaterial实体类到heima-leadnews-model模块下的com.heima.model.wemedia.pojos包下
@Data
@TableName("wm_material")
public class WmMaterial implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 自媒体用户ID
*/
@TableField("user_id")
private Integer userId;
/**
* 图片地址
*/
@TableField("url")
private String url;
/**
* 素材类型
0 图片
1 视频
*/
@TableField("type")
private Short type;
/**
* 是否收藏
*/
@TableField("is_collection")
private Short isCollection;
/**
* 创建时间
*/
@TableField("created_time")
private Date createdTime;
}
3.2 图片上传实现思路
3.2.1 token解析用户id存入header
在heima-leadnews-wemedia-gateway中对http请求中的token进行解析
因为在AppJwtUtil类中最开始组装JWT时,就将用户id传入claimMaps中,最后通过Jwts.builder().addClaims(claimMaps)存入JWT令牌中
public static String getToken(Long id) {
Map<String, Object> claimMaps = new HashMap<>();
claimMaps.put("id", id);
long currentTime = System.currentTimeMillis();
return Jwts.builder()
.setId(UUID.randomUUID().toString())
.setIssuedAt(new Date(currentTime)) //签发时间
.setSubject("system") //说明
.setIssuer("heima") //签发者信息
.setAudience("app") //接收用户
.compressWith(CompressionCodecs.GZIP) //数据压缩方式
.signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳
.addClaims(claimMaps) //cla信息
.compact();
}
之后在wemedia的拦截器AuthorizeFilter中,先一步步判断:
1.获取request和response对象
2.判断是否是登录
3.获取token
4.判断token是否存在
5.判断token是否有效
在第五步中通过Claims claimsBody = AppJwtUtil.getClaimsBody(token);
对token进行解析得到claimsBody,再从claimsBody中就能拿到当前用户的id。
@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//2.判断是否是登录
if(request.getURI().getPath().contains("/login")){
//放行
return chain.filter(exchange);
}
//3.获取token
String token = request.getHeaders().getFirst("token");
//4.判断token是否存在
if(StringUtils.isBlank(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//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();
}
//获取用户信息
Integer userId = (Integer) claimsBody.get("id");
//将用户信息放入到header中
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
httpHeaders.add("userId", userId + "");
}).build();
//重置请求
exchange.mutate().request(serverHttpRequest);
} catch (Exception e) {
e.printStackTrace();
}
//6.放行
return chain.filter(exchange);
}
/**
* 优先级设置 值越小 优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
在将用户id存入header中并重置请求
//获取用户信息
Integer userId = (Integer) claimsBody.get("id");
//将用户信息放入到header中
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
httpHeaders.add("userId", userId + "");
}).build();
//重置请求
exchange.mutate().request(serverHttpRequest);
3.2.2 为微服务创建拦截器解析token
在heima-leadnews-service模块中的heima-leadnews-wemedia创建创建com.heima.wemedia.interceptor.WmTokenInterceptor拦截器类,但是拦截器需要将用户信息存入当前线程中,所以需要WmThreadLocalUtil来创建线程并存入。
3.2.2.1 创建WmThreadLocalUtils线程工具类
在heima-leadnews-utils模块中创建com.heima.utils.thread.WmThreadLocalUtils
public class WmThreadLocalUtil {
private static final ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();
public static void setUser(WmUser user) {
WM_USER_THREAD_LOCAL.set(user);
}
public static WmUser getUser() {
return WM_USER_THREAD_LOCAL.get();
}
public static void clear() {
WM_USER_THREAD_LOCAL.remove();
}
}
3.2.2.2 创建拦截器WmTokenInterceptor
在heima-leadnews-service模块中的heima-leadnews-wemedia创建创建com.heima.wemedia.interceptor.WmTokenInterceptor拦截器类,因为WmThreadLocalUtils线程工具类都是静态方法,故可以直接通过WmThreadLocalUtil.setUser()直接进行调用
public class WmTokenInterceptor implements HandlerInterceptor {
/**
* 拦截器的前置方法,得到header中的用户信息,存入到当前线程中
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userId = request.getHeader("userId");
if(userId != null){
//存入当前线程
WmUser wmUser = new WmUser();
wmUser.setId(Integer.valueOf(userId));
WmThreadLocalUtil.setUser(wmUser);
}
return true;
}
/**
* 后置方法,清除当前线程中的用户信息
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
WmThreadLocalUtil.clear();
}
}
3.2.2.3 通过配置类将WmTokenInterceptor导入Spring中
在heima-leadnews-service模块中的heima-leadnews-wemedia中创捷com.heima.wemedia.config.WebMvcConfig
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**");
}
}
通过@Configuration
为InterceptorRegistry registry
添加拦截器WmTokenInterceptor
拦截所有路径("/**")
3.2.3 完成图片上传
3.2.3.1 创建Controller、Service、Mapper
在heima-leadnews-service模块中的heima-leadnews-wemedia中创捷com.heima.wemedia.controller.v1.WmMaterialController
@RestController
@RequestMapping("/api/v1/material")
public class WmMaterialController {
@PostMapping("/upload_picture")
public ResponseResult uploadPicture(MultipartFile multipartFile) {
return null;
}
}
Service接口
public interface WmMaterialService extends IService<WmMaterial> {
}
实现类
@Slf4j
@Service
@Transactional
public class WmMaterialServiceImpl extends ServiceImpl<WmMaterialMapper, WmMaterial> implements WmMaterialService {
}
Mapper接口
@Mapper
public interface WmMaterialMapper extends BaseMapper<WmMaterial> {
}
3.2.3.2 完善
为heima-leadnews-wemedia引入minio依赖
<dependencies>
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-file-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
将minio的配置放入nacos中的leadnews-wemedia配置中
minio:
accessKey: minio
secretKey: minio123
bucket: leadnews
endpoint: http://192.168.204.129:9000
readPath: http://192.168.204.129:9000
Controller
@RestController
@RequestMapping("/api/v1/material")
public class WmMaterialController {
@Autowired
private WmMaterialService wmMaterialService;
@PostMapping("/upload_picture")
public ResponseResult uploadPicture(MultipartFile multipartFile) {
return wmMaterialService.uploadPicture(multipartFile);
}
}
Service接口
public interface WmMaterialService extends IService<WmMaterial> {
public ResponseResult uploadPicture(MultipartFile multipartFile);
}
Service实现类
@Slf4j
@Service
@Transactional
public class WmMaterialServiceImpl extends ServiceImpl<WmMaterialMapper, WmMaterial> implements WmMaterialService {
@Autowired
private FileStorageService fileStorageService;
/**
* 上传图片
* @param multipartFile
* @return
*/
@Override
public ResponseResult uploadPicture(MultipartFile multipartFile) {
//1.校验参数
if(multipartFile == null||multipartFile.getSize() == 0){
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
}
//2.上传图片到minIO中
//2.1 使用UUID生成文件名
String fileName = UUID.randomUUID().toString().replace("-", "");
//2.2 拿到文件的后缀
String originalFilename = multipartFile.getOriginalFilename();
String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));
//2.3 上传文件
String fileId=null;
try {
fileId = fileStorageService.uploadImgFile("", fileName + fileType, multipartFile.getInputStream());
log.info("上传图片到minIO成功,fileId:{}",fileId);
} catch (IOException e) {
e.printStackTrace();
log.error("WmMaterialServiceImpl上传图片失败");
}
//3.保存图片信息到数据库
WmMaterial wmMaterial = new WmMaterial();
wmMaterial.setUserId(WmThreadLocalUtil.getUser().getId());
wmMaterial.setUrl(fileId);
wmMaterial.setIsCollection((short) 0);
wmMaterial.setType((short) 0);//0 图片 1 视频
wmMaterial.setCreatedTime(new Date());
save(wmMaterial);
//4.返回结果
return ResponseResult.okResult(wmMaterial);
}
}
3.2.3.3 测试
启动微服务及网关
访问http://localhost:8802/#/login
浏览器访问url也正常,userid也与浏览器登陆时admin能对的上
3.3 图片列表及收藏
3.3.1 接口定义
3.3.2 创建接收参数WmMaterialDto
在heima-leadnews-model模块中的com.heima.model.wemedia.dtos中创建WmMaterialDto
@Data
public class WmMaterialDto extends PageRequestDto {
/**
* 1 收藏
* 0 未收藏
*/
private Short isCollection;
}
3.3.2 添加三层业务
在heima-leadnews-service模块中的heima-leadnews-wemedia中的com.heima.wemedia.controller.v1.WmMaterialController中添加新的方法
@PostMapping("/list")
public ResponseResult findList(@RequestBody WmMaterialDto wmMaterialDto){
return wmMaterialService.findList(wmMaterialDto);
}
service接口及实现
public interface WmMaterialService extends IService<WmMaterial> {
public ResponseResult uploadPicture(MultipartFile multipartFile);
public ResponseResult findList(@RequestBody WmMaterialDto wmMaterialDto);
}
/**
* 查询素材列表
* @param wmMaterialDto
* @return
*/
@Override
public ResponseResult findList(WmMaterialDto wmMaterialDto) {
//1.校验参数
wmMaterialDto.checkParam();
//2.分页查询
IPage pageCheck=new Page(wmMaterialDto.getPage(),wmMaterialDto.getSize());
LambdaQueryWrapper<WmMaterial> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//2.1是否收藏
if(wmMaterialDto.getIsCollection()!=null&&wmMaterialDto.getIsCollection()==1){
lambdaQueryWrapper.eq(WmMaterial::getIsCollection,wmMaterialDto.getIsCollection());
}
//2.2 按照用户查询
lambdaQueryWrapper.eq(WmMaterial::getUserId,WmThreadLocalUtil.getUser().getId());
//2.3 按照时间排序
lambdaQueryWrapper.orderByDesc(WmMaterial::getCreatedTime);
//2.4 查询
pageCheck = page(pageCheck, lambdaQueryWrapper);
//3.结果返回
ResponseResult responseResult = new PageResponseResult(wmMaterialDto.getPage(), wmMaterialDto.getSize(), (int) pageCheck.getTotal());
responseResult.setData(pageCheck.getRecords());
return responseResult;
}
mapper
@Mapper
public interface WmMaterialMapper extends BaseMapper<WmMaterial> {
}
3.3.3 使mp生效,启动类注入mp
使分页插件生效interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL))
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.wemedia.mapper")
public class WemediaApplication {
public static void main(String[] args) {
SpringApplication.run(WemediaApplication.class,args);
}
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
3.3.4 重启服务
已经实现
4 自媒体文章管理
4.1 频道列表查询
4.1.1 需求分析
4.1.2 表结构
4.1.3 导入实体类WmChannel
@Data
@TableName("wm_channel")
public class WmChannel implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 频道名称
*/
@TableField("name")
private String name;
/**
* 频道描述
*/
@TableField("description")
private String description;
/**
* 是否默认频道
* 1:默认 true
* 0:非默认 false
*/
@TableField("is_default")
private Boolean isDefault;
/**
* 是否启用
* 1:启用 true
* 0:禁用 false
*/
@TableField("status")
private Boolean status;
/**
* 默认排序
*/
@TableField("ord")
private Integer ord;
/**
* 创建时间
*/
@TableField("created_time")
private Date createdTime;
}
4.1.4 接口定义
4.1.5 实现
4.1.5.1 添加三层业务
Controller
@RestController
@RequestMapping("/api/v1/channel")
public class WmChannelController {
@Autowired
private WmChannelService wmChannelService;
@GetMapping("/channels")
public ResponseResult findList(){
return wmChannelService.findList();
}
}
Service接口及实现
public interface WmChannelService extends IService<WmChannel> {
public ResponseResult findList();
}
@Service
public class WmChannelServiceImpl extends ServiceImpl<WmChannelMapper, WmChannel> implements WmChannelService {
@Override
public ResponseResult findList() {
// 查询所有的频道
return ResponseResult.okResult(list());
}
}
Mapper
@Mapper
public interface WmChannelMapper extends BaseMapper<WmChannel>{
}
4.1.5.2 重启
4.2 文章列表加载
4.2.1 需求分析
4.2.2 表结构分析
4.2.3 导入实体类WmNews
@Data
@TableName("wm_news")
public class WmNews implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 自媒体用户ID
*/
@TableField("user_id")
private Integer userId;
/**
* 标题
*/
@TableField("title")
private String title;
/**
* 图文内容
*/
@TableField("content")
private String content;
/**
* 文章布局
0 无图文章
1 单图文章
3 多图文章
*/
@TableField("type")
private Short type;
/**
* 图文频道ID
*/
@TableField("channel_id")
private Integer channelId;
@TableField("labels")
private String labels;
/**
* 创建时间
*/
@TableField("created_time")
private Date createdTime;
/**
* 提交时间
*/
@TableField("submited_time")
private Date submitedTime;
/**
* 当前状态
0 草稿
1 提交(待审核)
2 审核失败
3 人工审核
4 人工审核通过
8 审核通过(待发布)
9 已发布
*/
@TableField("status")
private Short status;
/**
* 定时发布时间,不定时则为空
*/
@TableField("publish_time")
private Date publishTime;
/**
* 拒绝理由
*/
@TableField("reason")
private String reason;
/**
* 发布库文章ID
*/
@TableField("article_id")
private Long articleId;
/**
* //图片用逗号分隔
*/
@TableField("images")
private String images;
@TableField("enable")
private Short enable;
//状态枚举类
@Alias("WmNewsStatus")
public enum Status{
NORMAL((short)0),SUBMIT((short)1),FAIL((short)2),ADMIN_AUTH((short)3),ADMIN_SUCCESS((short)4),SUCCESS((short)8),PUBLISHED((short)9);
short code;
Status(short code){
this.code = code;
}
public short getCode(){
return this.code;
}
}
}
4.2.4 接口定义
4.2.5 实现
4.2.5.1 添加三层业务
Controller
@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
@Autowired
private WmNewsService wmNewsService;
@PostMapping("/list")
public ResponseResult findList(@RequestBody WmNewsPageReqDto wmNewsPageReqDto){
return wmNewsService.findList(wmNewsPageReqDto);
}
}
Service接口及实现
public interface WmNewsService extends IService<WmNews> {
ResponseResult findList(WmNewsPageReqDto wmNewsPageReqDto);
}
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
@Override
public ResponseResult findList(WmNewsPageReqDto wmNewsPageReqDto) {
// 1.参数检查
if(wmNewsPageReqDto == null){
return ResponseResult.errorResult(400,"参数错误");
}
wmNewsPageReqDto.checkParam();
// 2.分页查询
IPage pageCheck=new Page(wmNewsPageReqDto.getPage(),wmNewsPageReqDto.getSize());
// 2.0 按照不同需求查询
LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//2.1 文章状态精确查询
if(wmNewsPageReqDto.getStatus()!=null){
lambdaQueryWrapper.eq(WmNews::getStatus,wmNewsPageReqDto.getStatus());
}
//2.2 关键字模糊匹配
if(StringUtils.isNotBlank(wmNewsPageReqDto.getKeyword())){
lambdaQueryWrapper.like(WmNews::getTitle,wmNewsPageReqDto.getKeyword());
}
//2.3 所属频道精确查询
if(wmNewsPageReqDto.getChannelId()!=null){
lambdaQueryWrapper.eq(WmNews::getChannelId,wmNewsPageReqDto.getChannelId());
}
//2.4 时间范围查询
if(wmNewsPageReqDto.getBeginPubDate()!=null&&wmNewsPageReqDto.getEndPubDate()!=null){
lambdaQueryWrapper.between(WmNews::getPublishTime,wmNewsPageReqDto.getBeginPubDate(),wmNewsPageReqDto.getEndPubDate());
}
//2.5 查询当前用户的文章
lambdaQueryWrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId());
//2.6 排序
lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
pageCheck = page(pageCheck, lambdaQueryWrapper);
// 3.返回结果
ResponseResult responseResult = new PageResponseResult(wmNewsPageReqDto.getPage(), wmNewsPageReqDto.getSize(), (int) pageCheck.getTotal());
responseResult.setData(pageCheck.getRecords());
return responseResult;
}
}
Mapper
@Mapper
public interface WmNewsMapper extends BaseMapper<WmNews>{
}
4.2.5.2重启
4.3 发布文章
4.3.1 需求分析
4.3.2 表结构分析
4.3.3 导入表实体WmNewsMaterial、WmNewsDto
@Data
@TableName("wm_news_material")
public class WmNewsMaterial implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 素材ID
*/
@TableField("material_id")
private Integer materialId;
/**
* 图文ID
*/
@TableField("news_id")
private Integer newsId;
/**
* 引用类型
0 内容引用
1 主图引用
*/
@TableField("type")
private Short type;
/**
* 引用排序
*/
@TableField("ord")
private Short ord;
}
@Data
public class WmNewsDto {
private Integer id;
/**
* 标题
*/
private String title;
/**
* 频道id
*/
private Integer channelId;
/**
* 标签
*/
private String labels;
/**
* 发布时间
*/
private Date publishTime;
/**
* 文章内容
*/
private String content;
/**
* 文章封面类型 0 无图 1 单图 3 多图 -1 自动
*/
private Short type;
/**
* 提交时间
*/
private Date submitedTime;
/**
* 状态 提交为1 草稿为0
*/
private Short status;
/**
* 封面图片列表 多张图以逗号隔开
*/
private List<String> images;
}
4.3.4 实现思路
4.3.5 接口定义
响应
4.3.6 实现
在WmNewsController中添加新方法submitNews
@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
@Autowired
private WmNewsService wmNewsService;
@PostMapping("/list")
public ResponseResult findList(@RequestBody WmNewsPageReqDto wmNewsPageReqDto){
return wmNewsService.findList(wmNewsPageReqDto);
}
@PostMapping("/submit")
public ResponseResult submitNews(@RequestBody WmNewsDto wmNewsDto){
return wmNewsService.submitNews(wmNewsDto);
}
}
Service及实现
public interface WmNewsService extends IService<WmNews> {
ResponseResult findList(WmNewsPageReqDto wmNewsPageReqDto);
ResponseResult submitNews(WmNewsDto wmNewsDto);
}
@Override
public ResponseResult submitNews(WmNewsDto wmNewsDto) {
// 0.参数检查
if(wmNewsDto == null||wmNewsDto.getContent()==null){
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
}
//1. 保存或修改文章
WmNews wmNews = new WmNews();
BeanUtils.copyProperties(wmNewsDto,wmNews);
//1.1 封面
if(wmNewsDto.getImages()!=null&& wmNewsDto.getImages().size()>0){
String imageStr = StringUtils.join(wmNewsDto.getImages(), ",");
wmNews.setImages(imageStr);
}
//1.2 如果封面为自动-1,则需要手动设置封面规则
if(wmNewsDto.getImages().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
wmNews.setType(WemediaConstants.WM_NEWS_TYPE_AUTO);
}
saveOrUpdateWmNews(wmNews);
//2.判断是否为草稿,如果为草稿结束当前发放
if(wmNews.getStatus().equals(WmNews.Status.NORMAL.getCode())){
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
//3.不是草稿,保存文章内容与图片素材的关系
//3.1 获取文章内容的图片素材
List<String> imageList=extractUrlInfo(wmNewsDto.getContent());
saveRelativeInfoForContent(imageList,wmNews.getId());
//4.不是草稿,保存文章封面图片与图片素材的关系
//
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
/**
* 保存文章封面图片与图片素材的关系
* @param imageList
* @param id
*/
private void saveRelativeInfoForContent(List<String> imageList, Integer id) {
saveRelativeInfo(imageList,id,WemediaConstants.WM_CONTENT_REFERENCE);
}
@Autowired
private WmMaterialMapper wmMaterialMapper;
/**
* 保存文章图片与素材的关系到数据库
* @param imageList
* @param id
* @param wmContentReference
*/
private void saveRelativeInfo(List<String> imageList, Integer id, Short wmContentReference) {
if(imageList!=null||!imageList.isEmpty()){
//通过图片url查询素材的id
List<WmMaterial> dbMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, imageList));
//判断素材是否有用
if(dbMaterials==null||dbMaterials.size()==0){
//手动抛出异常
throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_INVAILD);
}
if(dbMaterials.size()!=imageList.size()){
throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_INVAILD);
}
List<Integer> dbMaterialsIds = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());
wmNewsMaterialMapper.saveRelations(dbMaterialsIds,id,wmContentReference);
}
}
/**
* 提取文章内容中的图片信息
* @param content
* @return
*/
private List<String> extractUrlInfo(String content) {
List<String> materials =new ArrayList<>();
List<Map> maps = JSON.parseArray(content, Map.class);
for (Map map : maps) {
if(map.get("type").equals("image")){
String imgUrl = (String) map.get("value");
materials.add(imgUrl);
}
}
return materials;
}
@Autowired
private WmNewsMaterialMapper wmNewsMaterialMapper;
/**
* 保存或修改文章
* @param wmNews
*/
private void saveOrUpdateWmNews(WmNews wmNews) {
//补全属性
wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
wmNews.setCreatedTime(new Date());
wmNews.setSubmitedTime(new Date());
//默认上架
wmNews.setEnable(WemediaConstants.WEMEDIA_ARTICLE_SHOW_ON);
if(wmNews.getId()==null) {
//如果没有就是新增
save(wmNews);
}
else {
//修改
//删除文章与素材的关系
wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,wmNews.getId()));
//更新文章
updateById(wmNews);
}
}
mapper
@Mapper
public interface WmNewsMaterialMapper extends BaseMapper<WmNewsMaterial> {
void saveRelations(@Param("materialIds") List<Integer> materialIds,@Param("newsId")Integer newsId, @Param("type")Short type);
}
对应resource/mapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.heima.wemedia.mapper.WmNewsMaterialMapper">
<insert id="saveRelations">
insert into wm_news_material (material_id,news_id,type,ord)
values
<foreach collection="materialIds" index="ord" item="mid" separator=",">
(#{mid},#{newsId},#{type},#{ord})
</foreach>
</insert>
</mapper>
测试
4.3.7 完善service功能
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
@Override
public ResponseResult findList(WmNewsPageReqDto wmNewsPageReqDto) {
// 1.参数检查
if(wmNewsPageReqDto == null){
return ResponseResult.errorResult(400,"参数错误");
}
wmNewsPageReqDto.checkParam();
// 2.分页查询
IPage pageCheck=new Page(wmNewsPageReqDto.getPage(),wmNewsPageReqDto.getSize());
// 2.0 按照不同需求查询
LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//2.1 文章状态精确查询
if(wmNewsPageReqDto.getStatus()!=null){
lambdaQueryWrapper.eq(WmNews::getStatus,wmNewsPageReqDto.getStatus());
}
//2.2 关键字模糊匹配
if(StringUtils.isNotBlank(wmNewsPageReqDto.getKeyword())){
lambdaQueryWrapper.like(WmNews::getTitle,wmNewsPageReqDto.getKeyword());
}
//2.3 所属频道精确查询
if(wmNewsPageReqDto.getChannelId()!=null){
lambdaQueryWrapper.eq(WmNews::getChannelId,wmNewsPageReqDto.getChannelId());
}
//2.4 时间范围查询
if(wmNewsPageReqDto.getBeginPubDate()!=null&&wmNewsPageReqDto.getEndPubDate()!=null){
lambdaQueryWrapper.between(WmNews::getPublishTime,wmNewsPageReqDto.getBeginPubDate(),wmNewsPageReqDto.getEndPubDate());
}
//2.5 查询当前用户的文章
lambdaQueryWrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId());
//2.6 排序
lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
pageCheck = page(pageCheck, lambdaQueryWrapper);
// 3.返回结果
ResponseResult responseResult = new PageResponseResult(wmNewsPageReqDto.getPage(), wmNewsPageReqDto.getSize(), (int) pageCheck.getTotal());
responseResult.setData(pageCheck.getRecords());
return responseResult;
}
@Override
public ResponseResult submitNews(WmNewsDto wmNewsDto) {
// 0.参数检查
if(wmNewsDto == null||wmNewsDto.getContent()==null){
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
}
//1. 保存或修改文章
WmNews wmNews = new WmNews();
BeanUtils.copyProperties(wmNewsDto,wmNews);
//1.1 封面
if(wmNewsDto.getImages()!=null&& wmNewsDto.getImages().size()>0){
String imageStr = StringUtils.join(wmNewsDto.getImages(), ",");
wmNews.setImages(imageStr);
}
//1.2 如果封面为自动-1,则需要手动设置封面规则
if(wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
wmNews.setType(null);
}
saveOrUpdateWmNews(wmNews);
//2.判断是否为草稿,如果为草稿结束当前方法
if(wmNews.getStatus().equals(WmNews.Status.NORMAL.getCode())){
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
//3.不是草稿,保存文章内容与图片素材的关系
//3.1 获取文章内容的图片素材
List<String> imageList=extractUrlInfo(wmNewsDto.getContent());
saveRelativeInfoForContent(imageList,wmNews.getId());
//4.不是草稿,保存文章封面图片与图片素材的关系
saveRelativeInfoForCover(wmNewsDto,wmNews,imageList);
//
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
/**
* 保存文章封面图片与图片素材的关系
* 如果当前为自动,进行匹配:
* 1、若当前图片大于等于1,小于三张,设置为单图 type=1
* 2、若当前图片大于等于三张,设置为多图 type=3
* 3、若当前没有图片,设置为无图 type=0
*
* @param wmNewsDto
* @param wmNews
* @param imageList
*/
private void saveRelativeInfoForCover(WmNewsDto wmNewsDto, WmNews wmNews, List<String> imageList) {
List<String> images = wmNewsDto.getImages();
//处理封面为自动的情况
if(wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
//单图
if(imageList.size()>=1&&imageList.size()<3){
wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
images=imageList.stream().limit(1).collect(Collectors.toList());
}
else if(imageList.size()>=3){
//多图
wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
images=imageList.stream().limit(3).collect(Collectors.toList());
}
else {
//无图
wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
}
//修改文章
if(images!=null&&images.size()>0){
wmNews.setImages(StringUtils.join(images,","));
}
updateById(wmNews);
}
//保存封面与图片素材的关系
if(images!=null&&images.size()>0){
saveRelativeInfo(images,wmNews.getId(),WemediaConstants.WM_COVER_REFERENCE);
}
}
/**
* 保存文章封面图片与图片素材的关系
* @param imageList
* @param id
*/
private void saveRelativeInfoForContent(List<String> imageList, Integer id) {
saveRelativeInfo(imageList,id,WemediaConstants.WM_CONTENT_REFERENCE);
}
@Autowired
private WmMaterialMapper wmMaterialMapper;
/**
* 保存文章图片与素材的关系到数据库
* @param imageList
* @param id
* @param wmContentReference
*/
private void saveRelativeInfo(List<String> imageList, Integer id, Short wmContentReference) {
if(imageList!=null&&!imageList.isEmpty()){
//通过图片url查询素材的id
List<WmMaterial> dbMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, imageList));
//判断素材是否有用
if(dbMaterials==null||dbMaterials.size()==0){
//手动抛出异常
throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_INVAILD);
}
if(dbMaterials.size()!=imageList.size()){
throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_INVAILD);
}
List<Integer> dbMaterialsIds = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());
wmNewsMaterialMapper.saveRelations(dbMaterialsIds,id,wmContentReference);
}
}
/**
* 提取文章内容中的图片信息
* @param content
* @return
*/
private List<String> extractUrlInfo(String content) {
List<String> materials =new ArrayList<>();
List<Map> maps = JSON.parseArray(content, Map.class);
for (Map map : maps) {
if(map.get("type").equals("image")){
String imgUrl = (String) map.get("value");
materials.add(imgUrl);
}
}
return materials;
}
@Autowired
private WmNewsMaterialMapper wmNewsMaterialMapper;
/**
* 保存或修改文章
* @param wmNews
*/
private void saveOrUpdateWmNews(WmNews wmNews) {
//补全属性
wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
wmNews.setCreatedTime(new Date());
wmNews.setSubmitedTime(new Date());
//默认上架
wmNews.setEnable(WemediaConstants.WEMEDIA_ARTICLE_SHOW_ON);
if(wmNews.getId()==null) {
//如果没有就是新增
save(wmNews);
}
else {
//修改
//删除文章与素材的关系
wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,wmNews.getId()));
//更新文章
updateById(wmNews);
}
}
}