“我读过很多书,但后来大部分都忘记了,你说这样的阅读究竟有什么意义?”
“当我还是个孩子时,我吃过很多食物,现在已经记不起来吃过什么了。但可以肯定的是,它们中的一部分已经长成我的骨头和肉。”
系列文章目录
- 项目搭建
- App登录及网关
- App文章
- 自媒体平台(博主后台)
文章目录
- 系列文章目录
- 一、前后端搭建
- 1. 后台搭建
- ⑴. 导入文章数据库
- ⑵. 实体类
- ①. 自媒体用户信息
- ②. 自媒体登录
- ⑶. 导入媒体微服务
- ⑷. Nacos配置
- ⑸. 导入媒体网关
- ⑹. Nacos配置
- 2. 前台搭建
- ⑴. 导入nginx
- ⑵. 配置nginx.conf文件
- ⑶. 测试
- 二、自媒体素材管理
- 1. 图片上传
- ⑴. 实体类
- ⑵. 网关
- ⑶. 拦截器
- ①. 线程公共方法
- ②. 过滤器
- ③. 自定义拦截器
- ⑷. 接口定义
- ①. 基础搭建
- Ⅰ. Controller
- Ⅱ. Mapper
- Ⅲ. Service
- Ⅳ. Impl
- ②. 业务层
- ③. 配置
- Ⅰ. 引入minio
- Ⅱ. Nacos配置
- ④. 业务层实现类
- ⑤. Controller
- ⑸. 测试
- 2. 图片列表
- ⑴. Dto
- ⑵. Controller
- ⑶. Service
- ⑷. ServiceImpl
- ⑸. 引入 MyBatisPlus 插件
- ⑹. Controller
- ⑺. 测试
- 三、文章管理
- 1. 频道列表查询
- ⑴. 接口定义
- ⑵. 实体类
- ⑶. 基础搭建
- ①. Controller
- ②. Mapper
- ③. Service
- ④. ServiceImpl
- ⑷. Service
- ⑸. ServiceImpl
- ⑹. Controller
- ⑺. 测试
- 2. 文章列表查询
- ⑴. 表结构分析
- ⑵. 接口定义
- ①. 接口说明
- ②. Dto
- ⑶. 基础搭建
- ①. Controller
- ②. Mapper
- ③. Service
- ④. ServiceImpl
- ⑷. Service
- ⑸. ServiceImpl
- ⑹. Controller
- ⑺. 测试
- 3. 文章发布
- ⑴. 需求分析
- ⑵. 表结构分析
- ①. 表关系
- ②. 实体类
- ⑶. 思路分析
- ⑷. 接口定义
- ①. 接口说明
- ②. Dto
- ⑸. 基础搭建
- ①. Controller
- ②. Mapper
- ③. Mapper配置文件
- ④. Service
- ⑤. ServiceImpl
- ⑹. 定义常量
- ⑺. 自定义提示信息
- ⑻. ServiceImpl(内容图片与素材)
- ⑼. Controller
- ⑽. 测试
- ①. 单图
- Ⅰ. 发布文章
- Ⅱ. 内容列表
- Ⅲ. 数据库
- ②. 草稿
- Ⅰ. 发布文章
- Ⅱ. 内容列表
- Ⅲ. 数据库
- ③. 自动
- Ⅰ. 发布文章
- Ⅱ. 内容列表
- Ⅲ. 数据库
一、前后端搭建
1. 后台搭建
⑴. 导入文章数据库
sql链接: https://pan.baidu.com/s/18xmuhipX1EKuPLa3clC47g?pwd=abcd
创建同名空数据库 => localhost右键 => 运行SQL文件 => 刷新
⑵. 实体类
①. 自媒体用户信息
新建 heima-leadnews-model/src/main/java/com/heima/model/wemedia/pojos/WmUser.java
文件:
/**
* <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;
}
②. 自媒体登录
新建 heima-leadnews-model/src/main/java/com/heima/model/wemedia/dtos/WmLoginDto.java
文件:
@Data
public class WmLoginDto {
/**
* 用户名
*/
private String name;
/**
* 密码
*/
private String password;
}
⑶. 导入媒体微服务
微服务资源: https://pan.baidu.com/s/1JfRzXiawYo1Iixf8b2gaTw?pwd=abcd
解压至 leadnews-heima-service
目录下
编辑 heima-leadnews-service/pom.xml
pom依赖:
<modules>
<module>heima-leadnews-user</module>
<!--文章模块-->
<module>heima-leadnews-article</module>
<!--自媒体模块-->
<module>heima-leadnews-wemedia</module>
</modules>
刷新Maven
⑷. Nacos配置
新建 leadnews-wemedia
配置:
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/leadnews_wemedia?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 123456
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.heima.model.media.pojos
⑸. 导入媒体网关
微服务资源: https://pan.baidu.com/s/1PusVjATzWk-Z299PTcQAAQ?pwd=abcd
解压至 heima-leadnews-gateway
目录下
编辑 heima-leadnews-gateway/pom.xml
pom依赖:
<modules>
<module>heima-leadnews-app-gateway</module>
<module>heima-leadnews-wemedia-gateway</module>
</modules>
刷新Maven
⑹. 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
2. 前台搭建
⑴. 导入nginx
资源链接: https://pan.baidu.com/s/1TAwwtNcO7gWOUQOIvpgU3w?pwd=abcd
解压至 nginx 目录下(同APP端nginx)
⑵. 配置nginx.conf文件
(nginx包中)新建 D:\code\hm\leadnews\config\nginx-1.18.0\conf\leadnews.conf\heima-leadnews-wemedia.conf
文件:
upstream heima-wemedia-gateway{
server localhost:51602; # 根据网关去做的请求
}
server {
listen 8802;
location / {
root D:/code/hm/leadnews/config/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; #记录代理信息
}
}
⑶. 测试
启动nginx,启动自媒体微服务和自媒体网关,自媒体地址: http://localhost:8802/#/login
二、自媒体素材管理
1. 图片上传
⑴. 实体类
新建 heima-leadnews-model/src/main/java/com/heima/model/wemedia/pojos/WmMaterial.java
文件:
/**
* <p>
* 自媒体图文素材信息表
* </p>
*
* @author itheima
*/
@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;
}
⑵. 网关
编辑 heima-leadnews-gateway/heima-leadnews-wemedia-gateway/src/main/java/com/heima/wemedia/gateway/filter/AuthorizeFilter.java
文件:
//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解析后,把解析后的用户信息存储到header中
// 获取用户信息
Object userId = claimsBody.get("id");
// 存放到header中
ServerHttpRequest serverHttpRequest = request.mutate().headers(httpHeaders -> {
httpHeaders.add("userId", userId + "");
}).build();
// 重置请求
exchange.mutate().request(serverHttpRequest);
} catch (Exception e) {
e.printStackTrace();
}
⑶. 拦截器
①. 线程公共方法
新建 heima-leadnews-utils/src/main/java/com/heima/utils/thread/WmThreadLocalUtils.java
文件:
public class WmThreadLocalUtils {
private final static ThreadLocal<WmUser> WM_USER_THREAD_LOCAL = new ThreadLocal<>();
// 存入线程中
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();
}
}
②. 过滤器
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/interceptor/WmTokenInterceptor.java
文件:
public class WmTokenInterceptor implements HandlerInterceptor {
/**
* 前置处理器: 得到hedaer中的用户信息, 并且存入到当前线程中
* @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));
WmThreadLocalUtils.setUser(wmUser);
}
return true;
}
/**
* 后置处理器: 清理线程中的数据
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
WmThreadLocalUtils.clear();
}
}
③. 自定义拦截器
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/config/WebMvcConfig.java
文件:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 添加自定义拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new WmTokenInterceptor()).addPathPatterns("/**");
}
}
⑷. 接口定义
说明 | |
---|---|
接口路径 | /api/v1/material/upload_picture |
请求方式 | POST |
参数 | MultipartFile |
响应结果 | ResponseResult |
①. 基础搭建
Ⅰ. Controller
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/controller/v1/WmMaterialController.java
文件:
@RestController
@RequestMapping("/api/v1/material")
public class WmMaterialController {
@PostMapping("upload_picture")
public ResponseResult uploadPicture(MultipartFile multipartFile) {
return null;
}
}
Ⅱ. Mapper
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/mapper/WmMaterialMapper.java
文件:
@Mapper
public interface WmMaterialMapper extends BaseMapper<WmMaterial> {
}
Ⅲ. Service
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/WmMaterialService.java
文件:
public interface WmMaterialService extends IService<WmMaterial> {
}
Ⅳ. Impl
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmMaterialServiceImpl.java
文件:
@Slf4j
@Service
@Transactional
public class WmMaterialServiceImpl extends ServiceImpl<WmMaterialMapper, WmMaterial> implements WmMaterialService {
}
②. 业务层
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/WmMaterialService.java
文件:
public interface WmMaterialService extends IService<WmMaterial> {
/**
* 图片上传
* @param multipartFile
* @return
*/
public ResponseResult uploadPicture(MultipartFile multipartFile);
}
③. 配置
Ⅰ. 引入minio
编辑 heima-leadnews-service/heima-leadnews-wemedia/pom.xml
配置文件:
<dependencies>
<dependency>
<groupId>com.heima</groupId>
<artifactId>heima-file-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
Ⅱ. Nacos配置
编辑 leadnews-wemedia
配置:
minio:
accessKey: minio
secretKey: minio123
bucket: leadnews
endpoint: http://192.168.200.130:9000
readPath: http://192.168.200.130:9000
④. 业务层实现类
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmMaterialServiceImpl.java
文件:
@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中
String fileName = UUID.randomUUID().toString().replace("-", "");
// aa.kpg
String originalFilename = multipartFile.getOriginalFilename();
String postfix = originalFilename.substring(originalFilename.lastIndexOf("."));
String fileId = null;
try {
fileId = fileStorageService.uploadImgFile("", fileName + postfix, multipartFile.getInputStream());
log.info("上传图片到minio中, fileId{}", fileId);
} catch (IOException e) {
e.printStackTrace();
log.error("WmMaterialServiceImpl-上传图片失败");
}
// 3. 储存到数据库中
WmMaterial wmMaterial = new WmMaterial();
wmMaterial.setUserId(WmThreadLocalUtils.getUser().getId());
wmMaterial.setUrl(fileId);
wmMaterial.setIsCollection((short)0);
wmMaterial.setType((short)0);
wmMaterial.setCreatedTime(new Date());
save(wmMaterial);
// 4. 返回结果
return ResponseResult.okResult(wmMaterial);
}
}
⑤. Controller
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmMaterialServiceImpl.java
文件:
@RestController
@RequestMapping("/api/v1/material")
public class WmMaterialController {
@Autowired
private WmMaterialService wmMaterialService;
@PostMapping("upload_picture")
public ResponseResult uploadPicture(MultipartFile multipartFile) {
return wmMaterialService.uploadPicture(multipartFile);
}
}
⑸. 测试
启动nginx,启动自媒体微服务和自媒体网关,自媒体地址: http://localhost:8802/#/login
查看 数据库,新增了上传的素材图片
2. 图片列表
说明 | |
---|---|
接口路径 | /api/v1/material/list |
请求方式 | POST |
参数 | WmMaterialDto |
响应结果 | ResponseResult |
⑴. Dto
新建 heima-leadnews-model/src/main/java/com/heima/model/wemedia/dtos/WmMaterialDto.java
文件:
@Data
public class WmMaterialDto extends PageRequestDto {
/**
* 0 未收藏 1 已收藏
*/
private Short isCollection;
}
⑵. Controller
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/controller/v1/WmMaterialController.java
文件:
@PostMapping("/list")
public ResponseResult findList(@RequestBody WmMaterialDto dto) {
return null;
}
⑶. Service
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/WmMaterialService.java
文件:
/**
* 图片列表
* @param dto
* @return
*/
public ResponseResult findList(WmMaterialDto dto);
⑷. ServiceImpl
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmMaterialServiceImpl.java
文件:
/**
* 素材列表查询
* @param dto
* @return
*/
@Override
public ResponseResult findList(WmMaterialDto dto) {
// 1. 检查参数
dto.checkParam();
// 2. 分页查询
IPage page = new Page(dto.getPage(), dto.getSize());
LambdaQueryWrapper<WmMaterial> lambdaQueryWrapper = new LambdaQueryWrapper<>();
// 是否收藏
if(dto.getIsCollection() != null && dto.getIsCollection() == 1) {
lambdaQueryWrapper.eq(WmMaterial::getIsCollection, dto.getIsCollection());
}
// 按照用户查询
lambdaQueryWrapper.eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId());
// 按时间倒序
lambdaQueryWrapper.orderByDesc(WmMaterial::getCreatedTime);
page = page(page, lambdaQueryWrapper);
// 3. 返回结果
ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
responseResult.setData(page.getRecords());
return responseResult;
}
⑸. 引入 MyBatisPlus 插件
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/WemediaApplication.java
文件:
// myBatisPlus插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
⑹. Controller
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/controller/v1/WmMaterialController.java
文件:
@PostMapping("/list")
public ResponseResult findList(@RequestBody WmMaterialDto dto) {
return wmMaterialService.findList(dto);
}
⑺. 测试
启动nginx,启动自媒体微服务和自媒体网关,自媒体地址: http://localhost:8802/#/login
三、文章管理
1. 频道列表查询
⑴. 接口定义
说明 | |
---|---|
接口路径 | /api/v1/channel/channels |
请求方式 | POST |
参数 | 无 |
响应结果 | ResponseResult |
ResponseResult :
{
"host": "null",
"code": 0,
"errorMessage": "操作成功",
"data": [
{
"id": 4,
"name": "java",
"description": "java",
"isDefault": true,
"status": false,
"ord": 3,
"createdTime": "2019-08-16T10:55:41.000+0000"
},
Object { ... },
Object { ... }
]
}
⑵. 实体类
新建 heima-leadnews-model/src/main/java/com/heima/model/wemedia/pojos/WmChannel.java
文件:
/**
* <p>
* 频道信息表
* </p>
*
* @author itheima
*/
@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;
}
⑶. 基础搭建
①. Controller
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/controller/v1/WmChannelController.java
文件:
@RestController
@RequestMapping("/api/v1/channel")
public class WmChannelController {
@GetMapping("/channels")
public ResponseResult findAll() {
return null;
}
}
②. Mapper
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/mapper/WmChannelMapper.java
文件:
@Mapper
public interface WmChannelMapper extends BaseMapper<WmChannel> {
}
③. Service
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/WmChannelService.java
文件:
public interface WmChannelService extends IService<WmChannel> {
}
④. ServiceImpl
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmChannelServiceImpl.java
文件:
@Service
@Transactional
@Slf4j
public class WmChannelServiceImpl extends ServiceImpl<WmChannelMapper, WmChannel> implements WmChannelService {
}
⑷. Service
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/WmChannelService.java
文件:
public interface WmChannelService extends IService<WmChannel> {
/**
* 查询所有频道
* @return
*/
public ResponseResult findAll();
}
⑸. ServiceImpl
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmChannelServiceImpl.java
文件:
@Service
@Transactional
@Slf4j
public class WmChannelServiceImpl extends ServiceImpl<WmChannelMapper, WmChannel> implements WmChannelService {
/**
* 查询所有频道
* @return
*/
@Override
public ResponseResult findAll() {
return ResponseResult.okResult(list());
}
}
⑹. Controller
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/controller/v1/WmChannelController.java
文件:
@RestController
@RequestMapping("/api/v1/channel")
public class WmChannelController {
@Autowired
private WmChannelService wmChannelService;
@GetMapping("/channels")
public ResponseResult findAll() {
return wmChannelService.findAll();
}
}
⑺. 测试
启动nginx,启动自媒体微服务和自媒体网关,自媒体地址: http://localhost:8802/#/login
2. 文章列表查询
⑴. 表结构分析
新建 heima-leadnews-model/src/main/java/com/heima/model/wemedia/pojos/WmNews.java
对应实体类:
/**
* <p>
* 自媒体图文内容信息表
* </p>
*
* @author itheima
*/
@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;
}
}
}
⑵. 接口定义
①. 接口说明
说明 | |
---|---|
接口路径 | /api/v1/news/list |
请求方式 | POST |
参数 | WmNewsPageReqDto |
响应结果 | ResponseResult |
ResponseResult :
{
"host": "null",
"code": 0,
"errorMessage": "操作成功",
"data": [
Object { ... },
Object { ... },
Object { ... }
],
"currentPage":1,
"size":10,
"total":21
}
②. Dto
新建 heima-leadnews-model/src/main/java/com/heima/model/wemedia/dtos/WmNewsPageReqDto.java
文件:
@Data
public class WmNewsPageReqDto extends PageRequestDto {
/**
* 状态
*/
private Short status;
/**
* 开始时间
*/
private Date beginPubDate;
/**
* 结束时间
*/
private Date endPubDate;
/**
* 所属频道ID
*/
private Integer channelId;
/**
* 关键字
*/
private String keyword;
}
⑶. 基础搭建
①. Controller
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/controller/v1/WmNewsController.java
文件:
@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
@PostMapping("/list")
public ResponseResult findList(@RequestBody WmNewsPageReqDto dto) {
return null;
}
}
②. Mapper
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/mapper/WmNewsMapper.java
文件:
@Mapper
public interface WmNewsMapper extends BaseMapper<WmNews> {
}
③. Service
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/WmNewsService.java
文件:
public interface WmNewsService extends IService<WmNews> {
}
④. ServiceImpl
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmNewsServiceImpl.java
文件:
@Service
@Slf4j
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
}
⑷. Service
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/WmNewsService.java
文件:
/**
* 条件查询文章列表
* @param dto
* @return
*/
public ResponseResult findList(WmNewsPageReqDto dto);
⑸. ServiceImpl
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmNewsServiceImpl.java
文件:
@Service
@Slf4j
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
/**
* 条件查询文章列表
* @param dto
* @return
*/
@Override
public ResponseResult findList(WmNewsPageReqDto dto) {
// 1. 检查参数
dto.checkParam(); // 分页检查
// 2. 分页条件查询
IPage page = new Page(dto.getPage(), dto.getSize());
LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper();
// 2.1 状态精准查询
if(dto.getStatus() != null) {
lambdaQueryWrapper.eq(WmNews::getStatus, dto.getStatus());
}
// 2.2 频道精准查询
if(dto.getChannelId() != null) {
lambdaQueryWrapper.eq(WmNews::getChannelId, dto.getChannelId());
}
// 2.3 时间范围查询
if(dto.getBeginPubDate() != null && dto.getEndPubDate() != null) {
lambdaQueryWrapper.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
}
// 2.4 关键词的模糊查询
if(StringUtils.isNoneBlank(dto.getKeyword())) {
lambdaQueryWrapper.like(WmNews::getTitle, dto.getKeyword());
}
// 2.5 查询当前登录人的文章
lambdaQueryWrapper.eq(WmNews::getUserId, WmThreadLocalUtils.getUser().getId());
// 2.6 发布时间倒序查询
lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
page = page(page, lambdaQueryWrapper);
// 3. 返回结果
ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
responseResult.setData(page.getRecords());
return responseResult;
}
}
⑹. Controller
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/controller/v1/WmNewsController.java
文件:
@RestController
@RequestMapping("/api/v1/news")
public class WmNewsController {
@Autowired
private WmNewsService wmNewsService;
@PostMapping("/list")
public ResponseResult findList(@RequestBody WmNewsPageReqDto dto) {
return wmNewsService.findList(dto);
}
}
⑺. 测试
启动nginx,启动自媒体微服务和自媒体网关,自媒体地址: http://localhost:8802/#/login
3. 文章发布
⑴. 需求分析
⑵. 表结构分析
①. 表关系
wm_material 素材表
wm_news_material 文章素材关系表
②. 实体类
新建 heima-leadnews-model/src/main/java/com/heima/model/wemedia/pojos/WmNewsMaterial.java
文件:
/**
* <p>
* 自媒体图文引用素材信息表
* </p>
*
* @author itheima
*/
@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;
}
⑶. 思路分析
该功能为保存、修改(是否有id)、保存草稿的共有方法
- 前端提交发布或保存为草稿
- 后台判断请求中是否包含了文章id
- 如果不包含id,则为新增
3.1. 执行新增文章的操作
3.2 关联文章内容图片与素材的关系
3.3 关联文章封面图片与素材的关系 - 如果包含了id,则为修改请求
4.1 删除该文章与素材的所有关系
4.2 执行修改操作
4.3 关联文章内容图片与素材的关系
4.4 关联文章封面图片与素材的关系
⑷. 接口定义
①. 接口说明
说明 | |
---|---|
接口路径 | /api/v1/channel/submit |
请求方式 | POST |
参数 | WmNewsDto |
响应结果 | ResponseResult |
ResponseResult:
{
“code”:501,
“errorMessage”:“参数失效"
}
{
“code”:200,
“errorMessage”:“操作成功"
}
{
“code”:501,
“errorMessage”:“素材引用失效"
}
②. Dto
新建 heima-leadnews-model/src/main/java/com/heima/model/wemedia/dtos/WmNewsDto.java
文件:
@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;
}
⑸. 基础搭建
①. Controller
编辑 heima-leadnews-model/src/main/java/com/heima/model/wemedia/dtos/WmNewsPageReqDto.java
文件:
@PostMapping("/submit")
public ResponseResult submitNews(@RequestBody WmNewsDto dto) {
return null;
}
②. Mapper
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/mapper/WmNewsMaterialMapper.java
文件:
@Mapper
public interface WmNewsMaterialMapper extends BaseMapper<WmNewsMaterial> {
// 批量保存文章素材的方法
void saveRelations(@Param("materialIds") List<Integer> materialIds, @Param("newsId") Integer newsId, @Param("type") Short type);
}
③. Mapper配置文件
新建 heima-leadnews-service/heima-leadnews-wemedia/src/main/resources/mapper/WmNewsMaterialMapper.xml
文件:
<?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>
④. Service
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/WmNewsService.java
文件:
/**
* 发布/修改文章或保存为草稿
* @param dto
* @return
*/
public ResponseResult submitNews(WmNewsDto dto);
⑤. ServiceImpl
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmNewsServiceImpl.java
文件:
/**
* 发布/修改文章或发布为草稿
* @param dto
* @return
*/
@Override
public ResponseResult submitNews(WmNewsDto dto) {
return null;
}
⑹. 定义常量
新建 heima-leadnews-common/src/main/java/com/heima/common/constants/WemediaConstants.java
文件:
public class WemediaConstants {
public static final Short COLLECT_MATERIAL = 1;//收藏
public static final Short CANCEL_COLLECT_MATERIAL = 0;//取消收藏
public static final String WM_NEWS_TYPE_IMAGE = "image";
public static final Short WM_NEWS_NONE_IMAGE = 0;
public static final Short WM_NEWS_SINGLE_IMAGE = 1;
public static final Short WM_NEWS_MANY_IMAGE = 3;
public static final Short WM_NEWS_TYPE_AUTO = -1;
public static final Short WM_CONTENT_REFERENCE = 0;
public static final Short WM_COVER_REFERENCE = 1;
}
⑺. 自定义提示信息
编辑 heima-leadnews-model/src/main/java/com/heima/model/common/enums/AppHttpCodeEnum.java
文件:
NEED_ADMIND(3001,"需要管理员权限"),
// 自媒体文章错误 3501~3600
MATERIAL_REFERENCE_FAIL(3501, "素材引用失效");
⑻. ServiceImpl(内容图片与素材)
编辑 heima-leadnews-service/heima-leadnews-wemedia/src/main/java/com/heima/wemedia/service/impl/WmNewsServiceImpl.java
文件:
@Service
@Slf4j
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
@Autowired
private WmNewsMaterialMapper wmNewsMaterialMapper;
@Autowired
private WmMaterialMapper wmMaterialMapper;
/**
* 条件查询文章列表
* @param dto
* @return
*/
@Override
public ResponseResult findList(WmNewsPageReqDto dto) {
// 1. 检查参数
dto.checkParam(); // 分页检查
// 2. 分页条件查询
IPage page = new Page(dto.getPage(), dto.getSize());
LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper();
// 2.1 状态精准查询
if(dto.getStatus() != null) {
lambdaQueryWrapper.eq(WmNews::getStatus, dto.getStatus());
}
// 2.2 频道精准查询
if(dto.getChannelId() != null) {
lambdaQueryWrapper.eq(WmNews::getChannelId, dto.getChannelId());
}
// 2.3 时间范围查询
if(dto.getBeginPubDate() != null && dto.getEndPubDate() != null) {
lambdaQueryWrapper.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
}
// 2.4 关键词的模糊查询
if(StringUtils.isNoneBlank(dto.getKeyword())) {
lambdaQueryWrapper.like(WmNews::getTitle, dto.getKeyword());
}
// 2.5 查询当前登录人的文章
lambdaQueryWrapper.eq(WmNews::getUserId, WmThreadLocalUtils.getUser().getId());
// 2.6 发布时间倒序查询
lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
page = page(page, lambdaQueryWrapper);
// 3. 返回结果
ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
responseResult.setData(page.getRecords());
return responseResult;
}
/**
* 发布/修改文章或发布为草稿
* @param dto
* @return
*/
@Override
public ResponseResult submitNews(WmNewsDto dto) {
// 1. 参数校验
if(dto == null || dto.getContent() == null) {
return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
}
// 2. 保存或修改文章
WmNews wmNews = new WmNews();
// 属性拷贝 属性名词和类型相同才能拷贝
BeanUtils.copyProperties(dto, wmNews);
// 封面图片 list---> string
if(dto.getImages() != null && dto.getImages().size() > 0) {
String imageStr = StringUtils.join(dto.getImages(), ",");
wmNews.setImages(imageStr);
}
// 如果当前封面为自动类型 -1
if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
wmNews.setType(null);
}
saveOrUpdateWmNews(wmNews);
// 3. 判断是否为草稿, 如果是草稿, 结束当前方法
if(dto.getStatus().equals(WmNews.Status.NORMAL.getCode())) {
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
// 4. 保存文章内容图片和素材的关系
// 提取文章内容中的图片信息
List<String> materials = ectractUrlInfo(dto.getContent());
saveRelativeInfoForContent(materials, wmNews.getId());
// 5. 保存文章封面图片和素材的关系, 如果当然布局是自动, 需要匹配封面图片
saveRelativeInfoForCover(dto, wmNews, materials);
return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
/**
* 第一个功能:如果当前封面类型为自动,则设置封面类型的数据
* 匹配规则:
* 1,如果内容图片大于等于1,小于3 单图 type 1
* 2,如果内容图片大于等于3 多图 type 3
* 3,如果内容没有图片,无图 type 0
*
* 第二个功能:保存封面图片与素材的关系
* @param dto
* @param wmNews
* @param materials
*/
private void saveRelativeInfoForCover(WmNewsDto dto, WmNews wmNews, List<String> materials) {
// 如果当前封面类型为自动,则设置封面类型的数据
List<String> images = dto.getImages();
if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
// 多图
if(materials.size() >= 3) {
wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
images = materials.stream().limit(3).collect(Collectors.toList());
} else if(materials.size() >= 1 && materials.size() < 3) {
// 单图
wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
images = materials.stream().limit(1).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 materials
* @param newsId
*/
private void saveRelativeInfoForContent(List<String> materials, Integer newsId) {
saveRelativeInfo(materials, newsId, WemediaConstants.WM_CONTENT_REFERENCE);
}
/**
* 保存文章图片与素材的关系到数据库中
* @param materials
* @param newsId
* @param type
*/
private void saveRelativeInfo(List<String> materials, Integer newsId, Short type) {
// 参数校验
if(materials != null && !materials.isEmpty()) {
// 通过图片的url查询素材的id
List<WmMaterial> dbMaterials = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, materials));
// 判断素材是否有效
if(dbMaterials == null ||dbMaterials.size() == 0) {
// 提示调用者素材失效(还可以进行数据回滚)
throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FAIL);
}
// 判断素材和数据库素材是否一致
if(dbMaterials.size() != materials.size()) {
// 提示调用者素材失效(还可以进行数据回滚)
throw new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FAIL);
}
List<Integer> idList = dbMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());
// 批量保存
wmNewsMaterialMapper.saveRelations(idList, newsId, type);
}
}
/**
* 提取文章内容中的图片信息
* @param content
* @return
*/
private List<String> ectractUrlInfo(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;
}
/**
* 保存或修改文章
* @param wmNews
*/
private void saveOrUpdateWmNews(WmNews wmNews) {
// 属性补全
wmNews.setUserId(WmThreadLocalUtils.getUser().getId());
wmNews.setCreatedTime(new Date());
wmNews.setSubmitedTime(new Date());
wmNews.setEnable((short) 0); // 默认上架
if(wmNews.getId() == null) {
// 保存文章
save(wmNews);
} else {
// 修改文章
wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId()));
updateById(wmNews);
}
}
}
⑼. Controller
编辑 heima-leadnews-model/src/main/java/com/heima/model/wemedia/dtos/WmNewsPageReqDto.java
文件:
@PostMapping("/submit")
public ResponseResult submitNews(@RequestBody WmNewsDto dto) {
return wmNewsService.submitNews(dto);
}
⑽. 测试
启动nginx,启动自媒体微服务和自媒体网关,自媒体地址: http://localhost:8802/#/login
①. 单图
Ⅰ. 发布文章
Ⅱ. 内容列表
Ⅲ. 数据库
②. 草稿
Ⅰ. 发布文章
Ⅱ. 内容列表
Ⅲ. 数据库
数据库有文章,但是图片和素材未关联
③. 自动
Ⅰ. 发布文章
Ⅱ. 内容列表
Ⅲ. 数据库
数据库有文章,内容、封面图片和素材已关联