黑*头条_第5章_文章发布粉丝管理成形记
创始人
2024-04-10 07:40:23
0

黑*头条_第5章_文章发布&粉丝管理成形记

文章目录

  • 黑*头条_第5章_文章发布&粉丝管理成形记
  • 文章发布&粉丝管理
  • 1 需求分析
    • 1.1 功能需求
    • 1.2 前端需求
      • 1.2.1 图文数据需求
      • 1.2.2 素材管理需求
      • 1.2.3 发布文章需求
      • 1.2.4 内容列表需求
      • 1.2.5 粉丝概况需求
  • 2 定义
    • 2.1 后端定义
      • 2.1.1 路由定义
      • 2.1.2 工程定义
      • 2.1.3 接口定义
      • 2.1.4 结构定义
  • 3 wemedia登录功能实现
    • 3.1工程创建
    • 3.2登录功能后台
      • 3.2.1 接口定义
      • 3.2.2 mapper文件
      • 3.2.3 代码实现
    • 3.3 登录功能前台
      • 3.3.1 定义api
      • 3.3.2 路由调整
      • 3.3.3 实现登录功能
  • 4 素材管理开发
    • 4.1 FASTDFS封装
      • 4.1.1 FASTDFS配置
      • 4.1.2 FastDfsConfig
      • 4.1.3 FastDfsClient
    • 4.2 素材上传接口
      • 4.2.1
      • 4.2.2 接口定义
      • 4.2.3 类定义
      • 4.2.4 Mapper实现
      • 4.2.5 service思路分析
      • 4.2.6 代码实现
    • 4.3 删除图片接口
      • 4.3.1 接口定义
      • 4.3.2 类定义
      • 4.3.3 Mapper实现
      • 4.3.4 时序说明
      • 4.3.5 代码实现
    • 4.4 素材列表接口
      • 4.4.1 接口定义
      • 4.4.2 类定义
      • 4.4.3 Mapper实现
      • 4.4.4 时序说明
      • 4.4.5 代码实现
    • 4.5 收藏、取消收藏接口
      • 4.5.1 接口定义
      • 4.5.2 类定义
      • 4.5.3 Mapper实现
      • 4.5.4 时序说明
      • 4.5.5 代码实现
    • 4.6 素材管理前台
      • 4.6.1 定义api
      • 4.6.2 路由调整
      • 4.6.3 菜单调整
      • 4.6.4 实现素材管理页面
  • 5 文章管理功能
    • 5.1 文章发布、保存草稿后台接口
      • 5.1.1接口定义
      • 5.1.2 Mapper实现
      • 5.1.3 时序说明
      • 5.1.4 代码实现
    • 5.2 文章发布,保存草稿前台
      • 5.2.1 接口定义
      • 5.2.2 路由调整
      • 5.2.3 实现文章发布页面
    • 5.3 文章列表后台接口
      • 5.3.1 接口定义
      • 5.3.2 Mapper实现
      • 5.3.3 时序说明
      • 5.3.4 代码实现
    • 5.4 文章详情后台接口
      • 5.4.1 接口定义
      • 5.4.2 Mapper实现
      • 5.4.3 时序说明
      • 5.4.4 代码实现
    • 5.5 删除文章后台接口
      • 5.5.1 接口定义
      • 5.5.2 Mapper实现
      • 5.5.3 时序说明
      • 5.5.4 代码实现
    • 5.6 文章内容列表-前台
      • 5.6.1 接口定义
      • 5.6.2 路由调整
      • 5.6.3 实现文章列表
  • 6 图文和粉丝统计报表
    • 6.1 图文统计后台接口
      • 6.1.1 接口定义
      • 6.1.2 Mapper实现
      • 6.1.3 时序说明
      • 6.1.5 代码实现
    • 6.2 粉丝统计后台接口
      • 6.2.1 接口定义
      • 6.2.2 Mapper实现
      • 6.2.3 时序说明
      • 6.2.4 代码实现
    • 6.3 图文数据前台开发
      • 6.3.1 接口定义
      • 5.4.2 路由调整
      • 5.4.3 实现图文数据
      • 5.4.4 实现粉丝概况

文章发布&粉丝管理

  • 熟悉FastDfs的封装集成
  • 熟悉自媒体系统的核心功能需求
  • 掌握VUE+Echarts的集成使用
  • 掌握后台功能的通用封装技巧
  • 熟悉跨平台富文本的处理方案

1 需求分析

1.1 功能需求

在自媒体后台中主要包含的功能有内容管理:素材管理、文章发布、内容列表的查看、评论列表查看、图文数据统计;粉丝管理:粉丝概况、粉丝画像、粉丝列表

本案例开发功能包括:

  • 根据不同时间范围查询图文明细数据

  • 发布文章、保存草稿

  • 根据状态查询当前用户的内容数据、修改、删除

  • 素材查看、收藏素材、删除素材、取消收藏

  • 粉丝概况:性别分布、年龄分布、终端分布、七日阅读量分布

1.2 前端需求

1.2.1 图文数据需求

在图文数据中我们主要实现自媒体用户所发布的文章的相关数据(发布量、阅读量、点赞量、评论量、收藏量、转发量、不喜欢)统计,为自媒体用户提供直观的运营数据。

在这里插入图片描述
在这里插入图片描述

1.2.2 素材管理需求

素材管理部分的相关需求主要是实现图片的上传、删除、查询等功能,提供给自媒体人图文素材管理的空间。

在这里插入图片描述

1.2.3 发布文章需求

发布文章部分主要实现自媒体用户编辑文章内容,进行文章的发布或则保存草稿。

在这里插入图片描述
在这里插入图片描述

1.2.4 内容列表需求

内容列表功能模块, 主要实现根据不同的条件对文章内容进行查询,以及对文章的删除、修改等操作。

在这里插入图片描述

1.2.5 粉丝概况需求

粉丝概况和图文数据功能类似,但此处统计的数据只是当前自媒体用户的粉丝产生的相关数据。

在这里插入图片描述
在这里插入图片描述

2 定义

2.1 后端定义

2.1.1 路由定义

本功能会涉及以下相关数据表,用于读取文章内容和配置,存储在文章详情页面产生的各种行为,相关表的Mycat路由定义如下:

表名描述主键方式存放DN分表字段
wm_news_statistics自媒体图文数据统计表zk_sequenceDN[0~5]burst=(id,user_id)
wm_sub_user自媒体子账号信息表auto_incrementDN[0~2]parent_id
wm_user_auth自媒体子账号权限信息表auto_incrementDN[1~3]user_id
wm_user自媒体用户信息表auto_incrementDN[0~5]id
wm_user_login自媒体用户登录行为信息表auto_incrementDN[4]user_id
wm_user_equipment自媒体用户设备信息表auto_incrementDN[1~3]user_id
wm_fans_statistics自媒体粉丝数据统计表zk_sequenceDN[0~5]burst=(id,user_id)
wm_fans_portrait自媒体粉丝画像信息表zk_sequenceDN[0~5]burst=(id,user_id)
wm_news自媒体图文内容信息表auto_incrementDN[0]id
wm_material自媒体图文素材信息表auto_incrementDN[0]id
wm_news_statistics自媒体图文数据统计表auto_incrementDN[0]id

2.1.2 工程定义

自媒体服务:heima-leadnews-media

2.1.3 接口定义

自媒体后端,主要接口如下:

登录功能

  • 登录

素材管理相关:

  • 上传图片接口:用于上传用户提交的图片到素材库

  • 删除图片接口:用于用户删除拥有的素材

  • 收藏图片接口:收藏某个素材

  • 取消收藏:取消收藏

  • 素材列表:用户所有的素材

文章发布相关:

  • 提交文章接口:用于提交文章

  • 保存草稿接口:用于提交草稿文章

  • 列表接口:用于查询当前用户的文章

  • 详情接口接口:用于查询某篇文章详情

  • 删除接口:用于删除谋篇文章

统计相关:

  • 图文数据统计:当前自媒体用户文章被游客以及粉丝操作的相关数据

  • 粉丝相关文章数据统计: 由粉丝对文章的相关操作产生的数据

2.1.4 结构定义

在这里插入图片描述

3 wemedia登录功能实现

3.1工程创建

在父工程下heima-leadnews创建模块heima-leadnews-media,从其他模块分别拷贝maven_dev.properties、
maven_prod.properties、maven_test.properties到项目的根目录

拷贝application.properties、log4j2.xml到项目的resources目录下,修改application.properties

server.port=${port.media}
spring.application.name=${sn.media}

从其他微服务下拷贝pom文件,创建对应模块的包名及引导类

分别引入mysql、jackson,security的配置

3.2登录功能后台

3.2.1 接口定义

参考标准请参考通用接口规范
接口名称/login/in
请求DTOcom.heima.model.media.pojos.WmUser
响应DTO返回map{token:xxx,user:{…}}

3.2.2 mapper文件

WmUser 用户实体com.heima.model.media.pojos.WmUser

@Data
public class WmUser {private Long id;private String name;private String password;private String salt;private String nickname;private String image;private String location;private String phone;private Integer status;private String email;private Integer type;private Integer score;private Long apUserId;private Integer apAuthorId;private Date loginTime;private Date createdTime;
}

创建mapper接口:com.heima.model.mappers.wemedia.WmUserMapper

