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

news/2024/10/25 9:26:54/

作者主页:编程千纸鹤

作者简介:Java、前端、Pythone开发多年,做过高程,项目经理,架构师

主要内容: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 用户文章内容上传图片界面

四,核心代码展示

package com.qst.controller;import com.qst.pojo.ResultInfo;
import com.qst.pojo.schoolarticle.Article;
import com.qst.service.ArticleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/article")
public class ArticleController {@AutowiredArticleService articleService;//添加普通文章@RequestMapping("/addArticle")public ResultInfo addArticle(Article article){System.out.println(article);try {return articleService.addArticle(article);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常啦,请稍后再试");}}//添加文章草稿@RequestMapping("/addArticleDraft")public ResultInfo addArticleDraft(Article article){try {return articleService.addArticleDraft(article);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常啦,请稍后再试");}    }//展示文章列表、分页、多条件查询@RequestMapping("/showArticleList")public ResultInfo showArticleList( Article article,Integer current, Integer size){try {return articleService.showArticleList(article,current,size);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常啦,查询失败,请稍后再试");}}//展示首页文章列表@RequestMapping("/initGetArticleList")public ResultInfo initGetArticleList( Article article,Integer current, Integer size){try {return articleService.initGetArticleList(article,current,size);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常啦,查询失败,请稍后再试");}}//将文章放入回收站或者将回收站的文章恢复或将文章彻底删除@RequestMapping("/delArticleList")public ResultInfo delArticleList(Article article,Integer[] idList){try {return articleService.delArticleList(article,idList);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统暂时出现异常,请稍后再试");}}//搜索功能@RequestMapping("/search")public ResultInfo search(String keywords){try {return articleService.search(keywords);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统暂时出现异常,请稍后再试");}}
}
package com.qst.controller;import com.qst.pojo.ResultInfo;
import com.qst.pojo.schoolarticle.Comments;
import com.qst.pojo.schoolarticle.Likes;
import com.qst.service.CommentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/comment")
public class CommentController {@AutowiredCommentService commentService;//获取文章所拥有的评论数量@RequestMapping("/getArticleCommentCount")public ResultInfo getArticleCommentCount(Integer articleid){try {return commentService.getArticleCommentCount(articleid);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,获取文章评论数失败");}}//添加一条评论@RequestMapping("/addComment")public ResultInfo addComment(Comments comments){System.out.println(comments);try {return commentService.addComment(comments);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,插入评论失败");}}//查询当前页的评论@RequestMapping("/showComment")public ResultInfo showComment(Comments comments,Integer current){try {return commentService.showComment(comments,current);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,查询评论失败");}}//查询评论的回复评论@RequestMapping("/showCommentReply")public ResultInfo showCommentReply(Comments comments,Integer current,Integer size){try {return commentService.showCommentReply(comments,current,size);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,查询评论的回复失败");}}//查询当前用户的评论@RequestMapping("/getCommentListByUserid")public ResultInfo getCommentListByUserid(Comments comments,Integer current,Integer size,String keywords){try {return commentService.getCommentListByUserid(comments,current,size,keywords);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,查询评论列表失败");}}//查询当前用户发布的评论@RequestMapping("/getUserComment")public ResultInfo getUserComment(Comments comments,Integer current,Integer size,String keywords){try {return commentService.getUserComment(comments,current,size,keywords);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,查询评论列表失败");}}//查询当前用户对该片文章的信息点赞了多少@RequestMapping("/getLikesByUseridArticleid")public ResultInfo getLikesByUseridArticleid(Likes likes){try {return commentService.getLikesByUseridArticleid(likes);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,查询用户点赞信息失败");}}//用户对评论点赞或取消点赞@RequestMapping("/setCommentLikes")public ResultInfo setCommentLikes(Likes likes,Integer status){try {return commentService.setCommentLikes(likes,status);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,用户点赞操作失败");}}//用户通过评论@RequestMapping("/setCommentStatus")public ResultInfo setCommentStatus(Integer[] idList){try {return commentService.setCommentStatus(idList);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,通过评论失败");}}//用户删除评论@RequestMapping("/delComment")public ResultInfo delComment(Integer[] idList){try {return commentService.delComment(idList);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,删除评论失败");}}//更改用户收到的回复的状态@RequestMapping("/setReceivedRelpyStatus")public ResultInfo setReceivedRelpyStatus(Comments comments){try {return commentService.setReceivedRelpyStatus(comments);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,修改回复状态失败");}}
}

