学习内容:springboot博客项目搭建和最热标签等功能的实现
前端工程:下载前端源码、Hbuilderx和node.js
- 导入前端源码
- 找到src目录下的main函数,并进行如下操作:npm run build → npm run dev

- 点击链接即可进入网站

新建spring工程
目录:

父工程
<?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 https://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>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mszlu</groupId>
<artifactId>blog-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>blog-parent</name>
<description>blog-parent</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- 排除 默认使用的logback -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.14</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.7.1</version>
</plugin>
</plugins>
</build>
</project>
子工程
<?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 https://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>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mszlu</groupId>
<artifactId>blog-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>blog-api</name>
<description>blog-api</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- 排除 默认使用的logback -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.14</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置
application.properties
server.port=8888 spring.application.name=mszlu_blog #datasource spring.datasource.url=jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #mybatis-plus mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl mybatis-plus.global-config.db-config.table-prefix=ms_
mybatisPlusConfig
package com.mszlu.blogapi.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan("com.mszlu.blogapi.dao.mapper")
public class MybatisPlusConfig {
//分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
WebMVCConfig
package com.mszlu.blogapi.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMVCConfig implements WebMvcConfigurer{
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:8080");
}
}
启动类
package com.mszlu.blogapi;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan
public class BlogApiApplication {
public static void main(String[] args) {
SpringApplication.run(BlogApiApplication.class, args);
}
}
首页-文章列表
文章
package com.mszlu.blogapi.dao.pojo;
import lombok.Data;
@Data
public class Article {
public static final int Article_TOP = 1;
public static final int Article_Common = 0;
private Long id;
private String title;
private String summary;
private int commentCounts;
private int viewCounts;
/**
* 作者id
*/
private Long authorId;
/**
* 内容id
*/
private Long bodyId;
/**
*类别id
*/
private Long categoryId;
/**
* 置顶
*/
private int weight = Article_Common;
/**
* 创建时间
*/
private Long createDate;
}
用户
package com.mszlu.blogapi.dao.pojo;
import lombok.Data;
@Data
public class SysUser {
private Long id;
private String account;
private Integer admin;
private String avatar;
private Long createDate;
private Integer deleted;
private String email;
private Long lastLogin;
private String mobilePhoneNumber;
private String nickname;
private String password;
private String salt;
private String status;
}
标签
package com.mszlu.blogapi.dao.pojo;
import lombok.Data;
@Data
public class Tag {
private Long id;
private String avatar;
private String tagName;
}
Controller
controller层。控制器,导入service层,因为service中的方法是我们使用到的,controller通过接收前端传过来的参数进行业务操作,在返回一个指定的路径或者数据表。
ArticleController
package com.mszlu.blogapi.controller;
import com.mszlu.blogapi.service.ArticleService;
import com.mszlu.blogapi.vo.ArticleVo;
import com.mszlu.blogapi.vo.Result;
import com.mszlu.blogapi.vo.params.PageParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
//json数据进行交互
@RestController
@RequestMapping("articles")
public class ArticleController {
@Autowired
private ArticleService articleService;
/*
* 首页 文章列表
* @param pageParams
* @return*/
@PostMapping
public Result listArticle(@RequestBody PageParams pageParams) {
// List<ArticleVo> articles = articleService.listArticlesPage(pageParams);
return articleService.listArticle(pageParams);
}
/*
* 首页 最热文章
* @return*/
@PostMapping("hot")
public Result hotArticle() {
int limit = 5;
return articleService.hotArticle(limit);
}
/*
* 首页 最新文章
* @return*/
@PostMapping("new")
public Result newArticles() {
int limit = 5;
return articleService.newArticle(limit);
}
/*
* 首页 文章归档
* @return*/
@PostMapping("listArchives")
public Result listArchives() {
return articleService.listArchives();
}
}
TagController
package com.mszlu.blogapi.controller;
import com.mszlu.blogapi.service.TagService;
import com.mszlu.blogapi.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("tags")
public class TagController {
@Autowired
private TagService tagService;
// tags/hot
@GetMapping("hot")
public Result hot() {
int limit = 6;
return tagService.hots(limit);
}
}
建立与前端交互的Vo文件
ArticleVo
package com.mszlu.blogapi.vo;
import java.util.List;
public class ArticleVo {
private Long id;
private String title;
private String summary;
private Integer commentCounts;
private Integer viewCounts;
private Integer weight;
/**
* 创建时间
*/
private String createDate;
private String author;
// private ArticleBodyVo body;
private List<TagVo> tags;
public void setCreateDate(String createDate) {
this.createDate = createDate;
}
public void setAuthor(String nickname) {
this.author = nickname;
}
public void setTags(List<TagVo> tagsByArticleId) {
this.tags = tagsByArticleId;
}
// private List<CategoryVo> categorys;
}
TagVo
package com.mszlu.blogapi.vo;
import lombok.Data;
@Data
public class TagVo {
private Long id;
private String tagName;
}
Result
package com.mszlu.blogapi.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import javax.xml.soap.SAAJResult;
@Data
@AllArgsConstructor
public class Result {
private boolean success;
private int code;
private String msg;
private Object data;
public static Result success(Object data) {
return new Result(true, 200, "success", data);
}
public static Result fail(int code, String msg) {
return new Result(false, code, msg, null);
}
}
PageParams
package com.mszlu.blogapi.vo.params;
import lombok.Data;
@Data
public class PageParams {
private int page = 1;
private int pageSize = 10;
}
Service
service层主要是写业务逻辑方法,service层经常要调用dao层(也叫mapper层)的方法对数据进行增删改查的操作。
ArticleService
package com.mszlu.blogapi.service;
import com.mszlu.blogapi.vo.Result;
import com.mszlu.blogapi.vo.params.PageParams;
public interface ArticleService {
/*
* 分页查询 文章列表
* @param pageParams
* @return*/
Result listArticle(PageParams pageParams);
/*
* 最热文章
* @param limit
* @return*/
Result hotArticle(int limit);
/*
* 最新文章
* @param limit
* @return*/
Result newArticle(int limit);
/*
* 文章归档
* @param limit
* @return*/
Result listArchives();
}
实现类ArticleServiceImpl
package com.mszlu.blogapi.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mszlu.blogapi.dao.dos.Archives;
import com.mszlu.blogapi.dao.mapper.ArticleMapper;
import com.mszlu.blogapi.dao.pojo.Article;
import com.mszlu.blogapi.service.ArticleService;
import com.mszlu.blogapi.service.SysUserService;
import com.mszlu.blogapi.service.TagService;
import com.mszlu.blogapi.vo.ArticleVo;
import com.mszlu.blogapi.vo.Result;
import com.mszlu.blogapi.vo.params.PageParams;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired(required = false)
private ArticleMapper articleMapper;
@Autowired
private TagService tagService;
@Autowired
private SysUserService sysUserService;
@Override
public Result listArticle(PageParams pageParams) {
/*
1.分页查询 article数据库表
*/
Page<Article> page = new Page<>(pageParams.getPage(), pageParams.getPageSize());
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
//是否置顶排序
//order by create_date desc
queryWrapper.orderByDesc(Article::getWeight, Article::getCreateDate);
Page<Article> articlePage = articleMapper.selectPage(page, queryWrapper);
List<Article> records = articlePage.getRecords();
//能直接返回吗? 不能
List<ArticleVo> articleVoList = copyList(records, true, true);
return Result.success(articleVoList);
}
@Override
public Result hotArticle(int limit) {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(Article::getViewCounts);
queryWrapper.select(Article::getId, Article::getTitle);
queryWrapper.last("limit " + limit);
//select id,title from article order by view_counts desc limit 5
List<Article> articles = articleMapper.selectList(queryWrapper);
return Result.success(copyList(articles, false, false));
}
@Override
public Result newArticle(int limit) {
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.orderByDesc(Article::getCreateDate);
queryWrapper.select(Article::getId, Article::getTitle);
queryWrapper.last("limit " + limit);
//select id,title from article order by create_date desc limit 5
List<Article> articles = articleMapper.selectList(queryWrapper);
return Result.success(copyList(articles, false, false));
}
@Override
public Result listArchives() {
List<Archives> archivesList = articleMapper.listArchives();
return Result.success(archivesList);
}
private List<ArticleVo> copyList(List<Article> records, boolean isTag, boolean isAuthor) {
List<ArticleVo> articleVoList = new ArrayList<>();
for (Article record : records) {
articleVoList.add(copy(record, isTag, isAuthor));
}
return articleVoList;
}
private ArticleVo copy(Article article, boolean isTag, boolean isAuthor) {
ArticleVo articleVo = new ArticleVo();
BeanUtils.copyProperties(article, articleVo);
articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm"));
//并不是所有接口都需要标签和作者信息
if (isTag) {
Long articleId = article.getId();
articleVo.setTags(tagService.findTagsByArticleId(articleId));
}
if (isAuthor) {
Long authorId = article.getAuthorId();
articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname());
}
return articleVo;
}
}
SysUserService
package com.mszlu.blogapi.service;
import com.mszlu.blogapi.dao.pojo.SysUser;
public interface SysUserService {
SysUser findUserById(Long id);
}
实现类SysUserServiceImpl
package com.mszlu.blogapi.service.impl;
import com.mszlu.blogapi.dao.mapper.SysUserMapper;
import com.mszlu.blogapi.dao.pojo.SysUser;
import com.mszlu.blogapi.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SysUserServiceImpl implements SysUserService {
@Autowired(required = false)
private SysUserMapper sysUserMapper;
public SysUser findUserById(Long id) {
SysUser sysUser = sysUserMapper.selectById(id);
if(sysUser == null) {
sysUser = new SysUser();
sysUser.setNickname("码神之路");
}
return sysUserMapper.selectById(id);
}
}
TagService
package com.mszlu.blogapi.service;
import com.mszlu.blogapi.vo.Result;
import com.mszlu.blogapi.vo.TagVo;
import java.util.List;
public interface TagService {
List<TagVo> findTagsByArticleId(Long articleId);
Result hots(int limit);
}
实现类TagServiceImpl
package com.mszlu.blogapi.service.impl;
import com.mszlu.blogapi.dao.mapper.TagMapper;
import com.mszlu.blogapi.dao.pojo.Tag;
import com.mszlu.blogapi.service.TagService;
import com.mszlu.blogapi.vo.Result;
import com.mszlu.blogapi.vo.TagVo;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Service
public class TagServiceImpl implements TagService {
@Autowired(required = false)
private TagMapper tagMapper;
public TagVo copy(Tag tag){
TagVo tagVo = new TagVo();
BeanUtils.copyProperties(tag,tagVo);
return tagVo;
}
public List<TagVo> copyList(List<Tag> tagList){
List<TagVo> tagVoList = new ArrayList<>();
for (Tag tag : tagList) {
tagVoList.add(copy(tag));
}
return tagVoList;
}
@Override
public List<TagVo> findTagsByArticleId(Long articleId) {
//mybatis plus 无法进行多表查询
List<Tag> tags = tagMapper.findTagsByArticleId(articleId);
return copyList(tags);
}
@Override
public Result hots(int limit) {
/*
* 1. 标签所拥有的文章数量最多 最热标签
* 2. 查询 根据tag_id分组 计数,从大到小 排列 取前limit个
* */
List<Long> tagIds = tagMapper.findHotsTagIds(limit);
if (CollectionUtils.isEmpty(tagIds)) {
return Result.success(Collections.emptyList());
}
//需求的是 tagId 和 tagName Tag对象
//select * from tag where id in (1,2,3,4)
List<Tag> tagList = tagMapper.findTagsByTagId(tagIds);
return Result.success(tagList);
}
}
Dao层
mapper层=dao层,现在用mybatis逆向工程生成的mapper层,其实就是dao层。
dao层对数据库进行数据持久化操作,他的方法语句是直接针对数据库操作的,而service层是针对我们controller,也就是针对我们使用者。service的impl是把mapper和service进行整合的文件。
dao层和service层关系:service层经常要调用dao层的方法对数据进行增删改查的操作,现实开发中,对业务的操作会涉及到数据的操作,而对数据操作常常要用到数据库,所以service层会经常调用dao层的方法。
ArticleMapper
package com.mszlu.blogapi.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mszlu.blogapi.dao.dos.Archives;
import com.mszlu.blogapi.dao.pojo.Article;
import org.springframework.context.annotation.Configuration;
import java.util.List;
public interface ArticleMapper extends BaseMapper<Article> {
List<Archives> listArchives();
}
SysUserMapper
package com.mszlu.blogapi.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mszlu.blogapi.dao.pojo.SysUser;
import com.mszlu.blogapi.dao.pojo.Tag;
public interface SysUserMapper extends BaseMapper<SysUser> {
}
TagMapper
package com.mszlu.blogapi.dao.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mszlu.blogapi.dao.pojo.Tag;
import java.util.List;
public interface TagMapper extends BaseMapper<Tag> {
/*
* 根据文章id查询标签列表
* @param articleId
* @return*/
List<Tag> findTagsByArticleId(Long articleId);
/*
* 查询最热的标签 前n条
* @param limit
* @return*/
List<Long> findHotsTagIds(int limit);
List<Tag> findTagsByTagId(List<Long> tagIds);
}
统一异常处理
不管是controller层还是service,dao层,都有可能报异常,如果是预料中的异常,可以直接捕获处理,如果是意料之外的异常,需要统一进行处理,进行记录,并给用户提示相对比较友好的信息。
AllExceptionHandler
package com.mszlu.blogapi.handler;
import com.mszlu.blogapi.vo.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
//对加了@Controller注解的方法进行拦截处理 AOP的实现
@ControllerAdvice
public class AllExceptionHandler {
//进行异常处理,处理Exception.class的异常
@ExceptionHandler(Exception.class)
@ResponseBody //返回json数据
public Result doException(Exception ex) {
ex.printStackTrace();
return Result.fail(-999, "系统异常");
}
}
遇到的问题和bug
1.后端接口与前端接口不一致,导致连接超时

解决思路:
去掉dev.env.js文件中,“8888”的后缀“/api”

2.在打开父工程的时候,无法识别子过程主函数。
解决思路:
- 从新建中选择已有模块

- 选择子工程
- 选择maven工程
- 创建后即可识别子工程

明后两天学习计划
继续看教学视频学习springboot
学习进度和反思
- 目前看到第十集,登录功能的实现
- 看弹幕的时候要谨慎,不能不辨对错就将弹幕所说的操作执行,可能会带来严重后果(弹幕说把前端接口加上“/api”,否则后期难以部署,结果我加上以后前后端接口就对不上了,找bug找了三个小时)

3万+

被折叠的 条评论
为什么被折叠?



