diff --git a/management-web-app/pom.xml b/management-web-app/pom.xml
new file mode 100644
index 000000000..f45ef6288
--- /dev/null
+++ b/management-web-app/pom.xml
@@ -0,0 +1,88 @@
+
+
+
+ onemall
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ management-web-app
+ 管理后台,提供管理员管理的所有功能
+
+
+
+
+
+ cn.iocoder.mall
+ mall-dependencies
+ 1.0-SNAPSHOT
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-dubbo
+
+
+
+
+ cn.iocoder.mall
+ user-service-api
+ 1.0-SNAPSHOT
+
+
+
+ cn.iocoder.mall
+ system-service-api
+ 1.0-SNAPSHOT
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ org.mapstruct
+ mapstruct
+
+
+ org.mapstruct
+ mapstruct-jdk8
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/ManagementWebApplication.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/ManagementWebApplication.java
new file mode 100644
index 000000000..121979533
--- /dev/null
+++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/ManagementWebApplication.java
@@ -0,0 +1,13 @@
+package cn.iocoder.mall.managementweb;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ManagementWebApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ManagementWebApplication.class, args);
+ }
+
+}
diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/passport/AdminPassportController.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/passport/AdminPassportController.java
new file mode 100644
index 000000000..89ed6e92a
--- /dev/null
+++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/passport/AdminPassportController.java
@@ -0,0 +1,35 @@
+package cn.iocoder.mall.managementweb.controller.passport;
+
+import cn.iocoder.common.framework.util.HttpUtil;
+import cn.iocoder.common.framework.vo.CommonResult;
+import cn.iocoder.mall.managementweb.controller.passport.dto.AdminPassportLoginDTO;
+import cn.iocoder.mall.managementweb.controller.passport.vo.AdminPassportVO;
+import cn.iocoder.mall.managementweb.manager.admin.AdminPassportManager;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static cn.iocoder.common.framework.vo.CommonResult.success;
+
+@Api(tags = "管理员 Passport API")
+@RestController
+@RequestMapping("/passport")
+public class AdminPassportController {
+
+ @Autowired
+ private AdminPassportManager adminPassportManager;
+
+ @PostMapping("/login")
+ @ApiOperation("账号密码登陆")
+// @RequiresNone TODO 晚点加上
+ public CommonResult login(AdminPassportLoginDTO loginDTO,
+ HttpServletRequest request) {
+ return success(adminPassportManager.login(loginDTO, HttpUtil.getIp(request)));
+ }
+
+}
diff --git a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/request/oauth2/AdminsOAuth2UsernameAuthenticateRequest.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/passport/dto/AdminPassportLoginDTO.java
similarity index 75%
rename from system/system-rest/src/main/java/cn/iocoder/mall/system/rest/request/oauth2/AdminsOAuth2UsernameAuthenticateRequest.java
rename to management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/passport/dto/AdminPassportLoginDTO.java
index 2388568f3..fd5c301da 100644
--- a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/request/oauth2/AdminsOAuth2UsernameAuthenticateRequest.java
+++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/passport/dto/AdminPassportLoginDTO.java
@@ -1,4 +1,4 @@
-package cn.iocoder.mall.system.rest.request.oauth2;
+package cn.iocoder.mall.managementweb.controller.passport.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -8,11 +8,12 @@ import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
+import java.io.Serializable;
-@ApiModel("管理员 - OAuth2 模块 - 用户名认证请求")
+@ApiModel("管理登录 DTO")
@Data
@Accessors(chain = true)
-public class AdminsOAuth2UsernameAuthenticateRequest {
+public class AdminPassportLoginDTO implements Serializable {
@ApiModelProperty(value = "用户名", required = true, example = "yudaoyuanma")
@NotEmpty(message = "登陆账号不能为空")
@@ -20,7 +21,7 @@ public class AdminsOAuth2UsernameAuthenticateRequest {
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
private String username;
- @ApiModelProperty(value = "用户名", required = true, example = "buzhidao")
+ @ApiModelProperty(value = "密码", required = true, example = "buzhidao")
@NotEmpty(message = "密码不能为空")
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/passport/vo/AdminPassportVO.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/passport/vo/AdminPassportVO.java
new file mode 100644
index 000000000..854e1744e
--- /dev/null
+++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/controller/passport/vo/AdminPassportVO.java
@@ -0,0 +1,50 @@
+package cn.iocoder.mall.managementweb.controller.passport.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+@ApiModel("管理员通信证信息 VO")
+@Data
+@Accessors(chain = true)
+public class AdminPassportVO {
+
+ @ApiModel("认证信息")
+ @Data
+ @Accessors(chain = true)
+ public static class Authentication {
+
+ @ApiModelProperty(value = "访问令牌", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
+ private String accessToken;
+ @ApiModelProperty(value = "刷新令牌", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
+ private String refreshToken;
+ @ApiModelProperty(value = "过期时间", required = true)
+ private Date expiresTime;
+
+ }
+
+ @ApiModel("管理员信息")
+ @Data
+ @Accessors(chain = true)
+ public static class Admin {
+
+ @ApiModelProperty(value = "用户编号", required = true, example = "1")
+ private Integer id;
+ @ApiModelProperty(value = "真实姓名", required = true, example = "小王")
+ private String name;
+
+ }
+
+ /**
+ * 管理员信息
+ */
+ private Admin admin;
+ /**
+ * 认证信息
+ */
+ private Authentication authorization;
+
+}
diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/passport/AdminPassportConvert.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/passport/AdminPassportConvert.java
new file mode 100644
index 000000000..ef04b1b39
--- /dev/null
+++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/convert/passport/AdminPassportConvert.java
@@ -0,0 +1,24 @@
+package cn.iocoder.mall.managementweb.convert.passport;
+
+import cn.iocoder.mall.managementweb.controller.passport.dto.AdminPassportLoginDTO;
+import cn.iocoder.mall.managementweb.controller.passport.vo.AdminPassportVO;
+import cn.iocoder.mall.systemservice.rpc.admin.dto.AdminVerifyPasswordDTO;
+import cn.iocoder.mall.systemservice.rpc.admin.vo.AdminVO;
+import cn.iocoder.mall.systemservice.rpc.oauth.vo.OAuth2AccessTokenVO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface AdminPassportConvert {
+
+ AdminPassportConvert INSTANCE = Mappers.getMapper(AdminPassportConvert.class);
+
+ AdminVerifyPasswordDTO convert(AdminPassportLoginDTO loginDTO);
+
+ default AdminPassportVO convert(AdminVO adminVO, OAuth2AccessTokenVO accessTokenVO) {
+ return new AdminPassportVO().setAdmin(convert(adminVO)).setAuthorization(convert(accessTokenVO));
+ }
+ AdminPassportVO.Admin convert(AdminVO adminVO);
+ AdminPassportVO.Authentication convert(OAuth2AccessTokenVO accessTokenVO);
+
+}
diff --git a/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminPassportManager.java b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminPassportManager.java
new file mode 100644
index 000000000..35df88de6
--- /dev/null
+++ b/management-web-app/src/main/java/cn/iocoder/mall/managementweb/manager/admin/AdminPassportManager.java
@@ -0,0 +1,37 @@
+package cn.iocoder.mall.managementweb.manager.admin;
+
+import cn.iocoder.common.framework.enums.UserTypeEnum;
+import cn.iocoder.common.framework.vo.CommonResult;
+import cn.iocoder.mall.managementweb.controller.passport.dto.AdminPassportLoginDTO;
+import cn.iocoder.mall.managementweb.controller.passport.vo.AdminPassportVO;
+import cn.iocoder.mall.managementweb.convert.passport.AdminPassportConvert;
+import cn.iocoder.mall.systemservice.rpc.admin.AdminRpc;
+import cn.iocoder.mall.systemservice.rpc.admin.vo.AdminVO;
+import cn.iocoder.mall.systemservice.rpc.oauth.OAuth2Rpc;
+import cn.iocoder.mall.systemservice.rpc.oauth.dto.OAuth2CreateAccessTokenDTO;
+import cn.iocoder.mall.systemservice.rpc.oauth.vo.OAuth2AccessTokenVO;
+import org.apache.dubbo.config.annotation.Reference;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AdminPassportManager {
+
+ @Reference(version = "${dubbo.consumer.AdminRpc.version}", validation = "false")
+ private AdminRpc adminRpc;
+ @Reference(version = "${dubbo.consumer.OAuth2Rpc.version}", validation = "false")
+ private OAuth2Rpc oauth2Rpc;
+
+ public AdminPassportVO login(AdminPassportLoginDTO loginDTO, String ip) {
+ // 校验管理员密码
+ CommonResult verifyPasswordResult = adminRpc.verifyPassword(AdminPassportConvert.INSTANCE.convert(loginDTO).setIp(ip));
+ verifyPasswordResult.checkError();
+ // 创建访问令牌
+ CommonResult createAccessTokenResult = oauth2Rpc.createAccessToken(
+ new OAuth2CreateAccessTokenDTO().setUserId(verifyPasswordResult.getData().getId())
+ .setUserType(UserTypeEnum.ADMIN.getValue()).setCreateIp(ip));
+ createAccessTokenResult.checkError();
+ // 返回
+ return AdminPassportConvert.INSTANCE.convert(verifyPasswordResult.getData(), createAccessTokenResult.getData());
+ }
+
+}
diff --git a/management-web-app/src/main/resources/application-dev.yml b/management-web-app/src/main/resources/application-dev.yml
new file mode 100644
index 000000000..4cfe1567c
--- /dev/null
+++ b/management-web-app/src/main/resources/application-dev.yml
@@ -0,0 +1,14 @@
+spring:
+ # Spring Cloud 配置项
+ cloud:
+ nacos:
+ # Spring Cloud Nacos Discovery 配置项
+ discovery:
+ server-addr: 400-infra.server.iocoder.cn:8848 # Nacos 服务器地址
+ namespace: dev # Nacos 命名空间
+
+# Dubbo 配置项
+dubbo:
+ # Dubbo 注册中心
+ registry:
+ address: spring-cloud://400-infra.server.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址
diff --git a/management-web-app/src/main/resources/application-local.yml b/management-web-app/src/main/resources/application-local.yml
new file mode 100644
index 000000000..2e9e99973
--- /dev/null
+++ b/management-web-app/src/main/resources/application-local.yml
@@ -0,0 +1,14 @@
+spring:
+ # Spring Cloud 配置项
+ cloud:
+ nacos:
+ # Spring Cloud Nacos Discovery 配置项
+ discovery:
+ server-addr: 400-infra.server.iocoder.cn:8848 # Nacos 服务器地址
+ namespace: local # Nacos 命名空间
+
+# Dubbo 配置项
+dubbo:
+ # Dubbo 注册中心
+ registry:
+ address: spring-cloud://400-infra.server.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址
diff --git a/management-web-app/src/main/resources/application.yml b/management-web-app/src/main/resources/application.yml
new file mode 100644
index 000000000..48d8f39e3
--- /dev/null
+++ b/management-web-app/src/main/resources/application.yml
@@ -0,0 +1,30 @@
+# 服务器的配置项
+server:
+ port: 18083
+ servlet:
+ context-path: /management-api/
+
+spring:
+ # Application 的配置项
+ application:
+ name: management-web
+ # Profile 的配置项
+ profiles:
+ active: local
+
+# Dubbo 配置项
+dubbo:
+ # Spring Cloud Alibaba Dubbo 专属配置
+ cloud:
+ subscribed-services: 'user-service, system-service' # 设置订阅的应用列表,默认为 * 订阅所有应用
+ # Dubbo 服务消费者的配置
+ consumer:
+ timeout: 10000
+ UserSmsCodeRpc:
+ version: 1.0.0
+ UserRpc:
+ version: 1.0.0
+ OAuth2Rpc:
+ version: 1.0.0
+ AdminRpc:
+ version: 1.0.0
diff --git a/pom.xml b/pom.xml
index 0928ad1a2..d434b6539 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,8 @@
user-service-project
user-web-app
system-service-project
+ management-web-app
+ shop-web-app
pom
diff --git a/shop-web-app/pom.xml b/shop-web-app/pom.xml
new file mode 100644
index 000000000..0ee3de356
--- /dev/null
+++ b/shop-web-app/pom.xml
@@ -0,0 +1,15 @@
+
+
+
+ onemall
+ cn.iocoder.mall
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ shop-web-app
+ 商城,用于用户购物
+
+
diff --git a/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/enums/SystemErrorCodeEnum.java b/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/enums/SystemErrorCodeEnum.java
index 76fd3f68b..884e6721f 100644
--- a/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/enums/SystemErrorCodeEnum.java
+++ b/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/enums/SystemErrorCodeEnum.java
@@ -25,9 +25,8 @@ public enum SystemErrorCodeEnum implements ServiceExceptionUtil.Enumerable verifyPassword(AdminVerifyPasswordDTO verifyPasswordDTO);
+
+}
diff --git a/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/rpc/admin/dto/AdminVerifyPasswordDTO.java b/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/rpc/admin/dto/AdminVerifyPasswordDTO.java
new file mode 100644
index 000000000..22ffd2904
--- /dev/null
+++ b/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/rpc/admin/dto/AdminVerifyPasswordDTO.java
@@ -0,0 +1,36 @@
+package cn.iocoder.mall.systemservice.rpc.admin.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.Pattern;
+
+/**
+ * 管理员校验密码 DTO
+ */
+@Data
+@Accessors(chain = true)
+public class AdminVerifyPasswordDTO {
+
+ /**
+ * 用户名
+ */
+ @NotEmpty(message = "登陆账号不能为空")
+ @Length(min = 5, max = 16, message = "账号长度为 5-16 位")
+ @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
+ private String username;
+ /**
+ * 密码
+ */
+ @NotEmpty(message = "密码不能为空")
+ @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
+ private String password;
+ /**
+ * IP
+ */
+ @NotEmpty(message = "IP 不能为空")
+ private String ip;
+
+}
diff --git a/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/rpc/admin/vo/AdminVO.java b/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/rpc/admin/vo/AdminVO.java
new file mode 100644
index 000000000..66e67317b
--- /dev/null
+++ b/system-service-project/system-service-api/src/main/java/cn/iocoder/mall/systemservice/rpc/admin/vo/AdminVO.java
@@ -0,0 +1,38 @@
+package cn.iocoder.mall.systemservice.rpc.admin.vo;
+
+import cn.iocoder.mall.systemservice.enums.admin.AdminStatusEnum;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 管理员 DO
+ */
+@Data
+@Accessors(chain = true)
+public class AdminVO {
+
+ /**
+ * 管理员编号
+ */
+ private Integer id;
+ /**
+ * 真实名字
+ */
+ private String name;
+ /**
+ * 部门编号
+ */
+ private Integer departmentId;
+ /**
+ * 在职状态
+ *
+ * 枚举 {@link AdminStatusEnum}
+ */
+ private Integer status;
+
+ /**
+ * 登陆账号
+ */
+ private String username;
+
+}
diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/convert/admin/AdminConvert.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/convert/admin/AdminConvert.java
new file mode 100644
index 000000000..db880169f
--- /dev/null
+++ b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/convert/admin/AdminConvert.java
@@ -0,0 +1,24 @@
+package cn.iocoder.mall.systemservice.convert.admin;
+
+import cn.iocoder.common.framework.vo.PageResult;
+import cn.iocoder.mall.systemservice.dal.mysql.dataobject.admin.AdminDO;
+import cn.iocoder.mall.systemservice.rpc.admin.vo.AdminVO;
+import cn.iocoder.mall.systemservice.service.admin.bo.AdminBO;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.factory.Mappers;
+
+@Mapper
+public interface AdminConvert {
+
+ AdminConvert INSTANCE = Mappers.getMapper(AdminConvert.class);
+
+ AdminBO convert(AdminDO bean);
+
+ @Mapping(source = "records", target = "list")
+ PageResult convertPage(IPage bean);
+
+ AdminVO convert(AdminBO adminBO);
+
+}
diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/convert/package-info.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/convert/package-info.java
deleted file mode 100644
index c57c3b722..000000000
--- a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/convert/package-info.java
+++ /dev/null
@@ -1 +0,0 @@
-package cn.iocoder.mall.systemservice.convert;
diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/manager/admin/AdminManager.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/manager/admin/AdminManager.java
new file mode 100644
index 000000000..025b5c495
--- /dev/null
+++ b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/manager/admin/AdminManager.java
@@ -0,0 +1,23 @@
+package cn.iocoder.mall.systemservice.manager.admin;
+
+import cn.iocoder.mall.systemservice.convert.admin.AdminConvert;
+import cn.iocoder.mall.systemservice.rpc.admin.dto.AdminVerifyPasswordDTO;
+import cn.iocoder.mall.systemservice.rpc.admin.vo.AdminVO;
+import cn.iocoder.mall.systemservice.service.admin.AdminService;
+import cn.iocoder.mall.systemservice.service.admin.bo.AdminBO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AdminManager {
+
+ @Autowired
+ private AdminService adminService;
+
+ public AdminVO verifyPassword(AdminVerifyPasswordDTO verifyPasswordDTO) {
+ AdminBO adminBO = adminService.verifyPassword(verifyPasswordDTO.getUsername(),
+ verifyPasswordDTO.getPassword(), verifyPasswordDTO.getIp());
+ return AdminConvert.INSTANCE.convert(adminBO);
+ }
+
+}
diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/manager/package-info.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/manager/package-info.java
deleted file mode 100644
index 4e0936108..000000000
--- a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/manager/package-info.java
+++ /dev/null
@@ -1 +0,0 @@
-package cn.iocoder.mall.systemservice.manager;
diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/rpc/admin/AdminRpcImpl.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/rpc/admin/AdminRpcImpl.java
new file mode 100644
index 000000000..3360d8417
--- /dev/null
+++ b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/rpc/admin/AdminRpcImpl.java
@@ -0,0 +1,23 @@
+package cn.iocoder.mall.systemservice.rpc.admin;
+
+import cn.iocoder.common.framework.vo.CommonResult;
+import cn.iocoder.mall.systemservice.manager.admin.AdminManager;
+import cn.iocoder.mall.systemservice.rpc.admin.dto.AdminVerifyPasswordDTO;
+import cn.iocoder.mall.systemservice.rpc.admin.vo.AdminVO;
+import org.apache.dubbo.config.annotation.Service;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import static cn.iocoder.common.framework.vo.CommonResult.success;
+
+@Service(version = "${dubbo.provider.AdminRpc.version}", validation = "false")
+public class AdminRpcImpl implements AdminRpc {
+
+ @Autowired
+ private AdminManager adminManager;
+
+ @Override
+ public CommonResult verifyPassword(AdminVerifyPasswordDTO verifyPasswordDTO) {
+ return success(adminManager.verifyPassword(verifyPasswordDTO));
+ }
+
+}
diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/rpc/package-info.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/rpc/package-info.java
deleted file mode 100644
index e75b646bd..000000000
--- a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/rpc/package-info.java
+++ /dev/null
@@ -1 +0,0 @@
-package cn.iocoder.mall.systemservice.rpc;
diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/admin/AdminService.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/admin/AdminService.java
new file mode 100644
index 000000000..0a7791a41
--- /dev/null
+++ b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/admin/AdminService.java
@@ -0,0 +1,39 @@
+package cn.iocoder.mall.systemservice.service.admin;
+
+import cn.iocoder.common.framework.util.DigestUtils;
+import cn.iocoder.common.framework.util.ServiceExceptionUtil;
+import cn.iocoder.mall.systemservice.convert.admin.AdminConvert;
+import cn.iocoder.mall.systemservice.dal.mysql.dataobject.admin.AdminDO;
+import cn.iocoder.mall.systemservice.dal.mysql.mapper.admin.AdminMapper;
+import cn.iocoder.mall.systemservice.enums.SystemErrorCodeEnum;
+import cn.iocoder.mall.systemservice.enums.admin.AdminStatusEnum;
+import cn.iocoder.mall.systemservice.service.admin.bo.AdminBO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AdminService {
+
+ @Autowired
+ private AdminMapper adminMapper;
+
+ public AdminBO verifyPassword(String username, String password, String ip) {
+ AdminDO adminDO = adminMapper.selectByUsername(username);
+ if (adminDO == null) {
+ throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.ADMIN_NOT_FOUND);
+ }
+ // 校验密码是否正确
+ String encodedPassword = DigestUtils.bcrypt(password, adminDO.getPasswordSalt());
+ if (encodedPassword.equals(adminDO.getPassword())) {
+ // TODO 需要补充密码错误上限
+ throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.ADMIN_PASSWORD_ERROR);
+ }
+ // 账号被禁用
+ if (!AdminStatusEnum.ACTIVE.getStatus().equals(adminDO.getStatus())) {
+ throw ServiceExceptionUtil.exception(SystemErrorCodeEnum.ADMIN_IS_DISABLE);
+ }
+ // 返回
+ return AdminConvert.INSTANCE.convert(adminDO);
+ }
+
+}
diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/admin/bo/AdminBO.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/admin/bo/AdminBO.java
new file mode 100644
index 000000000..34e798b63
--- /dev/null
+++ b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/admin/bo/AdminBO.java
@@ -0,0 +1,41 @@
+package cn.iocoder.mall.systemservice.service.admin.bo;
+
+import cn.iocoder.mall.systemservice.dal.mysql.dataobject.admin.DepartmentDO;
+import cn.iocoder.mall.systemservice.enums.admin.AdminStatusEnum;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ * 管理员信息 BO
+ */
+@Data
+@Accessors(chain = true)
+public class AdminBO {
+
+ /**
+ * 管理员编号
+ */
+ private Integer id;
+ /**
+ * 真实名字
+ */
+ private String name;
+ /**
+ * 部门编号
+ *
+ * 关联 {@link DepartmentDO#getId()}
+ */
+ private Integer departmentId;
+ /**
+ * 在职状态
+ *
+ * 枚举 {@link AdminStatusEnum}
+ */
+ private Integer status;
+
+ /**
+ * 登陆账号
+ */
+ private String username;
+
+}
diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/package-info.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/package-info.java
deleted file mode 100644
index 960445f36..000000000
--- a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/service/package-info.java
+++ /dev/null
@@ -1 +0,0 @@
-package cn.iocoder.mall.systemservice.service;
diff --git a/system-service-project/system-service-app/src/main/resources/application.yaml b/system-service-project/system-service-app/src/main/resources/application.yaml
index d3840c46d..349b7252d 100644
--- a/system-service-project/system-service-app/src/main/resources/application.yaml
+++ b/system-service-project/system-service-app/src/main/resources/application.yaml
@@ -35,3 +35,5 @@ dubbo:
filter: -exception
OAuth2Rpc:
version: 1.0.0
+ AdminRpc:
+ version: 1.0.0
diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/admin/AdminServiceImpl.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/admin/AdminServiceImpl.java
index a6c02e803..8416ae3f2 100644
--- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/admin/AdminServiceImpl.java
+++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/admin/AdminServiceImpl.java
@@ -11,19 +11,6 @@ import org.springframework.stereotype.Service;
@Service
public class AdminServiceImpl implements AdminService {
- @Autowired
- private AdminMapper adminMapper;
-
- @Override
- public AdminBO getAdmin(Integer id) {
- return AdminConvert.INSTANCE.convert(adminMapper.selectById(id));
- }
-
- @Override
- public AdminBO getAdminByAccountId(Integer accountId) {
- return AdminConvert.INSTANCE.convert(adminMapper.selectByAccountId(accountId));
- }
-
@Override
public PageResult getAdminPage(AdminPageDTO pageDTO) {
return AdminConvert.INSTANCE.convertPage(adminMapper.selectPage(pageDTO));
diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/user/UserService.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/user/UserService.java
index 2bc3173f7..bd74169ad 100644
--- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/user/UserService.java
+++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/user/UserService.java
@@ -1,13 +1,12 @@
package cn.iocoder.mall.system.biz.service.user;
import cn.iocoder.common.framework.vo.PageResult;
-import cn.iocoder.mall.system.biz.bo.user.UserAuthenticateBO;
import cn.iocoder.mall.system.biz.bo.user.UserBO;
-import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2MobileCodeAuthenticateDTO;
import cn.iocoder.mall.system.biz.dto.user.UserPageDTO;
import cn.iocoder.mall.system.biz.dto.user.UserUpdateDTO;
import cn.iocoder.mall.system.biz.dto.user.UserUpdateStatusDTO;
import org.springframework.validation.annotation.Validated;
+
import javax.validation.Valid;
/**
@@ -16,10 +15,6 @@ import javax.validation.Valid;
@Validated
public interface UserService {
- UserAuthenticateBO authenticate(OAuth2MobileCodeAuthenticateDTO authenticateDTO);
-
- UserBO getUserByAccountId(Integer accountId);
-
/**
* 根据条件分页获取用户列表
* @param userPageDTO
diff --git a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/user/UserServiceImpl.java b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/user/UserServiceImpl.java
index a5c816924..f3c327117 100644
--- a/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/user/UserServiceImpl.java
+++ b/system/system-biz/src/main/java/cn/iocoder/mall/system/biz/service/user/UserServiceImpl.java
@@ -27,30 +27,6 @@ public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
- @Autowired
- private OAuth2Service oAuth2Service;
-
- @Override
- @Transactional
- public UserAuthenticateBO authenticate(OAuth2MobileCodeAuthenticateDTO authenticateDTO) {
- // 执行认证
- OAuth2AuthenticateBO accessTokenBO = oAuth2Service.authenticate(authenticateDTO);
- // 获得用户
- UserDO userDO = userMapper.selectById(accessTokenBO.getAccountId());
- if (userDO == null) {
- userDO = this.creatUser(accessTokenBO.getAccountId());
- }
- UserBO userBO = UserConvert.INSTANCE.convert(userDO);
- // 拼装返回
- return UserConvert.INSTANCE.convert(userBO, accessTokenBO);
- }
-
- @Override
- public UserBO getUserByAccountId(Integer accountId) {
- UserDO userDO = userMapper.selectById(accountId);
- return UserConvert.INSTANCE.convert(userDO);
- }
-
/**
* 根据条件分页获取用户列表
* @param userPageDTO
@@ -109,12 +85,4 @@ public class UserServiceImpl implements UserService {
return true;
}
- private UserDO creatUser(Integer accountId) {
- UserDO user = new UserDO();
- user.setAccountId(accountId);
- user.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
- userMapper.insert(user);
- return user;
- }
-
}
diff --git a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/AdminsOAuth2Controller.java b/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/AdminsOAuth2Controller.java
deleted file mode 100644
index c8421fccc..000000000
--- a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/AdminsOAuth2Controller.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package cn.iocoder.mall.system.rest.controller.oauth2;
-
-import cn.iocoder.common.framework.enums.MallConstants;
-import cn.iocoder.common.framework.util.ServiceExceptionUtil;
-import cn.iocoder.common.framework.vo.CommonResult;
-import cn.iocoder.mall.security.core.annotation.RequiresNone;
-import cn.iocoder.mall.system.biz.bo.admin.AdminBO;
-import cn.iocoder.mall.system.biz.bo.ouath2.OAuth2AuthenticateBO;
-import cn.iocoder.mall.system.biz.dto.oatuh2.OAuth2UsernameAuthenticateDTO;
-import cn.iocoder.mall.system.biz.service.admin.AdminService;
-import cn.iocoder.mall.system.biz.service.oauth2.OAuth2Service;
-import cn.iocoder.mall.system.rest.convert.oauth2.AdminsOAuth2Convert;
-import cn.iocoder.mall.system.rest.request.oauth2.AdminsOAuth2UsernameAuthenticateRequest;
-import cn.iocoder.mall.system.rest.response.oauth2.AdminsOAuth2AuthenticateResponse;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import static cn.iocoder.mall.system.biz.enums.SystemErrorCodeEnum.*;
-
-@RestController
-@RequestMapping(MallConstants.ROOT_PATH_ADMIN + "/oauth2")
-@Api(tags = "管理员 - OAuth2 API")
-public class AdminsOAuth2Controller {
-
- @Autowired
- private OAuth2Service oauth2Service;
- @Autowired
- private AdminService adminService;
-
- @PostMapping("/username-authenticate")
- @ApiOperation("用户名认证(登陆)")
- @RequiresNone
- public CommonResult usernameAuthenticate(AdminsOAuth2UsernameAuthenticateRequest request) {
- // 执行认证
- OAuth2UsernameAuthenticateDTO authenticateDTO = AdminsOAuth2Convert.INSTANCE.convert(request);
- OAuth2AuthenticateBO accessTokenBO = oauth2Service.authenticate(authenticateDTO);
- // 获得 Admin 信息
- AdminBO adminBO = adminService.getAdmin(accessTokenBO.getAccountId());
- if (adminBO == null) {
- throw ServiceExceptionUtil.exception(ADMIN_NOT_FOUND);
- }
- // 转换返回
- return CommonResult.success(
- AdminsOAuth2Convert.INSTANCE.convert(adminBO, accessTokenBO)
- );
- }
-
-}
diff --git a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/UsersOAuth2Controller.java b/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/UsersOAuth2Controller.java
deleted file mode 100644
index 2677bf67e..000000000
--- a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/controller/oauth2/UsersOAuth2Controller.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package cn.iocoder.mall.system.rest.controller.oauth2;
-
-import cn.iocoder.common.framework.enums.MallConstants;
-import cn.iocoder.mall.system.biz.service.oauth2.OAuth2MobileCodeService;
-import cn.iocoder.mall.system.biz.service.oauth2.OAuth2Service;
-import cn.iocoder.mall.system.biz.service.user.UserService;
-import io.swagger.annotations.Api;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-@RequestMapping(MallConstants.ROOT_PATH_USER + "/oauth2")
-@Api(tags = "用户 - OAuth2 API")
-public class UsersOAuth2Controller {
-
- @Autowired
- private OAuth2Service oauth2Service;
- @Autowired
- private UserService userService;
- @Autowired
- private OAuth2MobileCodeService oauth2MobileCodeService;
-
-
-
-}
diff --git a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/request/oauth2/UsersOAuth2MobileCodeAuthenticateRequest.java b/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/request/oauth2/UsersOAuth2MobileCodeAuthenticateRequest.java
deleted file mode 100644
index 0cb2cae7d..000000000
--- a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/request/oauth2/UsersOAuth2MobileCodeAuthenticateRequest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package cn.iocoder.mall.system.rest.request.oauth2;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-import org.hibernate.validator.constraints.Length;
-
-import javax.validation.constraints.NotEmpty;
-import javax.validation.constraints.Pattern;
-
-@ApiModel("用户 - OAuth2 模块 - 手机验证码认证请求")
-@Data
-@Accessors(chain = true)
-public class UsersOAuth2MobileCodeAuthenticateRequest {
-
- @ApiModelProperty(value = "手机号", required = true, example = "15601691300")
- @NotEmpty(message = "手机号不能为空")
- @Length(min = 11, max = 11, message = "账号长度为 11 位")
- @Pattern(regexp = "^[0-9]+$", message = "手机号必须都是数字")
- private String mobile;
-
- @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
- @NotEmpty(message = "手机验证码不能为空")
- @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
- @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
- private String code;
-
-}
diff --git a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/response/oauth2/AdminsOAuth2AuthenticateResponse.java b/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/response/oauth2/AdminsOAuth2AuthenticateResponse.java
deleted file mode 100644
index 3310902c6..000000000
--- a/system/system-rest/src/main/java/cn/iocoder/mall/system/rest/response/oauth2/AdminsOAuth2AuthenticateResponse.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package cn.iocoder.mall.system.rest.response.oauth2;
-
-import io.swagger.annotations.ApiModel;
-import io.swagger.annotations.ApiModelProperty;
-import lombok.Data;
-import lombok.experimental.Accessors;
-
-import java.util.Date;
-
-@ApiModel("管理员 - OAuth2 模块 - 认证响应")
-@Data
-@Accessors(chain = true)
-public class AdminsOAuth2AuthenticateResponse {
-
- @ApiModel("管理员")
- @Data
- public static class Admin {
-
- @ApiModelProperty(value = "管理员编号", required = true, example = "1")
- private Integer id;
-
- @ApiModelProperty(value = "真实名字", required = true, example = "小王")
- private String name;
-
- }
-
- @ApiModel("访问令牌")
- @Data
- public static class Token {
-
- @ApiModelProperty(value = "access token", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
- private String accessToken;
-
- @ApiModelProperty(value = "refresh token", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
- private String refreshToken;
-
- @ApiModelProperty(value = "过期时间", required = true)
- private Date expiresTime;
-
- }
-
- /**
- * 管理员
- */
- private Admin admin;
- /**
- * 访问令牌
- */
- private Token token;
-
-}
diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/UserPassportController.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/UserPassportController.java
index cf27edc8b..253ace630 100644
--- a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/UserPassportController.java
+++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/UserPassportController.java
@@ -6,6 +6,7 @@ import cn.iocoder.mall.userweb.controller.passport.dto.UserPassportLoginBySmsDTO
import cn.iocoder.mall.userweb.controller.passport.dto.UserPassportSendSmsCodeDTO;
import cn.iocoder.mall.userweb.controller.passport.vo.UserPassportVO;
import cn.iocoder.mall.userweb.manager.passport.UserPassportManager;
+import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
@@ -16,6 +17,7 @@ import javax.servlet.http.HttpServletRequest;
import static cn.iocoder.common.framework.vo.CommonResult.success;
+@Api(tags = "用户 Passport API")
@RestController
@RequestMapping("/passport")
public class UserPassportController {
diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportLoginBySmsDTO.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportLoginBySmsDTO.java
index ddb4e6d93..5f4171146 100644
--- a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportLoginBySmsDTO.java
+++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/dto/UserPassportLoginBySmsDTO.java
@@ -5,8 +5,10 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
+import org.hibernate.validator.constraints.Length;
-import javax.validation.constraints.NotNull;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.Pattern;
import java.io.Serializable;
@ApiModel("用户短信验证码登陆 DTO")
@@ -14,11 +16,15 @@ import java.io.Serializable;
@Accessors(chain = true)
public class UserPassportLoginBySmsDTO implements Serializable {
- @ApiModelProperty(value = "手机号", example = "15601691234")
+ @ApiModelProperty(value = "手机号", required = true, example = "15601691300")
+ @NotEmpty(message = "手机号不能为空")
@Mobile
private String mobile;
- @ApiModelProperty(value = "验证码", example = "1234")
- @NotNull(message = "验证码不能为空")
+
+ @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
+ @NotEmpty(message = "手机验证码不能为空")
+ @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
+ @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
private String code;
}
diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/vo/UserPassportVO.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/vo/UserPassportVO.java
index 74771609c..81b11650d 100644
--- a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/vo/UserPassportVO.java
+++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/passport/vo/UserPassportVO.java
@@ -1,57 +1,41 @@
package cn.iocoder.mall.userweb.controller.passport.vo;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
-/**
- * 用户通信证信息
- */
+@ApiModel("用户通信证信息 VO")
@Data
@Accessors(chain = true)
public class UserPassportVO {
- /**
- * 认证信息
- */
+ @ApiModel("认证信息")
@Data
@Accessors(chain = true)
public static class Authentication {
- /**
- * 访问令牌
- */
+ @ApiModelProperty(value = "访问令牌", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
private String accessToken;
- /**
- * 刷新令牌
- */
+ @ApiModelProperty(value = "刷新令牌", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
private String refreshToken;
- /**
- * 过期时间
- */
+ @ApiModelProperty(value = "过期时间", required = true)
private Date expiresTime;
}
- /**
- * 用户信息
- */
+ @ApiModel("用户信息")
@Data
@Accessors(chain = true)
public static class User {
- /**
- * 用户编号
- */
+ @ApiModelProperty(value = "用户编号", required = true, example = "1")
private Integer id;
- /**
- * 昵称
- */
+ @ApiModelProperty(value = "用户昵称", required = true, example = "小王")
private String nickname;
- /**
- * 头像
- */
+ @ApiModelProperty(value = "用户头像", required = true, example = "http://www.iocoder.cn/image")
private String avatar;
}
diff --git a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/user/UserController.java b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/user/UserController.java
index b3bfea735..f3714e2c6 100644
--- a/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/user/UserController.java
+++ b/user-web-app/src/main/java/cn/iocoder/mall/userweb/controller/user/UserController.java
@@ -1,4 +1,7 @@
package cn.iocoder.mall.userweb.controller.user;
+import io.swagger.annotations.Api;
+
+@Api(tags = "用户信息 API")
public class UserController {
}