public interface WmUserMapper {WmUser selectByName(String name);
}

WmUserMapper.xml



id, name, password, ap_user_id, ap_author_id, salt, nickname, image, location, phone, status, email, type,score, login_time, created_time



3.2.3 代码实现

(1)创建接口com.heima.media.service.UserLoginService

public interface UserLoginService {ResponseResult login(WmUser user);
}

(2)实现类UserLoginServiceImpl

@Service
public class UserLoginServiceImpl implements UserLoginService {@Autowiredprivate WmUserMapper wmUserMapper;public ResponseResult login(WmUser user){if (StringUtils.isEmpty(user.getName())&&StringUtils.isEmpty(user.getPassword())) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE,"用户名和密码不能为空");}WmUser wmUser = wmUserMapper.selectByName(user.getName());if(wmUser!=null){if(user.getPassword().equals(wmUser.getPassword())){Map map = Maps.newHashMap();wmUser.setPassword("");wmUser.setSalt("");map.put("token",AppJwtUtil.getToken(wmUser));map.put("user",wmUser);return ResponseResult.okResult(map);}else{return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);}}else{return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户不存在");}}
}

(3)创建apis接口

public interface LoginControllerApi {public ResponseResult login(WmUser user);
}

(4)实现controller:com.heima.media.controller.v1.LoginController

@RestController
@RequestMapping("/login")
public class LoginController implements LoginControllerApi {@Autowiredprivate UserLoginService userLoginService ;@Override@RequestMapping("/in")public ResponseResult login(@RequestBody WmUser user){return userLoginService.login(user);}
}

3.3 登录功能前台

3.3.1 定义api

在src/constants/api.js中定义常量映射到后端请求地址

export const  API_USERAUTH = '/login/in' //用户认证

在src/api/login.js中定义请求方法,在请求成功之后,需要把后台返回的token数据写入本地缓存

