尚硅谷SpringBoot项目--微头条实战(有完整项目代码)

目录

项目模块

技术栈

功能展示

环境搭建

前端环境搭建:

后端环境搭建:

数据库创建:

(建议使用数据库软件操作)

springboot项目搭建:

mybatis-plus逆向工程

后端功能开发

用户模块开发

jwt 与 token

登录功能实现

根据token获取用户信息

注册用户名检查

用户注册功能

首页模块开发

查询首页分类

分页查询首页头条信息

查询头条详情

头条模块开发

登录验证和保护

头条发布实现

修改头条回显

头条修改实现

删除头条功能


完整后端代码:https://pan.baidu.com/s/1sEsK4x6uM_o32qdsVTxagQ?pwd=d67f

apifox测试文件:https://pan.baidu.com/s/11liz1MxjYeitrsJbua3VFA?pwd=k8we

项目模块

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

技术栈

前端技术栈:

  • ES6作为基础JS语法
  • nodejs用于运行环境
  • npm用于项目依赖管理工具
  • vite用于项目的构建架工具
  • Vue3用于项目数据的渲染框架
  • Axios用于前后端数据的交互
  • Router用于页面的跳转
  • Pinia用于存储用户的数据
  • LocalStorage作为用户校验token的存储手段
  • Element-Plus提供组件

后端技术栈:

  • JAVA作为开发语言,版本为JDK17
  • Tomcat作为服务容器,版本为10.1.7
  • Mysql8用于项目存储数据
  • SpringMVC用于控制层实现前后端数据交互
  • MyBatis-Plus用于实现数据的CURD
  • Druid用于提供数据源的连接池
  • SpringBoot作为项目基础架构
  • MD5用于用户密码的加密
  • Jwt用于token的生成和校验
  • Jackson用于转换JSON

功能展示

头条首页信息搜索

登录功能

注册功能

展示功能

发布头条功能

修改头条功能

环境搭建

前端环境搭建:

前端环境下载icon-default.png?t=N7T8https://pan.baidu.com/s/16HJROtpdTZ6DrL0GMNJncw?pwd=42wf

下载后解压,使用vscode打开文件夹,在vscode中打开终端依次输入:

npm install

npm run dev

后项目启动:

后端环境搭建:

数据库创建:
(建议使用数据库软件操作)

https://pan.baidu.com/s/19vKtJCn_CwyFHPwinpcmsA?pwd=fkky

springboot项目搭建:

1.创建boot工程:springboot-news

2.导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.5</version></parent><groupId>com.qiu</groupId><artifactId>springboot-news</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- mybatis-plus  --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency><!-- 数据库相关配置启动器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- druid启动器的依赖  --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.18</version></dependency><!-- 驱动类--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

3.编写配置

# server配置
server:port: 8080servlet:context-path: /# 连接池配置
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:url: jdbc:mysql:///sm_dbusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver# mybatis-plus的配置
mybatis-plus:type-aliases-package: com.qiu.pojoglobal-config:db-config:logic-delete-field: isDeleted  #全局逻辑删除id-type: auto #主键策略自增长table-prefix: news_ # 设置表的前缀

4.解决druid问题

druid版本在1.2.20以下的需要:创建文件,添加内容

文件名:org.springframework.boot.autoconfigure.AutoConfiguration.imports
内容:com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure

5.编写启动类和mybatis-plus插件

包:com.qiu

@SpringBootApplication
@MapperScan("com.qiu.mapper")
public class Main {public static void main(String[] args) {System.out.println("Hello world!");SpringApplication.run(Main.class,args);}@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//分页interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//乐观锁interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//防止全局修改和删除interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());return interceptor;}
}

6.编写工具类

包:com.qiu.utils

结果封装类

/*** 全局统一返回结果类*/
public class Result<T> {// 返回码private Integer code;// 返回消息private String message;// 返回数据private T data;public Result(){}// 返回数据protected static <T> Result<T> build(T data) {Result<T> result = new Result<T>();if (data != null)result.setData(data);return result;}public static <T> Result<T> build(T body, Integer code, String message) {Result<T> result = build(body);result.setCode(code);result.setMessage(message);return result;}public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) {Result<T> result = build(body);result.setCode(resultCodeEnum.getCode());result.setMessage(resultCodeEnum.getMessage());return result;}/*** 操作成功* @param data  baseCategory1List* @param <T>* @return*/public static<T> Result<T> ok(T data){Result<T> result = build(data);return build(data, ResultCodeEnum.SUCCESS);}public Result<T> message(String msg){this.setMessage(msg);return this;}public Result<T> code(Integer code){this.setCode(code);return this;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getData() {return data;}public void setData(T data) {this.data = data;}
}

