飞道的博客

校园文件发布系统|基于Springboot实现校园文章发布系统

295人阅读  评论(0)

作者主页:编程指南针

作者简介:Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师

主要内容:Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助

收藏点赞不迷路  关注作者有好处

文末获取源码 

项目编号:BS-PT-077

前言:

对于当前的个人博客系统,比较封闭并且有自己的局限性,一旦流量大了以后就会受到不明来历的恶意流量攻击。无论你买什么服务器都没有用。对于个人来说,基本都是重启服务器是最佳的解决办法。所以,安全问题对于独立博客来说是一个很大的挑战。并且作为一个个人博客,网站中只有拥有者才能进行文章的发布等操作,这使得其他用户不能发布自己的文章。

校园文章发布系统吸收了个人博客部分特点。允许所有用户发布文章,摒弃掉博客网站只能拥有者发文章这一弊端,用户还能建立属于自己个人的分类与标签,使得用户个性化设置达到最大,并且每个用户既是文章发布者,也是文章的阅读者,能对别人发布的文章进行查看评论等操作,并且本系统主要面向对象为在校大学生,所以在网站安全方面,后期可以将项目运行在局域网内,使得只有使用校园网才能对我们的系统进行访问。

一,项目简介

本系统设计了两种用户角色:普通用户和系统管理员。普通用户可以新建属于个人的分类与标签,可以发布个人的公开或者私有文章,并且还能游览系统内的其他公开文章,对文章进行点赞、评论等操作。系统管理员主要是对用户产生的数据进行统计与维护;能对系统中的用户进行统计和操作,对用户的信息进行查看、删除等;能对用户发布的文章进行审核,对于一些非法的文章不予通过,并提醒用户所发的文章违规,还能对文章进行删除和维护等操作;能对用户的评论信息进行管理,查看用户们所发布的评论,还可以将违规评论直接删除;能对用户创建的分类信息进行查看、修改、删除等操作;能对用户创建的标签信息进行查看、修改、删除等。该系统的功能模块图如图3-1所示。

图3-1系统功能模块图

普通用户可以使用网站的基础功能,如查看平台首页、进入用户个人中心、进入用户管理、对个人的文章管理、搜索系统的所有文章等,用户功能用例图如图3-2所示。

图3-2 用户功能用例图

管理员通过系统自带的帐号登录,管理员账号除了可以使用普通用户的所有功能外,还可以进入后台管理页面,对用户和文章进行管理,对文章进行评论管理,对文章进行分类和标签管理。管理员功能用例图如图3-3所示。

图3-3 管理员用例图

本系统有文章搜索模块、文章管理模块、平台首页模块、用户管理模块、个人中心模块、管理员模块6个功能模块,其主要功能分析如下:

1.文章搜索模块功能

表3-1 文章搜索模块功能描述

功能名称

功能描述

按文章标题搜索

输入文章的部分标题,查看对应的文章

按分类搜索

通过文章的大分类得到一个类型的文章

按标签搜索

通过文章所包含的标签,得到对应的文章

按文章信息搜索

通过关键字搜索包含该关键字的文章

2.文章管理模块

表3-2 文章管理模块功能描述

功能名称

功能描述

文章回收站

用户可以将文章删除放入回收站

用户发布文章

用户可以发布自己的个人文章

用户修改文章

用户可以修改自己发布的文章的内容

3.平台首页模块

表3-3 平台首页模块功能描述

功能名称

功能描述

用户注册

用户通过QQ邮箱注册系统的账号

用户登录

用户通过个人账号登录系统

游览平台文章

对系统中的文章进行查阅游览

查看文章详细信息

查看某一篇文章的详细内容

用户评论文章

在文章详细内容界面可对文章发布个人的评论

文章点赞

在文章详细内容界面可对文章点赞

4.用户管理模块

表3-4 用户管理模块功能描述

功能名称

功能描述

查看归档信息

用户可按照文章发布时间查看发布的所有文章

管理分类信息

用户对个人文章的分类做增删改查

管理标签信息

用户对个人的文章标签做增删改查

个人通知

