目录
1.说明
该文章是使用 IDEA 开发工具和 Mysql 数据库。使用了 Swagger3+mybatis+mysql。
2.创建项目
点击 新建-项目 出现此界面,后 生成器 选择 Spring Boot,类型选择Maven。
点击下一步后 点到Web 选择 SpringWeb 点击创建。
创建好后则是下图样子。
3.搭建 Swagger3 界面
3.1.引入 pom 依赖
引入依赖的包。
实例:
代码附上:
<!-- 定义一个Maven依赖项,用于集成OpenAPI(原Swagger)到Spring Boot Web MVC项目中 --> <dependency> <!-- 指定该依赖项所属的组ID,这里是org.springdoc,它是一个组织或项目的唯一标识符 --> <groupId>org.springdoc</groupId> <!-- 指定该依赖项的具体名称,即Artifact ID,这里是springdoc-openapi-starter-webmvc-ui,表示这是一个用于Spring MVC项目的OpenAPI UI启动器 --> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <!-- 指定使用的具体版本号,这里是2.2.0,确保引入的是特定版本的库文件,以兼容当前项目的其他依赖 --> <version>2.2.0</version> </dependency>3.2.添加配置
application.properties 或 application.yml 文件添加配置
实例:
代码附上:
# 设置Spring Boot应用的名字为 "JavaDemoApi"。这个名称可以在服务发现和注册场景中被使用. # 比如在Eureka、Consul等服务注册与发现工具中作为服务标识。 spring.application.name=JavaDemoApi # 配置Spring MVC路径匹配策略为AntPathMatcher,默认情况下也是使用AntPathMatcher。 # 这里明确指定是为了确保即使Spring Boot版本更新后默认行为发生变化,仍然使用Ant风格的路径匹配规则。 spring.mvc.pathmatch.matching-strategy=ant_path_matcher # 设置Spring Boot应用运行的端口号为9090。 # 这是当有多个应用程序在同一台服务器上运行时,用来区分不同应用的一种方式。通过改变端口号,可以避免端口冲突的问题。 server.port=90903.3.添加Swagger3.0配置
添加 OpenAPIConfig 文件配置Swagger。
实例:
代码如下:
package com.example.javademoapi.Base; import io.swagger.v3.oas.models.ExternalDocumentation; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 配置类,用于定义Swagger3的OpenAPI规范。 * 通过Spring的@Bean注解,将OpenAPI对象注册到Spring容器中。 * * @author: Kevin * @datetime: 2024/11/21 11:05:28 * @desc: 配置Swagger3接口文档的基本信息 */ @Configuration public class OpenAPIConfig { /** * 创建并返回一个OpenAPI对象,该对象包含了接口文档的元数据信息。 * @return OpenAPI对象,包含接口文档的标题、描述、版本等信息 */ @Bean public OpenAPI openAPI() { return new OpenAPI() .info(new Info() .title("Java WebApi") // 设置接口文档的标题 .description("SpringBoot3+Swagger3 Test") // 设置接口文档的描述 .version("v1")) // 设置接口文档的版本号 .externalDocs(new ExternalDocumentation() .description("WebAPI文档") // 设置外部文档的描述 .url("https://www.baidu.com/")); // 设置外部文档的URL } }3.4.Swagger 添加完成
运行项目即可看到Swagger界面。
实例:
4. 集成 mybatis
集成 mybatis 操作Mysql数据库。
4.1.引入 pom 依赖
引入 mybatis 和 mysql 的包。
实例:
代码附上:
<!-- MyBatis --> <dependency> <!-- 指定该依赖项所属的组ID,这里是org.mybatis.spring.boot,表示该依赖项属于MyBatis Spring Boot项目 --> <groupId>org.mybatis.spring.boot</groupId> <!-- 指定该依赖项的具体名称,即Artifact ID,这里是mybatis-spring-boot-starter,表示这是一个用于Spring Boot项目的MyBatis启动器 --> <artifactId>mybatis-spring-boot-starter</artifactId> <!-- 指定使用的具体版本号,这里是3.0.3,确保引入的是特定版本的库文件,以兼容当前项目的其他依赖 --> <version>3.0.3</version> </dependency> <!-- MySQL --> <dependency> <!-- 指定该依赖项所属的组ID,这里是mysql,表示该依赖项属于MySQL项目 --> <groupId>mysql</groupId> <!-- 指定该依赖项的具体名称,即Artifact ID,这里是mysql-connector-java,表示这是一个用于Java应用程序连接MySQL数据库的驱动程序 --> <artifactId>mysql-connector-java</artifactId> <!-- 指定使用的具体版本号,这里是8.0.33,确保引入的是特定版本的库文件,以兼容当前项目的其他依赖 --> <version>8.0.33</version> </dependency>4.2.配置数据库连接字符串
在 application.properties 或 application.yml 文件添加配置 mysql 连接字符串和设置一下应用的路径。
实例:
代码附上:
# 设置应用的上下文路径,即应用的根路径,默认为空,表示应用的根路径为 / server.servlet.context-path=/ # 数据源配置 # 数据库连接 URL # 这里指定了数据库的地址、端口、数据库名称、以及其他连接参数 # test 表示连接的数据库名称 # useSSL=false 表示不使用 SSL 连接 # serverTimezone=UTC 设置服务器时区为 UTC spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC # 数据库用户名 spring.datasource.username=root # 数据库密码 spring.datasource.password=123456 # 数据库驱动类名,这里是 MySQL 的驱动 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # MyBatis 配置 # 指定 MyBatis 映射文件的位置,这里设置为 resources 下的 mapper 目录中的所有 XML 文件 mybatis.mapper-locations=classpath:mapper/*.xml # 指定 MyBatis 类型别名的包路径,这里的包路径为 com.example.demo_api.Api # 类型别名可以让你在映射文件中使用简短的名字代替全限定类名 mybatis.type-aliases-package=com.example.demo_api.Api
5.创建消息返回类
创建消息返回类是为了 Api 返回任何数据时保持规范性和统一性。
5.1.创建状态码枚举
实例:
代码附上:
package com.example.javademoapi.Base; /** * 状态枚举 * @author: Kevin * @datetime: 2024/11/21 * @desc: 定义消息的状态 */ public enum ResultCode { // 成功 SUCCESS(200), // 失败 FAILURE(400), // 未认证(签名错误) UNAUTHORIZED(401), // 禁止访问 FORBIDDEN(403), // 接口不存在 NOT_FOUND(404), // 服务器内部错误 INTERNAL_SERVER_ERROR(500); //状态码 public int code; ResultCode(int code) { this.code = code; } }5.2.定义泛型类
定义一个泛型类 ResponseResult,用于封装API响应结果
实例:
代码附上:
package com.example.javademoapi.Base; // 导入 ResultCode 枚举类,用于定义返回的状态码 import com.example.javademoapi.Base.ResultCode; import java.time.LocalDateTime; // 定义一个泛型类 ResponseResult,用于封装API响应结果 public class ResponseResult<T> { // 返回状态码,200表示成功 public int code; // 返回描述信息,用于提供详细的错误或成功信息 private String msg; // 返回内容体,泛型类型 T 表示可以返回任意类型的对象 private T data; private LocalDateTime time = LocalDateTime.now(); // 设置返回状态码的方法,参数为 ResultCode 枚举类型 public ResponseResult<T> setCode(ResultCode retCode) { this.code = retCode.code; // 将枚举中的状态码赋值给成员变量 code return this; // 返回当前对象,支持链式调用 } // 获取返回状态码的方法 public int getCode() { return code; // 返回成员变量 code 的值 } // 设置返回状态码的方法,参数为 int 类型 public ResponseResult<T> setCode(int code) { this.code = code; // 将传入的状态码赋值给成员变量 code return this; // 返回当前对象,支持链式调用 } // 获取返回描述信息的方法 public String getMsg() { return msg; // 返回成员变量 msg 的值 } // 设置返回描述信息的方法 public ResponseResult<T> setMsg(String msg) { this.msg = msg; // 将传入的描述信息赋值给成员变量 msg return this; // 返回当前对象,支持链式调用 } // 获取返回内容体的方法 public T getData() { return data; // 返回成员变量 data 的值 } // 设置返回内容体的方法 public ResponseResult<T> setData(T data) { this.data = data; // 将传入的内容体赋值给成员变量 data return this; // 返回当前对象,支持链式调用 } // 获取返回时间的方法 public LocalDateTime getTime() { return time; // 返回成员变量 msg 的值 } // 获取返回时间的方法 public LocalDateTime setTime() { this.time = LocalDateTime.now(); // 将传入的状态码赋值给成员变量 code return time; // 返回当前对象,支持链式调用 } }5.3.定义工具类
定义一个工具类 ResponseData,用于生成统一的响应结果
实例:
代码附上:
package com.example.javademoapi.Base; // 导入 ResultCode 枚举类,用于定义返回的状态码 import com.example.javademoapi.Base.ResultCode; // 定义一个工具类 ResponseData,用于生成统一的响应结果 public class ResponseData { // 定义一个常量字符串 "success",用于表示成功的消息 private final static String SUCCESS = "success"; // 创建一个成功的响应结果,不包含数据 public static <T> ResponseResult<T> makeOKRsp() { return new ResponseResult<T>() .setCode(ResultCode.SUCCESS) // 设置状态码为成功 .setMsg(SUCCESS); // 设置消息为 "success" } // 创建一个成功的响应结果,包含自定义的消息 public static <T> ResponseResult<T> makeOKRsp(String message) { return new ResponseResult<T>() .setCode(ResultCode.SUCCESS) // 设置状态码为成功 .setMsg(message); // 设置自定义的消息 } // 创建一个成功的响应结果,包含数据 public static <T> ResponseResult<T> makeOKRsp(T data) { return new ResponseResult<T>() .setCode(ResultCode.SUCCESS) // 设置状态码为成功 .setMsg(SUCCESS) // 设置消息为 "success" .setData(data); // 设置数据 } // 创建一个错误的响应结果,包含自定义的消息 public static <T> ResponseResult<T> makeErrRsp(String message) { return new ResponseResult<T>() .setCode(ResultCode.INTERNAL_SERVER_ERROR) // 设置状态码为内部服务器错误 .setMsg(message); // 设置自定义的消息 } // 创建一个响应结果,包含自定义的状态码和消息 public static <T> ResponseResult<T> makeRsp(ResultCode code, String msg) { return new ResponseResult<T>() .setCode(code) // 设置自定义的状态码 .setMsg(msg); // 设置自定义的消息 } // 创建一个响应结果,包含自定义的状态码、消息和数据 public static <T> ResponseResult<T> makeRsp(ResultCode code, String msg, T data) { return new ResponseResult<T>() .setCode(code) // 设置自定义的状态码 .setMsg(msg) // 设置自定义的消息 .setData(data); // 设置数据 } // 创建一个响应结果,包含自定义的状态码和消息(状态码为 int 类型) public static <T> ResponseResult<T> makeRsp(int code, String msg) { return new ResponseResult<T>() .setCode(code) // 设置自定义的状态码 .setMsg(msg); // 设置自定义的消息 } // 创建一个响应结果,包含自定义的状态码、消息和数据(状态码为 int 类型) public static <T> ResponseResult<T> makeRsp(int code, String msg, T data) { return new ResponseResult<T>() .setCode(code) // 设置自定义的状态码 .setMsg(msg) // 设置自定义的消息 .setData(data); // 设置数据 } }
6.创建四层架构
创建 Model,Controller,Mapper,Service 四层架构。
- Model 是数据库实体和接收响应数据模型层
- Controller 是 Api控制器层
- Mapper 是指永久层,即数据访问层
- Service 是业务逻辑层
实例:
6.1.Model
每操作一张数据库的表则创建一个相应的实体。
6.1.1.简化
为了简化实体的 get 和 set 我们可以在 pom 先加一个 Lombok 依赖包。
实例:
代码附上:
<!-- Lombok --> <dependency> <!-- 指定该依赖项所属的组ID,这里是 org.projectlombok,表示该依赖项属于 Project Lombok 项目 --> <groupId>org.projectlombok</groupId> <!-- 指定该依赖项的具体名称,即 Artifact ID,这里是 lombok,表示这是一个用于简化 Java 代码的 Lombok 库 --> <artifactId>lombok</artifactId> <!-- 注意:通常情况下,Lombok 的版本号会由 Spring Boot 的依赖管理自动处理,所以这里不需要显式指定版本号 --> <!-- 如果需要指定版本号,可以添加 <version> 标签,例如:<version>1.18.24</version> --> </dependency>6.1.2.实体
实例:
代码附上:
package com.example.javademoapi.Api.Model.Entity; import lombok.Data; /** * User实体类 * * @author: Kevin * @datetime: 2024/11/21 * @desc: */ @Data public class UserEntity { /** * id主键 **/ private String id; /** * 登录用户名 **/ private String userid; /** * 用户名 **/ private String username; /** * 密码 **/ private String pwd; /** * 邮箱 **/ private String email; }6.2.Mapper
创建 UserMapper 接口
6.2.1.接口
实例:
代码附上:
package com.example.javademoapi.Api.Mapper; import com.example.javademoapi.Api.Model.Entity.UserEntity; import org.apache.ibatis.annotations.*; import java.util.List; @Mapper public interface UserMapper { // 查询所有用户 @Select("SELECT * FROM users") List<UserEntity> getAllUsers(); // 根据用户ID查询用户 @Select("SELECT * FROM users WHERE id = #{id}") UserEntity getUserById(String id); // 插入新用户 @Insert("INSERT INTO users (id, userid, username, pwd, email) VALUES (#{id}, #{userid}, #{username}, #{pwd}, #{email})") int addUser(UserEntity user); // 更新用户信息 @Update("UPDATE users SET userid=#{userid}, username=#{username}, pwd=#{pwd}, email=#{email} WHERE id=#{id}") int updateUser(UserEntity user); // 删除用户 @Delete("DELETE FROM users WHERE id=#{id}") int removeUser(String id); }6.2.2.映射
创建XML文件映射SQL
实例:
代码附上:
<?xml version="1.0" encoding="UTF-8" ?> <!-- 定义文档类型,指明这是一个MyBatis的Mapper文件,遵循MyBatis 3.0的DTD规范 --> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 定义一个Mapper,namespace属性必须与对应的Mapper接口的全限定名一致 --> <mapper namespace="com.example.javademoapi.Api.Mapper"> <!-- 定义一个查询方法,id属性对应Mapper接口中的方法名,resultType属性指定返回结果的类型 --> <select id="getAllUsers" resultType="com.example.javademoapi.Api.Model.Entity.UserEntity"> <!-- SQL查询语句,查询users表中的所有记录 --> SELECT * FROM users </select> <!-- 定义一个查询方法,id属性对应Mapper接口中的方法名,parameterType属性指定输入参数的类型,resultType属性指定返回结果的类型 --> <select id="getUserById" parameterType="string" resultType="com.example.javademoapi.Api.Model.Entity.UserEntity"> <!-- SQL查询语句,根据id查询users表中的记录 --> SELECT * FROM users WHERE id = #{id} </select> <!-- 定义一个插入方法,id属性对应Mapper接口中的方法名,parameterType属性指定输入参数的类型 --> <insert id="insertUser" parameterType="com.example.javademoapi.Api.Model.Entity.UserEntity"> <!-- SQL插入语句,向users表中插入一条记录 --> INSERT INTO users (id, userid, username, pwd, email) VALUES (#{id}, #{userid}, #{username}, #{pwd}, #{email}) </insert> <!-- 定义一个更新方法,id属性对应Mapper接口中的方法名,parameterType属性指定输入参数的类型 --> <update id="updateUser" parameterType="com.example.javademoapi.Api.Model.Entity.UserEntity"> <!-- SQL更新语句,更新users表中指定id的记录 --> UPDATE users SET userid=#{userid}, username=#{username}, pwd=#{pwd}, email=#{email} WHERE id=#{id} </update> <!-- 定义一个删除方法,id属性对应Mapper接口中的方法名,parameterType属性指定输入参数的类型 --> <delete id="deleteUserById" parameterType="string"> <!-- SQL删除语句,删除users表中指定id的记录 --> DELETE FROM users WHERE id = #{id} </delete> <!-- 可以在这里定义其他CRUD方法 --> </mapper>6.3.Service
创建 UserService
实例:
代码附上:
package com.example.javademoapi.Api.Service; import com.example.javademoapi.Api.Mapper.UserMapper; import com.example.javademoapi.Api.Model.Entity.UserEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.List; import java.util.concurrent.CompletableFuture; /** * 用户业务逻辑类 * author: Kevin * datetime: 2024/11/21 * desc: 处理对用户表的业务逻辑 */ @Service public class UserService { // 注入UserMapper,用于执行数据库操作 private final UserMapper userMapper; // 构造函数注入UserMapper @Autowired public UserService(UserMapper userMapper) { this.userMapper = userMapper; } // 标记为异步方法,可以在后台线程中执行 @Async public CompletableFuture<List<UserEntity>> getAllUsers() { // 使用CompletableFuture异步执行查询所有用户的操作 return CompletableFuture.supplyAsync(userMapper::getAllUsers); } // 根据用户ID查询用户 public UserEntity getUserById(String id) { return userMapper.getUserById(id); } // 添加新用户 public int addUser(UserEntity user) { return userMapper.addUser(user); } // 更新用户信息 public int saveUser(UserEntity user) { return userMapper.updateUser(user); } // 删除用户 public int removeUser(String id) { return userMapper.removeUser(id); } }6.3.1.异步
如需启用 Spring 的异步方法支持,需添加 AsyncConfig,Service 里的 getAllUsers 方法就是一个异步方法。
实例:
代码附上:
package com.example.javademoapi.Base; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; //启用异步支持 @Configuration//这个注解表示该类是一个配置类,Spring 将会扫描并加载这个类中的配置 @EnableAsync//启用 Spring 的异步方法支持 public class AsyncConfig { }6.4.Controller
创建 UserController 控制器
实例:
代码附上:
package com.example.javademoapi.Api.Controller; import com.example.javademoapi.Api.Service.UserService; import com.example.javademoapi.Base.ResponseData; import com.example.javademoapi.Base.ResponseResult; import com.example.javademoapi.Api.Model.Entity.UserEntity; import com.example.javademoapi.Base.ResultCode; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; /** * 用户控制器 * author: Kevin * datetime: 2024/11/21 * desc: */ // 使用 Swagger3 标签注解描述控制器 @Tag(name = "User DatabaseController", description = "User DatabaseController") // 使用 Spring 的 RestController 注解标识这是一个 REST 控制器 @RestController // 设置请求路径前缀 @RequestMapping("/User") public class UserController { // 用户服务类,用于处理业务逻辑 private final UserService userService; // 构造函数注入用户服务类 @Autowired public UserController(UserService userService) { this.userService = userService; } // 获取所有用户数据的 API 方法 @Operation(summary = "获取所有用户数据", description = "此方法用于获取系统中所有用户的详细信息")//描述 API 方法的主要功能注解 //指定 HTTP GET 请求的路径和返回的内容类型。 @GetMapping(value = "/getAllUsers", produces = "application/json") //定义可能的 HTTP 响应代码及其描述 @ApiResponses({ @ApiResponse(responseCode = "200", description = "请求成功"), @ApiResponse(responseCode = "400", description = "请求参数没填好"), @ApiResponse(responseCode = "401", description = "没有权限"), @ApiResponse(responseCode = "403", description = "禁止访问"), @ApiResponse(responseCode = "404", description = "请求路径没有或页面跳转路径不对"), @ApiResponse(responseCode = "500", description = "服务器连接失败") }) //异步方法案例 public CompletableFuture<ResponseResult<List<UserEntity>>> getAllUsers() { // 调用 UserService 中的异步方法 getAllUsers,返回 CompletableFuture<List<UserEntity>> return userService.getAllUsers().thenApply(users -> { try { // 构建响应结果并返回 // 使用 ResponseData 类的静态方法 makeRsp 创建一个成功的响应结果 // 参数分别为:状态码、消息、数据 return ResponseData.makeRsp(ResultCode.SUCCESS, "操作成功", users); } catch (Exception e) { // 捕获并处理异常 // 使用 ResponseData 类的静态方法 makeRsp 创建一个失败的响应结果 // 参数分别为:状态码、消息、数据 return ResponseData.makeRsp(ResultCode.FAILURE, "操作失败: " + e.getMessage(), null); } }); } // 根据 ID 查询用户数据的 API 方法 @Operation(summary = "根据Id查询用户数据") //Parameters描述方法的所有参数,@Parameter用于描述单个参数的详细信息 @Parameters({@Parameter(name = "id", description = "Id 主键")}) @GetMapping(value = "/getUserById/{id}", produces = "application/json") //@RequestParam用于从请求的查询参数中提取值,@PathVariable用于从请求的 URL 路径中提取值,@Parameter用于提供参数的详细描述 public ResponseResult<?> getUserById(@RequestParam @Parameter(example = "1") @PathVariable String id) { // 调用用户服务类的 getUserById 方法根据 ID 获取用户数据 UserEntity users = userService.getUserById(id.trim()); // 构建响应结果并返回 return ResponseData.makeRsp(ResultCode.SUCCESS, "操作成功", users); } // 新增用户数据 API 方法 @Operation(summary = "新增用户数据") @PostMapping(value = "/addUser", produces = "application/json") //@RequestBody用于将 HTTP 请求的正文绑定到控制器方法的参数上,@Valid用于触发验证注解 public ResponseResult<Boolean> addUser(@Valid @RequestBody UserEntity user) { try { // 生成一个唯一的 guid 并设置给用户对象的 id user.setId(UUID.randomUUID().toString()); // 调用用户服务类的 addUser 方法插入用户数据 boolean result = userService.addUser(user) > 0; // 构建响应结果并返回 return ResponseData.makeRsp(ResultCode.SUCCESS, "操作成功", result); } catch (Exception e) { e.printStackTrace(); return ResponseData.makeRsp(ResultCode.FAILURE, "操作失败", false); } } // 修改用户数据 API 方法 @Operation(summary = "修改用户数据") @PutMapping(value = "/saveUser", produces = "application/json") public ResponseResult<Boolean> saveUser(@Valid @RequestBody UserEntity user) { try { // 调用用户服务类的 saveUser 方法插入用户数据 boolean result = userService.saveUser(user) > 0; // 构建响应结果并返回 return ResponseData.makeRsp(ResultCode.SUCCESS, "操作成功", result); } catch (Exception e) { e.printStackTrace(); return ResponseData.makeRsp(ResultCode.FAILURE, "操作失败", false); } } // 删除用户数据 API 方法 @Operation(summary = "删除用户数据") @Parameters({@Parameter(name = "id", description = "Id 主键")}) @DeleteMapping(value = "/removeUser", produces = "application/json") public ResponseResult<Boolean> removeUser(@RequestParam @Parameter(example = "1") String id) { try { // 调用用户服务类的 removeUser 方法插入用户数据 boolean result = userService.removeUser(id.trim()) > 0; // 构建响应结果并返回 return ResponseData.makeRsp(ResultCode.SUCCESS, "操作成功", result); } catch (Exception e) { e.printStackTrace(); return ResponseData.makeRsp(ResultCode.FAILURE, "操作失败", false); } } }
7.完成
至此已完成Java开发WebApi的所有步骤,效果如下

 
                   
                   
                   
                   
                            



















 
                     
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   2297
					2297
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            