import request from '@/utils/request'
import {setUser} from '@/utils/store'
import  { API_GETPHONECODE , API_USERAUTH , API_CAPTCHAS }  from '@/constants/api'export function loginByUsername(name,password) {const data = {name,password}return request({url: API_USERAUTH,method: 'post',data}).then(result => {if(result['code']==0){let temp = result.datasetUser({name:temp.user.name,photo:null,token:temp.token}) //设置用户的个人数据}return result})
}

3.3.2 路由调整

在src/router.js中asyncRouterMap对象的children数组中增加以下改动,以满足全局自动记录路由的功能:

设置登录为起始路由

export const asyncRouterMap = [{path: "/",component: Layout,redirect:'/login', //默认子路由name:'mainIndex',children:[{path:'/index',component: () => import('@/views/dashboard/index.vue'),}]},{path: '/login',component: () => import('@/views/login/index.vue'),},{path: '*',component: () => import('@/views/404.vue'),}
]
var  myRouter = new Router({routes: asyncRouterMap
})
export default myRouter

3.3.3 实现登录功能



4 素材管理开发

4.1 FASTDFS封装

4.1.1 FASTDFS配置

  • 在项目根目录pom.xml中增加以下配置
0.2.0

com.luhuiguofastdfs-spring-boot-starter${fastdfs.version}logback-classicch.qos.logback

  • 在common项目pom.xml中增加以下配置

com.luhuiguofastdfs-spring-boot-starterlogback-classicch.qos.logback

  • 在common\src\main\resources下创建文件fast-dfs.properties
fast.dfs.connect-timeout=3000
fast.dfs.so-timeout=6000
fast.dfs.tracker-server=192.168.25.133:22122

4.1.2 FastDfsConfig

在common下创建类:com.heima.common.fastdfs.FastDfsConfig,重载自动装载dfs和设定连接池。

/*** 自动化配置核心数据库的连接配置*/
@Setter
@Getter
@Configuration
@ConfigurationProperties(prefix="fast.dfs")
@PropertySource("classpath:fast-dfs.properties")
public class FastDfsConfig extends  FdfsAutoConfiguration {int soTimeout;int connectTimeout;String trackerServer;public FastDfsConfig(FdfsProperties properties){super(properties);}@Beanpublic PooledConnectionFactory pooledConnectionFactory() {PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();pooledConnectionFactory.setSoTimeout(getSoTimeout());pooledConnectionFactory.setConnectTimeout(getConnectTimeout());return pooledConnectionFactory;}@Beanpublic TrackerConnectionManager trackerConnectionManager(FdfsConnectionPool fdfsConnectionPool) {return new TrackerConnectionManager(fdfsConnectionPool, Arrays.asList(trackerServer));}
}

4.1.3 FastDfsClient

在common下创建类com.heima.fastdfs.FastDfsClient,封装dfs上传、下载等常用方法:

/*** dfs客服端*/
@Component
public class FastDfsClient {@AutowiredFastFileStorageClient storageClient;/*** 上传文件方法* 

Title: uploadFile

*

Description:

* @param fileName 文件全路径* @param extName 文件扩展名,不包含(.)* @return* @throws Exception*/public String uploadFile(String fileName, String extName) throws Exception {StorePath s = storageClient.uploadFile(FileUtils.readFileToByteArray(new File(fileName)),extName);String result = s.getFullPath();return result;}public String uploadFile(String fileName) throws Exception {return uploadFile(fileName, null);}/*** 上传文件方法*

Title: uploadFile

*

Description:

* @param fileContent 文件的内容,字节数组* @param extName 文件扩展名* @return* @throws Exception*/public String uploadFile(byte[] fileContent, String extName) throws Exception {StorePath s = storageClient.uploadFile(fileContent,extName);String result = s.getFullPath();return result;}public String uploadFile(byte[] fileContent) throws Exception {return uploadFile(fileContent, null);}/*** 文件下载方法*/public byte[] downFile(String fileId) throws Exception {return storageClient.downloadFile("",fileId);}/*** 文件下载方法*/public byte[] downGroupFile(String group, String fileId) throws Exception {return storageClient.downloadFile(group,fileId);}public int delFile(String fileId) throws Exception {storageClient.deleteFile(fileId);return 1;}}

4.2 素材上传接口

4.2.1

4.2.2 接口定义

(1)基本定义

参考标准请参考通用接口规范
接口名称/api/v1/media/material/upload_picture
请求DTOMultipartFile
响应DTOWmMaterial

(2) CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”),
PARAM_IMAGE_FORMAT_ERRORPARAM_IMAGE_FORMAT_ERROR(502,“图片格式有误”)
SERVER_ERRORSERVER_ERROR(503,“服务器内部错误”),

4.2.3 类定义

类说明:

  • MultipartFile是用于接收用户上传文件

  • FastDFSClient用于将用户上传的图片上传至图片服务器,放置在common模块

  • WmMaterialMapper、WmNewsMaterialMapper是MybatisMapper文件,放置在model模块

  • MaterialManageControllerApi是服务接口定义,放置在apis模块

  • MaterialManageController、MaterialService、MaterialServiceImpl是对功能的实现,放置在media模块

4.2.4 Mapper实现

(1)WmMaterial 实体

在model模块下创建类com.heima.model.media.pojos.WmMaterial

@Data  
public class WmMaterial {  private Integer id;  @IdEncrypt  private Long userId;  private String url;  private short type;  private Short isCollection;  private Date createdTime;  
}

(2)WmMaterialMapper

创建类com.heima.model.mappers.wemedia.WmMaterialMapper,增加素材插入方法:

public interface WmMaterialMapper {  int insert(WmMaterial record);  
}

(3)WmMaterialMapper.xml

同样在model模块中创建文件resources/mappers/wemedia/WmMaterialMapper.xml,并写出接口对应sql,
保存上传图片的信息到数据库中。

   insert into wm_material (user_id, url,  type, is_collection, created_time  )  values (#{userId}, #{url},  #{type}, #{isCollection}, #{createdTime}  )    

4.2.5 service思路分析

  • 判断入参multipartFile是否合法,不合法则返回PARAM_INVALID错误

  • 判断入参multipartFile是否有合法的扩展名,不合法则返回PARAM_INVALID错误

  • 上传图片到FastDFS服务器

  • 上传图片到服务器失败

  • 上传图片流程完成, 返回信息给前端

4.2.6 代码实现

(1)MaterialService

在media模块中(若模块不存在则创建模块儿)创建类:com.heima.media.service.MaterialService,并添加uploadPicture方法实现图片的上传逻辑

定义获取文章详情接口:

public interface MaterialService {  /**  * 上传图片接口*  * @param multipartFile*  * @return*  */  ResponseResult uploadPicture(MultipartFile multipartFile);  
}

(2)MaterialServiceImpl

同样在media中创建类:com.heima.media.service.impl.MaterialServiceImpl,在方法的实现中首先调用fastDFS实现图片上传至服务器,
然后将文件信息存储到关系型数据库中。

修改工程resources/application.properties文件,添加配置

FILE_SERVER_URL=http://192.168.25.133/

实现类

@Service  
@Slf4j  
public class MaterialServiceImpl implements MaterialService {  @Value("${FILE_SERVER_URL}")  private String fileServerUrl;  @Autowired  private FastDfsClient fastDFSClient;  @Autowired  private WmMaterialMapper wmMaterialMapper;  @Override  public ResponseResult uploadPicture(MultipartFile multipartFile) {  WmUser user = WmThreadLocalUtils.getUser();  if (multipartFile == null) {  return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);  }  String originFileName = multipartFile.getOriginalFilename();  String extName = originFileName.substring(originFileName.lastIndexOf(".") +1);  if(!extName.matches("(gif|png|jpg|jpeg)")) {  returnResponseResult.errorResult(AppHttpCodeEnum.PARAM_IMAGE_FORMAT_ERROR);  }  // StringBuilder imgUrl = new StringBuilder(fileServerUrl);  String fileId = null;  //上传图片获得文件id  try {  fileId = fastDFSClient.uploadFile(multipartFile.getBytes(), extName);  } catch (Exception e) {  e.printStackTrace();  log.error("user {} upload file {} to fastDFS error, error info:n",user.getId(),  originFileName, e.getMessage());  return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);  }  //上传成功保存媒体资源到数据库  WmMaterial wmMaterial = new WmMaterial();  wmMaterial.setCreatedTime(new Date());  wmMaterial.setType((short) 0);  wmMaterial.setUrl(fileId);  wmMaterial.setUserId(user.getId());  wmMaterial.setIsCollection((short) 0);  wmMaterialMapper.insert(wmMaterial);  //设置返回值  wmMaterial.setUrl(fileServerUrl + wmMaterial.getUrl());  return ResponseResult.okResult(wmMaterial);  }
}

(3)MaterialManageControllerApi

创建类:com.heima.media.apis.MaterialManageControllerApi,在此类中定义控制器接口。

此类在apis模块中创建,定义了相关接口,实现如下:

public interface MaterialManageControllerApi {  /**  * 上传图片* @param multipartFile* @return*/ ResponseResult uploadPicture(MultipartFile multipartFile);  
}

(4)MaterialManageController

创建类:com.heima.media.controller.v1.MaterialManageController,
在控制器中调用Service中写的方法即可

该类的实现较为简单,引入Service并调用即可:

@RestController  
@RequestMapping("/api/v1/media/material")  
public class MaterialManageController implements MaterialManageControllerApi{  @Autowired  private MaterialService materialService;  @PostMapping("/upload_picture")  @Override  public ResponseResult uploadPicture(MultipartFile file) {  return materialService.uploadPicture(file);  }  
}

4.3 删除图片接口

4.3.1 接口定义

(1)基本定义

此接口用于删除无关联的图片。

参考标准请参考通用接口规范
接口名称/api/v1/media/material/del_picture
请求DTOcom.heima.model.media.dtos.WmMaterialDto
响应DTO{ “host”: null, “code”: 0, “error_message”: “操作成功”, “data”: “SUCCESS” }

(2)CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”)
SERVER_ERRORSERVER_ERROR(503,“服务器内部错误”),

4.3.2 类定义

类说明:

  • 涉及的pojo和Mapper都存储在model模块中

  • 请求DTO也重用WmMaterialDto,缺少字段进行补充即可

  • Service、Controller等类都进行重用,定义新的方法

4.3.3 Mapper实现

相关类在model模块中实现,之后所有的mapper文件、dto实体类都默认在model模块儿中进行实现,service相关接口默认在media模块中实现。

(1)WmMaterialDto

创建类com.heima.model.media.dtos.WmMaterialDto,用于接收前端传递过来的参数。

@Data  
public class WmMaterialDto {  @IdEncrypt  private Integer id;  
}

(2)WmMaterialMapper

在com.heima.model.mappers.wemedia.WmMaterialMapper类中定义方法:

  • selectByPrimaryKey,依据id查询媒体文件
  • deleteByPrimaryKey,根据id删除图片
public interface WmMaterialMapper {  WmMaterial selectByPrimaryKey(Integer id);int deleteByPrimaryKey(Integer id);  
}

(3)WmMaterialMapper.xml

在文件resources/mappers/wemedia/WmMaterialMapper.xml,在当前文件中根据业务写出对应的SQL



id, user_id, url, type, is_collection, created_time
 
    delete from wm_material  where id = #{id}  

(4)WmNewsMaterialMapper

删除时,如果对应的material有关联引用,则不能删除,所以需要查询对应的引用数据:com.heima.model.mappers.wemedia.WmNewsMaterialMapper

public interface WmNewsMaterialMapper {int countByMid(Integer mid);
}

(2)WmNewsMaterialMapper.xml

在文件resources/ mappers/wemedia/WmNewsMaterialMapper.xml SQL如下:



4.3.4 时序说明

  • 判断行为实体参数是否存在,如果不存在则返回PARAM_REQUIRE错误

  • 查看当前删除图片是否存在于系统中

  • 当前图片是否被引用, 如果被引用则不可删除

  • 删除图片服务器上面的图片

  • 所有操作完成成功返回

4.3.5 代码实现

(1)MaterialService

在com.heima.media.service.MaterialService 中新增方法delPicture实现图片的删除逻辑,定义获取文章详情接口:

ResponseResult delPicture(WmMaterialDto dto);  

(2)MaterialServiceImpl

在类com.heima.media.service.impl.MaterialServiceImpl中实现图片资源管理。此处主要实现了图片资源的删除,删除逻辑主要分为两步第一步先删除fastDFS上面的文件,然后删除数据库中的关联关系。

@Override  
public ResponseResult delPicture(WmMaterialDto dto) {  WmUser user = WmThreadLocalUtils.getUser();  if (dto == null || dto.getId() == null) {  return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);  }  //删除fastDFS上的文件  WmMaterial wmMaterial = wmMaterialMapper.selectByPrimaryKey(dto.getId());  if (wmMaterial == null) {  return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);  }  int count = wmNewsMaterialMapper.countByMid(dto.getId());  if (count > 0) {  return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"当前图片被引用");  }  String fileId = wmMaterial.getUrl().replace(fileServerUrl, "");  try {  fastDFSClient.delFile(fileId);  } catch (Exception e) {  e.printStackTrace();  log.error("user {} delete file {} from fastDFS error, error info:n",user.getId(),  fileId, e.getMessage());  return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);  }  //删除数据库记录  wmMaterialMapper.deleteByPrimaryKey(dto.getId());  return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);  
}

(3)MaterialManageControllerApi

在类com.heima.media.apis.MaterialManageControllerApi中定义图片删除接口方法。

ResponseResult delPicture(WmMaterialDto wmMaterial);  

(4)MaterialManageController

在类com.heima.media.controller.v1.MaterialManageController中实现图片删除接口方法。

@PostMapping("/del_picture")  
@Override  
public ResponseResult delPicture(@RequestBody WmMaterialDto dto) {  return materialService.delPicture(dto);  
}  

4.4 素材列表接口

4.4.1 接口定义

(1)基本定义

此接口用于加载自媒体人的图文素材,和用于图片素材选择框。

参考标准请参考通用接口规范
接口名称/api/v1/media/material/list
请求DTOcom.heima.model.media.dtos.WmMaterialListDto
响应DTO{“host”: null,“code”: 0, “error_message”: “操作成功”, “data”:{size:1,total:1,list:[]}}

(2)CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”)
SERVER_ERRORSERVER_ERROR(503,“服务器内部错误”),

4.4.2 类定义

类说明:

  • 涉及的pojo和Mapper都存储在model模块中

  • 请求DTO也重用WmMaterialDto,缺少字段进行补充即可

  • Service、Controller等类都进行重用,定义新的方法

4.4.3 Mapper实现

相关类在model模块中实现

(1)PageRequestDto

创建类com.heima.model.common.dtos.PageRequestDto,再该类中定义了checkParam方法用于校验分页参数,
若分页参数异常并给分页参数默认值。

@Data  
@Slf4j  
public class PageRequestDto {  protected Integer size;  protected Integer page;  public void checkParam() {  if (this.page == null || this.page < 0) {  setPage(1);  }  if (this.size == null || this.size < 0 || this.size > 100) {  setSize(10);  }  }  
}

(2)WmMaterialListDto

创建类com.heima.model.media.dtos.WmMaterialListDto,该类继承了PageRequestDto用于实现分页参数的封装

@Data  
public class WmMaterialListDto extends PageRequestDto {  Short isCollected; //1 查询收藏的  
}

(3)WmMaterialMapper

在类com.heima.model.mappers.wemedia.WmMaterialMapper新增接口方法findListByUidAndStatus用于根据用户id和需要查询的图片状态(是否收藏)查询图片、countListByUidAndStatus进行分页统计:

List findListByUidAndStatus(WmMaterialListDto dto, Long uid);  
int countListByUidAndStatus(WmMaterialListDto dto, Long uid);  

(4)WmMaterialMapper.xml

在文件resources/mappers/wemedia/WmMaterialMapper.xml中新增以下内容,对应在接口中新增的方法。

  

4.4.4 时序说明

  • 检测分页参数是否正确,如果不存在则返回PARAM_REQUIRE错误

  • 操作成功,返回查询结果集

4.4.5 代码实现

(1)MaterialService

在com.heima.media.service.MaterialService中新增方法findList查找图片列表的接口方法:

ResponseResult findList(WmMaterialListDto dto);  

(2)MaterialServiceImpl

在com.heima.media.service.impl.MaterialServiceImpl,中实现findList方法用于实现查找图片列表

@Override  
public ResponseResult findList(WmMaterialListDto dto) {  dto.checkParam();  Long uid = WmThreadLocalUtils.getUser().getId();  List datas = wmMaterialMapper.findListByUidAndStatus(dto,uid);  datas = datas.stream().map((item) -> {  item.setUrl(fileServerUrl + item.getUrl());  return item;  }).collect(Collectors.toList());  int total = wmMaterialMapper.countListByUidAndStatus(dto, uid);  Map resDatas = new HashMap<>();  resDatas.put("curPage", dto.getPage());  resDatas.put("size", dto.getSize());  resDatas.put("list", datas);  resDatas.put("total", total);  return ResponseResult.okResult(resDatas);  
}

(3)MaterialManageControllerApi

在类com.heima.media.apis.MaterialManageControllerApi中定义了相关接口,实现如下:

ResponseResult list(WmMaterialListDto dto);

(4)MaterialManageController

在类com.heima.media.controller.v1.MaterialManageController中增加对应接口方法。

@RequestMapping("/list")  
@Override  
public ResponseResult list(@RequestBody WmMaterialListDto dto) {  return materialService.findList(dto);  
}  

4.5 收藏、取消收藏接口

4.5.1 接口定义

(1)基本定义

此接口用于标记素材图片收藏、取消收藏等操作。

参考标准请参考通用接口规范
收藏接口名称/api/v1/media/material/collect
取消收藏接口/api/v1/media/material/cancle_collect
请求DTOcom.heima.model.media.dtos.WmMaterialDto
响应DTO{“host”: null, “code”: 0,“error_message”: “操作成功”,“data”: “SUCCESS”}

(2)CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”)
SERVER_ERRORSERVER_ERROR(503,“服务器内部错误”),