当别人对用户的文章进行评论或者别人回复用户的文章后,都会在个人通知中显示

5.个人中心模块

表3-5 个人中心模块功能描述

功能名称

功能描述

查看个人信息

用户可查看个人的详细信息

修改个人信息

对自己的信息进行修改

修改密码

修改个人的账号密码

6.管理员模块

表3-6 管理员模块功能描述

功能名称

功能描述

登录注册

管理员可注册和登录后台系统

用户管理

可查看、删除用户的信息

文章管理

审核用户发布的文章,可对文章进行增删查

评论管理

查看用户的评论,对非法的评论进行删除

分类管理

对用户新建的分类进行管理

标签管理

对用户新建的标签进行管理

二,环境介绍

语言环境:Java:  jdk1.8

数据库:Mysql: mysql5.7

应用服务器:Tomcat:  tomcat8.5.31

开发工具:IDEA或eclipse

三,系统展示

5.1  平台首页模块

5.1.1  注册与登录

前端的登录界面由login.vue实现,注册页面由register.vue实现。用户注册时,用户需要先填写邮箱,接着发送验证码,通过/user/getSendCode接口将邮箱号传入后端,后端接收到请求后,首先判断当前邮箱是否已经在系统中,若存在系统则返回信息提示用户直接登录,否则调用UserService类中的getSendCode方法,先生成一个六位的随机验证码,将验证码存入Redis中,再通过qq邮箱接口,发送对应邮件到用户的邮箱中,返回成功信息,提示用户验证码发送成功,用户输入正确的邮箱和合法的密码后,点击注册按钮,此时调用/user/regist接口,将用户的信息和验证码一起传给后端,后端首先从Redis中获取当前用户的正确验证码,与传来的验证码进行匹配,如果匹配成功则将用户的信息写入数据库,返回成功信息给用户,提示用户注册成功,若验证码不正确,则直接返回验证码输入有误的信息给前端,前端提示用户验证码输入错误。界面实现效果如图5-1所示。

图5-1 用户注册界面

用户注册完成后,则可以进行账号登录,输入邮箱和密码之后点击登录,前端通过/user/loginPwd接口向后端发送请求,后端从数据库中查询是否存在这个用户,并且查询该用户的密码,与当前用户输入的是否一致,若密码也一致则返回用户的信息给前端,前端通过传回的数据是否为用户个人信息为判断的标准,如果返回了个人信息,则提示用户登录成功,将页面跳转到系统的主页,并且将用户的个人信息存储到游览器的sessionStorage中,以便用户只是刷新游览器时,保证用户的登录状态,若返回flase则提示用户账号或者密码输出错误。界面实现效果如图5-2所示。

图5-2 用户登录界面

5.1.2  游览平台文章

在用户登录后,前端通过this.$router.push('/homeIndex')进行路由跳转,进入主页,在主页界面进行挂载之前,前端通过/article/initGetArticleList接口,向后端发送请求,对主页的文章列表数据进行初始化,后端将查询到的数据返回给前端,前端接收到数据后对文章列表进行渲染。界面实现效果图如图5-3所示。接着前端还会通过/home/getTjCarouselArticle接口,向后端请求首页轮播图的数据,因为轮播图数据是通过文章的点赞量来确定,并且通过定时器每天凌晨五点对数据进行更新,因此会将数据放入Redis缓存,以此减轻数据库的压力,所以每次请求时都会从Redis缓存中获取数据,将结果返回给前端,前端对数据进行渲染。界面实现效果如图5-4所示。

图5-3 文章列表界面

图5-4 首页轮播图界面

5.1.3  查看文章详细信息

当用户点击某一篇文章时,前端进行路由跳转,并且会将该篇文章的id作为路由参数一起放入请求的路由中,在页面组件挂载前,会先准备文章详细信息界面所需要的数据,通过/home/getArticleById接口获取当前文章的详细信息,前端接收后端返回的数据后,对文章的详细内容进行渲染,加载文章的详细信息和目录信息,如图5-5所示;通过/home/getFiveArticleByUserid接口获取当前文章的作者的最新发布的五篇文章的跳转链接,后端实现为通过用户的id加以文章发布的日期做降序排列,最终取前五条数据得到需要的数据,前端接收后端的数据后,对数据进行渲染,界面实现效果图如图5-6所示;通过/comment/getArticleCommentCount接口分页获取当前文章的评论信息,后端接收请求后,通过文章id查询属于该文章的评论的信息,将得到的数据返回给前端,前端将得到的数据进行渲染,界面实现效果图如图5-7所示。