结果状态信息枚举类

/*** 统一返回结果状态信息类**/
public enum ResultCodeEnum {SUCCESS(200,"success"),USERNAME_ERROR(501,"usernameError"),PASSWORD_ERROR(503,"passwordError"),NOTLOGIN(504,"notLogin"),USERNAME_USED(505,"userNameUsed");private Integer code;private String message;private ResultCodeEnum(Integer code, String message) {this.code = code;this.message = message;}public Integer getCode() {return code;}public String getMessage() {return message;}
}

MD5加密工具类

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;@Component
public final class MD5Util {public static String encrypt(String strSrc) {try {char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8','9', 'a', 'b', 'c', 'd', 'e', 'f' };byte[] bytes = strSrc.getBytes();MessageDigest md = MessageDigest.getInstance("MD5");md.update(bytes);bytes = md.digest();int j = bytes.length;char[] chars = new char[j * 2];int k = 0;for (int i = 0; i < bytes.length; i++) {byte b = bytes[i];chars[k++] = hexChars[b >>> 4 & 0xf];chars[k++] = hexChars[b & 0xf];}return new String(chars);} catch (NoSuchAlgorithmException e) {e.printStackTrace();throw new RuntimeException("MD5加密出错!!+" + e);}}
}
mybatis-plus逆向工程

1.逆向工程

2.完善pojo类

删除@TableName注解,全局统一设置

添加主键,乐观锁,逻辑删除注解

@Data
public class User implements Serializable {@TableIdprivate Integer uid;private String username;private String userPwd;private String nickName;@Versionprivate Integer version;@TableLogicprivate Integer isDeleted;private static final long serialVersionUID = 1L;
}

后端功能开发

用户模块开发

jwt 与 token

令牌(token):用于验证用户身份或授权用户对特定资源的访问。普通令牌可以以多种形式出现:访问令牌,身份令牌,刷新令牌等。

就是在用户登录后生成一段字符或数字给他,用户之后每次访问都携带这个token来证明自己的身份,这段字符或数字就是token

JWT介绍

Token是一项规范和标准(接口)

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

JWT工作流程

  1. 用户通过其凭据(通常为账号密码)进行身份认证
  2. 服务器对凭据进行验证,验证成功后创建一个JWT
  3. 服务器将JWT发送给客户端,客户端在之后的请求中将JWT添加到请求头或参数中
  4. 服务器接收请求后,验证JWT的签名和有效性,并根据JWT中的声明进行身份验证和授权操作

JWT的数据组成和包含信息

JWT由三部分组成: header(头部).payload(载荷).signature(签名)

jwt可以携带很多信息:有效时间,签名秘钥,其他用户标识信息等

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

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

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

JWT的使用

1.导入依赖

<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>

2.编写配置

application.yaml

jwt:token:
#    tokenExpiration: 120 #有效时间,单位分钟tokenExpiration: 1 #为了测试token过期,设置一分钟tokenSignKey: qiu666 #当前程序签名秘钥 自定义

3.导入工具类

@Component
@Data
@ConfigurationProperties(prefix = "jwt.token")
public class JwtHelper {//有效时间,单位毫秒 1000毫秒 == 1秒private long tokenExpiration;//当前程序签名秘钥private String tokenSignKey;//生成tokenpublic String createToken(Long userId){String token = Jwts.builder().setSubject("YYGH-USER")
//                1分钟.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration * 1000 * 60))//存放数据.claim("userId", userId)//签名,当用户请求时携带token,根据签名判断身份是否通过.signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();return token;}//当用户请求中携带token时,从token中获取数据(useId)public Long getUserId(String token){//StringUtils的包为com.baomidou.mybatisplus.core.toolkit.StringUtils;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;}}}

4.使用和测试

@SpringBootTest
public class JwtTest {@Autowiredprivate JwtHelper jwtHelper;@Testpublic void test() throws InterruptedException {//生成数据 用户数据 userId 1LString token = jwtHelper.createToken(1L);System.out.println("token = " + token);//解析用户标识Long userId = jwtHelper.getUserId(token);System.out.println("userId = " + userId);//查看token是否过期 false 未到期 true到期boolean expiration = jwtHelper.isExpiration(token);;System.out.println("expiration = " + expiration);Thread.sleep(1000*60);System.out.println(" 程序睡眠一分钟后: ");//查看token是否过期 false 未到期 true到期boolean expiration1 = jwtHelper.isExpiration(token);;System.out.println("expiration = " + expiration1);}
}

登录功能实现

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

2.接口描述:

url:user/login

请求方式:POST

请求参数:

{"username":"zhangsan","userPwd":"123456"
}

响应数据:

成功

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

失败

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

3.实现代码:

controller:

@RestController
@RequestMapping("user")
@CrossOrigin    //解决跨域问题
public class UserController {@Autowiredprivate UserService userService;@PostMapping("login")public Result<User> login(@RequestBody User user){Result<User> result = userService.login(user);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获取用户信息

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

2.接口描述:

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":{}
}

3.代码实现:

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);
}

测试结果:

用户注册功能

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

2.接口描述:

url:user/regist

请求方式:POST

请求参数:

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

响应数据:

成功:

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

失败:
 

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

3.代码实现:

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);
}

测试结果:

首页模块开发

查询首页分类

1.需求:

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

2.接口描述:

url:portal/findAllTypes

请求方式:GET

请求参数:无

响应数据:

成功:

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

3.代码实现

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);}
}