package com.qst.controller;import com.qst.pojo.ResultInfo;
import com.qst.pojo.schoolarticle.Article;
import com.qst.service.HomeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/home")
public class HomeController {@AutowiredHomeService homeService;//查询前台首页用户的文章数、分类数、标签数@RequestMapping("/getUserArticleLabelClassifyCount")public ResultInfo getUserArticleLabelClassifyCount(Integer userid){try {return homeService.getUserArticleLabelClassifyCount(userid);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,获取用户文章数量失败");}}//查询首页轮播图的推荐文章列表@RequestMapping("/getTjCarouselArticle")public ResultInfo getTjCarouselArticle(){try {return homeService.getTjCarouselArticle();} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,获取推荐文章列表失败");}}//通过文章id,获取文章的详细信息@RequestMapping("/getArticleById")public ResultInfo getArticleById(Integer articleid){if (articleid==null)return new ResultInfo(false,"请选择正确的文章");try {return homeService.getArticleById(articleid);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常,获取文章详细信息失败");}}//通过用户id获取该用户的5篇最新文章@RequestMapping("/getFiveArticleByUserid")public ResultInfo getFiveArticleByUserid(Integer userid,Integer articleid){if(userid==null)return new ResultInfo(false,"用户名为空,所以无法查询用户的最新文章");try {return homeService.getFiveArticleByUserid(articleid,userid);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常啦,请稍后再试");}}//判断用户是否为当前文章点赞@RequestMapping("/isLikeArticle")public ResultInfo isLikeArticle(Article article){System.out.println(article);try {return homeService.isLikeArticle(article);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常啦,请稍后再试");}}//给当前文章点赞的状态进行修改@RequestMapping("/setArticleLike")public ResultInfo setArticleLike(Article article,Integer flag,Integer articleuserid){try {return homeService.setArticleLike(article,flag,articleuserid);} catch (Exception e) {e.printStackTrace();return new ResultInfo(false,"系统异常啦,请稍后再试");}}
}

五,项目总结

本系统采用前后端分离设计,前端与后端相互独立,前端使用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配置的文件,可以用于设置代理,打包配置等。


http://www.ppmy.cn/news/5785.html

相关文章

基于事件触发的二阶多智能体领导跟随一致性

【无限嚣张(菜菜)】:hello您好,我是菜菜,很高兴您能来访我的博客,我是一名爱好编程学习研究的菜菜,每天分享自己的学习,想法,博客来源与自己的学习项目以及编程中遇到问题…

Node基础——认识Node

什么是Node 首先JavaScript是一门编程语言,就像Java、Python、C#、GO一样,在Node出来之前,JavaScript主要运行于浏览器中,用来控制页面的展示逻辑,以及交互操作等。JavaScript之所以能够在浏览器中执行,是…

北面羽绒服成热议产品,小红书透露出哪些营销新趋势?

小红书浓厚的种草氛围,为品牌创造了良好的营销环境,想要在小红书做好内容种草,需要洞察用户的真实需求来推广产品,实现营销效果的最大化。那如何发现小红书上的热门品类?制定品牌营销策略?挑选优质合作达人…

【Qt源码笔记】深谈 Qt 绘制

之前写了一篇 浅谈Qt控件绘制 。之所以叫浅谈是因为调用都是比较表层的调用。其实 Qt 的绘制,可以说用 Qt 的人都有用到,但是对于绘制底层,了解的人并不见得很多。我其实之前也是云山雾罩,从来没有深究过。所以想着知其然还是要知…

Typora配合PicGo阿里云图床配置

写博客的时候,刚开始直接在各大平台上直接写,后来还是觉得不太方便,需要在各大平台之间来回切换。于是就改用Typora,但是有个问题就是图片的处理,只能放在本地。想要发布到各大平台,就需要图床。本文结合阿…

m基于kmeans和SVM的网络入侵数据分类算法matlab仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 首先计算整个数据集合的平均值点,作为第一个初始聚类中心C1; 然后分别计算所有对象到C1的欧式距离d,并且计算每个对象在半径R的范围内包含的对象个数W。 此时计…

用简单伪随机数发生器实现随机中点位移分形(Matlab代码实现)

目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨‍💻4 Matlab代码 💥1 概述 随机分形(random fractal)采用随机生成机制而得到的分形集.分形体不具有特征尺度(亦即大小尺度跨好几个量级),却有…

Biotin-PEG-AC,Biotin-PEG-Acrylate,生物素PEG丙烯酸酯线性杂双功能PEG试剂

英文名称:Biotin-PEG-AC,Biotin-PEG-Acrylate 中文名称:生物素-聚乙二醇-丙烯酸酯 生物素-聚乙二醇-丙烯酸酯是一种含有生物素和丙烯酸酯的线性杂双功能聚乙二醇试剂。它是一种有用的带有PEG间隔基的交联或生物结合试剂。生物素能与亲和素和…