diff --git a/docs/tasks/任务4-Sprint-1-restful接口回应数据的统一封装的实现.md b/docs/tasks/任务4-Sprint-1-restful接口回应数据的统一封装的实现.md index ed8b81e..e339563 100644 --- a/docs/tasks/任务4-Sprint-1-restful接口回应数据的统一封装的实现.md +++ b/docs/tasks/任务4-Sprint-1-restful接口回应数据的统一封装的实现.md @@ -102,18 +102,6 @@ public class ResultUtil { } ``` -#### 5. 自定义API接口版本号 -在application.yml配置文件中添加如下配置: -```yaml - -app: - version: v1 - default: - head-icon: https://assets.paopao.info/public/avatar/default/joshua.png - - -``` - ### 技术/工具需求: - [列出完成任务所需的技术栈、工具、软件版本等。] diff --git a/docs/tasks/任务6-Sprint-1-注册接口实现.md b/docs/tasks/任务6-Sprint-1-注册接口实现.md index c259c75..24c98ac 100644 --- a/docs/tasks/任务6-Sprint-1-注册接口实现.md +++ b/docs/tasks/任务6-Sprint-1-注册接口实现.md @@ -1,35 +1,29 @@ ## 任务名称: 实现注册接口 -#### 目标: +### 目标: - 掌握Restful API设计 - 掌握RestController类的使用 - 掌握JPA Entity类的使用 - 掌握JPA Repository接口的使用 -#### 预备知识: +### 预备知识: - Restful API设计 - JPA -#### 操作步骤: -##### 1. 创建RestController类:AuthController -```java -package com.lk.paopao.controller; +### 操作步骤: -@RestController -@RequestMapping("${app.version}/auth") -public class AuthController { - @Autowired - AuthService authService; - - @PostMapping("/register") - public DataResult register(@RequestBody RegisterRequest req) { - User saved = authService.register(req); - - return ResultUtil.ok(saved); - } -} +#### 1. 撰写注册接口的API文档 +使用模板[API接口说明模板](../API接口说明模板.md),根据演示站点的实际请求和回应,填写API接口说明模板。 +#### 2. 自定义配置 +在配置文件中设置接口的版本号,用户的缺省头像地址。在application.yml配置文件中添加如下配置: +```yaml +app: + version: v1 + default: + head-icon: https://assets.paopao.info/public/avatar/default/joshua.png ``` -##### 2. 创建请求类RegisterRequest +#### 3. 创建请求类RegisterRequest +根据注册api接口说明文档,前端通过post方式在请求体中携带用户帐号和密码信息,创建请求类RegisterRequest来获取用户帐号和密码信息。 ```java package com.lk.paopao.dto.rest.request; @@ -41,11 +35,20 @@ public class RegisterRequest { } ``` -##### 3. 创建实体类User +#### 4. 创建实体类User + +基于需求分析获得用户实体类User。 ```java -package com.lk.paopao.domain; +package com.lk.paopao.entity; +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.Comment; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; @Getter @Setter @@ -64,22 +67,18 @@ public class User { private Long id; @Comment("昵称") - @ColumnDefault("") - @Column(name = "nickname", nullable = false, length = 32) + @Column(name = "nickname", length = 32) private String nickname; @Comment("用户名") - @ColumnDefault("") @Column(name = "username", nullable = false, length = 32) private String username; @Comment("手机号") - @ColumnDefault("") @Column(name = "phone", length = 16) private String phone; @Comment("密码") - @ColumnDefault("") @Column(name = "password", nullable = false, length = 32) private String password; @@ -88,12 +87,11 @@ public class User { private Byte status = 1; @Comment("用户头像") - @ColumnDefault("") @Column(name = "avatar", nullable = false) private String avatar; @Comment("是否管理员") - @Column(name = "is_admin") + @Column(name = "is_admin",nullable = false) private Boolean isAdmin = false; @Comment("创建时间") @@ -107,9 +105,8 @@ public class User { private Long modifiedOn; @Comment("删除时间") - @ColumnDefault("0") - @Column(name = "deleted_on", nullable = false) - private Long deletedOn = 0L; + @Column(name = "deleted_on") + private Long deletedOn; @Comment("是否删除 0 为未删除、1 为已删除") @Column(name = "is_del") @@ -118,48 +115,166 @@ public class User { } ``` -##### 4. 创建接口UserRepository +**代码中使用的注解:** + +- **`@Getter`** 和 **`@Setter`**: Lombok库提供的注解,自动生成类的getter和setter方法,减少样板代码。 + +- **`@Comment`**: Hibernate的注解,为实体类或其属性添加注释说明,通常用于ORM映射时的描述。 + +- **`@Entity`**: 表示这个类是一个JPA实体,映射到数据库中的一个表。 + +- **`@Table`**: 指定实体类映射到的数据库表的名称,以及表的索引和唯一性约束。 + + - `name`: 映射到的表的名称。 + - `indexes`: 定义表的索引,提高查询效率。 + - `uniqueConstraints`: 定义表的唯一性约束,确保列的组合在表中是唯一的。 + +- **`@EntityListeners(AuditingEntityListener.class)`**: 指定实体类要使用的实体监听器,`AuditingEntityListener`用于自动维护实体的创建时间和修改时间。 + +- **`@Id`**: 标记实体类中的属性作为数据库表的主键。 + +- **`@GeneratedValue(strategy = GenerationType.IDENTITY)`**: 指定主键的生成策略,`IDENTITY`表示使用数据库的自增ID。 + +- **`@Column`**: 用于映射实体类属性到数据库表的列。 + + - `name`: 映射到的列的名称。 + - `nullable`: 指定列是否可以为null。 + - `length`: 指定列的最大长度。 + +- **`@CreatedDate`** 和 **`@LastModifiedDate`**: Spring Data JPA提供的注解,用于自动维护实体的创建时间和最后修改时间。 + +**关于实体类中的自动生成时间** + +> 阅读[JPA审计](../guides/JPA审计.md) + +**在项目的启动类中,添加@EnableJpaAuditing注解, 启用JPA审计功能。** + +```java +package com.lk.paopao; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@SpringBootApplication +@EnableJpaAuditing +public class PaopaoApplication { + public static void main(String[] args) { + SpringApplication.run(PaopaoApplication.class, args); + } +} + +``` + +#### 5. 创建JPA接口 UserRepository +为用户实体类User创建JPA接口UserRepository。 并提供一个根据用户帐号查询用户的方法。 ```java public interface UserRepository extends JpaRepository { - + Optional findByUsername(String username); } ``` -##### 5. 创建AuthService +**代码解释:** +- `JpaRepository`: UserRepository继承自JpaRepository接口,需要指定实体类和主键类型。 +- `Optional findByUsername(String username)`: 这是一个JPA查询方法,用于根据用户名查询用户,这是使用了基于命名的查询,jpa的命名查询会根据方法名自动生成SQL语句。 +- `Optional`: 这是一个Java8提供的新类型,用于表示可能为空的值。 + +#### 6. 创建AuthService + +AuthService类用于实现用户注册、登录等认证相关功能。 + ```java package com.lk.paopao.service; + +import com.lk.paopao.dto.rest.request.RegisterRequest; +import com.lk.paopao.entity.User; +import com.lk.paopao.exception.ResourceExistedException; +import com.lk.paopao.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + @Service @Transactional public class AuthService { - @Autowired - PasswordEncoder encoder; // 自动注入密码编码器 - @Autowired - UserRepository userRepository; // 自动注入用户仓库 + // 注入用户实体的数据库接口 + @Autowired + UserRepository userRepository; + // 从配置文件中读取默认头像地址 + @Value("${app.default.head-icon}") + private String DEFAULT_HEAD_ICON; - @Value("${app.default.head-icon}") - private String DEFAULT_HEAD_ICON; // 从配置文件中读取默认头像地址 - - /** - * 用户注册。 - * @param reg 注册请求对象,包含用户名和密码 - * @return 注册后的用户信息 - */ - public User register(RegisterRequest reg) { - // 检查用户是否已存在 - userRepository.findByUsername(reg.getUsername()).ifPresent((u)-> {throw new ResourceNotFoundException("","");}); - // 创建用户对象并设置信息 - User user = new User(); - user.setPassword(encoder.encode(reg.getPassword())); // 编码密码 - user.setAvatar(DEFAULT_HEAD_ICON); // 设置默认头像 - user.setNickname(reg.getUsername()); // 设置昵称为用户名 - user.setUsername(reg.getUsername()); // 设置用户名 - return userRepository.save(user); // 保存用户到数据库 - } + /** + * 用户注册。 + * @param reg 注册请求对象,包含用户名和密码 + * @return 注册后的用户信息 + */ + public User register(RegisterRequest reg) { + // 检查用户名是否已存在,如果存在则抛出异常 + userRepository.findByUsername(reg.getUsername()).orElseThrow(() -> new ResourceExistedException("User","useranme="+reg.getUsername())); + // 创建用户对象并设置信息 + User user = new User(); + // 设置密码,暂时使用明文密码 + user.setPassword(reg.getPassword()); + // 设置昵称为用户名 + user.setUsername(reg.getUsername()); + // 设置昵称为用户名 + user.setNickname(reg.getUsername()); + // 设置默认头像 + user.setAvatar(DEFAULT_HEAD_ICON); + // 在数据库中创建用户 + return userRepository.save(user); + } } - ``` +**代码中使用的注解:** +- **`@Service`**: 用于标识一个服务类,用于定义业务逻辑。 +- **`@Transactional`**: 用于标识一个事务方法,用于确保方法中的所有数据库操作在一个事务中执行,要么都成功,要么都失败。 +- **`@Autowired`**: 用于自动装配依赖。 +- **`@Value`**: 用于从配置文件中读取属性值。 -##### 6. 测试 +#### 7. 创建RestController类:AuthController +```java +package com.lk.paopao.controller; + +import com.lk.paopao.dto.rest.request.RegisterRequest; +import com.lk.paopao.dto.rest.response.DataResult; +import com.lk.paopao.dto.rest.response.ResultUtil; +import com.lk.paopao.entity.User; +import com.lk.paopao.service.AuthService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("${app.version}/auth") +public class AuthController { + @Autowired + AuthService authService; + + /** + * 处理用户注册请求。 + * @param req 包含用户注册信息的请求体。 + * @return 返回注册结果,包括注册成功的用户信息。 + */ + @PostMapping("/register") + public DataResult register(@RequestBody RegisterRequest req) { + User saved = authService.register(req); + return ResultUtil.ok(saved); + } +} +``` +**代码中使用的注解:** +- **`@RestController`**: 用于标识一个RESTful控制器,用于处理RESTful请求。 +- **`@RequestMapping`**: 用于映射请求路径。 +- **`@PostMapping`**: 用于映射POST请求。**`@PostMapping("/register")`**: 映射POST请求到/register路径。 +- **`@Autowired`**: 用于自动装配依赖。 +- **`@RequestBody`**: 用于从请求体中获取请求参数。 + +#### 8. 测试 http://localhost:8080/api-docs diff --git a/src/main/java/com/lk/paopao/controller/AuthController.java b/src/main/java/com/lk/paopao/controller/AuthController.java index d3d1083..cb78b9a 100644 --- a/src/main/java/com/lk/paopao/controller/AuthController.java +++ b/src/main/java/com/lk/paopao/controller/AuthController.java @@ -17,10 +17,14 @@ public class AuthController { @Autowired AuthService authService; + /** + * 处理用户注册请求。 + * @param req 包含用户注册信息的请求体。 + * @return 返回注册结果,包括注册成功的用户信息。 + */ @PostMapping("/register") public DataResult register(@RequestBody RegisterRequest req) { User saved = authService.register(req); - return ResultUtil.ok(saved); } } diff --git a/src/main/java/com/lk/paopao/entity/User.java b/src/main/java/com/lk/paopao/entity/User.java index 953165d..b2de521 100644 --- a/src/main/java/com/lk/paopao/entity/User.java +++ b/src/main/java/com/lk/paopao/entity/User.java @@ -3,7 +3,6 @@ package com.lk.paopao.entity; import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; -import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.Comment; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; @@ -38,7 +37,6 @@ public class User { private String phone; @Comment("密码") - @ColumnDefault("") @Column(name = "password", nullable = false, length = 32) private String password; @@ -51,7 +49,7 @@ public class User { private String avatar; @Comment("是否管理员") - @Column(name = "is_admin") + @Column(name = "is_admin",nullable = false) private Boolean isAdmin = false; @Comment("创建时间") @@ -65,9 +63,8 @@ public class User { private Long modifiedOn; @Comment("删除时间") - @ColumnDefault("0") @Column(name = "deleted_on") - private Long deletedOn = 0L; + private Long deletedOn; @Comment("是否删除 0 为未删除、1 为已删除") @Column(name = "is_del") diff --git a/src/main/java/com/lk/paopao/service/AuthService.java b/src/main/java/com/lk/paopao/service/AuthService.java index 21875e6..5823d79 100644 --- a/src/main/java/com/lk/paopao/service/AuthService.java +++ b/src/main/java/com/lk/paopao/service/AuthService.java @@ -3,6 +3,7 @@ package com.lk.paopao.service; import com.lk.paopao.dto.rest.request.RegisterRequest; import com.lk.paopao.entity.User; +import com.lk.paopao.exception.ResourceExistedException; import com.lk.paopao.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -12,13 +13,12 @@ import org.springframework.transaction.annotation.Transactional; @Service @Transactional public class AuthService { -// @Autowired -// PasswordEncoder encoder; // 自动注入密码编码器 + // 注入用户实体的数据库接口 @Autowired - UserRepository userRepository; // 自动注入用户仓库 - + UserRepository userRepository; + // 从配置文件中读取默认头像地址 @Value("${app.default.head-icon}") - private String DEFAULT_HEAD_ICON; // 从配置文件中读取默认头像地址 + private String DEFAULT_HEAD_ICON; /** * 用户注册。 @@ -26,17 +26,19 @@ public class AuthService { * @return 注册后的用户信息 */ public User register(RegisterRequest reg) { - // 检查用户是否已存在 - if(userRepository.findByUsername(reg.getUsername()).isPresent()){ - return null; - } + // 检查用户名是否已存在,如果存在则抛出异常 + userRepository.findByUsername(reg.getUsername()).orElseThrow(() -> new ResourceExistedException("User","useranme="+reg.getUsername())); // 创建用户对象并设置信息 User user = new User(); -// user.setPassword(encoder.encode(reg.getPassword())); // 编码密码 - user.setAvatar(DEFAULT_HEAD_ICON); // 设置默认头像 - user.setNickname(reg.getUsername()); // 设置昵称为用户名 - user.setUsername(reg.getUsername()); // 设置用户名 + // 设置密码,暂时使用明文密码 user.setPassword(reg.getPassword()); - return userRepository.save(user); // 保存用户到数据库 + // 设置昵称为用户名 + user.setUsername(reg.getUsername()); + // 设置昵称为用户名 + user.setNickname(reg.getUsername()); + // 设置默认头像 + user.setAvatar(DEFAULT_HEAD_ICON); + // 在数据库中创建用户 + return userRepository.save(user); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 6453e4e..afaeb53 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -4,13 +4,12 @@ springdoc: spring: datasource: - url: jdbc:h2:file:./paopao.h2 + url: jdbc:h2:file:./paopao.h2 # 使用文件存储 driverClassName: org.h2.Driver username: root password: root - # initialization-mode: always h2: - console: # 开启console 访问 默认false + console: # 开启console访问 默认false enabled: true settings: trace: true # 开启h2 console 跟踪 方便调试 默认 false @@ -22,11 +21,11 @@ spring: defer-datasource-initialization: true database-platform: org.hibernate.dialect.H2Dialect hibernate: - ddl-auto: create-drop # update create-drop + ddl-auto: create-drop + # 可选值:create-drop,create,update,none. create-drop:每次启动项目都会删除表,然后重新创建表,适合开发环境;create:每次启动项目都会创建表,适合开发环境;update:每次启动项目都会更新表,适合开发环境;none:不执行任何操作,适合生产环境。 properties: hibernate: format_sql: true - dialect: org.hibernate.dialect.H2Dialect app: version: v1