测试结果:

分页查询首页头条信息

1.需求:

        客户端向服务端发送查询关键字,新闻类别,页码数,页大小

        服务端根据条件搜索分页信息,返回含页码数,页大小,总页数,总记录数,当前页数据等信息,并根据时间降序,浏览量降序排序

2.接口描述:

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 // 总记录数}}
}

3.代码实现:

准备接收条件类:

@Data
public class PortalVo {private String keyWords;private Integer type;private Integer pageNum = 1;private Integer pageSize =10;
}

controller:

/*** 首页分页查询* @return*/
@PostMapping("findNewPages")
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);
}

mapper.xml:

<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>

测试结果:

查询头条详情

1.需求:

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

2.接口描述:

url:portal/showHeadlineDetial

请求方式:POST

请求参数: param形式

hid=1

响应数据:

成功:

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

3.代码实现:

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);

mapper.xml:

<!--    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请求头后,校验用户登录是否过期并做响应
        前端根据响应信息提示用户进入登录页还是进入正常业务页面

2.接口描述:

url:user/checkLogin

请求方式:GET

请求参数:无

请求头:token: 用户token

3.代码实现:

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);
}

创建拦截器:

@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;}}
}

配置拦截器:(使用 /headline 开头都拦截)

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Autowiredprivate LoginProtectInterceptor loginProtectInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginProtectInterceptor).addPathPatterns("/headline/**");}
}

测试结果:

头条发布实现

1.需求:

        用户在客户端输入发布的新闻信息完毕后
        发布前先请求后端的登录校验接口验证登录
        登录通过则提交新闻信息
        后端将新闻信息存入数据库

2.接口描述:

url:headline/publish

请求方式:POST

请求头:token:用户token

请求参数:

{"title":"尚硅谷宣布 ... ...",   // 文章标题"article":"... ...",          // 文章内容"type":"1"                    // 文章类别
}

响应数据:

未登录:

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

成功:

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

3.代码实现:

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);
}

测试结果:

修改头条回显

1.需求:

        前端先调用登录校验接口,校验登录是否过期

        登录校验通过后 ,则根据新闻id查询新闻的完整信息并响应给前端

2.接口描述:

url:headline/findHeadlineByHid

请求方式:POST

请求参数:param形式

hid=1

响应数据:

成功:

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

3.代码实现:

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);
}

测试结果:

头条修改实现

1.需求:

        客户端将新闻信息修改后,提交前先请求登录校验接口校验登录状态
        登录校验通过则提交修改后的新闻信息,后端接收并更新进入数据库

2.接口描述:

url:headline/update

请求方式:POST

请求参数:

{"hid":"1","title":"尚硅谷宣布 ... ...","article":"... ...","type":"2"
}

响应数据:

成功:

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

3.代码实现:

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);
}

测试结果:

删除头条功能

1.需求:

        将要删除的新闻id发送给服务端
        服务端校验登录是否过期,未过期则直接删除,过期则响应登录过期信息

2.接口描述:

url:headline/removeByHid

请求方式:POST

请求参数:param形式

hid=1

响应数据:

成功:

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

3.代码实现:

controller:

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

测试结果:

 

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

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

相关文章

