Java教程

82-day05-自媒体人发布文章

本文主要是介绍82-day05-自媒体人发布文章,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

第五章 自媒体文章发布

目标

  • 完成自媒体文章列表查询功能
  • 完成自媒体文章的发布功能
  • 完成自媒体文章的查询
  • 完成自媒体文章的删除功能
  • 完成自媒体文章的上下架功能功能

1 自媒体文章列表查询

1.1 需求分析

1597905150395

如图所示:

需要展示自媒体发布的文章列表,并实现分页查询展示,而且需要根据关键字(文章的标题)、频道列表 和发布日期范围来进行分页查询

1.2 表结构

wm_news 自媒体文章表

1614409460717

频道表:

1614409488222

1.3 功能实现

1.3.1 自媒体文章分页查询

1.3.1.1 思路分析
页面点击搜索按钮之后 将选中的条件作为请求体传递给后台,后台根据条件进行分页查询即可
请求路径:/search
参数:分页条件封装对象
返回值:分页结果返回对象
1.3.1.2 功能实现

(1)创建dto来接收页面传递的数值

@Data
@Getter
@Setter
public class WmNewsDto extends WmNews {
    private LocalDateTime startTime;
    private LocalDateTime endTime;

}

1614411295090

(2)修改controller进行分页查询

@PostMapping("/searchPage")
public Result<PageInfo<WmNews>> findByPageDto(@RequestBody PageRequestDto<WmNewsDto> pageRequestDto){
    PageInfo<WmNews> pageInfo = wmNewsService.findByPageDto(pageRequestDto);
    return Result.ok(pageInfo);
}

1614411356515

service:

@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Override
    public PageInfo<WmNews> findByPageDto(PageRequestDto<WmNewsDto> pageRequestDto) {

        //select * from wm_news where status=? and title=? and user_id=当前的用户的ID and channl_id=? and publishedTime between ? and ? limit 0,10

        //1.获取请求当前的页码 和每页显示的行
        Long page = pageRequestDto.getPage();
        Long size = pageRequestDto.getSize();
        //2.获取请求体对象
        WmNewsDto body = pageRequestDto.getBody();
        QueryWrapper<WmNews> queryWrapper = new QueryWrapper<>();

        //添加查询当前用户的文章的列表
        String userInfo = RequestContextUtil.getUserInfo();
        queryWrapper.eq("user_id",Integer.valueOf(userInfo));

        //3.判断 是否为空 如果不为空 则拼接查询条件
        if(body!=null){
            if(!StringUtils.isEmpty(body.getStatus())){
                queryWrapper.eq("status",body.getStatus());
            }

            if(!StringUtils.isEmpty(body.getTitle())){
                queryWrapper.like("title",body.getTitle());
            }

            if(!StringUtils.isEmpty(body.getChannelId())){
                queryWrapper.eq("channel_id",body.getChannelId());
            }

            if(!StringUtils.isEmpty(body.getStartTime()) && !StringUtils.isEmpty(body.getEndTime())){
                queryWrapper.between("publish_time", body.getStartTime(),body.getEndTime());
            }
        }


        //4.执行分页查询
        IPage<WmNews> page1 = new Page<WmNews>(page,size);

        IPage<WmNews> page2 = page(page1, queryWrapper);

        //5.封装结果

        return new PageInfo<WmNews>(page2.getCurrent(),page2.getSize(), page2.getTotal(),page2.getPages(),page2.getRecords());
    }
}
1.3.2.3 测试

1614411239069

1.3.2 频道列表查询

1.3.2.1 思路分析

​ 实际上频道列表 需要在页面展示下拉框我们可以直接使用现有的admin微服务中的查询所有的频道列表即可,因为数据量不大,可以直接列出来即可,然后通过自媒体网关路由转发到admin微服务即可。

1.3.2.2 功能实现

目前已经实现了在抽象类中,所以直接关联网关即可。

1614411671329

1614411682570

网关配置:

1614411775809