4.5.2 类定义

类说明:

  • 涉及的pojo和Mapper都存储在model模块中

  • 请求DTO也重用WmMaterialDto,缺少字段进行补充即可

  • Service、Controller等类都进行重用,定义新的方法

4.5.3 Mapper实现

相关类在model模块中实现

(1)WmMaterialDto

创建类com.heima.model.media.dtos.WmMaterialDto,该类不会输出给前端,所以相关属性可不做混淆加密设置。

@Data  
public class WmMaterialDto {  @IdEncrypt  private Integer id;  
}

(2)WmMaterialMapper

在类com.heima.model.mappers.wemedia.WmMaterialMapper中定义按照文章ID查询内容方法:

int updateStatusByUidAndId(Integer id, Long userId, Short type);

(3)WmMaterialMapper.xml

在WmMaterialMapper.xml文件中增加以下配置

  update wm_material  set is_collection = #{type}  where user_id = #{userId} and id = #{id}  

4.5.4 时序说明

(1)收藏时序图

  • 判断参数是否符合要求,如果不符合在则返回PARAM_REQUIRE错误

  • 更新当前素材的状态

  • 所有操作完成成功返回

(2)取消收藏时序图

  • 判断参数是否符合要求,如果不符合在则返回PARAM_REQUIRE错误

  • 更新当前素材的状态

  • 所有操作完成成功返回

4.5.5 代码实现

(1)MaterialService

在类com.heima.media.service.MaterialService中定义修改素材收藏状态的接口:

ResponseResult changeUserMaterialStatus(WmMaterialDto dto, Short type);  

(2)MaterialServiceImpl

在类com.heima.media.service.impl.MaterialServiceImpl中增加素材收藏或取消方法。

@Override  
public ResponseResult changeUserMaterialStatus(WmMaterialDto dto, Shorttype) {  if (dto == null || dto.getId() == null) {  return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);  }  WmUser user = WmThreadLocalUtils.getUser();  wmMaterialMapper.updateStatusByUidAndId(dto.getId(), user.getId(), type);  return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);  }

(3)MaterialManageControllerApi

在类com.heima.media.apis.MaterialManageControllerApi中定义了相关接口方法:

	ResponseResult collectionMaterial(WmMaterialDto dto);ResponseResult cancleCollectionMaterial(WmMaterialDto dto);  

(4)MaterialManageController

定义常量:com.heima.common.media.constans.WmMediaConstans

public class WmMediaConstans {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_DRAFT_STATUS = 0; //草稿public static final Short WM_NEWS_SUMMIT_STATUS = 1; //提交public static final Short WM_NEWS_AUTHED_STATUS = 8; //审核通过public static final Short WM_NEWS_PUBLISH_STATUS = 9; //已发布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_IMAGE_REFERENCE = 1;public static final char WM_NEWS_IMAGES_SWPARATOR = ',';public static final short WM_NEWS_STATISTIC_CUR = 0; //查询当日public static final short WM_NEWS_STATISTIC_WEEK = 1; //查询近一周public static final short WM_NEWS_STATISTIC_NEAR7 = 7; //查询近七天public static final short WM_NEWS_STATISTIC_NEWA30 = 30; //查询近30天}

在类com.heima.media.controller.v1.MaterialManageController中增加对应接口方法。

@PostMapping("/collect")  
@Override  
public ResponseResult collectionMaterial(@RequestBody WmMaterialDto dto) {  return materialService.changeUserMaterialStatus(dto,WmMediaConstans.COLLECT_MATERIAL);  
}  @PostMapping("/cancel_collect")  
@Override  
public ResponseResult cancleCollectionMaterial(@RequestBody WmMaterialDtodto) {  return materialService.changeUserMaterialStatus(dto,WmMediaConstans.CANCEL_COLLECT_MATERIAL);  }

4.6 素材管理前台

导入资料文件夹中的heima-leadnews-wemedia项目,使用web strom打开

4.6.1 定义api

(1)图片列表

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_USERIMAGES_LIST = '/api/v1/media/material/list'
  • 在src/api/publish.js中定义请求方法(此处省略了引入刚才定义的常量,此后所有导入省略请自行导入需要的常量及方法)
//拉取全部的素材图片  
export function getAllImgData (data) {  return Request({  url:API_USERIMAGES_LIST,  method:'post',  params:{},  data:data  })  
}

(2)删除图片

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_MODIFYIMAGE_DELETE ='/api/v1/media/material/del_picture' //删除图片
  • 在src/api/publish.js中定义请求方法
//删除图片素材  
export function delImg (id) {  return Request({  url:API_MODIFYIMAGE_DELETE,  method:'post',  params:{},  data:{id:id}  })  
}

(3)收藏或取消收藏图片

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_MODIFYIMAGE_COL = '/api/v1/media/material/collect'
//收藏用户素材 或 修改收藏状态接  
export const API_MODIFYIMAGE_COL_CANCEL = '/api/v1/media/material/cancel_collect' //取消用户素材 或 修改收藏状态接口
  • 在src/api/publish.js中定义请求方法
//收藏或取消收藏方法  
export function collectOrCancel (id,data) {  let collect = data.isCollected  let url = API_MODIFYIMAGE_COL  if(collect==0){  url = API_MODIFYIMAGE_COL_CANCEL  }  return Request({  url:url,  method:'post',  params:{},  data:{id:id}  })  
}

(4)上传图片

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_USERIMAGES_ADD ='/api/v1/media/material/upload_picture'
  • 在src/api/publish.js中定义请求方法
//上传图片  
export function uploadImg (data) {  return Request({  url:API_USERIMAGES_ADD,  method:'post',  data  })  
}

4.6.2 路由调整

在src/router.js中asyncRouterMap对象的children数组中增加以下改动,以满足全局自动记录路由的功能:

{  path:'/material/list',  component: () => import('./views/material/material.vue'),  
}

4.6.3 菜单调整

在src/constants/menus.js中的MenuData添加一下内容,
此处我们添加了我们之后将实现的所有菜单功能,此后将不再重复编写。

//导出菜单数据  
export const MenuData = [  {  title:'首页',path : '/' ,icon:'el-icon-s-home'  },  {  title:'内容管理',path:'/article',icon:'el-icon-edit',  children:[  { title:'图文数据' , path : '/material/data'},  { title:'发布文章' , path : '/article/publish'},  { title:'内容列表' , path : '/article/list'},  { title:'评论列表' , path : '/comment/list'},  { title:'素材管理' , path : '/material/list'}  ]  },  {  title:'粉丝管理', path:'/fans',icon:'el-icon-user',  children:[  { title:'粉丝概况' , path : '/fans/index'},  { title:'粉丝画像' , path : '/fans/info'},  { title:'粉丝列表' , path : '/fans/list'}  ]  },  { title:'账户信息',path:'/user/center',icon:'el-icon-setting'}  
]

4.6.4 实现素材管理页面

(1)上传组件定义

在src/components/Upload/中创建文件upload.vue, 并实现一下代码



(2)素材管理界面定义

在src/views/material/中定义material.vue, 代码如下




5 文章管理功能

5.1 文章发布、保存草稿后台接口

5.1.1接口定义

(1)基本定义

保存文章信息为草稿或发布文章

参考标准请参考通用接口规范
发布接口名称/api/v1/media/news/submit
草稿接口名称/api/v1/media/news/submit
请求DTOcom.heima.model.media.dtos.WmNewsDto
响应DTO{ “host”: null, “code”: 0, “error_message”: “操作成功”, “data”: “SUCCESS” }

查询所有的channel

参考标准请参考通用接口规范
发布接口名称/api/v1/channel/channels
请求DTO
响应DTO{ “host”: null, “code”: 0, “error_message”: “操作成功”, “data”: List }

(2)CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”),
PARAM_REQUIREPARAM_REQUIRE(500,“缺少参数”)

