From 91e36d5a843cf6e02446c0c6a02443e8e5d1932c Mon Sep 17 00:00:00 2001 From: Hanserwei Date: Sat, 4 Oct 2025 14:56:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(user):=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=9C=8D=E5=8A=A1=E6=A8=A1=E5=9D=97=20update?= =?UTF-8?q?(gateway):=E6=9B=B4=E6=96=B0=E7=BD=91=E5=85=B3=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E8=B7=AF=E7=94=B1=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加用户服务基础架构,包括 API 和 Biz 模块 - 配置用户服务的 Spring Boot 启动类及 MyBatis Plus 配置- 实现用户信息更新接口,支持头像、昵称、小憨书号等字段校验 - 添加全局异常处理器,统一处理业务异常和参数校验错误 - 集成 Nacos 服务发现与配置中心 - 添加日志配置文件,支持异步日志写入 - 新增用户相关枚举类,如性别、响应码等 - 添加参数校验工具类,用于昵称、小憨书号等格式校验 - 配置网关路由,将 /user/** 路径转发至用户服务 - 在 GitIgnore 中忽略用户服务的本地开发配置文件 - 更新认证服务中的用户相关字段命名与接口路径 - 添加用户数据对象 UserDO 及对应的 Mapper 和 XML 配置 - 实现 UserService 接口及默认实现类 UserServiceImpl - 添加用户信息更新请求 VO 类 UpdateUserInfoReqVO - 添加用户 Mapper 接口 UserDOMapper 继承 BaseMapper - 添加用户模块的 Maven 配置 pom.xml 文件 - 添加用户模块的编码配置,确保使用 UTF-8 编码 - 添加用户模块的启动日志配置 logback-spring.xml - 添加用户模块的 bootstrap.yml 配置文件 - 添加用户模块的 application.yml 配置文件 - 添加用户模块的异常处理类 GlobalExceptionHandler - 添加用户模块的枚举类 ResponseCodeEnum 和 SexEnum - 添加用户模块的工具类 ParamUtils用于参数校验 - 添加用户模块的控制器 UserController 处理用户信息更新请求 - 添加用户模块的服务接口 UserService 及其实现类 UserServiceImpl - 添加用户模块的数据访问对象 UserDO 及其映射文件 UserDOMapper.xml - 添加用户模块的请求视图对象 UpdateUserInfoReqVO - 添加用户模块的 API 模块 pom.xml 配置文件 - 添加用户模块的 Biz 模块 pom.xml 配置文件 - 添加用户模块的根 pom.xml 配置文件 - 在主 pom.xml 中添加用户模块 han-note-user作为子模块 - 修改 SaToken 配置,调整登录和登出接口路径白名单 - 移除 UserController 中的 @RequestMapping("/user") 注解- 修改 UserDO 中“小哈书号”为“小憨书号”以保持命名一致性 --- .gitignore | 1 + .idea/encodings.xml | 6 + .../auth/controller/UserController.java | 2 - .../auth/domain/dataobject/UserDO.java | 2 +- .../gateway/auth/SaTokenConfigure.java | 4 +- .../src/main/resources/application.yml | 6 + han-note-user/han-note-user-api/pom.xml | 25 ++++ han-note-user/han-note-user-biz/pom.xml | 80 +++++++++++++ .../user/biz/HannoteUserBizApplication.java | 13 +++ .../user/biz/controller/UserController.java | 33 ++++++ .../user/biz/domain/dataobject/UserDO.java | 107 ++++++++++++++++++ .../user/biz/domain/mapper/UserDOMapper.java | 7 ++ .../user/biz/enums/ResponseCodeEnum.java | 28 +++++ .../hannote/user/biz/enums/SexEnum.java | 26 +++++ .../biz/exception/GlobalExceptionHandler.java | 103 +++++++++++++++++ .../biz/model/vo/UpdateUserInfoReqVO.java | 52 +++++++++ .../hannote/user/biz/service/UserService.java | 17 +++ .../biz/service/impl/UserServiceImpl.java | 92 +++++++++++++++ .../src/main/resources/application.yml | 13 +++ .../src/main/resources/bootstrap.yml | 12 ++ .../src/main/resources/logback-spring.xml | 58 ++++++++++ .../main/resources/mapperxml/UserDOMapper.xml | 27 +++++ han-note-user/pom.xml | 26 +++++ .../framework/common/utils/ParamUtils.java | 69 +++++++++++ pom.xml | 1 + 25 files changed, 805 insertions(+), 5 deletions(-) create mode 100644 han-note-user/han-note-user-api/pom.xml create mode 100644 han-note-user/han-note-user-biz/pom.xml create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/HannoteUserBizApplication.java create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/controller/UserController.java create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/domain/dataobject/UserDO.java create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/domain/mapper/UserDOMapper.java create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/enums/ResponseCodeEnum.java create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/enums/SexEnum.java create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/exception/GlobalExceptionHandler.java create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/model/vo/UpdateUserInfoReqVO.java create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/service/UserService.java create mode 100644 han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/service/impl/UserServiceImpl.java create mode 100644 han-note-user/han-note-user-biz/src/main/resources/application.yml create mode 100644 han-note-user/han-note-user-biz/src/main/resources/bootstrap.yml create mode 100644 han-note-user/han-note-user-biz/src/main/resources/logback-spring.xml create mode 100644 han-note-user/han-note-user-biz/src/main/resources/mapperxml/UserDOMapper.xml create mode 100644 han-note-user/pom.xml create mode 100644 hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/utils/ParamUtils.java diff --git a/.gitignore b/.gitignore index 31eae03..c45175a 100755 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ build/ /logs/ /.idea/ /han-note-oss/han-note-oss-biz/src/main/resources/application-dev.yml +/han-note-user/han-note-user-biz/src/main/resources/application-dev.yml diff --git a/.idea/encodings.xml b/.idea/encodings.xml index 8126366..25b2c58 100755 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -11,6 +11,12 @@ + + + + + + diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/controller/UserController.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/controller/UserController.java index e00621c..d9281cc 100644 --- a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/controller/UserController.java +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/controller/UserController.java @@ -11,11 +11,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; 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("/user") @Slf4j @RequiredArgsConstructor public class UserController { diff --git a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserDO.java b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserDO.java index 8f62885..7bda988 100644 --- a/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserDO.java +++ b/han-note-auth/src/main/java/com/hanserwei/hannote/auth/domain/dataobject/UserDO.java @@ -28,7 +28,7 @@ public class UserDO { private Long id; /** - * 小哈书号(唯一凭证) + * 小憨书号(唯一凭证) */ @TableField(value = "han_note_id") private String hanNoteId; diff --git a/han-note-gateway/src/main/java/com/hanserwei/hannote/gateway/auth/SaTokenConfigure.java b/han-note-gateway/src/main/java/com/hanserwei/hannote/gateway/auth/SaTokenConfigure.java index e02e4c1..eb6330f 100644 --- a/han-note-gateway/src/main/java/com/hanserwei/hannote/gateway/auth/SaTokenConfigure.java +++ b/han-note-gateway/src/main/java/com/hanserwei/hannote/gateway/auth/SaTokenConfigure.java @@ -22,13 +22,13 @@ public class SaTokenConfigure { .setAuth(obj -> { // 登录校验 SaRouter.match("/**") // 拦截所有路由 - .notMatch("/auth/user/login") // 排除登录接口 + .notMatch("/auth/login") // 排除登录接口 .notMatch("/auth/verification/code/send") // 排除验证码发送接口 .check(r -> StpUtil.checkLogin()) // 校验是否登录 ; // 权限认证 -- 不同模块, 校验不同权限 - SaRouter.match("/auth/user/logout", r -> StpUtil.checkPermission("app:note:publish")); + SaRouter.match("/auth/logout", r -> StpUtil.checkPermission("app:note:publish")); // SaRouter.match("/user/**", r -> StpUtil.checkPermission("user")); // SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin")); // SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods")); diff --git a/han-note-gateway/src/main/resources/application.yml b/han-note-gateway/src/main/resources/application.yml index ddd669c..d01cb9a 100644 --- a/han-note-gateway/src/main/resources/application.yml +++ b/han-note-gateway/src/main/resources/application.yml @@ -10,6 +10,12 @@ spring: - Path=/auth/** filters: - StripPrefix=1 + - id: user + uri: lb://han-note-user + predicates: + - Path=/user/** + filters: + - StripPrefix=1 data: redis: database: 5 # Redis 数据库索引(默认为 0) diff --git a/han-note-user/han-note-user-api/pom.xml b/han-note-user/han-note-user-api/pom.xml new file mode 100644 index 0000000..94e452b --- /dev/null +++ b/han-note-user/han-note-user-api/pom.xml @@ -0,0 +1,25 @@ + + 4.0.0 + + + com.hanserwei + han-note-user + ${revision} + + + + jar + + han-note-user-api + ${project.artifactId} + RPC层, 供其他服务调用 + + + + com.hanserwei + hanserwei-common + + + + diff --git a/han-note-user/han-note-user-biz/pom.xml b/han-note-user/han-note-user-biz/pom.xml new file mode 100644 index 0000000..3474506 --- /dev/null +++ b/han-note-user/han-note-user-biz/pom.xml @@ -0,0 +1,80 @@ + + 4.0.0 + + + com.hanserwei + han-note-user + ${revision} + + + + jar + + han-note-user-biz + ${project.artifactId} + 用户服务业务模块 + + + + com.hanserwei + hanserwei-common + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + com.mysql + mysql-connector-j + + + com.baomidou + mybatis-plus-spring-boot3-starter + + + com.alibaba + druid-spring-boot-3-starter + + + com.hanserwei + hanserwei-spring-boot-starter-jackson + + + com.hanserwei + hanserwei-spring-boot-starter-biz-operationlog + + + com.hanserwei + hanserwei-spring-boot-starter-biz-context + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/HannoteUserBizApplication.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/HannoteUserBizApplication.java new file mode 100644 index 0000000..21ad5df --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/HannoteUserBizApplication.java @@ -0,0 +1,13 @@ +package com.hanserwei.hannote.user.biz; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.hanserwei.hannote.user.biz.domain.mapper") +public class HannoteUserBizApplication { + public static void main(String[] args) { + SpringApplication.run(HannoteUserBizApplication.class, args); + } +} diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/controller/UserController.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/controller/UserController.java new file mode 100644 index 0000000..4ce2807 --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/controller/UserController.java @@ -0,0 +1,33 @@ +package com.hanserwei.hannote.user.biz.controller; + +import com.hanserwei.framework.common.response.Response; +import com.hanserwei.hannote.user.biz.model.vo.UpdateUserInfoReqVO; +import com.hanserwei.hannote.user.biz.service.UserService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/user") +@Slf4j +public class UserController { + + @Resource + private UserService userService; + + /** + * 用户信息修改 + * + * @param updateUserInfoReqVO 修改信息请求 + * @return 响应 + */ + @PostMapping(value = "/update", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public Response updateUserInfo(@Validated UpdateUserInfoReqVO updateUserInfoReqVO) { + return userService.updateUserInfo(updateUserInfoReqVO); + } + +} \ No newline at end of file diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/domain/dataobject/UserDO.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/domain/dataobject/UserDO.java new file mode 100644 index 0000000..8c7d7fe --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/domain/dataobject/UserDO.java @@ -0,0 +1,107 @@ +package com.hanserwei.hannote.user.biz.domain.dataobject; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * 用户表 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "t_user") +public class UserDO { + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 小憨书号(唯一凭证) + */ + @TableField(value = "han_note_id") + private String hanNoteId; + + /** + * 密码 + */ + @TableField(value = "`password`") + private String password; + + /** + * 昵称 + */ + @TableField(value = "nickname") + private String nickname; + + /** + * 头像 + */ + @TableField(value = "avatar") + private String avatar; + + /** + * 生日 + */ + @TableField(value = "birthday") + private LocalDate birthday; + + /** + * 背景图 + */ + @TableField(value = "background_img") + private String backgroundImg; + + /** + * 邮箱 + */ + @TableField(value = "email") + private String email; + + /** + * 性别(0:女 1:男) + */ + @TableField(value = "sex") + private Integer sex; + + /** + * 状态(0:启用 1:禁用) + */ + @TableField(value = "`status`") + private Integer status; + + /** + * 个人简介 + */ + @TableField(value = "introduction") + private String introduction; + + /** + * 创建时间 + */ + @TableField(value = "create_time") + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(value = "update_time") + private LocalDateTime updateTime; + + /** + * 逻辑删除(0:未删除 1:已删除) + */ + @TableField(value = "is_deleted") + private Boolean isDeleted; +} \ No newline at end of file diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/domain/mapper/UserDOMapper.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/domain/mapper/UserDOMapper.java new file mode 100644 index 0000000..a1364a4 --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/domain/mapper/UserDOMapper.java @@ -0,0 +1,7 @@ +package com.hanserwei.hannote.user.biz.domain.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.hanserwei.hannote.user.biz.domain.dataobject.UserDO; + +public interface UserDOMapper extends BaseMapper { +} \ No newline at end of file diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/enums/ResponseCodeEnum.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/enums/ResponseCodeEnum.java new file mode 100644 index 0000000..1a61efe --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/enums/ResponseCodeEnum.java @@ -0,0 +1,28 @@ +package com.hanserwei.hannote.user.biz.enums; + +import com.hanserwei.framework.common.exception.BaseExceptionInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ResponseCodeEnum implements BaseExceptionInterface { + + // ----------- 通用异常状态码 ----------- + SYSTEM_ERROR("USER-10000", "出错啦,后台小维正在努力修复中..."), + PARAM_NOT_VALID("USER-10001", "参数错误!!!"), + + // ----------- 业务异常状态码 ----------- + NICK_NAME_VALID_FAIL("USER-20001", "昵称请设置2-24个字符,不能使用@《/等特殊字符"), + HAN_NOTE_ID_VALID_FAIL("USER-20002", "小憨书号请设置6-15个字符,仅可使用英文(必须)、数字、下划线"), + SEX_VALID_FAIL("USER-20003", "性别错误"), + INTRODUCTION_VALID_FAIL("USER-20004", "个人简介请设置1-100个字符"), + ; + ; + + // 异常码 + private final String errorCode; + // 错误信息 + private final String errorMsg; + +} \ No newline at end of file diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/enums/SexEnum.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/enums/SexEnum.java new file mode 100644 index 0000000..32bed10 --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/enums/SexEnum.java @@ -0,0 +1,26 @@ +package com.hanserwei.hannote.user.biz.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Objects; + +@Getter +@AllArgsConstructor +public enum SexEnum { + + WOMAN(0), + MAN(1); + + private final Integer value; + + public static boolean isValid(Integer value) { + for (SexEnum loginTypeEnum : SexEnum.values()) { + if (Objects.equals(value, loginTypeEnum.getValue())) { + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/exception/GlobalExceptionHandler.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..a5281a4 --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/exception/GlobalExceptionHandler.java @@ -0,0 +1,103 @@ +package com.hanserwei.hannote.user.biz.exception; + +import com.hanserwei.framework.common.exception.ApiException; +import com.hanserwei.framework.common.response.Response; +import com.hanserwei.hannote.user.biz.enums.ResponseCodeEnum; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.Optional; + +@SuppressWarnings("LoggingSimilarMessage") +@ControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + /** + * 捕获自定义业务异常 + * + * @return Response.fail(e) + */ + @ExceptionHandler({ApiException.class}) + @ResponseBody + public Response handleApiException(HttpServletRequest request, ApiException e) { + log.warn("{} request fail, errorCode: {}, errorMessage: {}", request.getRequestURI(), e.getErrorCode(), e.getErrorMsg()); + return Response.fail(e); + } + + /** + * 捕获参数校验异常 + * + * @return Response.fail(errorCode, errorMessage) + */ + @ExceptionHandler({MethodArgumentNotValidException.class}) + @ResponseBody + public Response handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) { + // 参数错误异常码 + String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(); + + // 获取 BindingResult + BindingResult bindingResult = e.getBindingResult(); + + StringBuilder sb = new StringBuilder(); + + // 获取校验不通过的字段,并组合错误信息,格式为: email 邮箱格式不正确, 当前值: '123124qq.com'; + Optional.of(bindingResult.getFieldErrors()).ifPresent(errors -> { + errors.forEach(error -> + sb.append(error.getField()) + .append(" ") + .append(error.getDefaultMessage()) + .append(", 当前值: '") + .append(error.getRejectedValue()) + .append("'; ") + + ); + }); + + // 错误信息 + String errorMessage = sb.toString(); + + log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMessage); + + return Response.fail(errorCode, errorMessage); + } + + /** + * 捕获 guava 参数校验异常 + * + * @return Response.fail(ResponseCodeEnum.PARAM_NOT_VALID) + */ + @ExceptionHandler({IllegalArgumentException.class}) + @ResponseBody + public Response handleIllegalArgumentException(HttpServletRequest request, IllegalArgumentException e) { + // 参数错误异常码 + String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(); + + // 错误信息 + String errorMessage = e.getMessage(); + + log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMessage); + + return Response.fail(errorCode, errorMessage); + } + + /** + * 其他类型异常 + * + * @param request 请求 + * @param e 异常 + * @return Response.fail(ResponseCodeEnum.SYSTEM_ERROR) + */ + @ExceptionHandler({Exception.class}) + @ResponseBody + public Response handleOtherException(HttpServletRequest request, Exception e) { + log.error("{} request error, ", request.getRequestURI(), e); + return Response.fail(ResponseCodeEnum.SYSTEM_ERROR); + } +} + diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/model/vo/UpdateUserInfoReqVO.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/model/vo/UpdateUserInfoReqVO.java new file mode 100644 index 0000000..61ab7a0 --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/model/vo/UpdateUserInfoReqVO.java @@ -0,0 +1,52 @@ +package com.hanserwei.hannote.user.biz.model.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDate; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UpdateUserInfoReqVO { + + /** + * 头像 + */ + private MultipartFile avatar; + + /** + * 昵称 + */ + private String nickname; + + /** + * 小憨书 ID + */ + private String hanNoteId; + + /** + * 性别 + */ + private Integer sex; + + /** + * 生日 + */ + private LocalDate birthday; + + /** + * 个人介绍 + */ + private String introduction; + + /** + * 背景图 + */ + private MultipartFile backgroundImg; + +} \ No newline at end of file diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/service/UserService.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/service/UserService.java new file mode 100644 index 0000000..cde35b9 --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/service/UserService.java @@ -0,0 +1,17 @@ +package com.hanserwei.hannote.user.biz.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.hanserwei.framework.common.response.Response; +import com.hanserwei.hannote.user.biz.domain.dataobject.UserDO; +import com.hanserwei.hannote.user.biz.model.vo.UpdateUserInfoReqVO; + +public interface UserService extends IService { + + /** + * 更新用户信息 + * + * @param updateUserInfoReqVO 更新用户信息请求参数 + * @return 响应结果 + */ + Response updateUserInfo(UpdateUserInfoReqVO updateUserInfoReqVO); +} \ No newline at end of file diff --git a/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/service/impl/UserServiceImpl.java b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..2052cf3 --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/java/com/hanserwei/hannote/user/biz/service/impl/UserServiceImpl.java @@ -0,0 +1,92 @@ +package com.hanserwei.hannote.user.biz.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.google.common.base.Preconditions; +import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder; +import com.hanserwei.framework.common.response.Response; +import com.hanserwei.framework.common.utils.ParamUtils; +import com.hanserwei.hannote.user.biz.domain.dataobject.UserDO; +import com.hanserwei.hannote.user.biz.domain.mapper.UserDOMapper; +import com.hanserwei.hannote.user.biz.enums.ResponseCodeEnum; +import com.hanserwei.hannote.user.biz.enums.SexEnum; +import com.hanserwei.hannote.user.biz.model.vo.UpdateUserInfoReqVO; +import com.hanserwei.hannote.user.biz.service.UserService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Objects; + +@Service +@Slf4j +public class UserServiceImpl extends ServiceImpl implements UserService { + @Override + public Response updateUserInfo(UpdateUserInfoReqVO updateUserInfoReqVO) { + UserDO userDO = new UserDO(); + // 设置当前需要更新的用户 ID + userDO.setId(LoginUserContextHolder.getUserId()); + // 标识位:是否需要更新 + boolean needUpdate = false; + + // 头像 + MultipartFile avatar = updateUserInfoReqVO.getAvatar(); + + if (Objects.nonNull(avatar)) { + // TODO: 上传头像,调用服务 + } + + // 昵称 + String nickname = updateUserInfoReqVO.getNickname(); + if (StringUtils.isNotBlank(nickname)) { + Preconditions.checkArgument(ParamUtils.checkNickname(nickname), ResponseCodeEnum.NICK_NAME_VALID_FAIL.getErrorMsg()); + userDO.setNickname(nickname); + needUpdate = true; + } + + // 小憨书 ID + String hanNoteId = updateUserInfoReqVO.getHanNoteId(); + if (StringUtils.isNotBlank(hanNoteId)) { + Preconditions.checkArgument(ParamUtils.checkHannoteId(hanNoteId), ResponseCodeEnum.HAN_NOTE_ID_VALID_FAIL.getErrorMsg()); + userDO.setHanNoteId(hanNoteId); + needUpdate = true; + } + + // 性别 + Integer sex = updateUserInfoReqVO.getSex(); + if (Objects.nonNull(sex)) { + Preconditions.checkArgument(SexEnum.isValid(sex), ResponseCodeEnum.SEX_VALID_FAIL.getErrorMsg()); + userDO.setSex(sex); + needUpdate = true; + } + + // 生日 + LocalDate birthday = updateUserInfoReqVO.getBirthday(); + if (Objects.nonNull(birthday)) { + userDO.setBirthday(birthday); + needUpdate = true; + } + + // 个人介绍 + String introduction = updateUserInfoReqVO.getIntroduction(); + if (StringUtils.isNotBlank(introduction)) { + Preconditions.checkArgument(ParamUtils.checkLength(introduction, 100), ResponseCodeEnum.INTRODUCTION_VALID_FAIL.getErrorMsg()); + userDO.setIntroduction(introduction); + needUpdate = true; + } + + // 背景图片 + MultipartFile backgroundImg = updateUserInfoReqVO.getBackgroundImg(); + if (Objects.nonNull(backgroundImg)) { + // TODO: 上传背景图片,调用服务 + } + + if (needUpdate) { + userDO.setUpdateTime(LocalDateTime.now()); + return updateById(userDO) ? Response.success() : Response.fail(); + } + return Response.success(); + } +} diff --git a/han-note-user/han-note-user-biz/src/main/resources/application.yml b/han-note-user/han-note-user-biz/src/main/resources/application.yml new file mode 100644 index 0000000..d30dcbc --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/resources/application.yml @@ -0,0 +1,13 @@ +server: + port: 8082 # 项目启动的端口 + +spring: + profiles: + active: dev # 默认激活 dev 本地开发环境 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true + log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl + global-config: + banner: false + mapper-locations: classpath*:/mapperxml/*.xml \ No newline at end of file diff --git a/han-note-user/han-note-user-biz/src/main/resources/bootstrap.yml b/han-note-user/han-note-user-biz/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..e1327f7 --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/resources/bootstrap.yml @@ -0,0 +1,12 @@ +spring: + application: + name: han-note-user # 应用名称 + profiles: + active: dev # 默认激活 dev 本地开发环境 + cloud: + nacos: + discovery: + enabled: true # 启用服务发现 + group: DEFAULT_GROUP # 所属组 + namespace: han-note # 命名空间 + server-addr: 127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址 diff --git a/han-note-user/han-note-user-biz/src/main/resources/logback-spring.xml b/han-note-user/han-note-user-biz/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..0bbe747 --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/resources/logback-spring.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + ${LOG_FILE}-%i.log + + 30 + + 10MB + + 0 + + false + + + ${LOG_PATTERN} + UTF-8 + + + + + + + 0 + + 256 + + + + + + + + + + + + + + + + + + + + + diff --git a/han-note-user/han-note-user-biz/src/main/resources/mapperxml/UserDOMapper.xml b/han-note-user/han-note-user-biz/src/main/resources/mapperxml/UserDOMapper.xml new file mode 100644 index 0000000..4a8138e --- /dev/null +++ b/han-note-user/han-note-user-biz/src/main/resources/mapperxml/UserDOMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + id, han_note_id, `password`, nickname, avatar, birthday, background_img, email, sex, + `status`, introduction, create_time, update_time, is_deleted + + \ No newline at end of file diff --git a/han-note-user/pom.xml b/han-note-user/pom.xml new file mode 100644 index 0000000..5f5476d --- /dev/null +++ b/han-note-user/pom.xml @@ -0,0 +1,26 @@ + + 4.0.0 + + + com.hanserwei + han-note + ${revision} + + + + pom + + + + han-note-user-api + han-note-user-biz + + + han-note-user + + ${project.artifactId} + + 用户服务 + + diff --git a/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/utils/ParamUtils.java b/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/utils/ParamUtils.java new file mode 100644 index 0000000..a20d2a0 --- /dev/null +++ b/hanserwei-framework/hanserwei-common/src/main/java/com/hanserwei/framework/common/utils/ParamUtils.java @@ -0,0 +1,69 @@ +package com.hanserwei.framework.common.utils; + +import java.util.regex.Pattern; + +public final class ParamUtils { + private ParamUtils() { + } + + // ============================== 校验昵称 ============================== + // 定义昵称长度范围 + private static final int NICK_NAME_MIN_LENGTH = 2; + private static final int NICK_NAME_MAX_LENGTH = 24; + + // 定义特殊字符的正则表达式 + private static final String NICK_NAME_REGEX = "[!@#$%^&*(),.?\":{}|<>]"; + + /** + * 昵称校验 + * + * @param nickname 昵称 + * @return boolean + */ + public static boolean checkNickname(String nickname) { + // 检查长度 + if (nickname.length() < NICK_NAME_MIN_LENGTH || nickname.length() > NICK_NAME_MAX_LENGTH) { + return false; + } + + // 检查是否含有特殊字符 + Pattern pattern = Pattern.compile(NICK_NAME_REGEX); + return !pattern.matcher(nickname).find(); + } + + // ============================== 校验小憨书号 ============================== + // 定义 ID 长度范围 + private static final int ID_MIN_LENGTH = 6; + private static final int ID_MAX_LENGTH = 15; + + // 定义正则表达式 + private static final String ID_REGEX = "^[a-zA-Z0-9_]+$"; + + /** + * 小憨书 ID 校验 + * + * @param hannoteId 小憨书 ID + * @return boolean + */ + public static boolean checkHannoteId(String hannoteId) { + // 检查长度 + if (hannoteId.length() < ID_MIN_LENGTH || hannoteId.length() > ID_MAX_LENGTH) { + return false; + } + // 检查格式 + Pattern pattern = Pattern.compile(ID_REGEX); + return pattern.matcher(hannoteId).matches(); + } + + /** + * 字符串长度校验 + * + * @param str 字符串 + * @param length 长度 + * @return boolean + */ + public static boolean checkLength(String str, int length) { + // 检查长度 + return !str.isEmpty() && str.length() <= length; + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 4226bd7..f65c49e 100755 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ hanserwei-framework han-note-gateway han-note-oss + han-note-user