1.3.2.3 测试

为了测试方便直接在本地访问路径,(当然也可以结合网关进行测试)

1614411903645

1.3.3 网关路由规则配置

1620440746762

结合网关测试:

(1)先启动微服务 和网关 (自媒体微服务 admin微服务 自媒体网关)登录

1620440778531

(2)测试获取频道列表: token从上一节登录之后获取

1620440835469

(3)测试文章分页列表查询:

1620440893433

3 自媒体文章-发布、修改,保存草稿

3.1 需求分析

总体上的需求:

原型:需求可以参考原型,但是会有稍微的变化

项目原型-HTML(使用火狐浏览器打开)/[原型图]_前台_黑马头条_V1.0/index.html#g=1

1614412121782

弹出窗口的图如下:有两种:1 选择现有素材作为 2 重新上传图片

(图3)

1614413072769

流程说明如下:

1.点击发布文章,添加标题 添加内容
2.添加内容有两种 一种是 纯文本  一种是是图片
3.点击文本的时候 弹出窗口直接添加文本 点击确定即可
4.点击图片的时候 弹出窗口 如上图片 图三表示 可以从素材库里面选择一张图,或者自己直接上传一张图 作为内容的一张图片
5.选择标签 频道 和发布定时时间
6.选择封面 选择单图 或者 多图 或者无图或者自动   需要弹出窗口图三那里进行 选择或者上传 多图需要上传3张
7.点击保存草稿或者直接提交

3.2 思路分析

涉及到的表如下:

1614568403844

1614568417349

思路分析:

这个其实也很简单。
发布文章的本质就是像wm_news文章表进行插入一条记录而已。
这里比较特殊的点在于 可以选择素材 所以需要弹出窗口查询素材 并选中之后将素材对应的图片的路径作为参数请求 传递给后台保存到mw_news文章表中存储起来
还有就是封面的无图 单图 自动 需要进行判断,自动的时候,需要判断当前的内容中是否有图片,如图有图片,则判断有几张,如果小于2 则作为单图 如果小于1 则作为无图,如果大于2 则作为多图即可。

实现步骤:

实现弹窗 获取素材列表--》 【已经实现】 直接调用分页搜索的请求路径即可
实现上传图片---》【已经实现】 直接调用dfs的请求路径即可
封面的单图 多图 选择图片上传--》就是弹窗功能已经实现

实现发表文章功能:

请求:/wmNews/save/{isSubmit}     POST
参数:2个  
	是否为提交  isSubitm    值为 1 或者 0  1标识为提交 0 标识为保存草稿 
    请求体对象  
返回值:result 成功与否即可    

前端应当传递的请求体对象数据为如下案例:

{
    "title": "黑马头条项目背景",
    "type": "1",
    "labels": "黑马头条",
    "publishTime": "2020-03-14T11:35:49.000Z",
    "channelId": 1,    
    "images": [
        "http://192.168.200.130/group1/M00/00/00/wKjIgl5swbGATaSAAAEPfZfx6Iw790.png"
    ],
    "status": 1,
    "content": [
        {
            "type": "text",
            "value": "随着智能手机的普及,人们更加习惯于通过手机来看新闻。由于生活节奏的加快,很多人只能利用碎片时间来获取信息,因此,对于移动资讯客户端的需求也越来越高。黑马头条项目正是在这样背景下开发出来。黑马头条项目采用当下火热的微服务+大数据技术架构实现。本项目主要着手于获取最新最热新闻资讯,通过大数据分析用户喜好精确推送咨询新闻"
        },
        {
            "type": "image",
            "value": "http://192.168.200.130/group1/M00/00/00/wKjIgl5swbGATaSAAAEPfZfx6Iw790.png"
        }
    ]
}

解释 :

type  : 指定为封面类型  0 是无图  1 是单图  3 是多图  -1 是自动
images: 指定为封面图片  以逗号分隔的图片路径
status: 自媒体文章的状态 0 保存草稿  1 提交(待审核).....  这个字段前端不必传递