图5-5 文章详细信息界面

图5-6 用户其他文章推荐界面

图5-7 文章评论信息界面

5.1.4  用户评论

用户评论功能分为两类,一类是用户对文章进行评论,另一类是用户对其他用户的评论进行回复。当用户对文章进行评论时,点击提交按钮,如图5-8所示,前端会先判断当前用户是否登录,若未登录,则提示用户登录,并通过this.$router.push('/login')语句跳转到登录界面,再判断当前用户评论的内容是否为空,若为空则提示用户评论不能为空,不为空则通过/comment/addComment发送请求到后端,后端在数据库中新增一条数据后,返回成功信息给前端,前端提示用户评论成功。如果是用户回复其他用户的评论,则先点击目标评论后面的回复按钮,此时会出现回复框,如图5-9所示,用户输入需要回复的内容后,点击提交按钮,前端也是通过/comment/addComment接口发送请求到后端,后端插入一条数据后,返回成功信息给前端,前端提示用户回复成功。

图5-8 用户评论文章界面

图5-9 用户回复评论界面

5.2  文章管理模块

5.2.1  文章回收站

用户可以在个人中心查看自己所发布的文章的列表,在这里用户可以点击删除按钮,将文章放入回收站,如图5-10所示,点击删除按钮后,界面会提示用户是否确认删除,用户点击确认,前端会通过/article/delArticleList接口向后端发送删除选中文件的请求,后端接收到请求后,将对应文章移入回收站中,返回成功信息给前端,前端提示用户删除成功。接着用户可以在回收站中找到被删除的文章,如图5-11所示,用户可以恢复被删除的文章,也可以彻底删除文章,若点击恢复按钮,前端会通过/article/delArticleList接口,发送对应的参数到后端,后端通过其中的参数去进行判断,是将文章移入回收站还是恢复该文章,恢复成功后,返回成功信息给前端,前端提示用户恢复文章成功;若用户点击的是删除按钮,则通过/article/delArticleList接口,给后端发送对应的参数,后端解析参数后会将选定的文章数据进行彻底删除,最后返回给前端成功信息,前端进行渲染,提示用户删除成功。

图5-10 用户操作个人文章界面

图5-11 用户查看文章回收站界面

5.2.2  用户发布文章

用户点击发布文章按钮后,进入文章编写界面,在这里用户可以编写文章内容,还可以在文章中插入图片,插入图片时通过/file/uploadImg接口直接将图片保存到服务器中,再将该图片的访问路径返回给前端,前端会通过markdown保存图片的请求路径,并显示图片的预览,如图5-12所示,当用户编写好文章内容后,则可以进入文章发布界面,如图5-13所示,在这里用户需要将文章的必要信息完善,在选择分类时,前端已经通过/classify/getMyClassify接口,获取了该用户现有的所有分类,接着监视分类输入框,展示用户可能想要选择的分类,也可以直接在这里创建一个新的分类,如图5-14所示,在选择标签时,系统不仅仅会显示用户个人的标签,还会推荐本平台文章数前五的五个标签供用户选择,平台前五标签通过/labels/getTJLabels接口向后端发送请求,将标签信息封装在数组中,返回给前端,前端就能进行展示,用户在这里可以选择多个标签,也会根据用户在搜索框中输入的关键字,通过/labels/getMyLabels接口进行模糊查询,显示用户可能想要选择的标签,如图5-15所示,最终用户点击发表按钮,前端通过/article/addArticle接口将文章信息发送给后端,后端接收数据后,将文章信息插入数据库,将分类表和标签表中文章数量加一,再文章分类表、文章标签表中添加文章和分类、标签之间的关联数据,返回成功信息给前端,前端接收后提示用户文章发布成功,并通过this.$router.push({path:'/userCenter/articleList'})跳转到用户中心的文章列表。

