#前后端分离# 头条发布系统

头条业务简介

  • 用户功能
    • 注册功能
    • 登录功能
    • jwt实现
  • 新闻
    • 新闻的分页浏览
    • 通过标题关键字搜索新闻
    • 查看新闻详情
    • 新闻的修改和删除

预览界面
在这里插入图片描述

开源上线

https://gitcode.net/NVG_Haru/NodeJS_5161447

数据库设计

在这里插入图片描述

数据库脚本

CREATE DATABASE sm_db;USE sm_db;SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for news_headline
-- ----------------------------
DROP TABLE IF EXISTS `news_headline`;
CREATE TABLE `news_headline`  (`hid` INT NOT NULL AUTO_INCREMENT COMMENT '头条id',`title` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头条标题',`article` VARCHAR(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头条新闻内容',`type` INT NOT NULL COMMENT '头条类型id',`publisher` INT NOT NULL COMMENT '头条发布用户id',`page_views` INT NOT NULL COMMENT '头条浏览量',`create_time` DATETIME(0) NULL DEFAULT NULL COMMENT '头条发布时间',`update_time` DATETIME(0) NULL DEFAULT NULL COMMENT '头条最后的修改时间',`version` INT DEFAULT 1 COMMENT '乐观锁',`is_deleted` INT DEFAULT 0 COMMENT '头条是否被删除 1 删除  0 未删除',PRIMARY KEY (`hid`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Table structure for news_type
-- ----------------------------
DROP TABLE IF EXISTS `news_type`;
CREATE TABLE `news_type`  (`tid` INT NOT NULL AUTO_INCREMENT COMMENT '新闻类型id',`tname` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '新闻类型描述',`version` INT DEFAULT 1 COMMENT '乐观锁',`is_deleted` INT DEFAULT 0 COMMENT '头条是否被删除 1 删除  0 未删除',PRIMARY KEY (`tid`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;-- ----------------------------
-- Table structure for news_user
-- ----------------------------
DROP TABLE IF EXISTS `news_user`;
CREATE TABLE `news_user`  (`uid` INT NOT NULL AUTO_INCREMENT COMMENT '用户id',`username` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户登录名',`user_pwd` VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户登录密码密文',`nick_name` VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户昵称',`version` INT DEFAULT 1 COMMENT '乐观锁',`is_deleted` INT DEFAULT 0 COMMENT '头条是否被删除 1 删除  0 未删除',PRIMARY KEY (`uid`) USING BTREE,UNIQUE INDEX `username_unique`(`username`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;SET FOREIGN_KEY_CHECKS = 1;

用户模块开发

jwt和token介绍

令牌(Token):在计算机领域,令牌是一种代表某种访问权限或身份认证信息的令牌。它可以是一串随机生成的字符或数字,用于验证用户的身份或授权用户对特定资源的访问。普通的令牌可能以各种形式出现,如访问令牌、身份令牌、刷新令牌等

在这里插入图片描述

简单理解 : 每个用户生成的唯一字符串标识,可以进行用户识别和校验

优势: token验证标识无法直接识别用户的信息,盗取token后也无法登录程序! 相对安全!

JWT(JSON Web Token)是具体可以生成,校验,解析等动作Token的技术(实现类)

在这里插入图片描述

jwt 工作流程

  • 用户提供其凭据(通常是用户名和密码)进行身份验证。
  • 服务器对这些凭据进行验证,并在验证成功后创建一个JWT。
  • 服务器将JWT发送给客户端,并客户端在后续的请求中将JWT附加在请求头或参数中。
  • 服务器接收到请求后,验证JWT的签名和有效性,并根据JWT中的声明进行身份验证和授权操作

jwt 数据组成和包含信息

JWT由三部分组成: header(头部).payload(载荷).signature(签名)
在这里插入图片描述

我们需要理解的是, jwt 可以携带很多信息! 一般情况,需要加入:有效时间,签名秘钥,其他用户标识信息!

有效时间为了保证 token 的时效性,过期可以重新登录获取!

签名秘钥为了防止其他人随意解析和校验 token 数据!

用户信息为了我们自己解析的时候,知道Token对应的具体用户!

jwt 使用和测试

导入依赖

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency><dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.0</version>
</dependency>

编写配置 application.yaml

#jwt配置
jwt:token:tokenExpiration: 120 #有效时间,单位分钟tokenSignKey: headline123456  #当前程序签名秘钥 自定义

导入工具类

封装jwt技术工具类

package com.atguigu.utils;import com.alibaba.druid.util.StringUtils;
import io.jsonwebtoken.*;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;import java.util.Date;@Data
@Component
@ConfigurationProperties(prefix = "jwt.token")
public class JwtHelper {private  long tokenExpiration; //有效时间,单位毫秒 1000毫秒 == 1秒private  String tokenSignKey;  //当前程序签名秘钥//生成token字符串public  String createToken(Long userId) {System.out.println("tokenExpiration = " + tokenExpiration);System.out.println("tokenSignKey = " + tokenSignKey);String token = Jwts.builder().setSubject("YYGH-USER").setExpiration(new Date(System.currentTimeMillis() + tokenExpiration*1000*60)) //单位分钟.claim("userId", userId).signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();return token;}//从token字符串获取useridpublic  Long getUserId(String token) {if(StringUtils.isEmpty(token)) return null;Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);Claims claims = claimsJws.getBody();Integer userId = (Integer)claims.get("userId");return userId.longValue();}//判断token是否有效public  boolean isExpiration(String token){try {boolean isExpire = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getExpiration().before(new Date());//没有过期,有效,返回falsereturn isExpire;}catch(Exception e) {//过期出现异常,返回truereturn true;}}
}

使用和测试

@org.springframework.boot.test.context.SpringBootTest
public class SpringBootTest {@Autowiredprivate JwtHelper jwtHelper;@Testpublic void test(){//生成 传入用户标识String token = jwtHelper.createToken(1L);System.out.println("token = " + token);//解析用户标识int userId = jwtHelper.getUserId(token).intValue();System.out.println("userId = " + userId);//校验是否到期! false 未到期 true到期boolean expiration = jwtHelper.isExpiration(token);System.out.println("expiration = " + expiration);}}
登录功能实现

需求描述
用户在客户端输入用户名密码并向后端提交,后端根据用户名和密码判断登录是否成功,用户有误或者密码有误响应不同的提示信息!

接口描述

url地址: user/login
请求方式:POST
请求参数:

{"username":"zhangsan", //用户名"userPwd":"123456"     //明文密码
}

响应成功

{"code":"200",         // 成功状态码 "message":"success"   // 成功状态描述"data":{"token":"... ..." // 用户id的token}
}

失败

{"code":"501","message":"用户名有误""data":{}
}
{"code":"503","message":"密码有误""data":{}
}

实现代码 controller

@RestController
@RequestMapping("user")
@CrossOrigin
public class UserController {@Autowiredprivate UserService userService;/*** 登录需求* 地址: /user/login* 方式: post* 参数:*    {*     "username":"zhangsan", //用户名*     "userPwd":"123456"     //明文密码*    }* 返回:*   {*    "code":"200",         // 成功状态码*    "message":"success"   // 成功状态描述*    "data":{*         "token":"... ..." // 用户id的token*     }*  }** 大概流程:*    1. 账号进行数据库查询 返回用户对象*    2. 对比用户密码(md5加密)*    3. 成功,根据userId生成token -> map key=token value=token值 - result封装*    4. 失败,判断账号还是密码错误,封装对应的枚举错误即可*/@PostMapping("login")public Result login(@RequestBody User user){Result result = userService.login(user);System.out.println("result = " + result);return result;}}

service

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService{@Autowiredprivate JwtHelper jwtHelper;@Autowiredprivate  UserMapper userMapper;/*** 登录业务实现* @param user* @return result封装*/@Overridepublic Result login(User user) {//根据账号查询LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUsername,user.getUsername());User loginUser = userMapper.selectOne(queryWrapper);//账号判断if (loginUser == null) {//账号错误return Result.build(null, ResultCodeEnum.USERNAME_ERROR);}//判断密码if (!StringUtils.isEmpty(user.getUserPwd())&& loginUser.getUserPwd().equals(MD5Util.encrypt(user.getUserPwd()))){//账号密码正确//根据用户唯一标识生成tokenString token = jwtHelper.createToken(Long.valueOf(loginUser.getUid()));Map data = new HashMap();data.put("token",token);return Result.ok(data);}//密码错误return Result.build(null,ResultCodeEnum.PASSWORD_ERROR);}
}
根据token获取用户数据

需求描述

客户端发送请求,提交token请求头,后端根据token请求头获取登录用户的详细信息并响应给客户端进行存储

接口描述

url地址:user/getUserInfo
请求方式:GET
请求头:token: token内容

成功

{"code": 200,"message": "success","data": {"loginUser": {"uid": 1,"username": "zhangsan","userPwd": "","nickName": "张三"}}
}

失败

{"code": 504,"message": "notLogin","data": null
}

代码实现controller

/*** 地址: user/getUserInfo* 方式: get* 请求头: token = token内容* 返回:*    {*     "code": 200,*     "message": "success",*     "data": {*         "loginUser": {*             "uid": 1,*             "username": "zhangsan",*             "userPwd": "",*             "nickName": "张三"*         }*      }*   }** 大概流程:*    1.获取token,解析token对应的userId*    2.根据userId,查询用户数据*    3.将用户数据的密码置空,并且把用户数据封装到结果中key = loginUser*    4.失败返回504 (本次先写到当前业务,后期提取到拦截器和全局异常处理器)*/
@GetMapping("getUserInfo")
public Result userInfo(@RequestHeader String token){Result result = userService.getUserInfo(token);return result;
}

service

/*** 查询用户数据* @param token* @return result封装*/
@Override
public Result getUserInfo(String token) {//1.判定是否有效期if (jwtHelper.isExpiration(token)) {//true过期,直接返回未登录return Result.build(null,ResultCodeEnum.NOTLOGIN);}//2.获取token对应的用户int userId = jwtHelper.getUserId(token).intValue();//3.查询数据User user = userMapper.selectById(userId);if (user != null) {user.setUserPwd(null);Map data = new HashMap();data.put("loginUser",user);return Result.ok(data);}return Result.build(null,ResultCodeEnum.NOTLOGIN);
}
注册用户名检查
  1. 需求描述
    用户在注册时输入用户名时,立刻将用户名发送给后端,后端根据用户名查询用户名是否可用并做出响应

  2. 接口描述

    url地址:user/checkUserName
    请求方式:POST
    请求参数:param形式 username=zhangsan

成功

{"code":"200","message":"success""data":{}
}

失败

{"code":"505","message":"用户名占用""data":{}
}

代码实现controller

/*** url地址:user/checkUserName* 请求方式:POST* 请求参数:param形式* username=zhangsan* 响应数据:* {*    "code":"200",*    "message":"success"*    "data":{}* }** 实现步骤:*   1. 获取账号数据*   2. 根据账号进行数据库查询*   3. 结果封装*/
@PostMapping("checkUserName")
public Result checkUserName(String username){Result result = userService.checkUserName(username);return result;
}

service

/*** 检查账号是否可以注册** @param username 账号信息* @return*/
@Override
public Result checkUserName(String username) {LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUsername,username);User user = userMapper.selectOne(queryWrapper);if (user != null){return Result.build(null,ResultCodeEnum.USERNAME_USED);}return Result.ok(null);
}
用户注册功能

需求描述
客户端将新用户信息发送给服务端,服务端将新用户存入数据库,存入之前做用户名是否被占用校验,校验通过响应成功提示,否则响应失败提示

接口描述

url地址:user/regist
请求方式:POST

请求参数:

{"username":"zhangsan","userPwd":"123456", "nickName":"张三"
}

成功回复

{"code":"200","message":"success""data":{}
}

失败回复

{"code":"505","message":"用户名占用""data":{}
}

controller

/**
* url地址:user/regist
* 请求方式:POST
* 请求参数:
* {
*     "username":"zhangsan",
*     "userPwd":"123456",
*     "nickName":"张三"
* }
* 响应数据:
* {
*    "code":"200",
*    "message":"success"
*    "data":{}
* }
*
* 实现步骤:
*   1. 将密码加密
*   2. 将数据插入
*   3. 判断结果,成 返回200 失败 505
*/@PostMapping("regist")
public Result regist(@RequestBody User user){Result result = userService.regist(user);return result;
}

service

@Override
public Result regist(User user) {LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUsername,user.getUsername());Long count = userMapper.selectCount(queryWrapper);if (count > 0){return Result.build(null,ResultCodeEnum.USERNAME_USED);}user.setUserPwd(MD5Util.encrypt(user.getUserPwd()));int rows = userMapper.insert(user);System.out.println("rows = " + rows);return Result.ok(null);
}

首页模块开发

查询首页分类

需求描述
进入新闻首页,查询所有分类并动态展示新闻类别栏位

接口描述

    url地址:portal/findAllTypes请求方式:get请求参数:无

成功

{"code":"200","message":"OK""data":{[{"tid":"1","tname":"新闻"},{"tid":"2","tname":"体育"},{"tid":"3","tname":"娱乐"},{"tid":"4","tname":"科技"},{"tid":"5","tname":"其他"}]}
}

代码实现controller

@RestController
@RequestMapping("portal")
@CrossOrigin
public class PortalController {@Autowiredprivate TypeService typeService;/*** 查询全部类别信息* @return*/@GetMapping("findAllTypes")public Result findAllTypes(){//直接调用业务层,查询全部数据List<Type> list = typeService.list();return  Result.ok(list);}
}
分页查询首页头条信息

需求描述

  • 客户端向服务端发送查询关键字,新闻类别,页码数,页大小
  • 服务端根据条件搜索分页信息,返回含页码数,页大小,总页数,总记录数,当前页数据等信息,并根据时间降序,浏览量降序排序

接口描述

    url地址:portal/findNewsPage请求方式:post请求参数:
{"keyWords":"马斯克", // 搜索标题关键字"type":0,           // 新闻类型"pageNum":1,        // 页码数"pageSize":10     // 页大小
}

成功

{"code":"200","message":"success""data":{"pageInfo":{"pageData":[{"hid":"1",                     // 新闻id "title":"尚硅谷宣布 ... ...",   // 新闻标题"type":"1",                    // 新闻所属类别编号"pageViews":"40",              // 新闻浏览量"pastHours":"3" ,              // 发布时间已过小时数"publisher":"1"                // 发布用户ID},{"hid":"1",                     // 新闻id "title":"尚硅谷宣布 ... ...",   // 新闻标题"type":"1",                    // 新闻所属类别编号"pageViews":"40",              // 新闻浏览量"pastHours":"3",              // 发布时间已过小时数"publisher":"1"                // 发布用户ID},{"hid":"1",                     // 新闻id "title":"尚硅谷宣布 ... ...",   // 新闻标题"type":"1",                    // 新闻所属类别编号"pageViews":"40",              // 新闻浏览量"pastHours":"3",               // 发布时间已过小时数"publisher":"1"                // 发布用户ID}],"pageNum":1,    //页码数"pageSize":10,  // 页大小"totalPage":20, // 总页数"totalSize":200 // 总记录数}}
}

代码实现

  1. 准备条件实体类
@Data
public class PortalVo {private String keyWords;private Integer type;private Integer pageNum = 1;private Integer pageSize =10;
}

controller

/*** 首页分页查询* @return*/
@PostMapping("findNewPage")
public Result findNewPage(@RequestBody PortalVo portalVo){Result result = headlineService.findNewPage(portalVo);return result;
}

service

@Service
public class HeadlineServiceImpl extends ServiceImpl<HeadlineMapper, Headline>implements HeadlineService{@Autowiredprivate HeadlineMapper headlineMapper;/*** 首页数据查询* @param portalVo* @return*/@Overridepublic Result findNewPage(PortalVo portalVo) {//1.条件拼接 需要非空判断LambdaQueryWrapper<Headline> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.like(!StringUtils.isEmpty(portalVo.getKeyWords()),Headline::getTitle,portalVo.getKeyWords()).eq(portalVo.getType()!= null,Headline::getType,portalVo.getType());//2.分页参数IPage<Headline> page = new Page<>(portalVo.getPageNum(),portalVo.getPageSize());//3.分页查询//查询的结果 "pastHours":"3"   // 发布时间已过小时数 我们查询返回一个map//自定义方法headlineMapper.selectPageMap(page, portalVo);//4.结果封装//分页数据封装Map<String,Object> pageInfo =new HashMap<>();pageInfo.put("pageData",page.getRecords());pageInfo.put("pageNum",page.getCurrent());pageInfo.put("pageSize",page.getSize());pageInfo.put("totalPage",page.getPages());pageInfo.put("totalSize",page.getTotal());Map<String,Object> pageInfoMap=new HashMap<>();pageInfoMap.put("pageInfo",pageInfo);// 响应JSONreturn Result.ok(pageInfoMap);}
}

mapper

接口:

public interface HeadlineMapper extends BaseMapper<Headline> {//自定义分页查询方法IPage<Map> selectPageMap(IPage<Headline> page, @Param("portalVo") PortalVo portalVo);
}

mapperxml:

<select id="selectPageMap" resultType="map">select hid,title,type,page_views pageViews,TIMESTAMPDIFF(HOUR,create_time,NOW()) pastHours,publisher from news_headline where is_deleted=0<if test="portalVo.keyWords !=null and portalVo.keyWords.length()>0 ">and title like concat('%',#{portalVo.keyWords},'%')</if><if test="portalVo.type != null and portalVo.type != 0">and type = #{portalVo.type}</if>
</select>
查询头条详情

需求描述

  • 用户点击"查看全文"时,向服务端发送新闻id
  • 后端根据新闻id查询完整新闻文章信息并返回
  • 后端要同时让新闻的浏览量+1

接口描述

    url地址:portal/showHeadlineDetail请求方式:post请求参数: hid=1 param形成参数

成功

{"code":"200","message":"success","data":{"headline":{"hid":"1",                     // 新闻id "title":"马斯克宣布 ... ...",   // 新闻标题"article":"... ..."            // 新闻正文"type":"1",                    // 新闻所属类别编号"typeName":"科技",             // 新闻所属类别"pageViews":"40",              // 新闻浏览量"pastHours":"3" ,              // 发布时间已过小时数"publisher":"1" ,              // 发布用户ID"author":"张三"                 // 新闻作者}}
}

controller

 /*** 首页详情接口* @param hid* @return*/
@PostMapping("showHeadlineDetail")
public Result showHeadlineDetail(Integer hid){Result result = headlineService.showHeadlineDetail(hid);return result;
}

service

/*** 详情数据查询* "headline":{* "hid":"1",                     // 新闻id* "title":"马斯克宣布 ... ...",   // 新闻标题* "article":"... ..."            // 新闻正文* "type":"1",                    // 新闻所属类别编号* "typeName":"科技",             // 新闻所属类别* "pageViews":"40",              // 新闻浏览量* "pastHours":"3" ,              // 发布时间已过小时数* "publisher":"1" ,              // 发布用户ID* "author":"张三"                 // 新闻作者* }* 注意: 是多表查询 , 需要更新浏览量+1** @param hid* @return*/
@Override
public Result showHeadlineDetail(Integer hid) {//1.实现根据id的查询(多表Map headLineDetail = headlineMapper.selectDetailMap(hid);//2.拼接头条对象(阅读量和version)进行数据更新Headline headline = new Headline();headline.setHid(hid);headline.setPageViews((Integer) headLineDetail.get("pageViews")+1); //阅读量+1headline.setVersion((Integer) headLineDetail.get("version")); //设置版本headlineMapper.updateById(headline);Map<String,Object> pageInfoMap=new HashMap<>();pageInfoMap.put("headline",headLineDetail);return Result.ok(pageInfoMap);
}

mapper

接口:

/*** 分页查询头条详情* @param hid* @return*/
Map selectDetailMap(Integer hid);

mapperxml:

<!--    Map selectDetailMap(Integer hid);-->
<select id="selectDetailMap" resultType="map">select hid,title,article,type, h.version ,tname typeName ,page_views pageViews,TIMESTAMPDIFF(HOUR,create_time,NOW()) pastHours,publisher,nick_name author from news_headline hleft join news_type t on h.type = t.tidleft join news_user u  on h.publisher = u.uidwhere hid = #{hid}
</select>

头条模块开发

登陆验证和保护
  1. 需求描述
  • 客户端在进入发布页前、发布新闻前、进入修改页前、修改前、删除新闻前先向服务端发送请求携带token请求头
  • 后端接收token请求头后,校验用户登录是否过期并做响应
  • 前端根据响应信息提示用户进入登录页还是进入正常业务页面
  1. 接口描述

url地址:user/checkLogin
请求方式:get
请求参数: 无
请求头: token: 用户token

未过期:

{"code":"200","message":"success","data":{}
}

过期:

{"code":"504","message":"loginExpired","data":{}
}

controller 【登录检查】

@GetMapping("checkLogin")
public Result checkLogin(@RequestHeader String token){if (StringUtils.isEmpty(token) || jwtHelper.isExpiration(token)){//没有传或者过期 未登录return Result.build(null, ResultCodeEnum.NOTLOGIN);}return Result.ok(null);
}

拦截器 【所有/headline开头都需要检查登陆】

@Component
public class LoginProtectInterceptor implements HandlerInterceptor {@Autowiredprivate JwtHelper jwtHelper;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("token");if (StringUtils.isEmpty(token) || jwtHelper.isExpiration(token)){Result result = Result.build(null, ResultCodeEnum.NOTLOGIN);ObjectMapper objectMapper = new ObjectMapper();String json = objectMapper.writeValueAsString(result);response.getWriter().print(json);//拦截return false;}else{//放行return true;}}
}

拦截器配置

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Autowiredprivate LoginProtectInterceptor loginProtectInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginProtectInterceptor).addPathPatterns("/headline/**");}
}
头条发布实现
  1. 需求描述
  • 用户在客户端输入发布的新闻信息完毕后
  • 发布前先请求后端的登录校验接口验证登录
  • 登录通过则提交新闻信息
  • 后端将新闻信息存入数据库
  1. 接口描述

     url地址:headline/publish请求方式:post请求头: token: ... ...请求参数:
    
    {"title":"尚硅谷宣布 ... ...",   // 文章标题"article":"... ...",          // 文章内容"type":"1"                    // 文章类别
    }
    

未登录

{"code":"504","message":"loginExpired","data":{}
}

成功

{"code":"200","message":"success","data":{}
}

controller

/*** 实现步骤:*   1. token获取userId [无需校验,拦截器会校验]*   2. 封装headline数据*   3. 插入数据即可*/
@PostMapping("publish")
public Result publish(@RequestBody Headline headline,@RequestHeader String token){int userId = jwtHelper.getUserId(token).intValue();headline.setPublisher(userId);Result result = headlineService.publish(headline);return result;
}

service

/*** 发布数据* @param headline* @return*/
@Override
public Result publish(Headline headline) {headline.setCreateTime(new Date());headline.setUpdateTime(new Date());headline.setPageViews(0);headlineMapper.insert(headline);return Result.ok(null);
}
3.3 修改头条回显
  1. 需求描述
  • 前端先调用登录校验接口,校验登录是否过期
  • 登录校验通过后 ,则根据新闻id查询新闻的完整信息并响应给前端
  1. 接口描述

     url地址:headline/findHeadlineByHid请求方式:post请求参数:hid=1 param形成参数
    

成功

{"code":"200","message":"success","data":{"headline":{"hid":"1","title":"马斯克宣布","article":"... ... ","type":"2"}}
}

controller

@PostMapping("findHeadlineByHid")
public Result findHeadlineByHid(Integer hid){Result result = headlineService.findHeadlineByHid(hid);return result;
}

service

/*** 根据id查询详情* @param hid* @return*/
@Override
public Result findHeadlineByHid(Integer hid) {Headline headline = headlineMapper.selectById(hid);Map<String,Object> pageInfoMap=new HashMap<>();pageInfoMap.put("headline",headline);return Result.ok(pageInfoMap);
}
3.4 头条修改实现
  1. 需求描述
  • 客户端将新闻信息修改后,提交前先请求登录校验接口校验登录状态
  • 登录校验通过则提交修改后的新闻信息,后端接收并更新进入数据库
  1. 接口描述

     url地址:headline/update请求方式:post请求参数:
    
    {"hid":"1","title":"尚硅谷宣布 ... ...","article":"... ...","type":"2"
    }
    

成功

{"code":"200","message":"success","data":{}
}

controller

@PostMapping("update")
public Result update(@RequestBody Headline headline){Result result = headlineService.updateHeadLine(headline);return result;
}

service

 /*** 修改业务* 1.查询version版本* 2.补全属性,修改时间 , 版本!** @param headline* @return*/
@Override
public Result updateHeadLine(Headline headline) {//读取版本Integer version = headlineMapper.selectById(headline.getHid()).getVersion();headline.setVersion(version);headline.setUpdateTime(new Date());headlineMapper.updateById(headline);return Result.ok(null);
}
删除头条功能
  • 将要删除的新闻id发送给服务端
  • 服务端校验登录是否过期,未过期则直接删除,过期则响应登录过期信息

接口描述
url地址:headline/removeByHid
请求方式:post
请求参数:hid=1 param形成参数

成功

{"code":"200","message":"success","data":{}
}

controller

@PostMapping("removeByHid")
public Result removeById(Integer hid){headlineService.removeById(hid);return Result.ok(null);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/587143.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

半导体行业-SECS/GEM协议 JAVA与SECS/GEM通信 什么是配方?springboot集成SECS通信协议 配方管理S7FX

Java与SECS基础通信 Java实现SECS指令S2F17获取时间 Java实现SECS指令 S10F3 终端单个显示例子 Java实现SECS指令 S7FX配方管理 Java实现SECS指令 S5F1报警/取消报警上传 实例源码及DEMO请查阅 JAVA开发SECS快速入门资料&#xff0c;SECS S7F19 什么是半导体配方&…

初识javaWeb

一、JavaWeb是什么&#xff1f; 1、概念 javaWeb指的是使用java语言进行互联网领域项目开发的技术栈——进行web项目开发所需的技术的集合。 -Web前端——在浏览器中用户可以看到的网页 -Web后端——为前端提供数据的程序 2、Web项目 java语言是可以进行多种类型的项目开发&a…

网大为卸任腾讯CXO;Midjourney 1 月训练视频模型;2023年马斯克赚了7700亿

投融资 • 2023 年大型科技公司在生成式 AI 初创企业上的投资远超风险投资集团• 恒信东方与无锡政府合作成立布局 MR/XR 技术及 3D 数字资产 AIGC 产业投资基金• 新公司法完善注册资本认缴登记制度• 网大为卸任腾讯CXO&#xff0c;曾促成南非MIH的投资• 宁波蔚孚科技完成数…

【VS】NETSDK1045 当前 .NET SDK 不支持将 .NET 6.0 设置为目标。

问题描述 报错 NETSDK1045 严重性代码说明项目文件行禁止显示状态错误NETSDK1045当前 .NET SDK 不支持将 .NET 6.0 设置为目标。请将 .NET 5.0 或更低版本设置为目标&#xff0c;或使用支持 .NET 6.0 的 .NET SDK 版本。RCSoftDrawMicrosoft.NET.TargetFrameworkInference.ta…

年度总结|存储随笔2023年度最受欢迎文章榜单TOP15-part2

TOP11&#xff1a;PCIe在狂飙&#xff0c;SAS存储之路还有多远&#xff1f; 随着科技的飞速发展&#xff0c;固态硬盘&#xff08;SSD&#xff09;已经成为现代计算机系统中不可或缺的一部分。它以其出色的性能和可靠性&#xff0c;改变了我们对于存储设备的期待。当前业内SSD广…

Java(算术,自增自减,赋值,关系,逻辑,三元)运算符,运算符的优先级,隐式转换,强制转换,字符串的+。

文章目录 1.运算符和表达式运算符&#xff1a;表达式&#xff1a; 2.算术运算符练习&#xff1a;数值拆分 3.隐式转换概念&#xff1a;简单记忆&#xff1a;两种提升规则&#xff1a;取值范围从小到大的关系&#xff1a; 4.隐式转换的练习案例一&#xff1a;案例二&#xff1a;…

Python中如何使用_new_实现单例模式

单例模式是一个经典设计模式&#xff0c;简要的说&#xff0c;一个类的单例模式就是它只能被实例化一次&#xff0c;实例变量在第一次实例化时就已经固定。 在Python中常见的单例模式有None&#xff0c;这就是一个很典型的设计&#xff0c;通常使用 if xxx is None或者if xxx …

【Linux Shell学习笔记】Linux Shell的位置参数与函数

一、位置参数 位置参数&#xff0c;也被称之为位置变量&#xff0c;通过位置参数&#xff0c;可以在执行程序的时候&#xff0c;向程序传递数据 1.1 shell接收参数的方法 1.2 向shell传递参数的方法 二、函数 2.1 函数基础 2.1.1 函数简介 函数本质上就是一个代码块&#xf…

微信小程序开发系列-07组件

微信小程序开发系列目录 《微信小程序开发系列-01创建一个最小的小程序项目》《微信小程序开发系列-02注册小程序》《微信小程序开发系列-03全局配置中的“window”和“tabBar”》《微信小程序开发系列-04获取用户图像和昵称》《微信小程序开发系列-05登录小程序》《微信小程序…

碎花连衣裙 I 森系女孩的衣橱

宛如来到了春天&#xff0c;身穿碎花连衣裙的少女 在充满阳光&#xff0c;树叶葱郁的森林里 自由快乐地穿梭&#xff0c;感受着春天的美好 感受着幸福而快乐的童年&#xff01;

10 款顶级的免费U盘数据恢复软件(2024 年 更新)

你曾经遇到过U盘无法访问的情况吗&#xff1f;现在我们教你如何恢复数据。 在信息时代&#xff0c;数据丢失往往会造成巨大的困扰。而USB闪存驱动器作为我们常用的数据存储设备&#xff0c;其重要性不言而喻。但是&#xff0c;U盘也可能会出现各种问题&#xff0c;如无法访问、…

conftest.py 配置

章节目录&#xff1a; 一、概述二、场景说明三、代码示例3.1 最外层3.2 商品模块测试3.3 订单模块测试3.4 用户模块测试3.5 执行结果 四、关于 conftest.py 配置 fixture五、结束语 一、概述 “conftest.py” 是 pytest 测试框架中的一个特殊的配置文件&#xff0c;它能够为整个…

分布式技术之缓存技术

文章目录 什么是分布式缓存&#xff1f;分布式缓存原理Redis 分布缓存原理Memcached 分布式缓存原理对比分析 在计算机领域的各个方面&#xff0c;缓存都非常重要&#xff0c;是提升访问性能的一个重要技术。为什么这么说呢&#xff1f;从单个计算机的体系结构来看&#xff0c;…

【并发设计模式】聊聊等待唤醒机制的规范实现

在多线程编程中&#xff0c;其实就是分工、协作、互斥。在很多场景中&#xff0c;比如A执行的过程中需要同步等待另外一个线程处理的结果&#xff0c;这种方式下&#xff0c;就是一种等待唤醒的机制。本篇我们来讲述等待唤醒机制的三种实现&#xff0c;以及对应的应用场景。 G…

守护青山绿水 千巡翼Q20无人机变身护林员

守护青山绿水 千巡翼Q20无人机变身护林员 无人机目前在林业上的应用主要在森林资源调查、森林资源监测、森林火灾监测、森林病虫害监测防治、野生动物监测等方面。传统手段在森林资源调查中需要耗费大量人力物力&#xff0c;利用无人机技术可快速获得所需区域高精度信息&#…

SpringMVC学习与开发(四)

注&#xff1a;此为笔者学习狂神说SpringMVC的笔记&#xff0c;其中包含个人的笔记和理解&#xff0c;仅做学习笔记之用&#xff0c;更多详细资讯请出门左拐B站&#xff1a;狂神说!!! 11、Ajax初体验 1、伪造Ajax 结果&#xff1a;并未有xhr异步请求 <!DOCTYPE html> &…

关于蚁剑(AntSword)的溯源反制

中国蚁剑(AntSword) RCE漏洞 此漏洞在AntSword2.7.1版本上修复 &#xff0c;所以适用于AntSword2.7.1以下版本。 下面介绍被低版本蚁剑攻击后如何进行溯源反打 以物理机为攻击机&#xff0c;虚拟机kali模拟受害者&#xff0c;之后使用kali进行溯源反制 物理机内网ip地址&…

nodejs+vue网上书城图书销售商城系统io69w

功能介绍 该系统将采用B/S结构模式&#xff0c;使用Vue和ElementUI框架搭建前端页面&#xff0c;后端使用Nodejs来搭建服务器&#xff0c;并使用MySQL&#xff0c;通过axios完成前后端的交互 系统的主要功能包括首页、个人中心、用户管理、图书类型管理、图书分类管理、图书信…

计算机操作系统(OS)——P3内存管理

1、内存的基础知识 学习目标&#xff1a; 什么是内存&#xff1f;有何作用&#xff1f; 内存可存放数据。程序执行前__需要先放内存中才能被CPU处理__——缓和CPU与硬盘之间的速度矛盾。 【思考】在多道程序程序下&#xff0c;系统会有多个进程并发执行&#xff0c;也就是说…