3.3 功能实现

(1)创建dto 用来接收页面传递过来的请求体

@Data
@Getter
@Setter
public class ContentNode {
    //type 指定类型  text 标识文本   image 标识 图片
    private String type;
    //value 指定内容
    private String value;

}

@Data
@Getter
@Setter
public class WmNewsDtoSave {
    //主键ID
    private Integer id;

    //文章标题
    private String title;

    //图文内容
    private List<ContentNode> content;

    //指定为封面类型  0 是无图  1 是单图  3 是多图  -1 是自动
    private Integer type;

    //指定选中的频道ID
    private Integer channelId;

    //指定标签
    private String labels;

    //状态 0 草稿  1 提交 待审核 (该字段可以不用设置,前端不必传递)
    private Integer status;

    //定时发布时间
    private LocalDateTime publishTime;

    //封面图片
    private List<String> images;
}

1614577714735

(2)编写controller

//保存自媒体文章 保存草稿 和 添加 或者修改
@PostMapping("/save/{isSubmit}")
public Result save(@PathVariable(name="isSubmit") Integer isSubmit,@RequestBody WmNewsDtoSave wmNewsDtoSave){
    if(StringUtils.isEmpty(isSubmit) || wmNewsDtoSave==null){
        return Result.errorMessage("数据不能为空");
    }
    if(isSubmit>1 || isSubmit<0){
        return Result.errorMessage("isSubmit的值有误");
    }
    wmNewsService.save(wmNewsDtoSave,isSubmit);
    return Result.ok();
}

1614577777751

(3)编写service 实现类

@Autowired
    private WmNewsMapper wmNewsMapper;

    //保存自媒体文章信息
    @Override
    public void save(WmNewsDtoSave wmNewsDtoSave, Integer isSubmit) {
        WmNews wmNews = new WmNews();
        //copy数据
        BeanUtils.copyProperties(wmNewsDtoSave, wmNews);
        //补充设置数据
        //设置登录的用户ID
        wmNews.setUserId(Integer.valueOf(RequestContextUtil.getUserInfo()));
        //设置成JSON 字符串到数据库中
        wmNews.setContent(JSON.toJSONString(wmNewsDtoSave.getContent()));

        //设置封面图片 将list 转成一个以逗号分隔的字符串
        if (wmNewsDtoSave.getImages() != null && wmNewsDtoSave.getImages().size() > 0) {
            wmNews.setImages(String.join(",", wmNewsDtoSave.getImages()));
        }

        //如果是自动图  则判断 图文内容中的图片有多少张,如果是>2 则为多图 如果是1 则为单图 如果是小于1 则为 无图
        if (wmNewsDtoSave.getType() == -1) {
            List<String> imagesFromContent = getImagesFromContent(wmNewsDtoSave);
            //说明是多图
            if (imagesFromContent.size() > 2) {
                //设置为多图
                wmNews.setType(3);
                //并设置图片 因为页面没有传递了
                wmNews.setImages(String.join(",", imagesFromContent));
            } else if (imagesFromContent.size() > 0 && imagesFromContent.size() <= 2) {
                //设置为单图
                wmNews.setType(1);
                //设置图片为一张
                wmNews.setImages(imagesFromContent.get(0));
            } else {
                //无图
                wmNews.setType(0);
                //空字符串
                wmNews.setImages("");
            }

        }
        //保存草稿或者提交审核
        wmNews.setStatus(isSubmit);
        if (isSubmit == 1) {
            wmNews.setSubmitedTime(LocalDateTime.now());
        }
        //修改数据
        if (wmNewsDtoSave.getId() != null) {
            wmNewsMapper.updateById(wmNews);
        } else {
            //添加数据
            wmNews.setCreatedTime(LocalDateTime.now());
            wmNewsMapper.insert(wmNews);
        }
    }

    //获取图片路径列表
    private List<String> getImagesFromContent(WmNewsDtoSave wmNewsDtoSave) {
        List<ContentNode> content = wmNewsDtoSave.getContent();
        List<String> images = new ArrayList<String>();
        for (ContentNode contentNode : content) {
            //图片
            if (contentNode.getType().equals("image")) {
                String value = contentNode.getValue();
                images.add(value);
            }
        }
        return images;
    }