图5-12 用户文章内容上传图片界面

四,核心代码展示


  
  1. package com.qst.controller;
  2. import com.qst.pojo.ResultInfo;
  3. import com.qst.pojo.schoolarticle.Article;
  4. import com.qst.service.ArticleService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. @RestController
  9. @RequestMapping("/article")
  10. public class ArticleController {
  11. @Autowired
  12. ArticleService articleService;
  13. //添加普通文章
  14. @RequestMapping("/addArticle")
  15. public ResultInfo addArticle (Article article){
  16. System.out.println(article);
  17. try {
  18. return articleService.addArticle(article);
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. return new ResultInfo( false, "系统异常啦,请稍后再试");
  22. }
  23. }
  24. //添加文章草稿
  25. @RequestMapping("/addArticleDraft")
  26. public ResultInfo addArticleDraft (Article article){
  27. try {
  28. return articleService.addArticleDraft(article);
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. return new ResultInfo( false, "系统异常啦,请稍后再试");
  32. } }
  33. //展示文章列表、分页、多条件查询
  34. @RequestMapping("/showArticleList")
  35. public ResultInfo showArticleList ( Article article,Integer current, Integer size){
  36. try {
  37. return articleService.showArticleList(article,current,size);
  38. } catch (Exception e) {
  39. e.printStackTrace();
  40. return new ResultInfo( false, "系统异常啦,查询失败,请稍后再试");
  41. }
  42. } //展示首页文章列表
  43. @RequestMapping("/initGetArticleList")
  44. public ResultInfo initGetArticleList ( Article article,Integer current, Integer size){
  45. try {
  46. return articleService.initGetArticleList(article,current,size);
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. return new ResultInfo( false, "系统异常啦,查询失败,请稍后再试");
  50. }
  51. }
  52. //将文章放入回收站或者将回收站的文章恢复或将文章彻底删除
  53. @RequestMapping("/delArticleList")
  54. public ResultInfo delArticleList (Article article,Integer[] idList){
  55. try {
  56. return articleService.delArticleList(article,idList);
  57. } catch (Exception e) {
  58. e.printStackTrace();
  59. return new ResultInfo( false, "系统暂时出现异常,请稍后再试");
  60. }
  61. }
  62. //搜索功能
  63. @RequestMapping("/search")
  64. public ResultInfo search (String keywords){
  65. try {
  66. return articleService.search(keywords);
  67. } catch (Exception e) {
  68. e.printStackTrace();
  69. return new ResultInfo( false, "系统暂时出现异常,请稍后再试");
  70. }
  71. }
  72. }

  
  1. package com.qst.controller;
  2. import com.qst.pojo.ResultInfo;
  3. import com.qst.pojo.schoolarticle.Comments;
  4. import com.qst.pojo.schoolarticle.Likes;
  5. import com.qst.service.CommentService;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.RestController;
  9. @RestController
  10. @RequestMapping("/comment")
  11. public class CommentController {
  12. @Autowired
  13. CommentService commentService;
  14. //获取文章所拥有的评论数量
  15. @RequestMapping("/getArticleCommentCount")
  16. public ResultInfo getArticleCommentCount (Integer articleid){
  17. try {
  18. return commentService.getArticleCommentCount(articleid);
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. return new ResultInfo( false, "系统异常,获取文章评论数失败");
  22. }
  23. }
  24. //添加一条评论
  25. @RequestMapping("/addComment")
  26. public ResultInfo addComment (Comments comments){
  27. System.out.println(comments);
  28. try {
  29. return commentService.addComment(comments);
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. return new ResultInfo( false, "系统异常,插入评论失败");
  33. }
  34. }
  35. //查询当前页的评论
  36. @RequestMapping("/showComment")
  37. public ResultInfo showComment (Comments comments,Integer current){
  38. try {
  39. return commentService.showComment(comments,current);
  40. } catch (Exception e) {
  41. e.printStackTrace();
  42. return new ResultInfo( false, "系统异常,查询评论失败");
  43. }
  44. }
  45. //查询评论的回复评论
  46. @RequestMapping("/showCommentReply")
  47. public ResultInfo showCommentReply (Comments comments,Integer current,Integer size){
  48. try {
  49. return commentService.showCommentReply(comments,current,size);
  50. } catch (Exception e) {
  51. e.printStackTrace();
  52. return new ResultInfo( false, "系统异常,查询评论的回复失败");
  53. }
  54. }
  55. //查询当前用户的评论
  56. @RequestMapping("/getCommentListByUserid")
  57. public ResultInfo getCommentListByUserid (Comments comments,Integer current,Integer size,String keywords){
  58. try {
  59. return commentService.getCommentListByUserid(comments,current,size,keywords);
  60. } catch (Exception e) {
  61. e.printStackTrace();
  62. return new ResultInfo( false, "系统异常,查询评论列表失败");
  63. }
  64. }
  65. //查询当前用户发布的评论
  66. @RequestMapping("/getUserComment")
  67. public ResultInfo getUserComment (Comments comments,Integer current,Integer size,String keywords){
  68. try {
  69. return commentService.getUserComment(comments,current,size,keywords);
  70. } catch (Exception e) {
  71. e.printStackTrace();
  72. return new ResultInfo( false, "系统异常,查询评论列表失败");
  73. }
  74. }
  75. //查询当前用户对该片文章的信息点赞了多少
  76. @RequestMapping("/getLikesByUseridArticleid")
  77. public ResultInfo getLikesByUseridArticleid (Likes likes){
  78. try {
  79. return commentService.getLikesByUseridArticleid(likes);
  80. } catch (Exception e) {
  81. e.printStackTrace();
  82. return new ResultInfo( false, "系统异常,查询用户点赞信息失败");
  83. }
  84. }
  85. //用户对评论点赞或取消点赞
  86. @RequestMapping("/setCommentLikes")
  87. public ResultInfo setCommentLikes (Likes likes,Integer status){
  88. try {
  89. return commentService.setCommentLikes(likes,status);
  90. } catch (Exception e) {
  91. e.printStackTrace();
  92. return new ResultInfo( false, "系统异常,用户点赞操作失败");
  93. }
  94. }
  95. //用户通过评论
  96. @RequestMapping("/setCommentStatus")
  97. public ResultInfo setCommentStatus (Integer[] idList){
  98. try {
  99. return commentService.setCommentStatus(idList);
  100. } catch (Exception e) {
  101. e.printStackTrace();
  102. return new ResultInfo( false, "系统异常,通过评论失败");
  103. }
  104. }
  105. //用户删除评论
  106. @RequestMapping("/delComment")
  107. public ResultInfo delComment (Integer[] idList){
  108. try {
  109. return commentService.delComment(idList);
  110. } catch (Exception e) {
  111. e.printStackTrace();
  112. return new ResultInfo( false, "系统异常,删除评论失败");
  113. }
  114. }
  115. //更改用户收到的回复的状态
  116. @RequestMapping("/setReceivedRelpyStatus")
  117. public ResultInfo setReceivedRelpyStatus (Comments comments){
  118. try {
  119. return commentService.setReceivedRelpyStatus(comments);
  120. } catch (Exception e) {
  121. e.printStackTrace();
  122. return new ResultInfo( false, "系统异常,修改回复状态失败");
  123. }
  124. }
  125. }


  
  1. package com.qst.controller;
  2. import com.qst.pojo.ResultInfo;
  3. import com.qst.pojo.schoolarticle.Article;
  4. import com.qst.service.HomeService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. @RestController
  9. @RequestMapping("/home")
  10. public class HomeController {
  11. @Autowired
  12. HomeService homeService;
  13. //查询前台首页用户的文章数、分类数、标签数
  14. @RequestMapping("/getUserArticleLabelClassifyCount")
  15. public ResultInfo getUserArticleLabelClassifyCount (Integer userid){
  16. try {
  17. return homeService.getUserArticleLabelClassifyCount(userid);
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. return new ResultInfo( false, "系统异常,获取用户文章数量失败");
  21. }
  22. }
  23. //查询首页轮播图的推荐文章列表
  24. @RequestMapping("/getTjCarouselArticle")
  25. public ResultInfo getTjCarouselArticle (){
  26. try {
  27. return homeService.getTjCarouselArticle();
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. return new ResultInfo( false, "系统异常,获取推荐文章列表失败");
  31. }
  32. }
  33. //通过文章id,获取文章的详细信息
  34. @RequestMapping("/getArticleById")
  35. public ResultInfo getArticleById (Integer articleid){
  36. if (articleid== null)
  37. return new ResultInfo( false, "请选择正确的文章");
  38. try {
  39. return homeService.getArticleById(articleid);
  40. } catch (Exception e) {
  41. e.printStackTrace();
  42. return new ResultInfo( false, "系统异常,获取文章详细信息失败");
  43. }
  44. }
  45. //通过用户id获取该用户的5篇最新文章
  46. @RequestMapping("/getFiveArticleByUserid")
  47. public ResultInfo getFiveArticleByUserid (Integer userid,Integer articleid){
  48. if(userid== null)
  49. return new ResultInfo( false, "用户名为空,所以无法查询用户的最新文章");
  50. try {
  51. return homeService.getFiveArticleByUserid(articleid,userid);
  52. } catch (Exception e) {
  53. e.printStackTrace();
  54. return new ResultInfo( false, "系统异常啦,请稍后再试");
  55. }
  56. }
  57. //判断用户是否为当前文章点赞
  58. @RequestMapping("/isLikeArticle")
  59. public ResultInfo isLikeArticle (Article article){
  60. System.out.println(article);
  61. try {
  62. return homeService.isLikeArticle(article);
  63. } catch (Exception e) {
  64. e.printStackTrace();
  65. return new ResultInfo( false, "系统异常啦,请稍后再试");
  66. }
  67. }
  68. //给当前文章点赞的状态进行修改
  69. @RequestMapping("/setArticleLike")
  70. public ResultInfo setArticleLike (Article article,Integer flag,Integer articleuserid){
  71. try {
  72. return homeService.setArticleLike(article,flag,articleuserid);
  73. } catch (Exception e) {
  74. e.printStackTrace();
  75. return new ResultInfo( false, "系统异常啦,请稍后再试");
  76. }
  77. }
  78. }