(3)思路分析

  • 如果用户传递参数为空或文章内容为空返回PARAM_REQUIRE错误

  • 如果用户本次为修改操作那么先删除数据库中的信息

  • 保存或修改文章的数据

  • 保存内容中的图片和当前文章的关系

  • 保存封面图片和当前文章的关系

  • 流程处理完成返回处理结果

5.1.2 Mapper实现

(1)WmMaterialMapper

在类com.heima.model.mappers.wemedia.WmMaterialMapper中定义findMaterialByUidAndimgUrls方法用过用户id以及图片url查询所有的Material:

List findMaterialByUidAndimgUrls(Long uid,
Collection values);  
 

WmMaterialMapper.xml

在文件resources/mappers/wemedia/WmMaterialMapper.xml,实现对应的SQL


(2)WmNewsMaterialMapper

在com.heima.model.mappers.wemedia.WmNewsMaterialMapper接口中添加delByNewsId方法用于根据id删除文章、saveRelationsByContent用于保存文章和图片的关联关系。

int delByNewsId(Integer nid);
void saveRelationsByContent(Map materials, Integer newsId,Short type);

WmNewsMaterialMapper.xml

在文件resources/ mappers/wemedia/WmNewsMaterialMapper.xml 中实现如下SQL如下:

  insert into wm_news_material (material_id, news_id, type, ord)  values    (#{mid}, #{newsId}, #{type}, #{ord})    
  delete from wm_news_material  where news_id = #{nid}  

(3)WmNewsMapper

WmNews实体类

@Data
public class WmNews {private Integer id;@IdEncryptprotected Long userId;private String title;private Short type;@IdEncryptprivate Integer channelId;private String labels;private Date createdTime;private Date submitedTime;private Short status;private Date publishTime;private String reason;@IdEncryptprivate Integer articleId;private String content;private String images; //图片用逗号分隔
}

在com.heima.model.mappers.wemedia.WmNewsMapper中添加接口方法insertNewsForEdit用于实现保存文章的操作、updateByPrimaryKey用于实现更新操作。

public interface WmNewsMapper {/*** 根据主键修改* @param dto* @return*/int updateByPrimaryKey(WmNews record);/*** 添加草稿新闻* @param dto* @return*/int insertNewsForEdit(WmNews dto);
}  

WmNewsMapper.xml

id, user_id,content, title, type, channel_id, labels, created_time, submited_time, status,enable,publish_time, reason, article_id, imagesupdate wm_newsset user_id = #{userId},title = #{title},type = #{type},channel_id = #{channelId},labels = #{labels},created_time = #{createdTime},submited_time = #{submitedTime},status = #{status,jdbcType=TINYINT},publish_time = #{publishTime},reason = #{reason},article_id = #{articleId},content = #{content},images = #{images}where id = #{id}insert  into wm_news(user_id, title, type, channel_id, labels, created_time, submited_time, status,publish_time,content)values (#{userId},#{title},#{type},#{channelId},#{labels},#{createdTime},#{submitedTime},#{status},#{publishTime}, #{content})

(4)获取所有的channel

AdChannel实体类

@Data
public class AdChannel {private Integer id;private String name;private String description;private Boolean isDefault;private Boolean status;private Byte ord;private Date createdTime;
}

定义AdChannelMapper接口:com.heima.model.mappers.admin.AdChannelMapper

public interface AdChannelMapper {/*** 查询所有*/public List selectAll();
}

AdChannelMapper.xml

id, name, description, is_default, status, ord, created_timeand name = #{name}and description = #{description}and is_default = #{isDefault}and status = #{status}and ord = #{ord}

5.1.3 时序说明

  • 如果用户传递参数为空或文章内容为空返回PARAM_REQUIRE错误

  • 如果用户本次为修改操作那么先删除数据库关联数据

  • 将用户提交的文章内容解析转为Map结构的数据

  • 保存或修改文章的数据

  • 保存内容中的图片和当前文章的关系

  • 保存封面图片和当前文章的关系

  • 流程处理完成返回处理结果

5.1.4 代码实现

(1)NewsService

创建类:com.heima.media.service.NewsService, 并添加saveNews接口方法

public interface NewsService {/**  * 自媒体发布文章 * @param wmNews* @return*/  ResponseResult saveNews(WmNewsDto wmNews, Short type);
}

NewsServiceImpl

创建类:com.heima.media.service.impl.NewsServiceImpl并实现接口中的方法,在保存新闻的实现方法中分为以下步骤:

  • 1.如果是修改先删除所有素材关联关系

  • 2.解析文章类容,进行图文素材关联信息提取

  • 3.保存发布文章信息

  • 4.如果存在引用并且是提交审核则需要做关联,否则只是进行保存草稿则不进行内容素材关联操作

  • 5.封面图片关联数据存储

@Service
@Slf4j
@SuppressWarnings("all")
public class NewsServiceImpl implements NewsService {@Autowiredprivate ObjectMapper objectMapper;@Autowiredprivate WmMaterialMapper wmMaterialMapper;@Autowiredprivate WmNewsMapper wmNewsMapper;@Autowiredprivate WmNewsMaterialMapper wmNewsMaterialMapper;@Autowiredprivate ApArticleConfigMapper apArticleConfigMapper;@Value("${FILE_SERVER_URL}")private String fileServerUrl;@Overridepublic ResponseResult saveNews(WmNewsDto dto, Short type) {if (dto == null || !StringUtils.isNotEmpty(dto.getContent())) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}WmUser user = WmThreadLocalUtils.getUser();//如果是修改先删除所有素材关联关系if (dto.getId() != null){wmNewsMaterialMapper.delByNewsId(dto.getId());}//解析文章类容,进行图文素材关联String content = dto.getContent();//Map<图片排序号, dfs文件id>Map materials;try {List list = objectMapper.readValue(content, List.class);//抽取信息Map extractInfo = extractUrlInfo(list);materials = (Map) extractInfo.get("materials");//文章图片总数量int countImageNum = (int) extractInfo.get("countImageNum");//保存发布文章信息WmNews wmNews = new WmNews();BeanUtils.copyProperties(dto, wmNews);if (dto.getType().equals(WmMediaConstans.WM_NEWS_TYPE_AUTO)){saveWmNews(wmNews, countImageNum, type);}else{saveWmNews(wmNews, dto.getType(), type);}//保存内容中的图片和当前文章的关系if (materials.keySet().size() != 0) {ResponseResult responseResult = saveRelativeInfoForContent(materials, wmNews.getId());if (responseResult != null) {return responseResult;}}//封面图片关联ResponseResult responseResult = coverImagesRelation(dto, materials, wmNews, countImageNum);if (responseResult != null) {return responseResult;}} catch (IOException e) {e.printStackTrace();log.error("parse content error, param content :{}", content);return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);}/*** 封面图片关联* @param dto* @param materials* @param wmNews* @param countImageNum* @return*/private ResponseResult coverImagesRelation(WmNewsDto dto, Map materials, WmNews wmNews, int countImageNum) {List images = dto.getImages();if (!WmMediaConstans.WM_NEWS_TYPE_AUTO.equals(dto.getType()) && dto.getType() != images.size()) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "图文模式不匹配");}//如果是自动匹配封面if (WmMediaConstans.WM_NEWS_TYPE_AUTO.equals(dto.getType())) {images = new ArrayList<>();if (countImageNum == WmMediaConstans.WM_NEWS_SINGLE_IMAGE) {for (Object value : materials.values()) {images.add(String.valueOf(value));break;}}if (countImageNum >= WmMediaConstans.WM_NEWS_MANY_IMAGE) {for (int i = 0; i < WmMediaConstans.WM_NEWS_MANY_IMAGE; i++) {images.add((String) materials.get(String.valueOf(i)));}}if (images.size() != 0) {ResponseResult responseResult = saveRelativeInfoForCover(images, wmNews.getId());if (responseResult != null) {return responseResult;}}} else if(images != null && images.size() != 0) {ResponseResult responseResult = saveRelativeInfoForCover(images, wmNews.getId());if (responseResult != null) {return responseResult;}}//更新images字段if (images != null) {wmNews.setImages(StringUtils.join(images.stream().map(s -> s.replace(fileServerUrl, "")).collect(Collectors.toList()),WmMediaConstans.WM_NEWS_IMAGES_SWPARATOR));wmNewsMapper.updateByPrimaryKey(wmNews);}return null;}/*** 提取信息* @param list* @return*/private Map extractUrlInfo(List list) {Map res = new HashMap<>();Map materials = new HashMap<>();int order = 0;int countImageNum = 0;//收集文章中引用的资源服务器的图片url以及排序for (Map map : list) {order++;if (WmMediaConstans.WM_NEWS_TYPE_IMAGE.equals(map.get("type"))) {countImageNum++;String imgUrl = String.valueOf(map.get("value"));if(imgUrl.startsWith(fileServerUrl)) {materials.put(String.valueOf(order), imgUrl.replace(fileServerUrl, ""));}}}res.put("materials", materials);res.put("countImageNum", countImageNum);return res;}/*** 保存关联信息到数据库* @param materials* @param newsId*/private ResponseResult saveRelativeInfo(Map materials, Integer newsId, Short type) {WmUser user = WmThreadLocalUtils.getUser();//手机数据库中的素材信息List dbMaterialInfos = wmMaterialMapper.findMaterialByUidAndimgUrls(user.getId(), materials.values());if (dbMaterialInfos != null && dbMaterialInfos.size() != 0) {Map urlIdMap = dbMaterialInfos.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));for (String key : materials.keySet()) {String fileId = String.valueOf(urlIdMap.get(materials.get(key)));if ("null".equals(fileId)) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "应用图片失效");}materials.put(key, String.valueOf(fileId));}//存储关系数据到数据库wmNewsMaterialMapper.saveRelationsByContent(materials, newsId, type);}return null;}/*** 保存图片关系为封面* @param images* @param newsId*/private ResponseResult saveRelativeInfoForCover(List images, Integer newsId) {Map materials = new HashMap<>();for (int i = 0; i < images.size(); i++) {String s = images.get(i);s = s.replace(fileServerUrl, "");materials.put(String.valueOf(i), s);}return saveRelativeInfo(materials, newsId, WmMediaConstans.WM_IMAGE_REFERENCE);}/*** 保存图片关系为内容* @param materials* @param newsId*/private ResponseResult saveRelativeInfoForContent(Map materials, Integer newsId) {return saveRelativeInfo(materials, newsId, WmMediaConstans.WM_CONTENT_REFERENCE);}/*** 保存/修改发布文章信息* @param wmNews* @param countImageNum* @param type*/private void saveWmNews(WmNews wmNews, int countImageNum, Short type) {WmUser user = WmThreadLocalUtils.getUser();//保存提交文章数据if (countImageNum == WmMediaConstans.WM_NEWS_SINGLE_IMAGE) {wmNews.setType(WmMediaConstans.WM_NEWS_SINGLE_IMAGE);} else if (countImageNum >= WmMediaConstans.WM_NEWS_MANY_IMAGE) {wmNews.setType(WmMediaConstans.WM_NEWS_MANY_IMAGE);} else {wmNews.setType(WmMediaConstans.WM_NEWS_NONE_IMAGE);}wmNews.setStatus(type);wmNews.setUserId(user.getId());wmNews.setCreatedTime(new Date());wmNews.setSubmitedTime(new Date());wmNews.setEnable((short)1);if (wmNews.getId() == null) {wmNewsMapper.insertNewsForEdit(wmNews);}else {wmNewsMapper.updateByPrimaryKey(wmNews);}}}

(2)查询所有的channel,定义接口:com.heima.media.service.AdChannelService

public interface AdChannelService {List selectAll();
}

AdChannelServiceImpl实现类

@Service
public class AdChannelServiceImpl implements AdChannelService {@Autowiredprivate AdChannelMapper channelMapper;@Overridepublic List selectAll() {return channelMapper.selectAll();}
}

(3)WmNewsDto

创建类:com.heima.model.media.dtos.WmNewsDto

此类在model模块中创建,定义请求入参,实现如下:

@Data
public class WmNewsDto {  private Integer id;  private String title;  @IdEncrypt  private Integer channelId;  private String labels;  private Date publishTime;  private String content;  private Short type;  private Date submitedTime;  private Short status;  private String reason;  private List images;  
}

(4)NewsControllerApi

在类com.heima.media.apis.NewsControllerApi中增加summitNews、saveDraftNews方法

public interface NewsControllerApi {  /**  * 提交文章*  * @param wmNews*  * @return*  */  ResponseResult summitNews(WmNewsDto wmNews);  /**  * 保存草稿* @param wmNews* @return*/ ResponseResult saveDraftNews(WmNewsDto wmNews);}

(5)NewsController

在com.heima.media.controller.v1.NewsController类中实现NewsControllerApi接口方法,调用对应的service接口即可。

@RestController
@RequestMapping("/api/v1/media/news")  
public class NewsController implements NewsControllerApi {  @Autowired  private NewsService newsService;  @PostMapping("/submit")  @Override  public ResponseResult summitNews(@RequestBody WmNewsDto wmNews) {  return newsService.saveNews(wmNews,WmMediaConstans.WM_NEWS_SUMMIT_STATUS);  }  @PostMapping("/save_draft")  @Override  public ResponseResult saveDraftNews(@RequestBody WmNewsDto wmNews) {  return newsService.saveNews(wmNews, WmMediaConstans.WM_NEWS_DRAFT_STATUS);  }}

(6)定义api接口:com.heima.media.apis.AdChannelControllerApi

public interface AdChannelControllerApi {public ResponseResult selectAll();
}

(7)AdChannelController

@RestController
@RequestMapping("/api/v1/channel")
public class ChannelController implements AdChannelControllerApi {@Autowiredprivate AdChannelService channelService ;@Override@RequestMapping("/channels")public ResponseResult selectAll(){return ResponseResult.okResult(channelService.selectAll());}
}

???经过测试,别管是草稿还是发布审核状态都是1,草稿的状态应该为0

5.2 文章发布,保存草稿前台

5.2.1 接口定义

(1)获取所有频道

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_CHANNELS = '/api/v1/channel/channels' //获取文章频道
  • 在src/api/publish.js中定义请求方法(此处省略了引入刚才定义的常量,此后所有导入省略请自行导入需要的常量及方法)
export function getChannels () {  return Request({  url:API_CHANNELS,  method:'get',  })  
}

(2)发布文章

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_ARTICLES = '/api/v1/media/news/submit' //post文章(新建)
  • 在src/api/publish.js中定义请求方法
//发表文章  
export function publishArticles (params,data) {  console.log(params,data)  return Request({  url:API_ARTICLES,  method:'post',  params,  data  })  
}

(3)修改文章

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_ARTICLES = '/api/v1/media/news/submit' //post文章(新建)
  • 在src/api/publish.js中定义请求方法
//编辑文章  
export function modifyArticles (articleId,params,data) {  return Request({  url:API_ARTICLES,  method:'post',  params,  data  })  
}

(4)根据ID获取文章

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_ARTICLES_INFO = '/api/v1/media/news/news' //获取文章
  • 在src/api/content.js中定义请求方法
//获取文章  
export function getArticleById (articlesId) {  return Request({  url:API_ARTICLES_INFO,  method:'post',  params:{},  data:{id:articlesId}  })  
}

5.2.2 路由调整

在src/router.js中asyncRouterMap对象的children数组中增加以下改动,以满足全局自动记录路由的功能:

{  path:'/article/publish',  component: () => import('@/views/publish/index.vue'),  
}

5.2.3 实现文章发布页面

文章内容要在不同平台上通用解析,直接使用html富文本编辑器做解析,实现成本较为之大,在黑马项目中通过JSON数组来存储文章内容数据,一个元素就是一段内容,支持文字、图片等混排以及样式的调整。

(1)黑马编辑器组件定义

在src/components/editor/中创建文件heima.vue, 并实现一下代码


(2)发布页面实现

发布页面就是基本的VUE表单页面,在src/views/publish/index.vue文件中实现如下:


5.3 文章列表后台接口

5.3.1 接口定义

(1)基本定义

由于框架封装只对JSON反序列化自增ID,需要请求文章ID需要封装为DTO.

参考标准请参考通用接口规范
发布接口名称/api/v1/media/news/list
请求DTOcom.heima.model.media.dtos.WmNewsPageReqDto
响应DTO{“host”:,“code”: 0,“error_message”: “操作成功”,“data”: []}

(2)CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”),

5.3.2 Mapper实现

(1)WmNewsMapper

在类com.heima.model.mappers.wemedia.WmNewsMapper中定义selectBySelective、countSelectBySelective实现分页查询

public interface WmNewsMapper {/**  * 查询根据dto条件* @param dto* @param uid* @return*/ List selectBySelective(WmNewsPageReqDto dto, Long uid);  /**  * 查询总数统计* @param dto* @param uid* @return*/int countSelectBySelective(WmNewsPageReqDto dto, Long uid);}

(2)WmNewsMapper.xml

在文件resources/mappers/wemedia/WmNewsMapper.xml中实现对应的SQL编写

  

5.3.3 时序说明

  • 如果用户传递参数为空返回PARAM_REQUIRE错误

  • 检测参数是否合法

  • 查询用户相关的图文数据

  • 统计当前与用户相关的图文数据一共多少

  • 流程处理完成返回处理结果

5.3.4 代码实现

(1)NewsService

在类com.heima.media.service.NewsService中定义listByUser方法

public interface NewsService {/**  * 查询发布库中当前用户文章信息* @param dto* @return*/  ResponseResult listByUser(WmNewsPageReqDto dto);
}

(2)NewsServiceImpl

在类:com.heima.media.service.impl.NewsServiceImpl中实现接口中的方法,在此处我们实现了listByUser,用于实现查询当前用户的文章

@Override  
public ResponseResult listByUser(WmNewsPageReqDto dto) {  if (dto == null) {  return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);  }  //检测参数  dto.checkParam();  Long uid = WmThreadLocalUtils.getUser().getId();  List datas = wmNewsMapper.selectBySelective(dto, uid);  int total = wmNewsMapper.countSelectBySelective(dto, uid);  PageResponseResult responseResult = new PageResponseResult(dto.getPage(),dto.getSize(), total);  responseResult.setData(datas);  responseResult.setHost(fileServerUrl);  return responseResult;  
}