基于SpringBoot的“实验室管理系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“实验室管理系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 登录界面 注册界面 实验室界面 设备界面图 消耗…

使用Python批量实现在Excel里新加一列

目录 一、引言 二、所需库介绍 三、代码实现 四、批量处理多个Excel文件 五、注意事项与扩展 六、案例演示 七、总结与展望 一、引言 Excel作为广泛使用的电子表格软件&#xff0c;在数据处理和分析中扮演着重要角色。然而&#xff0c;当面对大量Excel文件需要批量处理…

【六】【算法分析与设计】二分查找

69. x 的平方根 给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被 舍去 。 注意&#xff1a;不允许使用任何内置指数函数和算符&#xff0c;例如 pow(x, 0.5) 或者 x ** 0.5 。 示…

玩转 Spring 状态机:更优雅的实现订单状态流转

说起 Spring 状态机&#xff0c;大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢&#xff1f;没错&#xff0c;Spring 状态机就是状态模式的一种实现&#xff0c;在介绍 Spring 状态机之前&#xff0c;让我们来看看设计模式中的状态模式。 1. 状态模式 状态模式…

如何使用vue定义组件之——子组件调用父组件数据

1.定义父子模板template <div class"container"><my-father></my-father><my-father></my-father><my-father></my-father><!-- 此处无法调用子组件&#xff0c;子组件必须依赖于父组件进行展示 --><!-- <my-…

数据结构:图及相关算法讲解

图 1.图的基本概念2. 图的存储结构2.1邻接矩阵2.2邻接表2.3两种实现的比较 3.图的遍历3.1 图的广度优先遍历3.2 图的深度优先遍历 4.最小生成树4.1 Kruskal算法4.2 Prim算法4.3 两个算法比较 5.最短路径5.1两个抽象存储5.2单源最短路径--Dijkstra算法5.3单源最短路径--Bellman-…

【Git版本控制系统】:起步

目录 前言 版本控制 集中式与分布式的区别 Windows安装Git 核心 文件状态 工作区域 基本工作流程 配置用户信息 获取帮助 在线资源 前言 本篇文件的环境是Windows环境下实现。 在日常工作中git少不了&#xff0c;所以编写本篇文章介绍Git基础&#xff0c;专栏会不…

unicloud JQL数据库操作介绍

JQL数据库操作 JQL&#xff0c;全称 javascript query language&#xff0c;是一种js方式操作数据库的规范。 JQL大幅降低了js工程师操作数据库的难度&#xff0c;比SQL和传统MongoDB API更清晰、易掌握。JQL支持强大的DB Schema&#xff0c;内置数据规则和权限。DB Schema 支…

与结构数列顺序有关的两个方程组

已知一组4点的结构数列顺序为&#xff0c; 方程组41 (5*x1)/5.0r1 (2*x23*x3)/5.0r2 (4*x21*x8)/5.0r3 (3*x32*x5)/5.0r4 (5*x5)/5.0r5 (4*x31*x14)/5.0r6 (1*x13*x41*x16)/5.0r7 (2*x32*x71*x10)/5.0r8 (2*x53*x7)/5.0r9 (2*x23*x10)/5.0r10 (2*x42*x91*x11)/5.0r11…

Vue 中使用 v-for 渲染列表时绑定 key 的重要性

在 Vue.js 中&#xff0c;v-for 是一个常用的指令&#xff0c;用于渲染列表数据到页面上。然而&#xff0c;在使用 v-for 渲染列表时&#xff0c;绑定一个 key 是至关重要的实践之一。本文将详细介绍为什么在 Vue 中使用 v-for 渲染列表时绑定 key 是如此重要&#xff0c;并深入…

迅为iTOP-RK3588开发板Buildroot系统功能测试

第三章 Buildroot系统功能测试 烧写buildroot系统镜像&#xff0c;buildroot系统镜像在网盘资料“iTOP-3588开发板\01_【iTOP-RK3588开发板】基础资料\06_iTOP-RK3588开发板Linux镜像\01_Buildroot镜像”目录下&#xff0c;本小节测试buildroot系统。 3.1系统启动 Buildroot系…

Qt_vc++崩溃日志分析

环境 Clion &#xff1a;2019.3.6 Qt &#xff1a;5.9.6&#xff08;vc2015&#xff09; 编译工具&#xff1a;vs2015 update3 崩溃日志收集 自行百度&#xff0c;会查到很多&#xff0c;一下代码仅供参考&#xff08;来自https://blog.csdn.net/weixin_45571586/article/…