五,项目总结

本系统采用前后端分离设计,前端与后端相互独立,前端使用Vue+ Element-UI开发,后端基于SSM框架开发。前端项目名称byVue,如图4-1所示。后端项目名称final-project,如图4-2所示。

图4-1 前端项目结构图

前端目录说明如下:

node_modules: 它用于存储项目的各种依赖项,例如Axios。没有moudles文件,项目就不能运行;

public:用于存储静态文件;

public/index.html: 它是一个模板文件,用于生成项目的条目文件。webpack打包的JS和CSS也会自动注入到页面中。当我们的浏览器访问项目时,它将默认打开生成的index.html;

src:我们存放各种vue文件的地方;

src/assets:用于存放各种静态文件,如图片等;

src/compnents:用于存放我们的公共组件,如 header、footer等;

src/views:用于存放我们写好的各种页面,如login、main等;

src/APP.VUE: 主vue模块 引入其他模块,app.vue是项目的主组件,所有页面都是在app.vue下切换的;

src/main.js: 入口文件的主要功能是初始化Vue实例。同时,可以在这个文件中引用一些组件库,或者全局挂起一些变量;

src/router.js: 路由文件可以理解为我们访问的每个页面的地址路径。同时,路由保护可以直接写入其中;

src/store.js:主要用于项目里边的一些状态的保存,state中保存状态,mutations中写用于修改state中的状态;

package.json: 模块基本信息项目开发所需要模块,版本,项目名称;

package-lock.json: 在NPM安装过程中会生成一个文件,记录当前状态下实际安装的每个NPM包的具体源代码和版本号;

vue.config.js: 保存vue配置的文件,可以用于设置代理,打包配置等。


转载:https://blog.csdn.net/whirlwind526/article/details/128425907
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场