(3)WmNewsPageReqDto

创建类:com.heima.media.mysql.core.model.dtos.WmNewsPageReqDto,此类在model模块中创建,定义请求入参,实现如下:

@Data
public class WmNewsPageReqDto extends PageRequestDto {  private Short status;  private Date beginPubdate;  private Date endPubdate;  @IdEncrypt  private Integer channelId;  private String keyWord;  
}

(4)NewsControllerApi

在类com.heima.media.apis.NewsControllerApi中增加listByUser接口方法

	/**  * 用户查询* @return*/ ResponseResult listByUser(WmNewsPageReqDto dto);

(5)NewsController

在com.heima.media.controller.v1.NewsController类中实现NewsControllerApi接口方法,调用对应的service接口即可。

@RestController
@RequestMapping("/api/v1/media/news")  
public class NewsController implements NewsControllerApi {  @Autowired  private NewsService newsService;  @PostMapping("/list")  @Override  public ResponseResult listByUser(@RequestBody WmNewsPageReqDto dto) {  return newsService.listByUser(dto);  }
}

5.4 文章详情后台接口

5.4.1 接口定义

(1)基本定义

文章详情主要用于查看和编辑数据初始化.

参考标准请参考通用接口规范
发布接口名称/api/v1/media/news/news
请求DTOcom.heima.model.media.dtos.WmNewsDto
响应DTO{ “host”:, “code”: 0, “error_message”: “操作成功”, “data”: {} }