(4)测试:

先启动网关和自媒体微服务 并先登录

1614577986125

登录好了之后进行添加保存的操作:

1614578048566

1614578066875

测试数据参考思路分析里面的请求体数据。

3.4 优化

当保存之后需要将生成的主键返回

操作如下:

实现类中 修改如下:

1620458812065

接口修改:

1620458830849

controller修改如下:

1620458851192

4 自媒体文章-根据id查询

4.1 需求分析

1597836104065

点击修改的时候,就是根据文章id查询,跳转至编辑页面进行展示

1597836145040

4.2 思路分析

点击编辑 先根据文章的ID 获取到文章的数据,要注意的是,需要返回的数据不是数据库对应的实体对象,而是刚才我们定义的dto的对象。因为编辑的时候需要用到该数据

4.3 功能实现

(1)修改controller

@GetMapping("/one/{id}")
public Result<WmNewsDtoSave> getById(@PathVariable(name="id")Integer id){
    WmNewsDtoSave wmNewsDtoSave = wmNewsService.getDtoById(id);
    return Result.ok(wmNewsDtoSave);
}

1614579030125

(2)service实现类

@Override
public WmNewsDtoSave getDtoById(Integer id) {
    WmNews wmNews = wmNewsMapper.selectById(id);

    if(wmNews!=null){
        WmNewsDtoSave wmNewsDtoSave = new WmNewsDtoSave();
        BeanUtils.copyProperties(wmNews,wmNewsDtoSave);
        //设置内容
        String content = wmNews.getContent();
        List<ContentNode> contentNodes = JSON.parseArray(content, ContentNode.class);
        wmNewsDtoSave.setContent(contentNodes);
        //设置图片
        String images = wmNews.getImages();
        if(!StringUtils.isEmpty(images)){
            //设置图片列表
            wmNewsDtoSave.setImages(Arrays.asList(images.split(",")));
        }
        return wmNewsDtoSave;
    }
    return null;
}

(3)测试 通过网关测试(注意:测试也可以不用通过网关)

1614579104534

5 自媒体文章-删除

5.1 需求分析

1597836189478

5.2 思路分析

当文章状态为9 并且已上架的数据 不能删除。 如果是其他的状态可以删除。
删除之后需要同步数据到 APP文章中,该状态待做。
前端发送请求到后台 后台做逻辑判断处理即可

1614579363121

5.3 功能实现

controller 实现即可:

@Override
@DeleteMapping("/{id}")
public Result deleteById(@PathVariable(name = "id") Serializable id) {
    WmNews wmNews = wmNewsService.getById(id);
    if (wmNews == null) {
        return Result.errorMessage("不存在的文章");
    }
    Integer enable = wmNews.getEnable();
    Integer status = wmNews.getStatus();
    //已发布 且上架
    if (status == 9 && enable == 1) {
        return Result.errorMessage("已发布 且上架 不能删除");
    }
    wmNewsService.removeById(id);
    return Result.ok();
}

1614579727735

6 自媒体文章-上架、下架

6.1 需求分析

1597836253759

1597836273736

6.2 思路分析

当前已经发布(状态为9)的文章可以上架(enable = 1),也可以下架(enable = 0)
在上架和下架操作的同时,需要同步app端的文章配置信息,暂时不做,后期讲到审核文章的时候再优化

6.3 功能实现

修改controller 添加一个方法进行上架和下架。

