# info_island **Repository Path**: ltxking/info_island ## Basic Information - **Project Name**: info_island - **Description**: 这是龙洁的毕业设计,新闻聚合网站 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2024-11-03 - **Last Updated**: 2025-06-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # InfoIsland 信息岛 ## 项目简介 信息岛是一个基于Spring Boot和MyBatis-Plus构建的新闻资讯类网站,用户可以发布、收藏、点赞新闻,并且支持新闻搜索和多级评论。 ## 技术栈 - 后端:Spring Boot 2.6.13、MyBatis-Plus 3.5.3.1、MySQL 8.0.25、Redis、Elasticsearch 7.10.1、MinIO - 前端:Vue 3、Vite 5、Element Plus 2、Pinia 2、WindCSS 3 - 其他:JWT、OkHttp 4.9.3、Hutool 5.8.27、Fastjson2 ## 功能点 1. **用户管理**:用户注册、登录、修改个人信息等。 2. **新闻管理**:用户可以发布新闻、修改新闻、删除新闻等。 3. **新闻分类**:对新闻进行分类管理。 4. **新闻收藏和点赞**:用户可以对新闻进行收藏和点赞操作。 5. **新闻搜索**:支持关键词搜索新闻。 6. **新闻评论**:用户可以对新闻进行评论,支持多级评论和回复功能。 7. **热门新闻**:展示热门新闻。 8. **XXL-JOB集成**:用于定时任务,例如热点新闻更新。 ## 目录结构 - `src/main/java/com/lj/infoisland/common`:基础模块,常量类等。 - - `src/main/java/com/lj/infoisland/config`:配置类:配置了一下系统需要用到的组件框架信息、以及请求控制器(对于一下请求需要登录访问的请求,就需要先经过拦截器的处理对应类`TokenInterceptor登录拦截器`)。 - `src/main/java/com/lj/infoisland/controller`:控制器层,处理HTTP请求。 - `src/main/java/com/lj/infoisland/service`:服务层,业务逻辑处理。 - `src/main/java/com/lj/infoisland/domain`:领域模型,包括DTO(前端传给后端接受参数类型)、VO(后端返回给前端的参数类型)、PO(数据库中的表结构)等。 - - `src/main/java/com/lj/infoisland/job`:爬虫任务的定义,并且放入到XXL-JOB中执行。 - `src/main/java/com/lj/infoisland/mapper`:MyBatis-Plus的Mapper层。 - - `src/main/java/com/lj/infoisland/util`:工具类:包括JWT、断言工具类、id生成器、用户线程局部变量。 - `src/main/resources`:资源文件,包括配置文件、静态资源等。 ## 调用关系 下面从上至下的调用关系进行说明: 1. 用户进行请求调用:用户在浏览器中输入URL,请求被Spring Boot的DispatcherServlet接收,然后被映射到对应的控制器方法。 2. 映射到对应的控制器也就是到控制器方法,然后调用对应的服务方法。 3. 调用服务方法,然后调用对应的DAO层方法。 4. DAO层的方法实际上会调用MyBatis-Plus的Mapper层,完成数据库操作,也就是执行SQL语句查询数据库或者修改数据库。 如何进行调用: 1. 了解Springboot的Bean是什么东西,Bean是可以通过注解(@Component、@Service、@Controller、@Repository)或者xml配置,然后通过@Autowired注解或者@Resource注解注入到对应的类中。 2. 为什么要注入bean因为Springboot是一个容器,容器中存放了各种对象,这些对象可以通过注解或者xml配置注入到对应的类中,并且框架都是自动管理生命周期的也就是在使用这些框架的时候都是基于bean去操作。 3. 通过注入的Bean就用可以使用对应的类中的操作。 ## 配置文件 项目的配置文件为`[application.yaml](file://src/main/resources/application.yaml)`,包含了数据库、Redis、Elasticsearch、MinIO等服务的配置信息。 ## 示例代码 ### 用户控制器示例 ```java @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; /** * 注册 * @param user * @return */ @PostMapping("/register") public Result register(@RequestBody User user) { return Result.success(userService.register(user)); } } ``` 对于上面代码: 1. 假设是本地启动的项目用户如果是要进行注册的话:用户在输入了用户名和密码之后,然后点击注册按钮,实际上调用的就是localhost:8080/user/register这个接口, 通过方法@PostMapping("/register")来指定这个接口是post请求,然后通过@RequestBody来指定这个接口的参数是json格式的,所以就把用户输入的信息存储到了user这个实例中 2. 然后通过@Autowired来注入UserService这个类,从而就可以看下面的代码调用UserService这个类中的register方法,然后返回一个Result对象,这个Result对象就是用来封装返回给前端的结果的。 ### 用户服务示例 ```java import org.springframework.stereotype.Service; @Service public class UserServiceImpl extends ServiceImpl implements UsersService { @Override public boolean register(User user) { // 此处省略业务逻辑 return save(user); } } ``` ```java public interface UsersService extends IService { boolean register(User user); } ``` 对于上面代码: 1. 我们需要了解到的点:接口和类的关系:一个类可以实现一个接口,也就是接口只是一个空壳子,而类是对于这个空壳子的实现。 2. 对于在SpringBoot中的常见的业务逻辑都是在Service中进行实现的。 3. 上面的@Service注解就是用来标识这个类是一个Service的,然后通过@Autowired来注入这个类,然后就可以调用这个类中的方法了。 4. 对于这个类中的register方法,实际上就是调用了MyBatis-Plus的save方法,这个方法就是用来保存数据的,所以实际上就是将用户输入的信息保存到数据库中。 5. MyBatis-Plus是一个框架也就是可以理解为别人帮我们写的代码我们可以直接调用别的代码,这个框架帮我封装了很多的方法让我们可以直接的使用框架中的方法执行数据SQL语句 ### 用户DAO示例 ```java @Mapper public interface UsersMapper extends BaseMapper { } ``` 对于上面代码: 1. 这个代码就是用来定义一个接口,这个接口就是用来定义数据库的SQL语句的,这个接口就是用来操作数据库的。 2. 这是MyBatis-Plus的固定写法,也就是对于数据中的每一个表都可以创建一个这个接口进行数据的操作。 ### 用户表实体类 ```java /** * 用户类 */ @Data @NoArgsConstructor @AllArgsConstructor @Builder @TableName(value = "users") public class Users implements Serializable { @Serial private static final long serialVersionUID = 1L; @TableId(type = IdType.ASSIGN_ID) /* * id */ private Long id; /** * 用户唯一id */ @TableField(value = "user_unique_id", fill = FieldFill.INSERT) private String userUniqueId; /* * 用户名 */ @TableField(value = "username") private String username; /** * 密码 */ @TableField(value = "password") private String password; /** * 电子邮件 */ @TableField(value = "email") private String email; /** * 全名 */ @TableField(value = "full_name") private String fullName; /** * 性别 */ @TableField(value = "sex") private Integer sex; /** * 电话号码 */ @TableField(value = "phone_number") private String phoneNumber; /** * 地址 */ @TableField(value = "address") private String address; /** * 头像url */ @TableField(value = "avatar_url") private String avatarUrl; /** * 描述 */ @TableField(value = "description") private String description; /** * 点赞数 */ @TableField(value = "like_num") private Integer likeNum; /** * 收藏数 */ @TableField(value = "favorite_num") private Integer favoriteNum; @TableField(value = "ip") private String ip; /** * 用户角色: admin, user */ @TableField(value = "role") private String role; /** * 用户状态: 0: 禁用, 1: 启用 */ @TableField(value = "status") private Integer status; /** * 创建时间 */ @TableField(value = "created_at", fill = FieldFill.INSERT) @JsonFormat(pattern = "yyyy-MM-dd HH:mm") private Date createdAt; /** * 更新时间 */ @TableField(value = "updated_at", fill = FieldFill.INSERT_UPDATE) @JsonFormat(pattern = "yyyy-MM-dd HH:mm") private Date updatedAt; /** * 生日 */ @TableField(value = "birthday") private Date birthday; /** * 年龄 */ @TableField(value = "age") private Integer age; @TableField(value = "interests") private String interests; /** * 是否初始化标签 */ @TableField(value = "is_init") private Integer isInit; } ``` 对于上面的代码: 1. 这个代码就是用来定义一个实体类,这个实体类就是用来封装数据库中的数据,这个实体类就是用来操作数据库的。 2. 注解@TableName(value = "users")是定义了数据库中对于的真实的数据表名 3. @TableField(value = "user_unique_id", fill = FieldFill.INSERT)是定义了数据表中对于的正式字段的名字,看看代码就知道了下面的具体的字段和数据表中的字段有所不同,java代码中多使用驼峰命名也就是单词首字母大写,而数据库字段用"_"进行单词连接 4. @TableId(type = IdType.ASSIGN_ID)指定主键是什么 ### 返回数据 ```java package com.lj.infoisland.common.convention.result; import lombok.Data; import lombok.experimental.Accessors; import java.io.Serial; import java.io.Serializable; /** * 全局返回对象 */ @Data @Accessors(chain = true) public class Result implements Serializable { @Serial private static final long serialVersionUID = 5679018624309023727L; /** * 正确返回码 */ public static final String SUCCESS_CODE = "0"; /** * 返回码 */ private String code; /** * 返回消息 */ private String message; /** * 响应数据 */ private T data; /** * 请求ID */ private String requestId; public boolean isSuccess() { return SUCCESS_CODE.equals(code); } } ``` 对于上面的代码: 1. 这个代码就是用来定义一个返回对象,这个对象就是用来封装返回给前端的数据的。 2. 返回的格式是这样的: { code: "0", message: "成功", data : {...} } 3. 也就是查询到的结果放入到data中进行返回,但是整体的格式是json格式返回,返回有错误码和错误信息,如果成功就返回0、正确信息、数据,失败就返回错误码和错误信息。 - `src/main/java/com/lj/infoisland/common/convention/errorcode`:定义了所有错误码,方便前端进行错误处理。 - `src/main/java/com/lj/infoisland/common/convention/result`:定义了返回对象,方便前端进行错误处理。 ## 总结 springboot框架处理一个请求的整个调用逻辑大概就是上面说的,通过找到控制器在控制器中找到对于的方法,方法就可以调用对于的服务类进行对于的业务逻辑的处理,然后就可以把处理完业务逻辑的数据在数据库中进行操作了。 ### 爬虫代码讲解 ```java /* * @description 知乎热搜Java爬虫定时任务代码 * @create 2024-10-22 下午2:29 */ @Component @Slf4j public class ZhihuHotSearchJob { @Autowired private HotSearchRecordService hotSearchRecordService; @PostConstruct() public void init() { try { hotSearch(null); log.info("初始化知乎数据成功"); } catch (Exception e) { log.error("初始化知乎数据异常", e); } } /** * 定时触发爬虫方法,1个小时执行一次 */ @XxlJob("zhihuHotSearchJob") public ReturnT hotSearch(String param) { log.info("知乎热搜爬虫任务开始"); try { //查询知乎热搜数据 OkHttpClient client = new OkHttpClient().newBuilder().build(); Request request = new Request.Builder().url("https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total") .method("GET", null).build(); Response response = client.newCall(request).execute(); JSONObject jsonObject = JSONObject.parseObject(response.body().string()); JSONArray array = jsonObject.getJSONArray("data"); List hotSearchRecordList = Lists.newArrayList(); for (int i = 0, len = array.size(); i < len; i++) { //获取知乎热搜信息 JSONObject object = (JSONObject) array.get(i); JSONObject target = object.getJSONObject("target"); //构建热搜信息榜 HotSearchRecord hotSearchRecord = HotSearchRecord.builder().hotSearchResource(ZHIHU.getCode()).build(); //设置知乎三方ID hotSearchRecord.setHotSearchId(target.getString("id")); //设置文章连接 hotSearchRecord.setHotSearchUrl("https://www.zhihu.com/question/" + hotSearchRecord.getHotSearchId()); //设置文章标题 hotSearchRecord.setHotSearchTitle(target.getString("title")); //设置作者名称 hotSearchRecord.setHotSearchAuthor(target.getJSONObject("author").getString("name")); //设置作者头像 hotSearchRecord.setHotSearchAuthorAvatar(target.getJSONObject("author").getString("avatar_url")); //设置文章摘要 hotSearchRecord.setHotSearchExcerpt(target.getString("excerpt")); //设置热搜热度 hotSearchRecord.setHotSearchHeat(object.getString("detail_text").replace("热度", "")); //按顺序排名 hotSearchRecord.setHotSearchOrder(i + 1); hotSearchRecordList.add(hotSearchRecord); } if (CollectionUtils.isEmpty(hotSearchRecordList)) { return ReturnT.SUCCESS; } //数据加到缓存中 CACHE_MAP.put(ZHIHU.getCode(), HotSearchDetailDTO.builder() //热搜数据 .hotSearchDTOList( hotSearchRecordList.stream().map(HotSearchConvert::toDTOWhenQuery).collect(Collectors.toList())) //更新时间 .updateTime(Calendar.getInstance().getTime()).build()); //数据持久化 hotSearchRecordService.batchSave(hotSearchRecordList); log.info("知乎热搜爬虫任务结束"); return ReturnT.SUCCESS; } catch (IOException e) { log.error("获取知乎数据异常", e); } return ReturnT.FAIL; } } ``` 对于上面代码: 我们首先分析一下下面两行代码 ```java Request request = new Request.Builder().url("https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total") .method("GET", null).build(); Response response = client.newCall(request).execute(); ``` 很容易的看出来第一行的代码就是调用请求访问https://www.zhihu.com/api/v3/feed/topstory/hot-lists/total 这个网址 client.newCall(request).execute();就是具体的去调用,可以拿到网页的源代码也就是和下面图片中操作拿到的一样的 ![知乎热榜数据.png](知乎热榜数据.png) 后面的代码装换为一个Json对象使用Json对象的操作方法取出来各种对于的数据属性,并且把这些数据封装存储到数据中去。 如果老师要提问的就可以说这个上面的接口都是我浏览网站通过F12开发者工具中的网络请求,查看调用了哪些请求获取到到数据得到的结果。 也可以看根目录下的文件怎么查看到