(2)CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”),

5.4.2 Mapper实现

(1)WmNewsMapper

在类com.heima.model.mappers.wemedia.WmNewsMapper中定义selectNewsDetailByPrimaryKey方法:

WmNews selectNewsDetailByPrimaryKey(Integer id);

(2)WmNewsMapper.xml

在文件resources/mappers/wemedia/ WmNewsMapper.xml中编写接口对应的SQL语句


5.4.3 时序说明

  • 如果用户传递参数为空返回PARAM_REQUIRE错误

  • 根据id查询当前文章

  • 如果查询不到对应的文章则直接返回错误提示

  • 流程处理完成返回处理结果

5.4.4 代码实现

(1)NewsService

在类com.heima.media.service.NewsService中定义方法findWmNewsById

/*** 根据文章id查询文章* @return*/
ResponseResult findWmNewsById(WmNewsDto wmNews);

(2)NewsServiceImpl

在类com.heima.media.service.impl.NewsServiceImpl中实现对应的方法

@Override
public ResponseResult findWmNewsById(WmNewsDto dto) {if (dto == null || dto.getId() == null) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "文章ID不可缺少");}WmNews wmNews = wmNewsMapper.selectNewsDetailByPrimaryKey(dto.getId());if (wmNews == null) {return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");}ResponseResult responseResult = ResponseResult.okResult(wmNews);responseResult.setHost(fileServerUrl);return responseResult;
}

(3)NewsControllerApi

在类com.heima.media.apis.NewsControllerApi中增加summitNews、saveDraftNews方法

/**  * 根据id获取文章信息  * @param id  * @return  */ 
ResponseResult wmNews(@RequestBody WmNewsDto wmNews);

(4)NewsController

在com.heima.media.controller.v1.NewsController类中实现NewsControllerApi接口方法,调用对应的service接口即可。

@PostMapping("/news")  
@Override  
public ResponseResult wmNews(@RequestBody WmNewsDto dto) {  return newsService.findWmNewsById(dto);  
}

5.5 删除文章后台接口

5.5.1 接口定义

(1)基本定义

自从对于未发布的文章进行删除操作.

参考标准请参考通用接口规范
发布接口名称/api/v1/media/news/del_news
请求DTOcom.heima.model.media.dtos.WmNewsDto
响应DTO{ “host”:, “code”: 0, “error_message”: “操作成功”, “data”: {} }

(2)CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”),

5.5.2 Mapper实现

(1)WmNewsMapper

在类com.heima.model.mappers.wemedia.WmNewsMapper中定义以下方法

	WmNews selectByPrimaryKey(Integer id);int deleteByPrimaryKey(Integer id);

(2)WmNewsMapper.xml

在文件resources/mappers/wemedia/ WmNewsMapper.xml中增加对应SQL实现

delete from wm_newswhere id = #{id}

5.5.3 时序说明

  • 如果用户传递参数为空返回PARAM_REQUIRE错误

  • 根据id查询当前文章

  • 如果查询不到对应的文章则直接返回错误提示

  • 流程处理完成返回处理结果

5.5.4 代码实现

(1)NewsService

在类com.heima.media.service.NewsService:中定义delNews方法

/**  *** @param id  * @return  */  
ResponseResult delNews(WmNewsDto wmNews);

(2)NewsServiceImpl

在类com.heima.media.service.impl.NewsServiceImpl中实现接口中方法,此处需要注意不仅仅需要删除文章数据还需要删除文章资源关联数据

@Override
public ResponseResult delNews(WmNewsDto dto) {if (dto == null || dto.getId() == null) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}WmNews wmNews = wmNewsMapper.selectByPrimaryKey(dto.getId());if (wmNews == null) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "文章不存在");}//判断是否审核通过if (WmMediaConstans.WM_NEWS_AUTHED_STATUS.equals(wmNews.getStatus()) ||WmMediaConstans.WM_NEWS_PUBLISH_STATUS.equals(wmNews.getStatus())) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "当前文章已通过审核不可删除");}//删除文章素材关联表信息wmNewsMaterialMapper.delByNewsId(wmNews.getId());//删除文章信息wmNewsMapper.deleteByPrimaryKey(wmNews.getId());return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

(3)NewsControllerApi

在类com.heima.media.apis.NewsControllerApi中增加delNews方法

	/**  * 删除文章  * @param id  * @return  */  ResponseResult delNews(@RequestBody WmNewsDto wmNews);

(4)NewsController

在com.heima.media.controller.v1.NewsController类中实现NewsControllerApi接口方法,调用对应的service接口即可。

@PostMapping("/del_news")  
@Override  
public ResponseResult delNews(@RequestBody WmNewsDto dto) {  return newsService.delNews(dto);  
}

5.6 文章内容列表-前台

在内容列表界面中我们主要实现了对文章的检索功能。

5.6.1 接口定义

(1)删除文章

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_ARTICLES_DELETE = '/api/v1/media/news/del_news' //删除文章
  • 在src/api/content.js中定义请求方法(此处省略了引入刚才定义的常量,此后所有导入省略请自行导入需要的常量及方法)
export function deleteArticles (articlesId) {  return Request({  url:API_ARTICLES_DELETE ,  method:'post',  params:{},  data:{id:articlesId}  })  
}

(2)检索文章

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_SEARCHARTICELS = '/api/v1/media/news/list' //检索文章
  • 在src/api/content.js中定义请求方法
//搜索文章  
export function searchArticle (data) {  return Request({  url:API_SEARCHARTICELS,  method:'post',  data,  params:{}  })  
}

5.6.2 路由调整

在src/router.js中asyncRouterMap对象的children数组中增加以下改动,以满足全局自动记录路由的功能:

{  path:'/article/list',  component: () => import('./views/content/index.vue'),  
}

5.6.3 实现文章列表

(1)搜索工具组件定义

在src/views/content/components/中定义SearchTool.vue组件




(2)搜索结果组件定义

在src/views/content/components/中定义SearchResult.vue



(3)日期处理工具

在src/utils/中定义date.js日期处理工具

function FormatDate(){}
FormatDate.prototype= {formatDate:function(date, fmt) {if (/(y+)/.test(fmt)) {fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length))}let o = {'M+': date.getMonth() + 1,'d+': date.getDate(),'h+': date.getHours(),'m+': date.getMinutes(),'s+': date.getSeconds()}for (let k in o) {if (new RegExp(`(${k})`).test(fmt)) {let str = o[k] + ''fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : this.padLeftZero(str))}}return fmt},padLeftZero:function (str) {return ('00' + str).substr(str.length)},format10:function(time){return this.format13(time*1000);},format13:function(time){if(time==undefined){return ""}let date = new Date(time);return this.formatDate(date,'yyyy-MM-dd')},format13HH:function(time){if(time==undefined){return ""}let date = new Date(time);return this.formatDate(date,'yyyy-MM-dd hh:mm:ss')},// 最近几天时间getNearTime:function(AddDayCount) {var dd = new Date();return dd.getTime()-AddDayCount*24*3600000;},// 最近本周开始时间getWeekSTime:function() {var dd = new Date();dd.setDate(dd.getDate() -dd.getDay());return dd.getTime();},// 最近本周结束时间getWeekETime:function() {var dd = new Date();dd.setDate(dd.getDate() +(7-dd.getDay()));return dd.getTime();},diffTime:function(time){if(time.length==10){time = parseInt(time)*1000;}var nowDate = new Date().getTime(),oldDate = new Date(time).getTime(),diffTime = parseInt((nowDate - oldDate)/1000,10),oneMinute = 60,oneHour = 60 * oneMinute,oneDay = 24 * oneHour,oneMonth = 30 * oneDay,oneYear = 12 * oneMonth,compareArr = [oneYear,oneMonth,oneDay,oneHour,oneMinute],postfix = ['年前','个月前','天前','个小时前','分钟前','1分钟内'],diffYear,diffMonth,diffDay,diffHour,diffMinute,len=5;for(var i =0; i< len ;i++){var diff = Math.floor(diffTime/compareArr[i]);if(diff > 0){return diff + postfix[i];}else if(i === len -1 && diff === 0){return postfix[len];}}}
}
export  default new FormatDate()