@PutMapping("/upOrDown/{id}/{enable}")
public Result updateUpDown(@PathVariable(name = "id") Serializable id,@PathVariable(name="enable")Integer enable) {
    WmNews wmNews = wmNewsService.getById(id);
    if (wmNews == null) {
        return Result.errorMessage("不存在的文章");
    }

    Integer status = wmNews.getStatus();
    //已发布 且上架
    if (status != 9) {
        return Result.errorMessage("文章没发布,不能上下架");
    }
    if(enable>1 || enable<0){
        return Result.errorMessage("错误的数字范围 只能是0,1");
    }
    wmNews.setEnable(enable);
    wmNewsService.updateById(wmNews);
    return Result.ok();
}

1614580221565

7 优化登录和续约(扩展)

7.1 思路

以自媒体微服务为例

1634968072856

1.先登录。产生两个令牌 一个令牌为访问令牌 一个为刷新令牌
2.访问令牌 访问时 当过期之后,返回状态码403 表示需要刷新令牌
3.用户再发送请求,将之前产生的刷新令牌 传递到后台,后台校验通过之后,返回新的两个令牌

7.2 实现

1 创建如下类,参考资料中的类直接copy过来

1634970024996

参考类如下:

1634970079846

2 修改登录方法

controller:

1634970137997

@PostMapping("/login")
public Result<TokenJsonVo> login(@RequestBody LoginVo loginVo) throws LeadNewsException {
    TokenJsonVo tokenJsonVo = wmUserService.login(loginVo);
    return Result.ok(tokenJsonVo);
}

VO所在位置:

@Data
public class LoginVo {

    //登录用的用户名
    private String username;
    //登录用的密码
    private String password;
}

1638345221241

service接口及实现类:

public TokenJsonVo login(LoginVo loginVo) throws LeadNewsException;
@Override
public TokenJsonVo login(LoginVo loginVo) throws LeadNewsException {
    //1.校验数据是否为空   判断  如果为空直接报错
    if (StringUtils.isEmpty(loginVo.getUsername()) || StringUtils.isEmpty(loginVo.getPassword())) {
        throw new LeadNewsException("用户名和密码不能为null");
    }
    //2.要根据 用户名 获取 数据库中的用户的信息  判断  如果没有数据 直接报错  select * from ad_user where name=?

    QueryWrapper<WmUser> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("name", loginVo.getUsername());
    WmUser wmUserFromDb = wmUserMapper.selectOne(queryWrapper);
    if (wmUserFromDb == null) {
        throw new LeadNewsException("用户名或密码错误");
    }
    //3.根据 数据库中的密码(密文) 和 【页面传递过来的密码(明文)+salt--->md5加密之后的密文】 对比  如果不成功 报错

    String passwordFromWeb = DigestUtils.md5DigestAsHex((loginVo.getPassword() + wmUserFromDb.getSalt()).getBytes());
    String passwordFromDb = wmUserFromDb.getPassword();
    if (!passwordFromDb.equals(passwordFromWeb)) {
        throw new LeadNewsException("用户名或密码错误");
    }

    //4.生成令牌 组装数据返回
    UserTokenInfo userTokenInfo = new UserTokenInfo(Long.valueOf(wmUserFromDb.getId()),
            wmUserFromDb.getImage(),
            wmUserFromDb.getNickname(),
            wmUserFromDb.getName(),
            TokenRole.ROLE_MEDIA);
    TokenJsonVo token = JwtUtil.createToken(userTokenInfo);
    return token;
}

3 刷新令牌controller:

1634970340096

@PostMapping("/refreshToken")
    public Result refreshToken(@RequestBody Map<String, String> map) {
        String refreshToken = map.get("refreshToken");

        UserTokenInfoExp userTokenInfoExp = null;
        try {
            userTokenInfoExp = JwtUtil.parseJwtUserToken(refreshToken);

            Long exp = userTokenInfoExp.getExp();
            long now = System.currentTimeMillis();
            long chazhi = exp - now;

            //续约的要求是: 必须在 访问令牌的过期时间点 到 刷新令牌的过期时间点 之间  防止 出现过久的令牌来恶意刷新令牌
            if(chazhi>(JwtUtil.TOKEN_TIME_OUT*1000)){
                return Result.errorMessage("令牌续约时间不在有效范围之内");
            }

        } catch (Exception e) {
            return Result.errorMessage("令牌错误");
        }
        if (JwtUtil.isExpire(userTokenInfoExp)) {
            return Result.errorMessage("令牌错误");
        }
        TokenJsonVo token = JwtUtil.createToken(userTokenInfoExp);
        return Result.ok(token);
    }