(4)文章列表实现

在src/views/content/中定义index.vue, 具体代码如下:

  

6 图文和粉丝统计报表

6.1 图文统计后台接口

6.1.1 接口定义

(1)基本定义

图文统计涉及时间等查询条件,因此也需要请求DTO。

参考标准请参考通用接口规范
发布接口名称/api/v1/statistics/news
请求DTOcom.heima.model.media.dtos.StatisticDto
响应DTO{“host”:, “code”: 0, “error_message”: “操作成功”,“data”: {} }

(2)CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”),

6.1.2 Mapper实现

(1)WmNewsStatisticsMapper

创建类com.heima.model.mappers.wemedia.WmNewsStatisticsMapper:并定义findByTimeAndUserId根据时间和用户ID查询相关数据

public interface WmNewsStatisticsMapper {  List findByTimeAndUserId(String burst, Long userId,StatisticDto dto);  
}

WmNewsStatisticsMapper.xml

创建文件resources/mappers/wemedia/ WmNewsStatisticsMapper

                                id, user_id, article, read_count, comment, follow, collection, forward,likes, unlikes,  unfollow, created_time      

(2)根据id查询用户

在WmUserMapper接口新增方法

 WmUser selectById(Long id);

WmUserMapper.xml

 

6.1.3 时序说明

  • 如果用户传递参数为空返回PARAM_REQUIRE错误

  • 查询当前用户信息

  • 根据条件查询相应的数据

  • 流程处理完成返回处理结果

6.1.5 代码实现

(1)StatisticsService

创建类:com.heima.media.service.StatisticsService:

public interface StatisticsService {  /**  * 查找图文统计数据  * @param dto  * @return  */  ResponseResult findWmNewsStatistics(StatisticDto dto);  
}

(2)StatisticsServiceImpl

创建类:com.heima.media.service.impl.StatisticsServiceImpl

@Service
@SuppressWarnings("all")
public class StatisticsServiceImpl implements StatisticsService {@Autowiredprivate WmNewsStatisticsMapper wmNewsStatisticsMapper;@Autowiredprivate WmUserMapper wmUserMapper;@Overridepublic ResponseResult findWmNewsStatistics(StatisticDto dto) {ResponseResult responseResult = check(dto);if (responseResult != null){return responseResult;}WmUser wmUser = queryAllUserInfo();String burst = BurstUtils.groudOne(wmUser.getApUserId());returnResponseResult.okResult(wmNewsStatisticsMapper.findByTimeAndUserId(burst,wmUser.getApUserId(), dto));}private WmUser queryAllUserInfo() {WmUser user = WmThreadLocalUtils.getUser();user = wmUserMapper.selectById(user.getId());return user;}private ResponseResult check(StatisticDto dto) {if (dto == null && dto.getType() == null) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}if (WmMediaConstans.WM_NEWS_STATISTIC_CUR != dto.getType() &&(dto.getStime() == null || dto.getEtime() == null)) {return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}return null;}
}

(3)StatisticDto

创建类:com.heima.media.mysql.core.model.dtos.StatisticDto

此类在model模块中创建,定义请求入参,实现如下:

@Data  
public class StatisticDto {  
private Short type;  
private Date stime;  
private Date etime;  
private List time;  
}

(4)StatisticsControllerApi

在类com.heima.media.apis.StatisticsControllerApi中增加方法

public interface StatisticsControllerApi {  /**  * 文章数据* @param dto* @return*/  public ResponseResult newsData(StatisticDto dto);  
}

(5)StatisticsController

在com.heima.media.controller.v1.StatisticsController类中实现接口方法,调用对应的service接口即可。

@RestController  
@RequestMapping("/api/v1/statistics")  
public class StatisticsController implements StatisticsControllerApi {  @Autowired  private StatisticsService statisticsService;  @Override  @RequestMapping("/news")  public ResponseResult newsData(@RequestBody StatisticDto dto) {  return statisticsService.findWmNewsStatistics(dto);  }  
}

6.2 粉丝统计后台接口

6.2.1 接口定义

(1)基本定义

粉丝统计与

参考标准请参考通用接口规范
发布接口名称/api/v1/statistics/fans
请求DTOcom.heima.model.media.dtos.StatisticDto
响应DTO{ “host”:, “code”: 0, “error_message”: “操作成功”, “data”: {} }

(2)CODE定义

PARAM_INVALIDPARAM_INVALID(501,“无效参数”),

6.2.2 Mapper实现

(1)WmFansStatisticsMapper

创建类com.heima.model.mappers.wemedia.WmFansStatisticsMapper:

public interface WmFansStatisticsMapper {  List findByTimeAndUserId(String burst, Long userId,StatisticDto dto);  
}

(2)WmFansStatisticsMapper.xml

创建文件resources/mappers/wemedia/WmFansStatisticsMapper.xml

id, user_id, article, read_count, comment, follow, collection, forward, likes, unlikes,unfollow, created_time

6.2.3 时序说明

  • 如果用户传递参数为空返回PARAM_REQUIRE错误

  • 查询当前用户信息

  • 根据条件查询相应的数据

  • 流程处理完成返回处理结果

6.2.4 代码实现

(1)StatisticsService

在com.heima.media.service.StatisticsService中增加粉丝数据接口方法

/**  * 用户粉丝统计数据* @param dto* @return*/  
ResponseResult findFansStatistics(StatisticDto dto);  

(2)StatisticsServiceImpl

在com.heima.media.service.impl.StatisticsServiceImpl类中实现接口方法

@Autowired
private WmFansStatisticsMapper wmFansStatisticsMapper;@Override
public ResponseResult findFansStatistics(StatisticDto dto) {ResponseResult responseResult = check(dto);if (responseResult != null){return responseResult;}WmUser wmUser = queryAllUserInfo();Long userId = wmUser.getApUserId();String burst = BurstUtils.groudOne(userId);List datas =wmFansStatisticsMapper.findByTimeAndUserId(burst, userId, dto);return ResponseResult.okResult(datas);
}

(3)StatisticDto

创建类:com.heima.media.mysql.core.model.dtos.StatisticDto

此类在model模块中创建,定义请求入参,实现如下:

@Data  
public class StatisticDto {  private Short type;  private Date stime;  private Date etime;  private List time;  
}

(4)StatisticsControllerApi

在类com.heima.media.apis.StatisticsControllerApi中增加方法

/**  
* 粉丝数据*  
* @param dto*  
* @return*  
*/  
public ResponseResult fansData(@RequestBody StatisticDto dto);

(5)StatisticsController

在com.heima.media.controller.v1.StatisticsController类中实现接口方法,调用对应的service接口即可。

@Override  
@RequestMapping("/fans")  
public ResponseResult fansData(@RequestBody StatisticDto dto) {  return statisticsService.findFansStatistics(dto);  
}

6.3 图文数据前台开发

图文数据界面中我们主要实现了当前用户的图文数据的统计功能,并以图表的形式进行了展示。在这里的相关页面需要使用到echarts,需要在项目中安装echarts,后页面中导入使用。

6.3.1 接口定义

(1)查询图文数据

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const API_STATISTICS_NEWS = '/api/v1/statistics/news' //图文统计
  • 在src/api/content.js中定义请求方法(此处省略了引入刚才定义的常量,此后所有导入省略请自行导入需要的常量及方法)
//获取统计数据  
export function getNewsStatistics(data) {  return Request({  url:API_STATISTICS_NEWS,  method:'post',  params:{},  data:data  })  
}

(2)查询粉丝数据

  • 在src/constants/api.js中定义常量映射到后端请求地址
export const  API_GET_FANS_STATISTIC = '/api/v1/statistics/fans' //粉丝统计数据
  • 在src/api/fans.js中定义请求方法
//粉丝数据
export function getFansStatistics(data) {return Request({url: API_GET_FANS_STATISTIC,method: 'post',data})
}

5.4.2 路由调整

在src/router.js中asyncRouterMap对象的children数组中增加以下改动,以满足全局自动记录路由的功能:

    {  path:'/material/data',  component: () => import('./views/content/detail.vue'),  },{path:'/fans/index',component: () => import('./views/fans/index.vue'),}

5.4.3 实现图文数据

(1)统计组件定义

在src/views/content/components/中定义Statist.vue,
实现基本数据的展示,具体代码如下:


(2)线形图组件定义

在src/views/content/components/中定义LineChart.vue实现数据的线图展示功能:


(3)doughnut图表组件定义

在src/views/fans/components/中定义组件DoughnutChart.vue



(4)数据统计整体实现

在src/views/content/中定义组件detail.vue


5.4.4 实现粉丝概况

(1)统计组件定义

在src/views/fans/components/index/中定义组件Statist.vue


(2)线形图组件定义

在 src/views/fans/components/index/中定义LineChart.vue


(3)doughnut图表组件定义

在src/views/fans/components/index/中定义DoughnutChart.vue



(4)数据统计整体实现

在src/views/fans/中定义index.vue


相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
苏州离哪个飞机场近(苏州离哪个... 本篇文章极速百科小编给大家谈谈苏州离哪个飞机场近,以及苏州离哪个飞机场近点对应的知识点,希望对各位有...