4 网关过滤器实现

@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {

    //获取用户携带的token令牌  解析校验 校验通过放行 不通过 返回错误
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取请求对象 和 响应对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();


        //1.5 如果请求的路径是 自媒体登录【白名单】 放行即可
        String path = request.getURI().getPath();
        if(path.startsWith("/media/wmUser/login") ||path.startsWith("/media/wmUser/refreshToken") || path.endsWith("v2/api-docs")){
            return chain.filter(exchange);
        }


        //2.从请求头中获取访问令牌数据
        String token = request.getHeaders().getFirst("token");
        //3.判断 是否为空 如果为空 返回错误 401
        if(StringUtils.isEmpty(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        //4.校验令牌是否正确了 如果不是  返回错误 401
        try {
            UserTokenInfoExp userTokenInfoExp = JwtUtil.parseJwtUserToken(token);
            if(!JwtUtil.isValidRole(userTokenInfoExp, TokenRole.ROLE_MEDIA)){
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
            //直接返回 表示需要续约
            if(JwtUtil.isExpire(userTokenInfoExp)){
                //403
                response.setStatusCode(HttpStatus.FORBIDDEN);
                return response.setComplete();
            }


            //URL编码 否则有乱码产生
            String encode = URLEncoder.encode(JSON.toJSONString(userTokenInfoExp), "UTF-8");
            //将信息传递给下游微服务
            request.mutate().header(SystemConstants.USER_HEADER_NAME, encode);

        } catch (Exception e) {
            e.printStackTrace();
            //直接错误
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        return chain.filter(exchange);
    }

    //值越低 优先级越高 优先被执行
    @Override
    public int getOrder() {
        return -10;
    }
}

5 工具类中修改原来的请求头获取用户ID的方法:

public class RequestContextUtil {
    /**
     * 获取访问令牌信息解析之后的信息
     *
     * @return
     */
    public static UserTokenInfoExp getRequestUserTokenInfo() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String json = null;
        try {
            //解码
            json = URLDecoder.decode(request.getHeader(SystemConstants.USER_HEADER_NAME), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        UserTokenInfoExp userTokenInfoExp = JSON.parseObject(json, UserTokenInfoExp.class);

        return userTokenInfoExp;
    }


    //判断是否为匿名用户
    public static boolean isAnonymous() {
        return TokenRole.ROLE_ANONYMOUS == getRequestUserTokenInfo().getRole();
    }

    //获取用户ID值
    public static Integer getUserId() {
        return getRequestUserTokenInfo().getUserId().intValue();
    }


}

修改原来添加素材的代码:

1634972161432

  @Override
    @PostMapping
    public Result<WmMaterial> insert(@RequestBody WmMaterial record) {//该方法 叫:处理器handler
        Integer userId = RequestContextUtil.getUserId();
        //设置User_id的值为当前登录的自媒体的用户的ID
        record.setType(0);//图片
        record.setIsCollection(0);
        record.setCreatedTime(LocalDateTime.now());
        //一定是当前登录自媒体的用户的ID
        record.setUserId(userId);
        wmMaterialService.save(record);
        return Result.ok();
    }

修改 发布文章的代码:

1634972505895

7.3 测试

测试双令牌登录:

(1)为了测试过期 我们修改过期时间为30S

1638345015130

(2)测试登录

1638344790405

测试携带令牌访问:

1638345071355

(3)等过30S的时候再刷新令牌:

1638345110029

这篇关于82-day05-自媒体人发布文章的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!