diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java index 7c1950f..70d4e62 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java @@ -14,7 +14,6 @@ import org.springframework.util.Assert; import jakarta.annotation.Resource; import java.util.Set; -import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; import static java.util.Collections.emptySet; /** diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java index a981462..21ef03f 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java @@ -13,7 +13,6 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java index ae8a46a..b0a172e 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPerformanceServiceImpl.java @@ -15,7 +15,6 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/yudao-module-infra/pom.xml b/yudao-module-infra/pom.xml index c517e88..8b983cf 100644 --- a/yudao-module-infra/pom.xml +++ b/yudao-module-infra/pom.xml @@ -8,18 +8,112 @@ ${revision} 4.0.0 - - yudao-module-infra-api - yudao-module-infra-biz - yudao-module-infra - pom + jar ${project.artifactId} infra 模块,主要提供两块能力: - 1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等 - 2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等 + 1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等 + 2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等 + + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-tenant + + + + + cn.iocoder.boot + yudao-spring-boot-starter-security + + + + cn.iocoder.boot + yudao-spring-boot-starter-websocket + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + com.baomidou + mybatis-plus-generator + + + + cn.iocoder.boot + yudao-spring-boot-starter-redis + + + + + + + cn.iocoder.boot + yudao-spring-boot-starter-job + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mq + + + + + cn.iocoder.boot + yudao-spring-boot-starter-test + test + + + + + cn.iocoder.boot + yudao-spring-boot-starter-excel + + + + org.apache.velocity + velocity-engine-core + + + + + cn.iocoder.boot + yudao-spring-boot-starter-monitor + + + + de.codecentric + spring-boot-admin-starter-server + + + + + commons-net + commons-net + + + com.jcraft + jsch + + + + software.amazon.awssdk + s3 + + + + org.apache.tika + tika-core + + + + diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApi.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApi.java index 85de49e..85de49e 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApi.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApi.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApiImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApiImpl.java index d613fe4..d613fe4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApiImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/config/ConfigApiImpl.java diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApi.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApi.java index e97d11b..e97d11b 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApi.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApi.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApiImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApiImpl.java index 72c3511..72c3511 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApiImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/file/FileApiImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiAccessLogApiImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiAccessLogApiImpl.java index 69f8062..69f8062 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiAccessLogApiImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiAccessLogApiImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiErrorLogApiImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiErrorLogApiImpl.java index f782768..f782768 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiErrorLogApiImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/logger/ApiErrorLogApiImpl.java diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java index 9ed9a77..9831fe7 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java @@ -1,4 +1,4 @@ /** - * infra API 包,定义暴露给其它模块的 API + * infra API 包,定义并实现提供给其它模块的 API */ package cn.iocoder.yudao.module.infra.api; diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApi.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApi.java index 38582c2..38582c2 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApi.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApi.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApiImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApiImpl.java index cc060aa..cc060aa 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApiImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/api/websocket/WebSocketSenderApiImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java index 93d47f5..93d47f5 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/CodegenController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenCreateListReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenCreateListReqVO.java index 5e73377..5e73377 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenCreateListReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenCreateListReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenDetailRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenDetailRespVO.java index f7d8a62..f7d8a62 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenDetailRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenDetailRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenPreviewRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenPreviewRespVO.java index ffd924a..ffd924a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenPreviewRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenPreviewRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java index e8a8515..e8a8515 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/CodegenUpdateReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java index 29a8c74..29a8c74 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnSaveReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnSaveReqVO.java index 18067c2..18067c2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnSaveReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnSaveReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java index 032a9d8..032a9d8 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java index 8b438b2..8b438b2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableSaveReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableSaveReqVO.java index 201d94d..201d94d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableSaveReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableSaveReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/DatabaseTableRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/DatabaseTableRespVO.java index a1f683c..a1f683c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/DatabaseTableRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/DatabaseTableRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java index 7de4138..7de4138 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/ConfigController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigPageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigPageReqVO.java index 8caec39..8caec39 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigPageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigPageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java index b4f642f..b4f642f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigSaveReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigSaveReqVO.java index 0c791d6..0c791d6 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigSaveReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/config/vo/ConfigSaveReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DataSourceConfigController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DataSourceConfigController.java index 7a897fa..7a897fa 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DataSourceConfigController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/DataSourceConfigController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java index f979528..f979528 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigSaveReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigSaveReqVO.java index 9a5b13c..9a5b13c 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigSaveReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigSaveReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java index cf6935e..cf6935e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/Demo01ContactController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java index d337d2d..d337d2d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactPageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java index 5d176c2..5d176c2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java index 352e431..352e431 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo01/vo/Demo01ContactSaveReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java index 5cdc332..5cdc332 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/Demo02CategoryController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java index 69fcfd9..69fcfd9 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryListReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java index 1f2efd4..1f2efd4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategoryRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java index fbb76ec..fbb76ec 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo02/vo/Demo02CategorySaveReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java index bee5984..bee5984 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/Demo03StudentController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/package-info.java index 79682e2..79682e2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java index 6834991..6834991 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentPageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java index 5ae784f..5ae784f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java index ec9f29c..ec9f29c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/demo03/vo/Demo03StudentSaveReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/package-info.java index c448646..c448646 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.http b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.http index 14b6228..14b6228 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.http +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.http diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.java index ecf6b6e..ecf6b6e 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileConfigController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java index 119774b..119774b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java index 481fa2a..481fa2a 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java index e344f82..e344f82 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigSaveReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigSaveReqVO.java index f4bf7c2..f4bf7c2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigSaveReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigSaveReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileCreateReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileCreateReqVO.java index 5daa397..5daa397 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileCreateReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileCreateReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java index 21c1178..21c1178 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java index 72be6ae..72be6ae 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePresignedUrlRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java index a0357da..a0357da 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileUploadReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileUploadReqVO.java index 183462a..183462a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileUploadReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileUploadReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.http b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.http index 52e7394..52e7394 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.http +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.http diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.java index 372b639..372b639 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java index 71b098b..71b098b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/JobLogController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobPageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobPageReqVO.java index 3328335..3328335 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobPageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobPageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobRespVO.java index ea9cd5e..ea9cd5e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobSaveReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobSaveReqVO.java index b22060b..b22060b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobSaveReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/job/JobSaveReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogPageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogPageReqVO.java index 1d3d496..1d3d496 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogPageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogPageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogRespVO.java index 543339d..543339d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/job/vo/log/JobLogRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiAccessLogController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiAccessLogController.java index b4ee86c..b4ee86c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiAccessLogController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiAccessLogController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiErrorLogController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiErrorLogController.java index 25eb2e9..25eb2e9 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiErrorLogController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/ApiErrorLogController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java index c17c111..c17c111 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java index e991450..d9e65c4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java index 2ceb0d0..2ceb0d0 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java index 8029f15..a3f6f0e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java @@ -29,7 +29,7 @@ public class ApiErrorLogRespVO { @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") @ExcelProperty(value = "用户类型", converter = DictConvert.class) - @DictFormat(cn.iocoder.yudao.module.system.enums.DictTypeConstants.USER_TYPE) + @DictFormat(DictTypeConstants.USER_TYPE) private Integer userType; @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "dashboard") diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http index 68b5487..68b5487 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.http diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java index 88f29f9..88f29f9 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/RedisController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisMonitorRespVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisMonitorRespVO.java index cc0aa27..cc0aa27 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisMonitorRespVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/redis/vo/RedisMonitorRespVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/AppFileController.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/AppFileController.java index 7f85e99..7f85e99 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/AppFileController.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/AppFileController.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/vo/AppFileUploadReqVO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/vo/AppFileUploadReqVO.java index fe01e03..fe01e03 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/vo/AppFileUploadReqVO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/vo/AppFileUploadReqVO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/app/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/app/package-info.java index d0f921f..d0f921f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/app/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/app/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/package-info.java index 04fde2f..04fde2f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java index 6b97613..6b97613 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/codegen/CodegenConvert.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/config/ConfigConvert.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/config/ConfigConvert.java index 92094b0..92094b0 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/config/ConfigConvert.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/config/ConfigConvert.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/file/FileConfigConvert.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/file/FileConfigConvert.java index 7df2b57..7df2b57 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/file/FileConfigConvert.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/file/FileConfigConvert.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/package-info.java index 8e27593..8e27593 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java index 0c8ca98..0c8ca98 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/redis/RedisConvert.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md index 8153487..8153487 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenColumnDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenColumnDO.java index 44063ac..44063ac 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenColumnDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenColumnDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java index b46cbe4..b46cbe4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/codegen/CodegenTableDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/config/ConfigDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/config/ConfigDO.java index b0b37c3..b0b37c3 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/config/ConfigDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/config/ConfigDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java index c906937..c906937 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/db/DataSourceConfigDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java index de9c6d4..de9c6d4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo01/Demo01ContactDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java index 55005dc..55005dc 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo02/Demo02CategoryDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java index 6f7f3f4..6f7f3f4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03CourseDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java index afb7090..afb7090 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03GradeDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java index 99995c2..99995c2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/demo03/Demo03StudentDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java index 92d617e..92d617e 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileConfigDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java index 8ae7a59..8ae7a59 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileContentDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java index 5cb666e..5cb666e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/file/FileDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/job/JobDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/job/JobDO.java index 8080036..8080036 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/job/JobDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/job/JobDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/job/JobLogDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/job/JobLogDO.java index ac8eabb..ac8eabb 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/job/JobLogDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/job/JobLogDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java index 4f671c7..4f671c7 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java index 87d6974..87d6974 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenColumnMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenColumnMapper.java index ea5a9bb..ea5a9bb 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenColumnMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenColumnMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenTableMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenTableMapper.java index 0afe32a..0afe32a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenTableMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/codegen/CodegenTableMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/config/ConfigMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/config/ConfigMapper.java index 7d36610..7d36610 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/config/ConfigMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/config/ConfigMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/db/DataSourceConfigMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/db/DataSourceConfigMapper.java index 5ad8be4..5ad8be4 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/db/DataSourceConfigMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/db/DataSourceConfigMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo01/Demo01ContactMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo01/Demo01ContactMapper.java index f5f3cdb..f5f3cdb 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo01/Demo01ContactMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo01/Demo01ContactMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java index b16e18f..b16e18f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo02/Demo02CategoryMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03CourseMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03CourseMapper.java index 3cb3aa5..3cb3aa5 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03CourseMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03CourseMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03GradeMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03GradeMapper.java index 0440cc4..0440cc4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03GradeMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03GradeMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03StudentMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03StudentMapper.java index 00659d0..00659d0 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03StudentMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/demo03/Demo03StudentMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java index c0e2af3..c0e2af3 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java index 3d6ec51..3d6ec51 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileContentMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileMapper.java index 11233a4..11233a4 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/job/JobLogMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/job/JobLogMapper.java index 58ac6da..58ac6da 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/job/JobLogMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/job/JobLogMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/job/JobMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/job/JobMapper.java index 481b4e7..481b4e7 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/job/JobMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/job/JobMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiAccessLogMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiAccessLogMapper.java index dce3082..dce3082 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiAccessLogMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiAccessLogMapper.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiErrorLogMapper.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiErrorLogMapper.java index b597d79..b597d79 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiErrorLogMapper.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/logger/ApiErrorLogMapper.java diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/DictTypeConstants.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/DictTypeConstants.java index 36ad63d..fcab9e6 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/DictTypeConstants.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/DictTypeConstants.java @@ -7,6 +7,8 @@ package cn.iocoder.yudao.module.infra.enums; */ public interface DictTypeConstants { + String USER_TYPE = "user_type"; // 用户类型 + String JOB_STATUS = "infra_job_status"; // 定时任务状态的枚举 String JOB_LOG_STATUS = "infra_job_log_status"; // 定时任务日志状态的枚举 diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java index 4cce820..4cce820 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnHtmlTypeEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnHtmlTypeEnum.java index 2c0cdbb..2c0cdbb 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnHtmlTypeEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnHtmlTypeEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnListConditionEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnListConditionEnum.java index 7098700..7098700 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnListConditionEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenColumnListConditionEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java index db4326f..db4326f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenSceneEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenSceneEnum.java index f1fdea0..f1fdea0 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenSceneEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenSceneEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java index 9a21833..9a21833 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenTemplateTypeEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/config/ConfigTypeEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/config/ConfigTypeEnum.java index 15c2015..15c2015 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/config/ConfigTypeEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/config/ConfigTypeEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/job/JobLogStatusEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/job/JobLogStatusEnum.java index 9567053..9567053 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/job/JobLogStatusEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/job/JobLogStatusEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/job/JobStatusEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/job/JobStatusEnum.java index 3d9ec3f..3d9ec3f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/job/JobStatusEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/job/JobStatusEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/logger/ApiErrorLogProcessStatusEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/logger/ApiErrorLogProcessStatusEnum.java index b309753..b309753 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/logger/ApiErrorLogProcessStatusEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/logger/ApiErrorLogProcessStatusEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/package-info.java index 0749e8e..0749e8e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/enums/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenConfiguration.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenConfiguration.java index d49723d..d49723d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenConfiguration.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenConfiguration.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenProperties.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenProperties.java index cfa1fa2..cfa1fa2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenProperties.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/config/CodegenProperties.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/package-info.java index cf1d22c..cf1d22c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/codegen/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java index e0d3f8e..e0d3f8e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/config/YudaoFileAutoConfiguration.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java index 3c7883b..3c7883b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java index 053b3c5..053b3c5 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClient.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java index ed09275..ed09275 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientConfig.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java index e4c84ed..e4c84ed 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactory.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java index 2bd5464..2bd5464 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/FileClientFactoryImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java index 0a050d3..0a050d3 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClient.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java index 1f619df..1f619df 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/db/DBFileClientConfig.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java index 4207eb7..4207eb7 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClient.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java index a1b92ff..a1b92ff 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/ftp/FtpFileClientConfig.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java index 7fa2a7e..7fa2a7e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java index d56d4ef..d56d4ef 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClientConfig.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java index 6a1258e..6a1258e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/FilePresignedUrlRespDTO.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java index a33f0d7..a33f0d7 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java index fb19317..fb19317 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java index 000cbd1..000cbd1 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java index c56d045..c56d045 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClientConfig.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java index 866c43b..866c43b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/enums/FileStorageEnum.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java index 5f9308e..5f9308e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java index 04b6867..04b6867 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/config/AdminServerConfiguration.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/config/AdminServerConfiguration.java index 0c29865..0c29865 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/config/AdminServerConfiguration.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/config/AdminServerConfiguration.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/package-info.java index f52e72c..f52e72c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/《芋道 Spring Boot 监控工具 Admin 入门》.md b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/《芋道 Spring Boot 监控工具 Admin 入门》.md index a1e3676..a1e3676 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/《芋道 Spring Boot 监控工具 Admin 入门》.md +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/monitor/《芋道 Spring Boot 监控工具 Admin 入门》.md diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/package-info.java index 77a646a..77a646a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/security/config/SecurityConfiguration.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/security/config/SecurityConfiguration.java index 8d30408..8d30408 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/security/config/SecurityConfiguration.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/security/config/SecurityConfiguration.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/security/core/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/security/core/package-info.java index 7762626..7762626 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/security/core/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/security/core/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/web/config/InfraWebConfiguration.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/web/config/InfraWebConfiguration.java index 7ab1e9e..7ab1e9e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/web/config/InfraWebConfiguration.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/web/config/InfraWebConfiguration.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/web/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/web/package-info.java index 6fb49bf..6fb49bf 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/web/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/web/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/JobLogCleanJob.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/job/job/JobLogCleanJob.java index f2e6f42..f2e6f42 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/job/JobLogCleanJob.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/job/job/JobLogCleanJob.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/AccessLogCleanJob.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/job/logger/AccessLogCleanJob.java index 23a277b..23a277b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/AccessLogCleanJob.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/job/logger/AccessLogCleanJob.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/ErrorLogCleanJob.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/job/logger/ErrorLogCleanJob.java index 8b3b566..8b3b566 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/job/logger/ErrorLogCleanJob.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/job/logger/ErrorLogCleanJob.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java index 5ba5264..5ba5264 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java index 19edb02..19edb02 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/mq/message/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java index 343179c..343179c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/mq/producer/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/package-info.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/package-info.java index b45e7a2..b45e7a2 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/package-info.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/package-info.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java index 0d15c9d..0d15c9d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java index d00971a..d00971a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java index b529c49..b529c49 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilder.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java index d0ba5aa..d0ba5aa 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigService.java index 8ff2fb1..8ff2fb1 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImpl.java index e01ae89..e01ae89 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigService.java index 747f809..747f809 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java index 79ba102..79ba102 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableService.java index 9fd2ee9..9fd2ee9 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java index 289cae1..289cae1 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactService.java index efcb6e5..efcb6e5 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactServiceImpl.java index f695e3d..f695e3d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo01/Demo01ContactServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryService.java index 04d8160..04d8160 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java index 524d20c..524d20c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo02/Demo02CategoryServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentService.java index 1e384a3..1e384a3 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentServiceImpl.java index 21f4949..21f4949 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/demo/demo03/Demo03StudentServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java index 95ae94a..95ae94a 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java index 37e2c46..37e2c46 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java index 247fe5f..247fe5f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java index 4a0faad..4a0faad 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobLogService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobLogService.java index f1e7e7c..f1e7e7c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobLogService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobLogService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImpl.java index bbf5f21..bbf5f21 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobService.java index 971fd1b..971fd1b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImpl.java index 2ebf066..2ebf066 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogService.java index 186d115..186d115 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java index d47f192..d47f192 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogService.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogService.java index fd635b0..fd635b0 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogService.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogService.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java index a98c4b6..a98c4b6 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/DemoWebSocketMessageListener.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/websocket/DemoWebSocketMessageListener.java index a3b7150..a3b7150 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/DemoWebSocketMessageListener.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/websocket/DemoWebSocketMessageListener.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoReceiveMessage.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoReceiveMessage.java index 03a246c..03a246c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoReceiveMessage.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoReceiveMessage.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoSendMessage.java b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoSendMessage.java index f0c14f5..f0c14f5 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoSendMessage.java +++ b/yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/websocket/message/DemoSendMessage.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm b/yudao-module-infra/src/main/resources/codegen/java/controller/controller.vm index 5aa3bae..5aa3bae 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/controller.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/controller/controller.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/listReqVO.vm b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/listReqVO.vm index 46b6a25..46b6a25 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/listReqVO.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/listReqVO.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/pageReqVO.vm index 003bac9..003bac9 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/pageReqVO.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/respVO.vm b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm index 24c3519..24c3519 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/respVO.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/respVO.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/saveReqVO.vm b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/saveReqVO.vm index b432c75..b432c75 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/saveReqVO.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/controller/vo/saveReqVO.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm b/yudao-module-infra/src/main/resources/codegen/java/dal/do.vm index b019d6e..b019d6e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/dal/do.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do_sub.vm b/yudao-module-infra/src/main/resources/codegen/java/dal/do_sub.vm index 16be55e..16be55e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do_sub.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/dal/do_sub.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.vm b/yudao-module-infra/src/main/resources/codegen/java/dal/mapper.vm index b98b471..b98b471 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/dal/mapper.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.xml.vm b/yudao-module-infra/src/main/resources/codegen/java/dal/mapper.xml.vm index 290378d..290378d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper.xml.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/dal/mapper.xml.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper_sub.vm b/yudao-module-infra/src/main/resources/codegen/java/dal/mapper_sub.vm index 6ccaea7..6ccaea7 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/mapper_sub.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/dal/mapper_sub.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/enums/errorcode.vm b/yudao-module-infra/src/main/resources/codegen/java/enums/errorcode.vm index b7e21e6..b7e21e6 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/enums/errorcode.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/enums/errorcode.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/service.vm b/yudao-module-infra/src/main/resources/codegen/java/service/service.vm index c4ee4f0..c4ee4f0 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/service.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/service/service.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm b/yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm index 80bc71b..80bc71b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/service/serviceImpl.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm b/yudao-module-infra/src/main/resources/codegen/java/test/serviceTest.vm index bfd4600..bfd4600 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm +++ b/yudao-module-infra/src/main/resources/codegen/java/test/serviceTest.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/h2.vm b/yudao-module-infra/src/main/resources/codegen/sql/h2.vm index a073fdb..a073fdb 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/h2.vm +++ b/yudao-module-infra/src/main/resources/codegen/sql/h2.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/sql.vm b/yudao-module-infra/src/main/resources/codegen/sql/sql.vm index 41b107d..41b107d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/sql/sql.vm +++ b/yudao-module-infra/src/main/resources/codegen/sql/sql.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/api/api.js.vm b/yudao-module-infra/src/main/resources/codegen/vue/api/api.js.vm index 835c019..835c019 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/api/api.js.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue/api/api.js.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm index 99aa91a..99aa91a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue/views/components/form_sub_erp.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm index ca266be..ca266be 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue/views/components/form_sub_inner.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm index 48a404a..48a404a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue/views/components/form_sub_normal.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm index 589736b..589736b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue/views/components/list_sub_erp.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm index 90b8e41..90b8e41 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue/views/components/list_sub_inner.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/form.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue/views/form.vue.vm index 634d05d..634d05d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/form.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue/views/form.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue/views/index.vue.vm index 9c1e124..9c1e124 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue/views/index.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm b/yudao-module-infra/src/main/resources/codegen/vue3/api/api.ts.vm index c3044fb..c3044fb 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/api/api.ts.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3/api/api.ts.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm index 81cd977..81cd977 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_erp.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm index d8542c3..d8542c3 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_inner.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm index 3fa1eff..3fa1eff 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/form_sub_normal.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm index 3f0710e..3f0710e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_erp.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm index 3fe6488..3fe6488 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3/views/components/list_sub_inner.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3/views/form.vue.vm index e37474b..e37474b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/form.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3/views/form.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3/views/index.vue.vm index 399b58e..399b58e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3/views/index.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3/views/index.vue.vm diff --git a/yudao-module-infra/src/main/resources/codegen/vue3_vben/api/api.ts.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben/api/api.ts.vm new file mode 100644 index 0000000..b7f2651 --- /dev/null +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben/api/api.ts.vm @@ -0,0 +1,32 @@ +import { defHttp } from '@/utils/http/axios' +#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}") + +// 查询${table.classComment}列表 +export function get${simpleClassName}Page(params) { + return defHttp.get({ url: '${baseURL}/page', params }) +} + +// 查询${table.classComment}详情 +export function get${simpleClassName}(id: number) { + return defHttp.get({ url: `${baseURL}/get?id=${id}` }) +} + +// 新增${table.classComment} +export function create${simpleClassName}(data) { + return defHttp.post({ url: '${baseURL}/create', data }) +} + +// 修改${table.classComment} +export function update${simpleClassName}(data) { + return defHttp.put({ url: '${baseURL}/update', data }) +} + +// 删除${table.classComment} +export function delete${simpleClassName}(id: number) { + return defHttp.delete({ url: `${baseURL}/delete?id=${id}` }) +} + +// 导出${table.classComment} Excel +export function export${simpleClassName}(params) { + return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls') +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/data.ts.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben/views/data.ts.vm index 56f4e82..56f4e82 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/data.ts.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben/views/data.ts.vm diff --git a/yudao-module-infra/src/main/resources/codegen/vue3_vben/views/form.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben/views/form.vue.vm new file mode 100644 index 0000000..1804365 --- /dev/null +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben/views/form.vue.vm @@ -0,0 +1,58 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/main/resources/codegen/vue3_vben/views/index.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben/views/index.vue.vm new file mode 100644 index 0000000..9e59b12 --- /dev/null +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben/views/index.vue.vm @@ -0,0 +1,92 @@ + + diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm index d3342f1..d3342f1 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/api/api.ts.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm index c87cd2a..c87cd2a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/form.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm index 1888a7c..1888a7c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/index.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm index 6517e31..6517e31 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_erp.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm index 7f0ec3a..7f0ec3a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_inner.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm index 27c02ab..27c02ab 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/form_sub_normal.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm index 9ee3364..9ee3364 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_erp.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm index d46037b..d46037b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/general/views/modules/list_sub_inner.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm index d3342f1..d3342f1 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/api/api.ts.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm index 5ca4c08..5ca4c08 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/data.ts.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm index 1c1b7d7..1c1b7d7 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/form.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm index 1d8700e..1d8700e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/index.vue.vm diff --git a/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm new file mode 100644 index 0000000..c71e0b4 --- /dev/null +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm @@ -0,0 +1,90 @@ +#set ($subTable = $subTables.get($subIndex))##当前表 +#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组 +#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 +#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) + + + diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm index 2c59695..2c59695 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_inner.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm index 2acd9de..2acd9de 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_normal.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm index 6246ff1..6246ff1 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_erp.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm index 2d8aa20..2d8aa20 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm +++ b/yudao-module-infra/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/list_sub_inner.vue.vm diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/file/erweima.jpg b/yudao-module-infra/src/main/resources/file/erweima.jpg index 1447283..1447283 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/file/erweima.jpg +++ b/yudao-module-infra/src/main/resources/file/erweima.jpg diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java new file mode 100644 index 0000000..8db47da --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.ftp; + +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.extra.ftp.FtpMode; +import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * {@link FtpFileClient} 集成测试 + * + * @author 芋道源码 + */ +public class FtpFileClientTest { + +// docker run -d \ +// -p 2121:21 -p 30000-30009:30000-30009 \ +// -e FTP_USER=foo \ +// -e FTP_PASS=pass \ +// -e PASV_ADDRESS=127.0.0.1 \ +// -e PASV_MIN_PORT=30000 \ +// -e PASV_MAX_PORT=30009 \ +// -v $(pwd)/ftp-data:/home/vsftpd \ +// fauria/vsftpd + + @Test + @Disabled + public void test() { + // 创建客户端 + FtpFileClientConfig config = new FtpFileClientConfig(); + config.setDomain("http://127.0.0.1:48080"); + config.setBasePath("/home/ftp"); + config.setHost("127.0.0.1"); + config.setPort(2121); + config.setUsername("foo"); + config.setPassword("pass"); + config.setMode(FtpMode.Passive.name()); + FtpFileClient client = new FtpFileClient(0L, config); + client.init(); + // 上传文件 + String path = IdUtil.fastSimpleUUID() + ".jpg"; + byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); + String fullPath = client.upload(content, path, "image/jpeg"); + System.out.println("访问地址:" + fullPath); + if (false) { + byte[] bytes = client.getContent(path); + System.out.println("文件内容:" + bytes); + } + if (false) { + client.delete(path); + } + } + +} diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java new file mode 100644 index 0000000..7c622a5 --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.local; + +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class LocalFileClientTest { + + @Test + @Disabled + public void test() { + // 创建客户端 + LocalFileClientConfig config = new LocalFileClientConfig(); + config.setDomain("http://127.0.0.1:48080"); + config.setBasePath("/Users/yunai/file_test"); + LocalFileClient client = new LocalFileClient(0L, config); + client.init(); + // 上传文件 + String path = IdUtil.fastSimpleUUID() + ".jpg"; + byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); + String fullPath = client.upload(content, path, "image/jpeg"); + System.out.println("访问地址:" + fullPath); + client.delete(path); + } + +} diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java new file mode 100644 index 0000000..3c40ce2 --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java @@ -0,0 +1,118 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.s3; + +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig; +import jakarta.validation.Validation; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class S3FileClientTest { + + @Test + @Disabled // MinIO,如果要集成测试,可以注释本行 + public void testMinIO() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 + config.setAccessKey("admin"); + config.setAccessSecret("password"); + config.setBucket("yudaoyuanma"); + config.setDomain(null); + // 默认 9000 endpoint + config.setEndpoint("http://127.0.0.1:9000"); + + // 执行上传 + testExecuteUpload(config); + } + + @Test + @Disabled // 阿里云 OSS,如果要集成测试,可以注释本行 + public void testAliyun() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 + config.setAccessKey(System.getenv("ALIYUN_ACCESS_KEY")); + config.setAccessSecret(System.getenv("ALIYUN_SECRET_KEY")); + config.setBucket("yunai-aoteman"); + config.setDomain(null); // 如果有自定义域名,则可以设置。http://ali-oss.iocoder.cn + // 默认北京的 endpoint + config.setEndpoint("oss-cn-beijing.aliyuncs.com"); + + // 执行上传 + testExecuteUpload(config); + } + + @Test + @Disabled // 腾讯云 COS,如果要集成测试,可以注释本行 + public void testQCloud() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 + config.setAccessKey(System.getenv("QCLOUD_ACCESS_KEY")); + config.setAccessSecret(System.getenv("QCLOUD_SECRET_KEY")); + config.setBucket("aoteman-1255880240"); + config.setDomain(null); // 如果有自定义域名,则可以设置。http://tengxun-oss.iocoder.cn + // 默认上海的 endpoint + config.setEndpoint("cos.ap-shanghai.myqcloud.com"); + + // 执行上传 + testExecuteUpload(config); + } + + @Test + @Disabled // 七牛云存储,如果要集成测试,可以注释本行 + public void testQiniu() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 +// config.setAccessKey(System.getenv("QINIU_ACCESS_KEY")); +// config.setAccessSecret(System.getenv("QINIU_SECRET_KEY")); + config.setAccessKey("b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8"); + config.setAccessSecret("kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP"); + config.setBucket("ruoyi-vue-pro"); + config.setDomain("http://test.yudao.iocoder.cn"); // 如果有自定义域名,则可以设置。http://static.yudao.iocoder.cn + // 默认上海的 endpoint + config.setEndpoint("s3-cn-south-1.qiniucs.com"); + + // 执行上传 + testExecuteUpload(config); + } + + @Test + @Disabled // 华为云存储,如果要集成测试,可以注释本行 + public void testHuaweiCloud() throws Exception { + S3FileClientConfig config = new S3FileClientConfig(); + // 配置成你自己的 +// config.setAccessKey(System.getenv("HUAWEI_CLOUD_ACCESS_KEY")); +// config.setAccessSecret(System.getenv("HUAWEI_CLOUD_SECRET_KEY")); + config.setBucket("yudao"); + config.setDomain(null); // 如果有自定义域名,则可以设置。 + // 默认上海的 endpoint + config.setEndpoint("obs.cn-east-3.myhuaweicloud.com"); + + // 执行上传 + testExecuteUpload(config); + } + + private void testExecuteUpload(S3FileClientConfig config) throws Exception { + // 校验配置 + ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config); + // 创建 Client + S3FileClient client = new S3FileClient(0L, config); + client.init(); + // 上传文件 + String path = IdUtil.fastSimpleUUID() + ".jpg"; + byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); + String fullPath = client.upload(content, path, "image/jpeg"); + System.out.println("访问地址:" + fullPath); + // 读取文件 + if (true) { + byte[] bytes = client.getContent(path); + System.out.println("文件内容:" + bytes.length); + } + // 删除文件 + if (false) { + client.delete(path); + } + } + +} diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java new file mode 100644 index 0000000..53c904e --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.infra.framework.file.core.sftp; + +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClient; +import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * {@link SftpFileClient} 集成测试 + * + * @author 芋道源码 + */ +public class SftpFileClientTest { + +// docker run -p 2222:22 -d \ +// -v $(pwd)/sftp-data:/home/foo/upload \ +// atmoz/sftp \ +// foo:pass:1001 + + @Test + @Disabled + public void test() { + // 创建客户端 + SftpFileClientConfig config = new SftpFileClientConfig(); + config.setDomain("http://127.0.0.1:48080"); + config.setBasePath("/upload"); // 注意,这个是相对路径,不是实际 linux 上的路径!!! + config.setHost("127.0.0.1"); + config.setPort(2222); + config.setUsername("foo"); + config.setPassword("pass"); + SftpFileClient client = new SftpFileClient(0L, config); + client.init(); + // 上传文件 + String path = IdUtil.fastSimpleUUID() + ".jpg"; + byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); + String fullPath = client.upload(content, path, "image/jpeg"); + System.out.println("访问地址:" + fullPath); + if (false) { + byte[] bytes = client.getContent(path); + System.out.println("文件内容:" + bytes); + } + if (false) { + client.delete(path); + } + } + +} diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java new file mode 100644 index 0000000..a6de8d9 --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.infra.service; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.generator.query.DefaultQuery; +import com.baomidou.mybatisplus.generator.config.DataSourceConfig; +import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; + +import java.util.List; + +public class DefaultDatabaseQueryTest { + + public static void main(String[] args) { +// DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:oracle:thin:@127.0.0.1:1521:xe", +// "root", "123456").build(); + DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro", + "root", "123456").build(); +// StrategyConfig strategyConfig = new StrategyConfig.Builder().build(); + + ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null); + + DefaultQuery query = new DefaultQuery(builder); + + long time = System.currentTimeMillis(); + List tableInfos = query.queryTables(); + for (TableInfo tableInfo : tableInfos) { + if (StrUtil.startWithAny(tableInfo.getName().toLowerCase(), "act_", "flw_", "qrtz_")) { + continue; + } + System.out.println(String.format("CREATE SEQUENCE %s_seq MINVALUE 1;", tableInfo.getName())); +// System.out.println(String.format("DELETE FROM %s WHERE deleted = '1';", tableInfo.getName())); + } + System.out.println(tableInfos.size()); + System.out.println(System.currentTimeMillis() - time); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImplTest.java index 8ca4a0e..8ca4a0e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImplTest.java +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImplTest.java diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java new file mode 100644 index 0000000..7a26ea9 --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java @@ -0,0 +1,87 @@ +package cn.iocoder.yudao.module.infra.service.codegen.inner; + +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; +import com.baomidou.mybatisplus.generator.config.po.TableField; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.baomidou.mybatisplus.generator.config.rules.IColumnType; +import org.apache.ibatis.type.JdbcType; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; + +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class CodegenBuilderTest extends BaseMockitoUnitTest { + + @InjectMocks + private CodegenBuilder codegenBuilder; + + @Test + public void testBuildTable() { + // 准备参数 + TableInfo tableInfo = mock(TableInfo.class); + // mock 方法 + when(tableInfo.getName()).thenReturn("system_user"); + when(tableInfo.getComment()).thenReturn("用户"); + + // 调用 + CodegenTableDO table = codegenBuilder.buildTable(tableInfo); + // 断言 + assertEquals("system_user", table.getTableName()); + assertEquals("用户", table.getTableComment()); + assertEquals("system", table.getModuleName()); + assertEquals("user", table.getBusinessName()); + assertEquals("User", table.getClassName()); + assertEquals("用户", table.getClassComment()); + } + + @Test + public void testBuildColumns() { + // 准备参数 + Long tableId = randomLongId(); + TableField tableField = mock(TableField.class); + List tableFields = Collections.singletonList(tableField); + // mock 方法 + TableField.MetaInfo metaInfo = mock(TableField.MetaInfo.class); + when(tableField.getMetaInfo()).thenReturn(metaInfo); + when(metaInfo.getJdbcType()).thenReturn(JdbcType.BIGINT); + when(tableField.getComment()).thenReturn("编号"); + when(tableField.isKeyFlag()).thenReturn(true); + IColumnType columnType = mock(IColumnType.class); + when(tableField.getColumnType()).thenReturn(columnType); + when(columnType.getType()).thenReturn("Long"); + when(tableField.getName()).thenReturn("id2"); + when(tableField.getPropertyName()).thenReturn("id"); + + // 调用 + List columns = codegenBuilder.buildColumns(tableId, tableFields); + // 断言 + assertEquals(1, columns.size()); + CodegenColumnDO column = columns.get(0); + assertEquals(tableId, column.getTableId()); + assertEquals("id2", column.getColumnName()); + assertEquals("BIGINT", column.getDataType()); + assertEquals("编号", column.getColumnComment()); + assertFalse(column.getNullable()); + assertTrue(column.getPrimaryKey()); + assertEquals(1, column.getOrdinalPosition()); + assertEquals("Long", column.getJavaType()); + assertEquals("id", column.getJavaField()); + assertNull(column.getDictType()); + assertNotNull(column.getExample()); + assertFalse(column.getCreateOperation()); + assertTrue(column.getUpdateOperation()); + assertFalse(column.getListOperation()); + assertEquals("=", column.getListOperationCondition()); + assertTrue(column.getListOperationResult()); + assertEquals("input", column.getHtmlType()); + } + +} diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java new file mode 100644 index 0000000..3c7390a --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java @@ -0,0 +1,138 @@ +package cn.iocoder.yudao.module.infra.service.codegen.inner; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.ZipUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; +import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.InjectMocks; +import org.mockito.Spy; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * {@link CodegenEngine} 的单元测试抽象基类 + * + * @author 芋道源码 + */ +public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { + + /** + * 测试文件资源目录 + */ + private String resourcesPath = ""; + + @InjectMocks + protected CodegenEngine codegenEngine; + + @Spy + protected CodegenProperties codegenProperties = new CodegenProperties() + .setBasePackage("cn.iocoder.yudao"); + + @BeforeEach + public void setUp() { + codegenEngine.setJakartaEnable(true); // 强制使用 jakarta,保证单测可以基于 jakarta 断言 + codegenEngine.initGlobalBindingMap(); + // 单测强制使用 + // 获取测试文件 resources 路径 + String absolutePath = FileUtil.getAbsolutePath("application-unit-test.yaml"); + // 系统不一样生成的文件也有差异,那就各自生成各自的 + resourcesPath = absolutePath.split("/target")[0] + "/src/test/resources/codegen/"; + } + + protected static CodegenTableDO getTable(String name) { + String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json"); + return JsonUtils.parseObject(content, "table", CodegenTableDO.class); + } + + protected static List getColumnList(String name) { + String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json"); + List list = JsonUtils.parseArray(content, "columns", CodegenColumnDO.class); + list.forEach(column -> { + if (column.getNullable() == null) { + column.setNullable(false); + } + if (column.getCreateOperation() == null) { + column.setCreateOperation(false); + } + if (column.getUpdateOperation() == null) { + column.setUpdateOperation(false); + } + if (column.getListOperation() == null) { + column.setListOperation(false); + } + if (column.getListOperationResult() == null) { + column.setListOperationResult(false); + } + }); + return list; + } + + @SuppressWarnings("rawtypes") + protected static void assertResult(Map result, String path) { + String assertContent = ResourceUtil.readUtf8Str("codegen/" + path + "/assert.json"); + List asserts = JsonUtils.parseArray(assertContent, HashMap.class); + assertEquals(asserts.size(), result.size()); + // 校验每个文件 + asserts.forEach(assertMap -> { + String contentPath = (String) assertMap.get("contentPath"); + String filePath = (String) assertMap.get("filePath"); + String content = ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath); + assertEquals(content, result.get(filePath), filePath + ":不匹配"); + }); + } + + // ==================== 调试专用 ==================== + + /** + * 【调试使用】将生成的代码,写入到文件 + * + * @param result 生成的代码 + * @param path 写入文件的路径 + */ + protected void writeFile(Map result, String path) { + // 生成压缩包 + String[] paths = result.keySet().toArray(new String[0]); + ByteArrayInputStream[] ins = result.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipUtil.zip(outputStream, paths, ins); + // 写入文件 + FileUtil.writeBytes(outputStream.toByteArray(), path); + } + + /** + * 【调试使用】将生成的结果,写入到文件 + * + * @param result 生成的代码 + * @param basePath 写入文件的路径(绝对路径) + */ + protected void writeResult(Map result, String basePath) { + // 写入文件内容 + List> asserts = new ArrayList<>(); + result.forEach((filePath, fileContent) -> { + String lastFilePath = StrUtil.subAfter(filePath, '/', true); + String contentPath = StrUtil.subAfter(lastFilePath, '.', true) + + '/' + StrUtil.subBefore(lastFilePath, '.', true); + asserts.add(MapUtil.builder().put("filePath", filePath) + .put("contentPath", contentPath).build()); + FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath); + }); + // 写入 assert.json 文件 + FileUtil.writeUtf8String(JsonUtils.toJsonPrettyString(asserts), basePath + "/assert.json"); + } + +} diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java new file mode 100644 index 0000000..a251484 --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.infra.service.codegen.inner; + +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * {@link CodegenEngine} 的 Vue2 + Element UI 单元测试 + * + * @author 芋道源码 + */ +@Disabled +public class CodegenEngineVue2Test extends CodegenEngineAbstractTest { + + @Test + public void testExecute_vue2_one() { + // 准备参数 + CodegenTableDO table = getTable("student") + .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); + List columns = getColumnList("student"); + + // 调用 + Map result = codegenEngine.execute(table, columns, null, null); + // 生成测试文件 + //writeResult(result, resourcesPath + "/vue2_one"); + // 断言 + assertResult(result, "/vue2_one"); + } + + @Test + public void testExecute_vue2_tree() { + // 准备参数 + CodegenTableDO table = getTable("category") + .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType()); + List columns = getColumnList("category"); + + // 调用 + Map result = codegenEngine.execute(table, columns, null, null); + // 生成测试文件 + //writeResult(result, resourcesPath + "/vue2_tree"); + // 断言 + assertResult(result, "/vue2_tree"); +// writeFile(result, "/Users/yunai/test/demo66.zip"); + } + + @Test + public void testExecute_vue2_master_normal() { + testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vue2_master_normal"); + } + + @Test + public void testExecute_vue2_master_erp() { + testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vue2_master_erp"); + } + + @Test + public void testExecute_vue2_master_inner() { + testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vue2_master_inner"); + } + + private void testExecute_vue2_master(CodegenTemplateTypeEnum templateType, + String path) { + // 准备参数 + CodegenTableDO table = getTable("student") + .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) + .setTemplateType(templateType.getType()); + List columns = getColumnList("student"); + // 准备参数(子表) + CodegenTableDO contactTable = getTable("contact") + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) + .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) + .setSubJoinColumnId(100L).setSubJoinMany(true); + List contactColumns = getColumnList("contact"); + // 准备参数(班主任) + CodegenTableDO teacherTable = getTable("teacher") + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) + .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) + .setSubJoinColumnId(200L).setSubJoinMany(false); + List teacherColumns = getColumnList("teacher"); + + // 调用 + Map result = codegenEngine.execute(table, columns, + Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns)); + // 生成测试文件 + //writeResult(result, resourcesPath + path); + // 断言 + assertResult(result, path); +// writeFile(result, "/Users/yunai/test/demo11.zip"); + } + +} diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue3Test.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue3Test.java new file mode 100644 index 0000000..7695dac --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue3Test.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.infra.service.codegen.inner; + +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; +import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * {@link CodegenEngine} 的 Vue2 + Element Plus 单元测试 + * + * @author 芋道源码 + */ +@Disabled +public class CodegenEngineVue3Test extends CodegenEngineAbstractTest { + + @Test + public void testExecute_vue3_one() { + // 准备参数 + CodegenTableDO table = getTable("student") + .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) + .setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); + List columns = getColumnList("student"); + + // 调用 + Map result = codegenEngine.execute(table, columns, null, null); + // 生成测试文件 + //writeResult(result, resourcesPath + "/vue3_one"); + // 断言 + assertResult(result, "/vue3_one"); + } + + @Test + public void testExecute_vue3_tree() { + // 准备参数 + CodegenTableDO table = getTable("category") + .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) + .setTemplateType(CodegenTemplateTypeEnum.TREE.getType()); + List columns = getColumnList("category"); + + // 调用 + Map result = codegenEngine.execute(table, columns, null, null); + // 生成测试文件 + //writeResult(result, resourcesPath + "/vue3_tree"); + // 断言 + assertResult(result, "/vue3_tree"); +// writeFile(result, "/Users/yunai/test/demo66.zip"); + } + + @Test + public void testExecute_vue3_master_normal() { + testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vue3_master_normal"); + } + + @Test + public void testExecute_vue3_master_erp() { + testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vue3_master_erp"); + } + + @Test + public void testExecute_vue3_master_inner() { + testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vue3_master_inner"); + } + + private void testExecute_vue3_master(CodegenTemplateTypeEnum templateType, + String path) { + // 准备参数 + CodegenTableDO table = getTable("student") + .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) + .setTemplateType(templateType.getType()); + List columns = getColumnList("student"); + // 准备参数(子表) + CodegenTableDO contactTable = getTable("contact") + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) + .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) + .setSubJoinColumnId(100L).setSubJoinMany(true); + List contactColumns = getColumnList("contact"); + // 准备参数(班主任) + CodegenTableDO teacherTable = getTable("teacher") + .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) + .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) + .setSubJoinColumnId(200L).setSubJoinMany(false); + List teacherColumns = getColumnList("teacher"); + + // 调用 + Map result = codegenEngine.execute(table, columns, + Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns)); + // 生成测试文件 + //writeResult(result, resourcesPath + path); + // 断言 + assertResult(result, path); + // writeFile(result, "/Users/yunai/test/demo11.zip"); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImplTest.java index 8459abb..8459abb 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImplTest.java +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/config/ConfigServiceImplTest.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java index 4be9e28..4be9e28 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/db/DataSourceConfigServiceImplTest.java diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImplTest.java new file mode 100644 index 0000000..e7a5072 --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImplTest.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.infra.service.db; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; +import com.baomidou.mybatisplus.generator.config.po.TableField; +import com.baomidou.mybatisplus.generator.config.po.TableInfo; +import com.baomidou.mybatisplus.generator.config.rules.DbColumnType; +import org.apache.ibatis.type.JdbcType; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Import(DatabaseTableServiceImpl.class) +public class DatabaseTableServiceImplTest extends BaseDbUnitTest { + + @Resource + private DatabaseTableServiceImpl databaseTableService; + + @MockBean + private DataSourceConfigService dataSourceConfigService; + + @Test + public void testGetTableList() { + // 准备参数 + Long dataSourceConfigId = randomLongId(); + // mock 方法 + DataSourceConfigDO dataSourceConfig = new DataSourceConfigDO().setUsername("sa").setPassword("") + .setUrl("jdbc:h2:mem:testdb"); + when(dataSourceConfigService.getDataSourceConfig(eq(dataSourceConfigId))) + .thenReturn(dataSourceConfig); + + // 调用 + List tables = databaseTableService.getTableList(dataSourceConfigId, + "config", "参数"); + // 断言 + assertEquals(1, tables.size()); + assertTableInfo(tables.get(0)); + } + + @Test + public void testGetTable() { + // 准备参数 + Long dataSourceConfigId = randomLongId(); + // mock 方法 + DataSourceConfigDO dataSourceConfig = new DataSourceConfigDO().setUsername("sa").setPassword("") + .setUrl("jdbc:h2:mem:testdb"); + when(dataSourceConfigService.getDataSourceConfig(eq(dataSourceConfigId))) + .thenReturn(dataSourceConfig); + + // 调用 + TableInfo tableInfo = databaseTableService.getTable(dataSourceConfigId, "infra_config"); + // 断言 + assertTableInfo(tableInfo); + } + + private void assertTableInfo(TableInfo tableInfo) { + assertEquals("infra_config", tableInfo.getName()); + assertEquals("参数配置表", tableInfo.getComment()); + assertEquals(13, tableInfo.getFields().size()); + // id 字段 + TableField idField = tableInfo.getFields().get(0); + assertEquals("id", idField.getName()); + assertEquals(JdbcType.BIGINT, idField.getMetaInfo().getJdbcType()); + assertEquals("编号", idField.getComment()); + assertFalse(idField.getMetaInfo().isNullable()); + assertTrue(idField.isKeyFlag()); + assertTrue(idField.isKeyIdentityFlag()); + assertEquals(DbColumnType.LONG, idField.getColumnType()); + assertEquals("id", idField.getPropertyName()); + // name 字段 + TableField nameField = tableInfo.getFields().get(3); + assertEquals("name", nameField.getName()); + assertEquals(JdbcType.VARCHAR, nameField.getMetaInfo().getJdbcType()); + assertEquals("名字", nameField.getComment()); + assertFalse(nameField.getMetaInfo().isNullable()); + assertFalse(nameField.isKeyFlag()); + assertFalse(nameField.isKeyIdentityFlag()); + assertEquals(DbColumnType.STRING, nameField.getColumnType()); + assertEquals("name", nameField.getPropertyName()); + } +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java index cedb875..cedb875 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java index c109b25..c109b25 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImplTest.java new file mode 100644 index 0000000..a6bd3c8 --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImplTest.java @@ -0,0 +1,172 @@ +package cn.iocoder.yudao.module.infra.service.job; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO; +import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO; +import cn.iocoder.yudao.module.infra.dal.mysql.job.JobLogMapper; +import cn.iocoder.yudao.module.infra.enums.job.JobLogStatusEnum; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Import(JobLogServiceImpl.class) +public class JobLogServiceImplTest extends BaseDbUnitTest { + + @Resource + private JobLogServiceImpl jobLogService; + @Resource + private JobLogMapper jobLogMapper; + + @Test + public void testCreateJobLog() { + // 准备参数 + JobLogDO reqVO = randomPojo(JobLogDO.class, o -> o.setExecuteIndex(1)); + + // 调用 + Long id = jobLogService.createJobLog(reqVO.getJobId(), reqVO.getBeginTime(), + reqVO.getHandlerName(), reqVO.getHandlerParam(), reqVO.getExecuteIndex()); + // 断言 + assertNotNull(id); + // 校验记录的属性是否正确 + JobLogDO job = jobLogMapper.selectById(id); + assertEquals(JobLogStatusEnum.RUNNING.getStatus(), job.getStatus()); + } + + @Test + public void testUpdateJobLogResultAsync_success() { + // mock 数据 + JobLogDO log = randomPojo(JobLogDO.class, o -> { + o.setExecuteIndex(1); + o.setStatus(JobLogStatusEnum.RUNNING.getStatus()); + }); + jobLogMapper.insert(log); + // 准备参数 + Long logId = log.getId(); + LocalDateTime endTime = randomLocalDateTime(); + Integer duration = randomInteger(); + boolean success = true; + String result = randomString(); + + // 调用 + jobLogService.updateJobLogResultAsync(logId, endTime, duration, success, result); + // 校验记录的属性是否正确 + JobLogDO dbLog = jobLogMapper.selectById(log.getId()); + assertEquals(endTime, dbLog.getEndTime()); + assertEquals(duration, dbLog.getDuration()); + assertEquals(JobLogStatusEnum.SUCCESS.getStatus(), dbLog.getStatus()); + assertEquals(result, dbLog.getResult()); + } + + @Test + public void testUpdateJobLogResultAsync_failure() { + // mock 数据 + JobLogDO log = randomPojo(JobLogDO.class, o -> { + o.setExecuteIndex(1); + o.setStatus(JobLogStatusEnum.RUNNING.getStatus()); + }); + jobLogMapper.insert(log); + // 准备参数 + Long logId = log.getId(); + LocalDateTime endTime = randomLocalDateTime(); + Integer duration = randomInteger(); + boolean success = false; + String result = randomString(); + + // 调用 + jobLogService.updateJobLogResultAsync(logId, endTime, duration, success, result); + // 校验记录的属性是否正确 + JobLogDO dbLog = jobLogMapper.selectById(log.getId()); + assertEquals(endTime, dbLog.getEndTime()); + assertEquals(duration, dbLog.getDuration()); + assertEquals(JobLogStatusEnum.FAILURE.getStatus(), dbLog.getStatus()); + assertEquals(result, dbLog.getResult()); + } + + @Test + public void testCleanJobLog() { + // mock 数据 + JobLogDO log01 = randomPojo(JobLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3)))) + .setExecuteIndex(1); + jobLogMapper.insert(log01); + JobLogDO log02 = randomPojo(JobLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1)))) + .setExecuteIndex(1); + jobLogMapper.insert(log02); + // 准备参数 + Integer exceedDay = 2; + Integer deleteLimit = 1; + + // 调用 + Integer count = jobLogService.cleanJobLog(exceedDay, deleteLimit); + // 断言 + assertEquals(1, count); + List logs = jobLogMapper.selectList(); + assertEquals(1, logs.size()); + // TODO @芋艿:createTime updateTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(log02, logs.get(0), "createTime", "updateTime"); + } + + @Test + public void testGetJobLog() { + // mock 数据 + JobLogDO dbJobLog = randomPojo(JobLogDO.class, o -> o.setExecuteIndex(1)); + jobLogMapper.insert(dbJobLog); + // 准备参数 + Long id = dbJobLog.getId(); + + // 调用 + JobLogDO jobLog = jobLogService.getJobLog(id); + // 断言 + assertPojoEquals(dbJobLog, jobLog); + } + + @Test + public void testGetJobPage() { + // mock 数据 + JobLogDO dbJobLog = randomPojo(JobLogDO.class, o -> { + o.setExecuteIndex(1); + o.setHandlerName("handlerName 单元测试"); + o.setStatus(JobLogStatusEnum.SUCCESS.getStatus()); + o.setBeginTime(buildTime(2021, 1, 8)); + o.setEndTime(buildTime(2021, 1, 8)); + }); + jobLogMapper.insert(dbJobLog); + // 测试 jobId 不匹配 + jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setJobId(randomLongId()))); + // 测试 handlerName 不匹配 + jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setHandlerName(randomString()))); + // 测试 beginTime 不匹配 + jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setBeginTime(buildTime(2021, 1, 7)))); + // 测试 endTime 不匹配 + jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setEndTime(buildTime(2021, 1, 9)))); + // 测试 status 不匹配 + jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setStatus(JobLogStatusEnum.FAILURE.getStatus()))); + // 准备参数 + JobLogPageReqVO reqVo = new JobLogPageReqVO(); + reqVo.setJobId(dbJobLog.getJobId()); + reqVo.setHandlerName("单元"); + reqVo.setBeginTime(dbJobLog.getBeginTime()); + reqVo.setEndTime(dbJobLog.getEndTime()); + reqVo.setStatus(JobLogStatusEnum.SUCCESS.getStatus()); + + // 调用 + PageResult pageResult = jobLogService.getJobLogPage(reqVo); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbJobLog, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImplTest.java index 7cd160c..7cd160c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImplTest.java +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobServiceImplTest.java diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImplTest.java new file mode 100644 index 0000000..b8da238 --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImplTest.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.infra.service.logger; + +import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO; +import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO; +import cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiAccessLogMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import java.time.Duration; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Import(ApiAccessLogServiceImpl.class) +public class ApiAccessLogServiceImplTest extends BaseDbUnitTest { + + @Resource + private ApiAccessLogServiceImpl apiAccessLogService; + + @Resource + private ApiAccessLogMapper apiAccessLogMapper; + + @Test + public void testGetApiAccessLogPage() { + ApiAccessLogDO apiAccessLogDO = randomPojo(ApiAccessLogDO.class, o -> { + o.setUserId(2233L); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setApplicationName("yudao-test"); + o.setRequestUrl("foo"); + o.setBeginTime(buildTime(2021, 3, 13)); + o.setDuration(1000); + o.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()); + }); + apiAccessLogMapper.insert(apiAccessLogDO); + // 测试 userId 不匹配 + apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setUserId(3344L))); + // 测试 userType 不匹配 + apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 applicationName 不匹配 + apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setApplicationName("test"))); + // 测试 requestUrl 不匹配 + apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setRequestUrl("bar"))); + // 测试 beginTime 不匹配:构造一个早期时间 2021-02-06 00:00:00 + apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setBeginTime(buildTime(2021, 2, 6)))); + // 测试 duration 不匹配 + apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setDuration(100))); + // 测试 resultCode 不匹配 + apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setResultCode(2))); + // 准备参数 + ApiAccessLogPageReqVO reqVO = new ApiAccessLogPageReqVO(); + reqVO.setUserId(2233L); + reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); + reqVO.setApplicationName("yudao-test"); + reqVO.setRequestUrl("foo"); + reqVO.setBeginTime(buildBetweenTime(2021, 3, 13, 2021, 3, 13)); + reqVO.setDuration(1000); + reqVO.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()); + + // 调用 + PageResult pageResult = apiAccessLogService.getApiAccessLogPage(reqVO); + // 断言,只查到了一条符合条件的 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(apiAccessLogDO, pageResult.getList().get(0)); + } + + @Test + public void testCleanJobLog() { + // mock 数据 + ApiAccessLogDO log01 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3)))); + apiAccessLogMapper.insert(log01); + ApiAccessLogDO log02 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1)))); + apiAccessLogMapper.insert(log02); + // 准备参数 + Integer exceedDay = 2; + Integer deleteLimit = 1; + + // 调用 + Integer count = apiAccessLogService.cleanAccessLog(exceedDay, deleteLimit); + // 断言 + assertEquals(1, count); + List logs = apiAccessLogMapper.selectList(); + assertEquals(1, logs.size()); + // TODO @芋艿:createTime updateTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(log02, logs.get(0), "createTime", "updateTime"); + } + + @Test + public void testCreateApiAccessLog() { + // 准备参数 + ApiAccessLogCreateReqDTO createDTO = randomPojo(ApiAccessLogCreateReqDTO.class); + + // 调用 + apiAccessLogService.createApiAccessLog(createDTO); + // 断言 + ApiAccessLogDO apiAccessLogDO = apiAccessLogMapper.selectOne(null); + assertPojoEquals(createDTO, apiAccessLogDO); + } + +} diff --git a/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImplTest.java b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImplTest.java new file mode 100644 index 0000000..d4854e7 --- /dev/null +++ b/yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImplTest.java @@ -0,0 +1,164 @@ +package cn.iocoder.yudao.module.infra.service.logger; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO; +import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO; +import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO; +import cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper; +import cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.time.Duration; +import java.util.List; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_NOT_FOUND; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_PROCESSED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Import(ApiErrorLogServiceImpl.class) +public class ApiErrorLogServiceImplTest extends BaseDbUnitTest { + + @Resource + private ApiErrorLogServiceImpl apiErrorLogService; + + @Resource + private ApiErrorLogMapper apiErrorLogMapper; + + @Test + public void testGetApiErrorLogPage() { + // mock 数据 + ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, o -> { + o.setUserId(2233L); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setApplicationName("yudao-test"); + o.setRequestUrl("foo"); + o.setExceptionTime(buildTime(2021, 3, 13)); + o.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); + }); + apiErrorLogMapper.insert(apiErrorLogDO); + // 测试 userId 不匹配 + apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setUserId(3344L))); + // 测试 userType 不匹配 + apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 applicationName 不匹配 + apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setApplicationName("test"))); + // 测试 requestUrl 不匹配 + apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setRequestUrl("bar"))); + // 测试 exceptionTime 不匹配:构造一个早期时间 2021-02-06 00:00:00 + apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setExceptionTime(buildTime(2021, 2, 6)))); + // 测试 progressStatus 不匹配 + apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, logDO -> logDO.setProcessStatus(ApiErrorLogProcessStatusEnum.DONE.getStatus()))); + // 准备参数 + ApiErrorLogPageReqVO reqVO = new ApiErrorLogPageReqVO(); + reqVO.setUserId(2233L); + reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); + reqVO.setApplicationName("yudao-test"); + reqVO.setRequestUrl("foo"); + reqVO.setExceptionTime(buildBetweenTime(2021, 3, 1, 2021, 3, 31)); + reqVO.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); + + // 调用 + PageResult pageResult = apiErrorLogService.getApiErrorLogPage(reqVO); + // 断言,只查到了一条符合条件的 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(apiErrorLogDO, pageResult.getList().get(0)); + } + + @Test + public void testCreateApiErrorLog() { + // 准备参数 + ApiErrorLogCreateReqDTO createDTO = randomPojo(ApiErrorLogCreateReqDTO.class); + + // 调用 + apiErrorLogService.createApiErrorLog(createDTO); + // 断言 + ApiErrorLogDO apiErrorLogDO = apiErrorLogMapper.selectOne(null); + assertPojoEquals(createDTO, apiErrorLogDO); + assertEquals(ApiErrorLogProcessStatusEnum.INIT.getStatus(), apiErrorLogDO.getProcessStatus()); + } + + @Test + public void testUpdateApiErrorLogProcess_success() { + // 准备参数 + ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, + o -> o.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus())); + apiErrorLogMapper.insert(apiErrorLogDO); + // 准备参数 + Long id = apiErrorLogDO.getId(); + Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus(); + Long processUserId = randomLongId(); + + // 调用 + apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId); + // 断言 + ApiErrorLogDO dbApiErrorLogDO = apiErrorLogMapper.selectById(apiErrorLogDO.getId()); + assertEquals(processStatus, dbApiErrorLogDO.getProcessStatus()); + assertEquals(processUserId, dbApiErrorLogDO.getProcessUserId()); + assertNotNull(dbApiErrorLogDO.getProcessTime()); + } + + @Test + public void testUpdateApiErrorLogProcess_processed() { + // 准备参数 + ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, + o -> o.setProcessStatus(ApiErrorLogProcessStatusEnum.DONE.getStatus())); + apiErrorLogMapper.insert(apiErrorLogDO); + // 准备参数 + Long id = apiErrorLogDO.getId(); + Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus(); + Long processUserId = randomLongId(); + + // 调用,并断言异常 + assertServiceException(() -> + apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId), + API_ERROR_LOG_PROCESSED); + } + + @Test + public void testUpdateApiErrorLogProcess_notFound() { + // 准备参数 + Long id = randomLongId(); + Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus(); + Long processUserId = randomLongId(); + + // 调用,并断言异常 + assertServiceException(() -> + apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId), + API_ERROR_LOG_NOT_FOUND); + } + + @Test + public void testCleanJobLog() { + // mock 数据 + ApiErrorLogDO log01 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3)))); + apiErrorLogMapper.insert(log01); + ApiErrorLogDO log02 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1)))); + apiErrorLogMapper.insert(log02); + // 准备参数 + Integer exceedDay = 2; + Integer deleteLimit = 1; + + // 调用 + Integer count = apiErrorLogService.cleanErrorLog(exceedDay, deleteLimit); + // 断言 + assertEquals(1, count); + List logs = apiErrorLogMapper.selectList(); + assertEquals(1, logs.size()); + // TODO @芋艿:createTime updateTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(log02, logs.get(0), "createTime", "updateTime"); + } + +} diff --git a/yudao-module-infra/src/test/resources/application-unit-test.yaml b/yudao-module-infra/src/test/resources/application-unit-test.yaml new file mode 100644 index 0000000..5ce2d4b --- /dev/null +++ b/yudao-module-infra/src/test/resources/application-unit-test.yaml @@ -0,0 +1,50 @@ +spring: + main: + lazy-initialization: true # 开启懒加载,加快速度 + banner-mode: off # 单元测试,禁用 Banner + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 + initial-size: 1 # 单元测试,配置为 1,提升启动速度 + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + encoding: UTF-8 + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 127.0.0.1 # 地址 + port: 16379 # 端口(单元测试,使用 16379 端口) + database: 0 # 数据库索引 + +mybatis-plus: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject + +--- #################### 定时任务相关配置 #################### + +--- #################### 配置中心相关配置 #################### + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项(单元测试,禁用 Lock4j) + +--- #################### 监控相关配置 #################### + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + info: + base-package: cn.iocoder.yudao diff --git a/yudao-module-infra/src/test/resources/codegen/table/category.json b/yudao-module-infra/src/test/resources/codegen/table/category.json new file mode 100644 index 0000000..033c048 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/table/category.json @@ -0,0 +1,52 @@ +{ + "table": { + "id": 10, + "scene" : 1, + "parentMenuId" : 888, + "tableName" : "infra_category", + "tableComment" : "分类表", + "moduleName" : "infra", + "businessName" : "demo", + "className" : "InfraCategory", + "classComment" : "分类", + "author" : "芋道源码", + "treeParentColumnId" : 22, + "treeNameColumnId" : 11 + }, + "columns": [ { + "columnName" : "id", + "dataType" : "BIGINT", + "columnComment" : "编号", + "primaryKey" : true, + "javaType" : "Long", + "javaField" : "id", + "example" : "1024", + "updateOperation" : true, + "listOperationResult" : true + }, { + "id" : 11, + "columnName" : "name", + "dataType" : "VARCHAR", + "columnComment" : "名字", + "javaType" : "String", + "javaField" : "name", + "example" : "芋头", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "LIKE", + "listOperationResult" : true, + "htmlType" : "input" + }, { + "id" : 22, + "columnName" : "description", + "dataType" : "VARCHAR", + "columnComment" : "父编号", + "javaType" : "Long", + "javaField" : "parentId", + "example" : "2048", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true + } ] +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/table/contact.json b/yudao-module-infra/src/test/resources/codegen/table/contact.json new file mode 100644 index 0000000..74f92cd --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/table/contact.json @@ -0,0 +1,143 @@ +{ + "table": { + "scene" : 1, + "tableName" : "infra_student_contact", + "tableComment" : "学生联系人表", + "moduleName" : "infra", + "businessName" : "demo", + "className" : "InfraStudentContact", + "classComment" : "学生联系人", + "author" : "芋道源码" + }, + "columns": [ { + "columnName" : "id", + "dataType" : "BIGINT", + "columnComment" : "编号", + "primaryKey" : true, + "javaType" : "Long", + "javaField" : "id", + "example" : "1024", + "updateOperation" : true, + "listOperationResult" : true + }, { + "id" : 100, + "columnName" : "student_id", + "dataType" : "BIGINT", + "columnComment" : "学生编号", + "javaType" : "Long", + "javaField" : "studentId", + "example" : "2048", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true + }, { + "columnName" : "name", + "dataType" : "VARCHAR", + "columnComment" : "名字", + "javaType" : "String", + "javaField" : "name", + "example" : "芋头", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "LIKE", + "listOperationResult" : true, + "htmlType" : "input" + }, { + "columnName" : "description", + "dataType" : "VARCHAR", + "columnComment" : "简介", + "javaType" : "String", + "javaField" : "description", + "example" : "我是介绍", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "textarea" + }, { + "columnName" : "birthday", + "dataType" : "DATE", + "columnComment" : "出生日期", + "javaType" : "LocalDateTime", + "javaField" : "birthday", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "=", + "listOperationResult" : true, + "htmlType" : "datetime" + }, { + "columnName" : "sex", + "dataType" : "INTEGER", + "columnComment" : "性别", + "javaType" : "Integer", + "javaField" : "sex", + "dictType" : "system_user_sex", + "example" : "1", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "=", + "listOperationResult" : true, + "htmlType" : "select" + }, { + "columnName" : "enabled", + "dataType" : "BOOLEAN", + "columnComment" : "是否有效", + "javaType" : "Boolean", + "javaField" : "enabled", + "dictType" : "infra_boolean_string", + "example" : "true", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "=", + "listOperationResult" : true, + "htmlType" : "radio" + }, { + "columnName" : "avatar", + "dataType" : "VARCHAR", + "columnComment" : "头像", + "javaType" : "String", + "javaField" : "avatar", + "example" : "https://www.iocoder.cn/1.png", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "imageUpload" + }, { + "columnName" : "video", + "dataType" : "VARCHAR", + "columnComment" : "附件", + "nullable" : true, + "javaType" : "String", + "javaField" : "video", + "example" : "https://www.iocoder.cn/1.mp4", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "fileUpload" + }, { + "columnName" : "memo", + "dataType" : "VARCHAR", + "columnComment" : "备注", + "javaType" : "String", + "javaField" : "memo", + "example" : "我是备注", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "editor" + }, { + "columnName" : "create_time", + "dataType" : "DATE", + "columnComment" : "创建时间", + "nullable" : true, + "javaType" : "LocalDateTime", + "javaField" : "createTime", + "listOperation" : true, + "listOperationCondition" : "BETWEEN", + "listOperationResult" : true, + "htmlType" : "datetime" + } ] +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/table/student.json b/yudao-module-infra/src/test/resources/codegen/table/student.json new file mode 100644 index 0000000..efe8af4 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/table/student.json @@ -0,0 +1,134 @@ +{ + "table": { + "id": 1, + "scene" : 1, + "parentMenuId" : 888, + "tableName" : "infra_student", + "tableComment" : "学生表", + "moduleName" : "infra", + "businessName" : "demo", + "className" : "InfraStudent", + "classComment" : "学生", + "author" : "芋道源码" + }, + "columns": [ { + "id" : 100, + "columnName" : "id", + "dataType" : "BIGINT", + "columnComment" : "编号", + "primaryKey" : true, + "javaType" : "Long", + "javaField" : "id", + "example" : "1024", + "updateOperation" : true, + "listOperationResult" : true + }, { + "columnName" : "name", + "dataType" : "VARCHAR", + "columnComment" : "名字", + "javaType" : "String", + "javaField" : "name", + "example" : "芋头", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "LIKE", + "listOperationResult" : true, + "htmlType" : "input" + }, { + "columnName" : "description", + "dataType" : "VARCHAR", + "columnComment" : "简介", + "javaType" : "String", + "javaField" : "description", + "example" : "我是介绍", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "textarea" + }, { + "columnName" : "birthday", + "dataType" : "DATE", + "columnComment" : "出生日期", + "javaType" : "LocalDateTime", + "javaField" : "birthday", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "=", + "listOperationResult" : true, + "htmlType" : "datetime" + }, { + "columnName" : "sex", + "dataType" : "INTEGER", + "columnComment" : "性别", + "javaType" : "Integer", + "javaField" : "sex", + "dictType" : "system_user_sex", + "example" : "1", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "=", + "listOperationResult" : true, + "htmlType" : "select" + }, { + "columnName" : "enabled", + "dataType" : "BOOLEAN", + "columnComment" : "是否有效", + "javaType" : "Boolean", + "javaField" : "enabled", + "dictType" : "infra_boolean_string", + "example" : "true", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "=", + "listOperationResult" : true, + "htmlType" : "radio" + }, { + "columnName" : "avatar", + "dataType" : "VARCHAR", + "columnComment" : "头像", + "javaType" : "String", + "javaField" : "avatar", + "example" : "https://www.iocoder.cn/1.png", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "imageUpload" + }, { + "columnName" : "video", + "dataType" : "VARCHAR", + "columnComment" : "附件", + "javaType" : "String", + "javaField" : "video", + "example" : "https://www.iocoder.cn/1.mp4", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "fileUpload" + }, { + "columnName" : "memo", + "dataType" : "VARCHAR", + "columnComment" : "备注", + "javaType" : "String", + "javaField" : "memo", + "example" : "我是备注", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "editor" + }, { + "columnName" : "create_time", + "dataType" : "DATE", + "columnComment" : "创建时间", + "nullable" : true, + "javaType" : "LocalDateTime", + "javaField" : "createTime", + "listOperation" : true, + "listOperationCondition" : "BETWEEN", + "listOperationResult" : true, + "htmlType" : "datetime" + } ] +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/table/teacher.json b/yudao-module-infra/src/test/resources/codegen/table/teacher.json new file mode 100644 index 0000000..4d93518 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/table/teacher.json @@ -0,0 +1,143 @@ +{ + "table": { + "scene" : 1, + "tableName" : "infra_student_teacher", + "tableComment" : "学生班主任表", + "moduleName" : "infra", + "businessName" : "demo", + "className" : "InfraStudentTeacher", + "classComment" : "学生班主任", + "author" : "芋道源码" + }, + "columns": [ { + "columnName" : "id", + "dataType" : "BIGINT", + "columnComment" : "编号", + "primaryKey" : true, + "javaType" : "Long", + "javaField" : "id", + "example" : "1024", + "updateOperation" : true, + "listOperationResult" : true + }, { + "id" : 200, + "columnName" : "student_id", + "dataType" : "BIGINT", + "columnComment" : "学生编号", + "javaType" : "Long", + "javaField" : "studentId", + "example" : "2048", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true + }, { + "columnName" : "name", + "dataType" : "VARCHAR", + "columnComment" : "名字", + "javaType" : "String", + "javaField" : "name", + "example" : "芋头", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "LIKE", + "listOperationResult" : true, + "htmlType" : "input" + }, { + "columnName" : "description", + "dataType" : "VARCHAR", + "columnComment" : "简介", + "javaType" : "String", + "javaField" : "description", + "example" : "我是介绍", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "textarea" + }, { + "columnName" : "birthday", + "dataType" : "DATE", + "columnComment" : "出生日期", + "javaType" : "LocalDateTime", + "javaField" : "birthday", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "=", + "listOperationResult" : true, + "htmlType" : "datetime" + }, { + "columnName" : "sex", + "dataType" : "INTEGER", + "columnComment" : "性别", + "javaType" : "Integer", + "javaField" : "sex", + "dictType" : "system_user_sex", + "example" : "1", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "=", + "listOperationResult" : true, + "htmlType" : "select" + }, { + "columnName" : "enabled", + "dataType" : "BOOLEAN", + "columnComment" : "是否有效", + "javaType" : "Boolean", + "javaField" : "enabled", + "dictType" : "infra_boolean_string", + "example" : "true", + "createOperation" : true, + "updateOperation" : true, + "listOperation" : true, + "listOperationCondition" : "=", + "listOperationResult" : true, + "htmlType" : "radio" + }, { + "columnName" : "avatar", + "dataType" : "VARCHAR", + "columnComment" : "头像", + "javaType" : "String", + "javaField" : "avatar", + "example" : "https://www.iocoder.cn/1.png", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "imageUpload" + }, { + "columnName" : "video", + "dataType" : "VARCHAR", + "columnComment" : "附件", + "nullable" : true, + "javaType" : "String", + "javaField" : "video", + "example" : "https://www.iocoder.cn/1.mp4", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "fileUpload" + }, { + "columnName" : "memo", + "dataType" : "VARCHAR", + "columnComment" : "备注", + "javaType" : "String", + "javaField" : "memo", + "example" : "我是备注", + "createOperation" : true, + "updateOperation" : true, + "listOperationResult" : true, + "htmlType" : "editor" + }, { + "columnName" : "create_time", + "dataType" : "DATE", + "columnComment" : "创建时间", + "nullable" : true, + "javaType" : "LocalDateTime", + "javaField" : "createTime", + "listOperation" : true, + "listOperationCondition" : "BETWEEN", + "listOperationResult" : true, + "htmlType" : "datetime" + } ] +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/assert.json new file mode 100644 index 0000000..8edb8e6 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/assert.json @@ -0,0 +1,73 @@ +[ { + "contentPath" : "java/InfraStudentPageReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" +}, { + "contentPath" : "java/InfraStudentRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" +}, { + "contentPath" : "java/InfraStudentSaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" +}, { + "contentPath" : "java/InfraStudentController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" +}, { + "contentPath" : "java/InfraStudentDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" +}, { + "contentPath" : "java/InfraStudentContactDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" +}, { + "contentPath" : "java/InfraStudentTeacherDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" +}, { + "contentPath" : "java/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" +}, { + "contentPath" : "java/InfraStudentContactMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" +}, { + "contentPath" : "java/InfraStudentTeacherMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" +}, { + "contentPath" : "xml/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" +}, { + "contentPath" : "java/InfraStudentServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" +}, { + "contentPath" : "java/InfraStudentService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" +}, { + "contentPath" : "java/InfraStudentServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" +}, { + "contentPath": "js/index", + "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" +}, { + "contentPath" : "vue/StudentForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" +}, { + "contentPath" : "vue/StudentContactForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue" +}, { + "contentPath" : "vue/StudentTeacherForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue" +}, { + "contentPath" : "vue/StudentContactList", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue" +}, { + "contentPath" : "vue/StudentTeacherList", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue" +} ] \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/ErrorCodeConstants_手动操作 new file mode 100644 index 0000000..d3201de --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/ErrorCodeConstants_手动操作 @@ -0,0 +1,6 @@ +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! +// ========== 学生 TODO 补充编号 ========== +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); +ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在"); +ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在"); +ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在"); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactDO new file mode 100644 index 0000000..17c668e --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生联系人 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_contact") +@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentContactDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactMapper new file mode 100644 index 0000000..ca662d1 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生联系人 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentContactMapper extends BaseMapperX { + + default PageResult selectPage(PageParam reqVO, Long studentId) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(InfraStudentContactDO::getStudentId, studentId) + .orderByDesc(InfraStudentContactDO::getId)); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentContactDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentController new file mode 100644 index 0000000..d6f2018 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentController @@ -0,0 +1,183 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; + +@Tag(name = "管理后台 - 学生") +@RestController +@RequestMapping("/infra/student") +@Validated +public class InfraStudentController { + + @Resource + private InfraStudentService studentService; + + @PostMapping("/create") + @Operation(summary = "创建学生") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { + return success(studentService.createStudent(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新学生") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { + studentService.updateStudent(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除学生") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudent(@RequestParam("id") Long id) { + studentService.deleteStudent(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得学生") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudent(@RequestParam("id") Long id) { + InfraStudentDO student = studentService.getStudent(id); + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得学生分页") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { + PageResult pageResult = studentService.getStudentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出学生 Excel") + @PreAuthorize("@ss.hasPermission('infra:student:export')") + @OperateLog(type = EXPORT) + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = studentService.getStudentPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, + BeanUtils.toBean(list, InfraStudentRespVO.class)); + } + + // ==================== 子表(学生联系人) ==================== + + @GetMapping("/student-contact/page") + @Operation(summary = "获得学生联系人分页") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentContactPage(PageParam pageReqVO, + @RequestParam("studentId") Long studentId) { + return success(studentService.getStudentContactPage(pageReqVO, studentId)); + } + + @PostMapping("/student-contact/create") + @Operation(summary = "创建学生联系人") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { + return success(studentService.createStudentContact(studentContact)); + } + + @PutMapping("/student-contact/update") + @Operation(summary = "更新学生联系人") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { + studentService.updateStudentContact(studentContact); + return success(true); + } + + @DeleteMapping("/student-contact/delete") + @Parameter(name = "id", description = "编号", required = true) + @Operation(summary = "删除学生联系人") + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudentContact(@RequestParam("id") Long id) { + studentService.deleteStudentContact(id); + return success(true); + } + + @GetMapping("/student-contact/get") + @Operation(summary = "获得学生联系人") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudentContact(@RequestParam("id") Long id) { + return success(studentService.getStudentContact(id)); + } + + // ==================== 子表(学生班主任) ==================== + + @GetMapping("/student-teacher/page") + @Operation(summary = "获得学生班主任分页") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentTeacherPage(PageParam pageReqVO, + @RequestParam("studentId") Long studentId) { + return success(studentService.getStudentTeacherPage(pageReqVO, studentId)); + } + + @PostMapping("/student-teacher/create") + @Operation(summary = "创建学生班主任") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { + return success(studentService.createStudentTeacher(studentTeacher)); + } + + @PutMapping("/student-teacher/update") + @Operation(summary = "更新学生班主任") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { + studentService.updateStudentTeacher(studentTeacher); + return success(true); + } + + @DeleteMapping("/student-teacher/delete") + @Parameter(name = "id", description = "编号", required = true) + @Operation(summary = "删除学生班主任") + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudentTeacher(@RequestParam("id") Long id) { + studentService.deleteStudentTeacher(id); + return success(true); + } + + @GetMapping("/student-teacher/get") + @Operation(summary = "获得学生班主任") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudentTeacher(@RequestParam("id") Long id) { + return success(studentService.getStudentTeacher(id)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentDO new file mode 100644 index 0000000..b0d4bd2 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentDO @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生 DO + * + * @author 芋道源码 + */ +@TableName("infra_student") +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentMapper new file mode 100644 index 0000000..34e70a0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 学生 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentMapper extends BaseMapperX { + + default PageResult selectPage(InfraStudentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(InfraStudentDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentPageReqVO new file mode 100644 index 0000000..41a3730 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentPageReqVO @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 学生分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class InfraStudentPageReqVO extends PageParam { + + @Schema(description = "名字", example = "芋头") + private String name; + + @Schema(description = "出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "是否有效", example = "true") + private Boolean enabled; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO new file mode 100644 index 0000000..c41a550 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - 学生 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraStudentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @ExcelProperty("简介") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "性别", converter = DictConvert.class) + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "是否有效", converter = DictConvert.class) + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @ExcelProperty("头像") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @ExcelProperty("附件") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @ExcelProperty("备注") + private String memo; + + @Schema(description = "创建时间") + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentSaveReqVO new file mode 100644 index 0000000..eaadf74 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentSaveReqVO @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; + +@Schema(description = "管理后台 - 学生新增/修改 Request VO") +@Data +public class InfraStudentSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @NotEmpty(message = "简介不能为空") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出生日期不能为空") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "性别不能为空") + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否有效不能为空") + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @NotEmpty(message = "头像不能为空") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @NotEmpty(message = "附件不能为空") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @NotEmpty(message = "备注不能为空") + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentService new file mode 100644 index 0000000..7df090d --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentService @@ -0,0 +1,139 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 学生 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraStudentService { + + /** + * 创建学生 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); + + /** + * 更新学生 + * + * @param updateReqVO 更新信息 + */ + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); + + /** + * 删除学生 + * + * @param id 编号 + */ + void deleteStudent(Long id); + + /** + * 获得学生 + * + * @param id 编号 + * @return 学生 + */ + InfraStudentDO getStudent(Long id); + + /** + * 获得学生分页 + * + * @param pageReqVO 分页查询 + * @return 学生分页 + */ + PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); + + // ==================== 子表(学生联系人) ==================== + + /** + * 获得学生联系人分页 + * + * @param pageReqVO 分页查询 + * @param studentId 学生编号 + * @return 学生联系人分页 + */ + PageResult getStudentContactPage(PageParam pageReqVO, Long studentId); + + /** + * 创建学生联系人 + * + * @param studentContact 创建信息 + * @return 编号 + */ + Long createStudentContact(@Valid InfraStudentContactDO studentContact); + + /** + * 更新学生联系人 + * + * @param studentContact 更新信息 + */ + void updateStudentContact(@Valid InfraStudentContactDO studentContact); + + /** + * 删除学生联系人 + * + * @param id 编号 + */ + void deleteStudentContact(Long id); + + /** + * 获得学生联系人 + * + * @param id 编号 + * @return 学生联系人 + */ + InfraStudentContactDO getStudentContact(Long id); + + // ==================== 子表(学生班主任) ==================== + + /** + * 获得学生班主任分页 + * + * @param pageReqVO 分页查询 + * @param studentId 学生编号 + * @return 学生班主任分页 + */ + PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId); + + /** + * 创建学生班主任 + * + * @param studentTeacher 创建信息 + * @return 编号 + */ + Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); + + /** + * 更新学生班主任 + * + * @param studentTeacher 更新信息 + */ + void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); + + /** + * 删除学生班主任 + * + * @param id 编号 + */ + void deleteStudentTeacher(Long id); + + /** + * 获得学生班主任 + * + * @param id 编号 + * @return 学生班主任 + */ + InfraStudentTeacherDO getStudentTeacher(Long id); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImpl new file mode 100644 index 0000000..793b2dd --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImpl @@ -0,0 +1,180 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 学生 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraStudentServiceImpl implements InfraStudentService { + + @Resource + private InfraStudentMapper studentMapper; + @Resource + private InfraStudentContactMapper studentContactMapper; + @Resource + private InfraStudentTeacherMapper studentTeacherMapper; + + @Override + public Long createStudent(InfraStudentSaveReqVO createReqVO) { + // 插入 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); + studentMapper.insert(student); + // 返回 + return student.getId(); + } + + @Override + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { + // 校验存在 + validateStudentExists(updateReqVO.getId()); + // 更新 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); + studentMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStudent(Long id) { + // 校验存在 + validateStudentExists(id); + // 删除 + studentMapper.deleteById(id); + + // 删除子表 + deleteStudentContactByStudentId(id); + deleteStudentTeacherByStudentId(id); + } + + private void validateStudentExists(Long id) { + if (studentMapper.selectById(id) == null) { + throw exception(STUDENT_NOT_EXISTS); + } + } + + @Override + public InfraStudentDO getStudent(Long id) { + return studentMapper.selectById(id); + } + + @Override + public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { + return studentMapper.selectPage(pageReqVO); + } + + // ==================== 子表(学生联系人) ==================== + + @Override + public PageResult getStudentContactPage(PageParam pageReqVO, Long studentId) { + return studentContactMapper.selectPage(pageReqVO, studentId); + } + + @Override + public Long createStudentContact(InfraStudentContactDO studentContact) { + studentContactMapper.insert(studentContact); + return studentContact.getId(); + } + + @Override + public void updateStudentContact(InfraStudentContactDO studentContact) { + // 校验存在 + validateStudentContactExists(studentContact.getId()); + // 更新 + studentContactMapper.updateById(studentContact); + } + + @Override + public void deleteStudentContact(Long id) { + // 校验存在 + validateStudentContactExists(id); + // 删除 + studentContactMapper.deleteById(id); + } + + @Override + public InfraStudentContactDO getStudentContact(Long id) { + return studentContactMapper.selectById(id); + } + + private void validateStudentContactExists(Long id) { + if (studentContactMapper.selectById(id) == null) { + throw exception(STUDENT_CONTACT_NOT_EXISTS); + } + } + + private void deleteStudentContactByStudentId(Long studentId) { + studentContactMapper.deleteByStudentId(studentId); + } + + // ==================== 子表(学生班主任) ==================== + + @Override + public PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId) { + return studentTeacherMapper.selectPage(pageReqVO, studentId); + } + + @Override + public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) { + // 校验是否已经存在 + if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) { + throw exception(STUDENT_TEACHER_EXISTS); + } + // 插入 + studentTeacherMapper.insert(studentTeacher); + return studentTeacher.getId(); + } + + @Override + public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) { + // 校验存在 + validateStudentTeacherExists(studentTeacher.getId()); + // 更新 + studentTeacherMapper.updateById(studentTeacher); + } + + @Override + public void deleteStudentTeacher(Long id) { + // 校验存在 + validateStudentTeacherExists(id); + // 删除 + studentTeacherMapper.deleteById(id); + } + + @Override + public InfraStudentTeacherDO getStudentTeacher(Long id) { + return studentTeacherMapper.selectById(id); + } + + private void validateStudentTeacherExists(Long id) { + if (studentTeacherMapper.selectById(id) == null) { + throw exception(STUDENT_TEACHER_NOT_EXISTS); + } + } + + private void deleteStudentTeacherByStudentId(Long studentId) { + studentTeacherMapper.deleteByStudentId(studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImplTest new file mode 100644 index 0000000..b5f4bf0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImplTest @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraStudentServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraStudentServiceImpl.class) +public class InfraStudentServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraStudentServiceImpl studentService; + + @Resource + private InfraStudentMapper studentMapper; + + @Test + public void testCreateStudent_success() { + // 准备参数 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); + + // 调用 + Long studentId = studentService.createStudent(createReqVO); + // 断言 + assertNotNull(studentId); + // 校验记录的属性是否正确 + InfraStudentDO student = studentMapper.selectById(studentId); + assertPojoEquals(createReqVO, student, "id"); + } + + @Test + public void testUpdateStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { + o.setId(dbStudent.getId()); // 设置更新的 ID + }); + + // 调用 + studentService.updateStudent(updateReqVO); + // 校验是否更新正确 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, student); + } + + @Test + public void testUpdateStudent_notExists() { + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); + } + + @Test + public void testDeleteStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbStudent.getId(); + + // 调用 + studentService.deleteStudent(id); + // 校验数据不存在了 + assertNull(studentMapper.selectById(id)); + } + + @Test + public void testDeleteStudent_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetStudentPage() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 + o.setName(null); + o.setBirthday(null); + o.setSex(null); + o.setEnabled(null); + o.setCreateTime(null); + }); + studentMapper.insert(dbStudent); + // 测试 name 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); + // 测试 birthday 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); + // 测试 sex 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); + // 测试 enabled 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); + // 测试 createTime 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); + // 准备参数 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); + reqVO.setName(null); + reqVO.setBirthday(null); + reqVO.setSex(null); + reqVO.setEnabled(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = studentService.getStudentPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbStudent, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherDO new file mode 100644 index 0000000..c19cf9f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生班主任 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_teacher") +@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentTeacherDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherMapper new file mode 100644 index 0000000..994212d --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生班主任 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentTeacherMapper extends BaseMapperX { + + default PageResult selectPage(PageParam reqVO, Long studentId) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(InfraStudentTeacherDO::getStudentId, studentId) + .orderByDesc(InfraStudentTeacherDO::getId)); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentTeacherDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/js/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/js/index new file mode 100644 index 0000000..211d95e --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/js/index @@ -0,0 +1,141 @@ +import request from '@/utils/request' + +// 创建学生 +export function createStudent(data) { + return request({ + url: '/infra/student/create', + method: 'post', + data: data + }) +} + +// 更新学生 +export function updateStudent(data) { + return request({ + url: '/infra/student/update', + method: 'put', + data: data + }) +} + +// 删除学生 +export function deleteStudent(id) { + return request({ + url: '/infra/student/delete?id=' + id, + method: 'delete' + }) +} + +// 获得学生 +export function getStudent(id) { + return request({ + url: '/infra/student/get?id=' + id, + method: 'get' + }) +} + +// 获得学生分页 +export function getStudentPage(params) { + return request({ + url: '/infra/student/page', + method: 'get', + params + }) +} +// 导出学生 Excel +export function exportStudentExcel(params) { + return request({ + url: '/infra/student/export-excel', + method: 'get', + params, + responseType: 'blob' + }) +} + +// ==================== 子表(学生联系人) ==================== + + // 获得学生联系人分页 + export function getStudentContactPage(params) { + return request({ + url: '/infra/student/student-contact/page', + method: 'get', + params + }) + } + // 新增学生联系人 + export function createStudentContact(data) { + return request({ + url: `/infra/student/student-contact/create`, + method: 'post', + data + }) + } + + // 修改学生联系人 + export function updateStudentContact(data) { + return request({ + url: `/infra/student/student-contact/update`, + method: 'post', + data + }) + } + + // 删除学生联系人 + export function deleteStudentContact(id) { + return request({ + url: `/infra/student/student-contact/delete?id=` + id, + method: 'delete' + }) + } + + // 获得学生联系人 + export function getStudentContact(id) { + return request({ + url: `/infra/student/student-contact/get?id=` + id, + method: 'get' + }) + } + +// ==================== 子表(学生班主任) ==================== + + // 获得学生班主任分页 + export function getStudentTeacherPage(params) { + return request({ + url: '/infra/student/student-teacher/page', + method: 'get', + params + }) + } + // 新增学生班主任 + export function createStudentTeacher(data) { + return request({ + url: `/infra/student/student-teacher/create`, + method: 'post', + data + }) + } + + // 修改学生班主任 + export function updateStudentTeacher(data) { + return request({ + url: `/infra/student/student-teacher/update`, + method: 'post', + data + }) + } + + // 删除学生班主任 + export function deleteStudentTeacher(id) { + return request({ + url: `/infra/student/student-teacher/delete?id=` + id, + method: 'delete' + }) + } + + // 获得学生班主任 + export function getStudentTeacher(id) { + return request({ + url: `/infra/student/student-teacher/get?id=` + id, + method: 'get' + }) + } \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/sql/h2 new file mode 100644 index 0000000..6c1875f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/sql/h2 @@ -0,0 +1,17 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_student" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "birthday" varchar NOT NULL, + "sex" int NOT NULL, + "enabled" bit NOT NULL, + "avatar" varchar NOT NULL, + "video" varchar NOT NULL, + "memo" varchar NOT NULL, + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id") +) COMMENT '学生表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/sql/sql new file mode 100644 index 0000000..83df279 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '学生管理', '', 2, 0, 888, + 'student', '', 'infra/demo/index', 0, 'InfraStudent' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生查询', 'infra:student:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生创建', 'infra:student:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生更新', 'infra:student:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生删除', 'infra:student:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生导出', 'infra:student:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactForm new file mode 100644 index 0000000..de3b0a7 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactForm @@ -0,0 +1,151 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactList b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactList new file mode 100644 index 0000000..00f1ce0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactList @@ -0,0 +1,129 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentForm new file mode 100644 index 0000000..d89e506 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentForm @@ -0,0 +1,149 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherForm new file mode 100644 index 0000000..874a03b --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherForm @@ -0,0 +1,151 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherList b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherList new file mode 100644 index 0000000..7d561a0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherList @@ -0,0 +1,129 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/index new file mode 100644 index 0000000..9c7588f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/vue/index @@ -0,0 +1,233 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/xml/InfraStudentMapper new file mode 100644 index 0000000..155aa5c --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_erp/xml/InfraStudentMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/assert.json new file mode 100644 index 0000000..8edb8e6 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/assert.json @@ -0,0 +1,73 @@ +[ { + "contentPath" : "java/InfraStudentPageReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" +}, { + "contentPath" : "java/InfraStudentRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" +}, { + "contentPath" : "java/InfraStudentSaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" +}, { + "contentPath" : "java/InfraStudentController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" +}, { + "contentPath" : "java/InfraStudentDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" +}, { + "contentPath" : "java/InfraStudentContactDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" +}, { + "contentPath" : "java/InfraStudentTeacherDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" +}, { + "contentPath" : "java/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" +}, { + "contentPath" : "java/InfraStudentContactMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" +}, { + "contentPath" : "java/InfraStudentTeacherMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" +}, { + "contentPath" : "xml/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" +}, { + "contentPath" : "java/InfraStudentServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" +}, { + "contentPath" : "java/InfraStudentService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" +}, { + "contentPath" : "java/InfraStudentServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" +}, { + "contentPath": "js/index", + "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" +}, { + "contentPath" : "vue/StudentForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" +}, { + "contentPath" : "vue/StudentContactForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue" +}, { + "contentPath" : "vue/StudentTeacherForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue" +}, { + "contentPath" : "vue/StudentContactList", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue" +}, { + "contentPath" : "vue/StudentTeacherList", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue" +} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/ErrorCodeConstants_手动操作 index f8be662..f8be662 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/ErrorCodeConstants_手动操作 +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/ErrorCodeConstants_手动操作 diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactDO new file mode 100644 index 0000000..17c668e --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生联系人 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_contact") +@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentContactDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactMapper new file mode 100644 index 0000000..35bbd53 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactMapper @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生联系人 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentContactMapper extends BaseMapperX { + + default List selectListByStudentId(Long studentId) { + return selectList(InfraStudentContactDO::getStudentId, studentId); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentContactDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentController new file mode 100644 index 0000000..b9a587b --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentController @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; + +@Tag(name = "管理后台 - 学生") +@RestController +@RequestMapping("/infra/student") +@Validated +public class InfraStudentController { + + @Resource + private InfraStudentService studentService; + + @PostMapping("/create") + @Operation(summary = "创建学生") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { + return success(studentService.createStudent(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新学生") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { + studentService.updateStudent(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除学生") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudent(@RequestParam("id") Long id) { + studentService.deleteStudent(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得学生") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudent(@RequestParam("id") Long id) { + InfraStudentDO student = studentService.getStudent(id); + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得学生分页") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { + PageResult pageResult = studentService.getStudentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出学生 Excel") + @PreAuthorize("@ss.hasPermission('infra:student:export')") + @OperateLog(type = EXPORT) + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = studentService.getStudentPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, + BeanUtils.toBean(list, InfraStudentRespVO.class)); + } + + // ==================== 子表(学生联系人) ==================== + + @GetMapping("/student-contact/list-by-student-id") + @Operation(summary = "获得学生联系人列表") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { + return success(studentService.getStudentContactListByStudentId(studentId)); + } + + // ==================== 子表(学生班主任) ==================== + + @GetMapping("/student-teacher/get-by-student-id") + @Operation(summary = "获得学生班主任") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { + return success(studentService.getStudentTeacherByStudentId(studentId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentDO new file mode 100644 index 0000000..b0d4bd2 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentDO @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生 DO + * + * @author 芋道源码 + */ +@TableName("infra_student") +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentMapper new file mode 100644 index 0000000..34e70a0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 学生 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentMapper extends BaseMapperX { + + default PageResult selectPage(InfraStudentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(InfraStudentDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentPageReqVO new file mode 100644 index 0000000..41a3730 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentPageReqVO @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 学生分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class InfraStudentPageReqVO extends PageParam { + + @Schema(description = "名字", example = "芋头") + private String name; + + @Schema(description = "出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "是否有效", example = "true") + private Boolean enabled; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO new file mode 100644 index 0000000..c41a550 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - 学生 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraStudentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @ExcelProperty("简介") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "性别", converter = DictConvert.class) + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "是否有效", converter = DictConvert.class) + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @ExcelProperty("头像") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @ExcelProperty("附件") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @ExcelProperty("备注") + private String memo; + + @Schema(description = "创建时间") + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentSaveReqVO new file mode 100644 index 0000000..faa491d --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentSaveReqVO @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; + +@Schema(description = "管理后台 - 学生新增/修改 Request VO") +@Data +public class InfraStudentSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @NotEmpty(message = "简介不能为空") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出生日期不能为空") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "性别不能为空") + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否有效不能为空") + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @NotEmpty(message = "头像不能为空") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @NotEmpty(message = "附件不能为空") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @NotEmpty(message = "备注不能为空") + private String memo; + + @Schema(description = "学生联系人列表") + private List studentContacts; + + @Schema(description = "学生班主任") + private InfraStudentTeacherDO studentTeacher; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentService new file mode 100644 index 0000000..afa7d22 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentService @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 学生 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraStudentService { + + /** + * 创建学生 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); + + /** + * 更新学生 + * + * @param updateReqVO 更新信息 + */ + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); + + /** + * 删除学生 + * + * @param id 编号 + */ + void deleteStudent(Long id); + + /** + * 获得学生 + * + * @param id 编号 + * @return 学生 + */ + InfraStudentDO getStudent(Long id); + + /** + * 获得学生分页 + * + * @param pageReqVO 分页查询 + * @return 学生分页 + */ + PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); + + // ==================== 子表(学生联系人) ==================== + + /** + * 获得学生联系人列表 + * + * @param studentId 学生编号 + * @return 学生联系人列表 + */ + List getStudentContactListByStudentId(Long studentId); + + // ==================== 子表(学生班主任) ==================== + + /** + * 获得学生班主任 + * + * @param studentId 学生编号 + * @return 学生班主任 + */ + InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImpl new file mode 100644 index 0000000..c57cba6 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImpl @@ -0,0 +1,147 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 学生 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraStudentServiceImpl implements InfraStudentService { + + @Resource + private InfraStudentMapper studentMapper; + @Resource + private InfraStudentContactMapper studentContactMapper; + @Resource + private InfraStudentTeacherMapper studentTeacherMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createStudent(InfraStudentSaveReqVO createReqVO) { + // 插入 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); + studentMapper.insert(student); + + // 插入子表 + createStudentContactList(student.getId(), createReqVO.getStudentContacts()); + createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); + // 返回 + return student.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { + // 校验存在 + validateStudentExists(updateReqVO.getId()); + // 更新 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); + studentMapper.updateById(updateObj); + + // 更新子表 + updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); + updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStudent(Long id) { + // 校验存在 + validateStudentExists(id); + // 删除 + studentMapper.deleteById(id); + + // 删除子表 + deleteStudentContactByStudentId(id); + deleteStudentTeacherByStudentId(id); + } + + private void validateStudentExists(Long id) { + if (studentMapper.selectById(id) == null) { + throw exception(STUDENT_NOT_EXISTS); + } + } + + @Override + public InfraStudentDO getStudent(Long id) { + return studentMapper.selectById(id); + } + + @Override + public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { + return studentMapper.selectPage(pageReqVO); + } + + // ==================== 子表(学生联系人) ==================== + + @Override + public List getStudentContactListByStudentId(Long studentId) { + return studentContactMapper.selectListByStudentId(studentId); + } + + private void createStudentContactList(Long studentId, List list) { + list.forEach(o -> o.setStudentId(studentId)); + studentContactMapper.insertBatch(list); + } + + private void updateStudentContactList(Long studentId, List list) { + deleteStudentContactByStudentId(studentId); + list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新 + createStudentContactList(studentId, list); + } + + private void deleteStudentContactByStudentId(Long studentId) { + studentContactMapper.deleteByStudentId(studentId); + } + + // ==================== 子表(学生班主任) ==================== + + @Override + public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { + return studentTeacherMapper.selectByStudentId(studentId); + } + + private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { + if (studentTeacher == null) { + return; + } + studentTeacher.setStudentId(studentId); + studentTeacherMapper.insert(studentTeacher); + } + + private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { + if (studentTeacher == null) { + return; + } + studentTeacher.setStudentId(studentId); + studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新 + studentTeacherMapper.insertOrUpdate(studentTeacher); + } + + private void deleteStudentTeacherByStudentId(Long studentId) { + studentTeacherMapper.deleteByStudentId(studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImplTest new file mode 100644 index 0000000..b5f4bf0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImplTest @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraStudentServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraStudentServiceImpl.class) +public class InfraStudentServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraStudentServiceImpl studentService; + + @Resource + private InfraStudentMapper studentMapper; + + @Test + public void testCreateStudent_success() { + // 准备参数 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); + + // 调用 + Long studentId = studentService.createStudent(createReqVO); + // 断言 + assertNotNull(studentId); + // 校验记录的属性是否正确 + InfraStudentDO student = studentMapper.selectById(studentId); + assertPojoEquals(createReqVO, student, "id"); + } + + @Test + public void testUpdateStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { + o.setId(dbStudent.getId()); // 设置更新的 ID + }); + + // 调用 + studentService.updateStudent(updateReqVO); + // 校验是否更新正确 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, student); + } + + @Test + public void testUpdateStudent_notExists() { + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); + } + + @Test + public void testDeleteStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbStudent.getId(); + + // 调用 + studentService.deleteStudent(id); + // 校验数据不存在了 + assertNull(studentMapper.selectById(id)); + } + + @Test + public void testDeleteStudent_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetStudentPage() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 + o.setName(null); + o.setBirthday(null); + o.setSex(null); + o.setEnabled(null); + o.setCreateTime(null); + }); + studentMapper.insert(dbStudent); + // 测试 name 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); + // 测试 birthday 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); + // 测试 sex 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); + // 测试 enabled 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); + // 测试 createTime 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); + // 准备参数 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); + reqVO.setName(null); + reqVO.setBirthday(null); + reqVO.setSex(null); + reqVO.setEnabled(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = studentService.getStudentPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbStudent, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherDO new file mode 100644 index 0000000..c19cf9f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生班主任 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_teacher") +@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentTeacherDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherMapper new file mode 100644 index 0000000..0521bba --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherMapper @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生班主任 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentTeacherMapper extends BaseMapperX { + + default InfraStudentTeacherDO selectByStudentId(Long studentId) { + return selectOne(InfraStudentTeacherDO::getStudentId, studentId); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentTeacherDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/js/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/js/index new file mode 100644 index 0000000..b4e6ac5 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/js/index @@ -0,0 +1,74 @@ +import request from '@/utils/request' + +// 创建学生 +export function createStudent(data) { + return request({ + url: '/infra/student/create', + method: 'post', + data: data + }) +} + +// 更新学生 +export function updateStudent(data) { + return request({ + url: '/infra/student/update', + method: 'put', + data: data + }) +} + +// 删除学生 +export function deleteStudent(id) { + return request({ + url: '/infra/student/delete?id=' + id, + method: 'delete' + }) +} + +// 获得学生 +export function getStudent(id) { + return request({ + url: '/infra/student/get?id=' + id, + method: 'get' + }) +} + +// 获得学生分页 +export function getStudentPage(params) { + return request({ + url: '/infra/student/page', + method: 'get', + params + }) +} +// 导出学生 Excel +export function exportStudentExcel(params) { + return request({ + url: '/infra/student/export-excel', + method: 'get', + params, + responseType: 'blob' + }) +} + +// ==================== 子表(学生联系人) ==================== + + // 获得学生联系人列表 + export function getStudentContactListByStudentId(studentId) { + return request({ + url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId, + method: 'get' + }) + } + +// ==================== 子表(学生班主任) ==================== + + // 获得学生班主任 + export function getStudentTeacherByStudentId(studentId) { + return request({ + url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId, + method: 'get' + }) + } + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2 new file mode 100644 index 0000000..6c1875f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2 @@ -0,0 +1,17 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_student" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "birthday" varchar NOT NULL, + "sex" int NOT NULL, + "enabled" bit NOT NULL, + "avatar" varchar NOT NULL, + "video" varchar NOT NULL, + "memo" varchar NOT NULL, + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id") +) COMMENT '学生表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql new file mode 100644 index 0000000..83df279 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '学生管理', '', 2, 0, 888, + 'student', '', 'infra/demo/index', 0, 'InfraStudent' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生查询', 'infra:student:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生创建', 'infra:student:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生更新', 'infra:student:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生删除', 'infra:student:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生导出', 'infra:student:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm new file mode 100644 index 0000000..c953bfa --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm @@ -0,0 +1,177 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList new file mode 100644 index 0000000..c0a8710 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList @@ -0,0 +1,89 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm new file mode 100644 index 0000000..6d93b61 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm @@ -0,0 +1,180 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm new file mode 100644 index 0000000..0dac19b --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm @@ -0,0 +1,127 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList new file mode 100644 index 0000000..9f57274 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList @@ -0,0 +1,93 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/index new file mode 100644 index 0000000..ddeafdf --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/vue/index @@ -0,0 +1,222 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper new file mode 100644 index 0000000..155aa5c --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/assert.json new file mode 100644 index 0000000..6f94535 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/assert.json @@ -0,0 +1,67 @@ +[ { + "contentPath" : "java/InfraStudentPageReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" +}, { + "contentPath" : "java/InfraStudentRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" +}, { + "contentPath" : "java/InfraStudentSaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" +}, { + "contentPath" : "java/InfraStudentController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" +}, { + "contentPath" : "java/InfraStudentDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" +}, { + "contentPath" : "java/InfraStudentContactDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" +}, { + "contentPath" : "java/InfraStudentTeacherDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" +}, { + "contentPath" : "java/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" +}, { + "contentPath" : "java/InfraStudentContactMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" +}, { + "contentPath" : "java/InfraStudentTeacherMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" +}, { + "contentPath" : "xml/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" +}, { + "contentPath" : "java/InfraStudentServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" +}, { + "contentPath" : "java/InfraStudentService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" +}, { + "contentPath" : "java/InfraStudentServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" +}, { + "contentPath": "js/index", + "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" +}, { + "contentPath" : "vue/StudentForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" +}, { + "contentPath" : "vue/StudentContactForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue" +}, { + "contentPath" : "vue/StudentTeacherForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue" +} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/ErrorCodeConstants_手动操作 index f8be662..f8be662 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/ErrorCodeConstants_手动操作 +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/ErrorCodeConstants_手动操作 diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO new file mode 100644 index 0000000..17c668e --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生联系人 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_contact") +@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentContactDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper new file mode 100644 index 0000000..35bbd53 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生联系人 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentContactMapper extends BaseMapperX { + + default List selectListByStudentId(Long studentId) { + return selectList(InfraStudentContactDO::getStudentId, studentId); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentContactDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController new file mode 100644 index 0000000..b9a587b --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; + +@Tag(name = "管理后台 - 学生") +@RestController +@RequestMapping("/infra/student") +@Validated +public class InfraStudentController { + + @Resource + private InfraStudentService studentService; + + @PostMapping("/create") + @Operation(summary = "创建学生") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { + return success(studentService.createStudent(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新学生") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { + studentService.updateStudent(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除学生") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudent(@RequestParam("id") Long id) { + studentService.deleteStudent(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得学生") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudent(@RequestParam("id") Long id) { + InfraStudentDO student = studentService.getStudent(id); + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得学生分页") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { + PageResult pageResult = studentService.getStudentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出学生 Excel") + @PreAuthorize("@ss.hasPermission('infra:student:export')") + @OperateLog(type = EXPORT) + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = studentService.getStudentPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, + BeanUtils.toBean(list, InfraStudentRespVO.class)); + } + + // ==================== 子表(学生联系人) ==================== + + @GetMapping("/student-contact/list-by-student-id") + @Operation(summary = "获得学生联系人列表") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { + return success(studentService.getStudentContactListByStudentId(studentId)); + } + + // ==================== 子表(学生班主任) ==================== + + @GetMapping("/student-teacher/get-by-student-id") + @Operation(summary = "获得学生班主任") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { + return success(studentService.getStudentTeacherByStudentId(studentId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO new file mode 100644 index 0000000..b0d4bd2 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生 DO + * + * @author 芋道源码 + */ +@TableName("infra_student") +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper new file mode 100644 index 0000000..34e70a0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 学生 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentMapper extends BaseMapperX { + + default PageResult selectPage(InfraStudentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(InfraStudentDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO new file mode 100644 index 0000000..41a3730 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 学生分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class InfraStudentPageReqVO extends PageParam { + + @Schema(description = "名字", example = "芋头") + private String name; + + @Schema(description = "出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "是否有效", example = "true") + private Boolean enabled; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO new file mode 100644 index 0000000..c41a550 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - 学生 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraStudentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @ExcelProperty("简介") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "性别", converter = DictConvert.class) + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "是否有效", converter = DictConvert.class) + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @ExcelProperty("头像") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @ExcelProperty("附件") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @ExcelProperty("备注") + private String memo; + + @Schema(description = "创建时间") + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO new file mode 100644 index 0000000..faa491d --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; + +@Schema(description = "管理后台 - 学生新增/修改 Request VO") +@Data +public class InfraStudentSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @NotEmpty(message = "简介不能为空") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出生日期不能为空") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "性别不能为空") + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否有效不能为空") + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @NotEmpty(message = "头像不能为空") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @NotEmpty(message = "附件不能为空") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @NotEmpty(message = "备注不能为空") + private String memo; + + @Schema(description = "学生联系人列表") + private List studentContacts; + + @Schema(description = "学生班主任") + private InfraStudentTeacherDO studentTeacher; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService new file mode 100644 index 0000000..afa7d22 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 学生 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraStudentService { + + /** + * 创建学生 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); + + /** + * 更新学生 + * + * @param updateReqVO 更新信息 + */ + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); + + /** + * 删除学生 + * + * @param id 编号 + */ + void deleteStudent(Long id); + + /** + * 获得学生 + * + * @param id 编号 + * @return 学生 + */ + InfraStudentDO getStudent(Long id); + + /** + * 获得学生分页 + * + * @param pageReqVO 分页查询 + * @return 学生分页 + */ + PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); + + // ==================== 子表(学生联系人) ==================== + + /** + * 获得学生联系人列表 + * + * @param studentId 学生编号 + * @return 学生联系人列表 + */ + List getStudentContactListByStudentId(Long studentId); + + // ==================== 子表(学生班主任) ==================== + + /** + * 获得学生班主任 + * + * @param studentId 学生编号 + * @return 学生班主任 + */ + InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl new file mode 100644 index 0000000..c57cba6 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl @@ -0,0 +1,147 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 学生 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraStudentServiceImpl implements InfraStudentService { + + @Resource + private InfraStudentMapper studentMapper; + @Resource + private InfraStudentContactMapper studentContactMapper; + @Resource + private InfraStudentTeacherMapper studentTeacherMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createStudent(InfraStudentSaveReqVO createReqVO) { + // 插入 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); + studentMapper.insert(student); + + // 插入子表 + createStudentContactList(student.getId(), createReqVO.getStudentContacts()); + createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); + // 返回 + return student.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { + // 校验存在 + validateStudentExists(updateReqVO.getId()); + // 更新 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); + studentMapper.updateById(updateObj); + + // 更新子表 + updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); + updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStudent(Long id) { + // 校验存在 + validateStudentExists(id); + // 删除 + studentMapper.deleteById(id); + + // 删除子表 + deleteStudentContactByStudentId(id); + deleteStudentTeacherByStudentId(id); + } + + private void validateStudentExists(Long id) { + if (studentMapper.selectById(id) == null) { + throw exception(STUDENT_NOT_EXISTS); + } + } + + @Override + public InfraStudentDO getStudent(Long id) { + return studentMapper.selectById(id); + } + + @Override + public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { + return studentMapper.selectPage(pageReqVO); + } + + // ==================== 子表(学生联系人) ==================== + + @Override + public List getStudentContactListByStudentId(Long studentId) { + return studentContactMapper.selectListByStudentId(studentId); + } + + private void createStudentContactList(Long studentId, List list) { + list.forEach(o -> o.setStudentId(studentId)); + studentContactMapper.insertBatch(list); + } + + private void updateStudentContactList(Long studentId, List list) { + deleteStudentContactByStudentId(studentId); + list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新 + createStudentContactList(studentId, list); + } + + private void deleteStudentContactByStudentId(Long studentId) { + studentContactMapper.deleteByStudentId(studentId); + } + + // ==================== 子表(学生班主任) ==================== + + @Override + public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { + return studentTeacherMapper.selectByStudentId(studentId); + } + + private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { + if (studentTeacher == null) { + return; + } + studentTeacher.setStudentId(studentId); + studentTeacherMapper.insert(studentTeacher); + } + + private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { + if (studentTeacher == null) { + return; + } + studentTeacher.setStudentId(studentId); + studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新 + studentTeacherMapper.insertOrUpdate(studentTeacher); + } + + private void deleteStudentTeacherByStudentId(Long studentId) { + studentTeacherMapper.deleteByStudentId(studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest new file mode 100644 index 0000000..b5f4bf0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraStudentServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraStudentServiceImpl.class) +public class InfraStudentServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraStudentServiceImpl studentService; + + @Resource + private InfraStudentMapper studentMapper; + + @Test + public void testCreateStudent_success() { + // 准备参数 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); + + // 调用 + Long studentId = studentService.createStudent(createReqVO); + // 断言 + assertNotNull(studentId); + // 校验记录的属性是否正确 + InfraStudentDO student = studentMapper.selectById(studentId); + assertPojoEquals(createReqVO, student, "id"); + } + + @Test + public void testUpdateStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { + o.setId(dbStudent.getId()); // 设置更新的 ID + }); + + // 调用 + studentService.updateStudent(updateReqVO); + // 校验是否更新正确 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, student); + } + + @Test + public void testUpdateStudent_notExists() { + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); + } + + @Test + public void testDeleteStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbStudent.getId(); + + // 调用 + studentService.deleteStudent(id); + // 校验数据不存在了 + assertNull(studentMapper.selectById(id)); + } + + @Test + public void testDeleteStudent_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetStudentPage() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 + o.setName(null); + o.setBirthday(null); + o.setSex(null); + o.setEnabled(null); + o.setCreateTime(null); + }); + studentMapper.insert(dbStudent); + // 测试 name 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); + // 测试 birthday 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); + // 测试 sex 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); + // 测试 enabled 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); + // 测试 createTime 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); + // 准备参数 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); + reqVO.setName(null); + reqVO.setBirthday(null); + reqVO.setSex(null); + reqVO.setEnabled(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = studentService.getStudentPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbStudent, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO new file mode 100644 index 0000000..c19cf9f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生班主任 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_teacher") +@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentTeacherDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper new file mode 100644 index 0000000..0521bba --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生班主任 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentTeacherMapper extends BaseMapperX { + + default InfraStudentTeacherDO selectByStudentId(Long studentId) { + return selectOne(InfraStudentTeacherDO::getStudentId, studentId); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentTeacherDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/js/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/js/index new file mode 100644 index 0000000..b4e6ac5 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/js/index @@ -0,0 +1,74 @@ +import request from '@/utils/request' + +// 创建学生 +export function createStudent(data) { + return request({ + url: '/infra/student/create', + method: 'post', + data: data + }) +} + +// 更新学生 +export function updateStudent(data) { + return request({ + url: '/infra/student/update', + method: 'put', + data: data + }) +} + +// 删除学生 +export function deleteStudent(id) { + return request({ + url: '/infra/student/delete?id=' + id, + method: 'delete' + }) +} + +// 获得学生 +export function getStudent(id) { + return request({ + url: '/infra/student/get?id=' + id, + method: 'get' + }) +} + +// 获得学生分页 +export function getStudentPage(params) { + return request({ + url: '/infra/student/page', + method: 'get', + params + }) +} +// 导出学生 Excel +export function exportStudentExcel(params) { + return request({ + url: '/infra/student/export-excel', + method: 'get', + params, + responseType: 'blob' + }) +} + +// ==================== 子表(学生联系人) ==================== + + // 获得学生联系人列表 + export function getStudentContactListByStudentId(studentId) { + return request({ + url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId, + method: 'get' + }) + } + +// ==================== 子表(学生班主任) ==================== + + // 获得学生班主任 + export function getStudentTeacherByStudentId(studentId) { + return request({ + url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId, + method: 'get' + }) + } + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2 new file mode 100644 index 0000000..6c1875f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2 @@ -0,0 +1,17 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_student" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "birthday" varchar NOT NULL, + "sex" int NOT NULL, + "enabled" bit NOT NULL, + "avatar" varchar NOT NULL, + "video" varchar NOT NULL, + "memo" varchar NOT NULL, + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id") +) COMMENT '学生表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql new file mode 100644 index 0000000..83df279 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '学生管理', '', 2, 0, 888, + 'student', '', 'infra/demo/index', 0, 'InfraStudent' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生查询', 'infra:student:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生创建', 'infra:student:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生更新', 'infra:student:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生删除', 'infra:student:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生导出', 'infra:student:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm new file mode 100644 index 0000000..c953bfa --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm @@ -0,0 +1,177 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm new file mode 100644 index 0000000..6d93b61 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm @@ -0,0 +1,180 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm new file mode 100644 index 0000000..0dac19b --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm @@ -0,0 +1,127 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/index new file mode 100644 index 0000000..4607581 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/vue/index @@ -0,0 +1,205 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper new file mode 100644 index 0000000..155aa5c --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/assert.json new file mode 100644 index 0000000..5a37c37 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/assert.json @@ -0,0 +1,49 @@ +[ { + "contentPath" : "java/InfraStudentPageReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" +}, { + "contentPath" : "java/InfraStudentRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" +}, { + "contentPath" : "java/InfraStudentSaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" +}, { + "contentPath" : "java/InfraStudentController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" +}, { + "contentPath" : "java/InfraStudentDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" +}, { + "contentPath" : "java/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" +}, { + "contentPath" : "xml/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" +}, { + "contentPath" : "java/InfraStudentServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" +}, { + "contentPath" : "java/InfraStudentService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" +}, { + "contentPath" : "java/InfraStudentServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" +}, { + "contentPath": "js/index", + "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" +}, { + "contentPath" : "vue/StudentForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" +} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/ErrorCodeConstants_手动操作 index d3201de..f8be662 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/ErrorCodeConstants_手动操作 +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/ErrorCodeConstants_手动操作 @@ -1,6 +1,3 @@ // TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! // ========== 学生 TODO 补充编号 ========== -ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); -ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在"); -ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在"); -ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在"); \ No newline at end of file +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController new file mode 100644 index 0000000..3796982 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; + +@Tag(name = "管理后台 - 学生") +@RestController +@RequestMapping("/infra/student") +@Validated +public class InfraStudentController { + + @Resource + private InfraStudentService studentService; + + @PostMapping("/create") + @Operation(summary = "创建学生") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { + return success(studentService.createStudent(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新学生") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { + studentService.updateStudent(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除学生") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudent(@RequestParam("id") Long id) { + studentService.deleteStudent(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得学生") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudent(@RequestParam("id") Long id) { + InfraStudentDO student = studentService.getStudent(id); + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得学生分页") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { + PageResult pageResult = studentService.getStudentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出学生 Excel") + @PreAuthorize("@ss.hasPermission('infra:student:export')") + @OperateLog(type = EXPORT) + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = studentService.getStudentPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, + BeanUtils.toBean(list, InfraStudentRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO new file mode 100644 index 0000000..b0d4bd2 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生 DO + * + * @author 芋道源码 + */ +@TableName("infra_student") +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper new file mode 100644 index 0000000..34e70a0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 学生 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentMapper extends BaseMapperX { + + default PageResult selectPage(InfraStudentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(InfraStudentDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO new file mode 100644 index 0000000..41a3730 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 学生分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class InfraStudentPageReqVO extends PageParam { + + @Schema(description = "名字", example = "芋头") + private String name; + + @Schema(description = "出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "是否有效", example = "true") + private Boolean enabled; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO new file mode 100644 index 0000000..c41a550 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - 学生 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraStudentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @ExcelProperty("简介") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "性别", converter = DictConvert.class) + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "是否有效", converter = DictConvert.class) + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @ExcelProperty("头像") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @ExcelProperty("附件") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @ExcelProperty("备注") + private String memo; + + @Schema(description = "创建时间") + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO new file mode 100644 index 0000000..43e7f14 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 学生新增/修改 Request VO") +@Data +public class InfraStudentSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @NotEmpty(message = "简介不能为空") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出生日期不能为空") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "性别不能为空") + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否有效不能为空") + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @NotEmpty(message = "头像不能为空") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @NotEmpty(message = "附件不能为空") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @NotEmpty(message = "备注不能为空") + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService new file mode 100644 index 0000000..c4a0e17 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 学生 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraStudentService { + + /** + * 创建学生 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); + + /** + * 更新学生 + * + * @param updateReqVO 更新信息 + */ + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); + + /** + * 删除学生 + * + * @param id 编号 + */ + void deleteStudent(Long id); + + /** + * 获得学生 + * + * @param id 编号 + * @return 学生 + */ + InfraStudentDO getStudent(Long id); + + /** + * 获得学生分页 + * + * @param pageReqVO 分页查询 + * @return 学生分页 + */ + PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl new file mode 100644 index 0000000..2292a66 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 学生 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraStudentServiceImpl implements InfraStudentService { + + @Resource + private InfraStudentMapper studentMapper; + + @Override + public Long createStudent(InfraStudentSaveReqVO createReqVO) { + // 插入 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); + studentMapper.insert(student); + // 返回 + return student.getId(); + } + + @Override + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { + // 校验存在 + validateStudentExists(updateReqVO.getId()); + // 更新 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); + studentMapper.updateById(updateObj); + } + + @Override + public void deleteStudent(Long id) { + // 校验存在 + validateStudentExists(id); + // 删除 + studentMapper.deleteById(id); + } + + private void validateStudentExists(Long id) { + if (studentMapper.selectById(id) == null) { + throw exception(STUDENT_NOT_EXISTS); + } + } + + @Override + public InfraStudentDO getStudent(Long id) { + return studentMapper.selectById(id); + } + + @Override + public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { + return studentMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest new file mode 100644 index 0000000..b5f4bf0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraStudentServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraStudentServiceImpl.class) +public class InfraStudentServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraStudentServiceImpl studentService; + + @Resource + private InfraStudentMapper studentMapper; + + @Test + public void testCreateStudent_success() { + // 准备参数 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); + + // 调用 + Long studentId = studentService.createStudent(createReqVO); + // 断言 + assertNotNull(studentId); + // 校验记录的属性是否正确 + InfraStudentDO student = studentMapper.selectById(studentId); + assertPojoEquals(createReqVO, student, "id"); + } + + @Test + public void testUpdateStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { + o.setId(dbStudent.getId()); // 设置更新的 ID + }); + + // 调用 + studentService.updateStudent(updateReqVO); + // 校验是否更新正确 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, student); + } + + @Test + public void testUpdateStudent_notExists() { + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); + } + + @Test + public void testDeleteStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbStudent.getId(); + + // 调用 + studentService.deleteStudent(id); + // 校验数据不存在了 + assertNull(studentMapper.selectById(id)); + } + + @Test + public void testDeleteStudent_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetStudentPage() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 + o.setName(null); + o.setBirthday(null); + o.setSex(null); + o.setEnabled(null); + o.setCreateTime(null); + }); + studentMapper.insert(dbStudent); + // 测试 name 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); + // 测试 birthday 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); + // 测试 sex 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); + // 测试 enabled 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); + // 测试 createTime 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); + // 准备参数 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); + reqVO.setName(null); + reqVO.setBirthday(null); + reqVO.setSex(null); + reqVO.setEnabled(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = studentService.getStudentPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbStudent, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/js/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/js/index new file mode 100644 index 0000000..44db468 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/js/index @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +// 创建学生 +export function createStudent(data) { + return request({ + url: '/infra/student/create', + method: 'post', + data: data + }) +} + +// 更新学生 +export function updateStudent(data) { + return request({ + url: '/infra/student/update', + method: 'put', + data: data + }) +} + +// 删除学生 +export function deleteStudent(id) { + return request({ + url: '/infra/student/delete?id=' + id, + method: 'delete' + }) +} + +// 获得学生 +export function getStudent(id) { + return request({ + url: '/infra/student/get?id=' + id, + method: 'get' + }) +} + +// 获得学生分页 +export function getStudentPage(params) { + return request({ + url: '/infra/student/page', + method: 'get', + params + }) +} +// 导出学生 Excel +export function exportStudentExcel(params) { + return request({ + url: '/infra/student/export-excel', + method: 'get', + params, + responseType: 'blob' + }) +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/h2 new file mode 100644 index 0000000..6c1875f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/h2 @@ -0,0 +1,17 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_student" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "birthday" varchar NOT NULL, + "sex" int NOT NULL, + "enabled" bit NOT NULL, + "avatar" varchar NOT NULL, + "video" varchar NOT NULL, + "memo" varchar NOT NULL, + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id") +) COMMENT '学生表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/sql new file mode 100644 index 0000000..83df279 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '学生管理', '', 2, 0, 888, + 'student', '', 'infra/demo/index', 0, 'InfraStudent' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生查询', 'infra:student:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生创建', 'infra:student:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生更新', 'infra:student:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生删除', 'infra:student:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生导出', 'infra:student:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm new file mode 100644 index 0000000..d89e506 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm @@ -0,0 +1,149 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/index new file mode 100644 index 0000000..4607581 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/vue/index @@ -0,0 +1,205 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper new file mode 100644 index 0000000..155aa5c --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/assert.json new file mode 100644 index 0000000..a7d2f56 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/assert.json @@ -0,0 +1,49 @@ +[ { + "contentPath" : "java/InfraCategoryListReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java" +}, { + "contentPath" : "java/InfraCategoryRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java" +}, { + "contentPath" : "java/InfraCategorySaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java" +}, { + "contentPath" : "java/InfraCategoryController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java" +}, { + "contentPath" : "java/InfraCategoryDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java" +}, { + "contentPath" : "java/InfraCategoryMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java" +}, { + "contentPath" : "xml/InfraCategoryMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml" +}, { + "contentPath" : "java/InfraCategoryServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java" +}, { + "contentPath" : "java/InfraCategoryService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java" +}, { + "contentPath" : "java/InfraCategoryServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" +}, { + "contentPath": "js/index", + "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" +}, { + "contentPath" : "vue/CategoryForm", + "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/CategoryForm.vue" +} ] \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作 new file mode 100644 index 0000000..36df675 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作 @@ -0,0 +1,8 @@ +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! +// ========== 分类 TODO 补充编号 ========== +ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, "分类不存在"); +ErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子分类,无法删除"); +ErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级分类不存在"); +ErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父分类"); +ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该名字的分类"); +ErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子InfraCategory为父InfraCategory"); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController new file mode 100644 index 0000000..a7b2f81 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService; + +@Tag(name = "管理后台 - 分类") +@RestController +@RequestMapping("/infra/category") +@Validated +public class InfraCategoryController { + + @Resource + private InfraCategoryService categoryService; + + @PostMapping("/create") + @Operation(summary = "创建分类") + @PreAuthorize("@ss.hasPermission('infra:category:create')") + public CommonResult createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) { + return success(categoryService.createCategory(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新分类") + @PreAuthorize("@ss.hasPermission('infra:category:update')") + public CommonResult updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) { + categoryService.updateCategory(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除分类") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:category:delete')") + public CommonResult deleteCategory(@RequestParam("id") Long id) { + categoryService.deleteCategory(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得分类") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:category:query')") + public CommonResult getCategory(@RequestParam("id") Long id) { + InfraCategoryDO category = categoryService.getCategory(id); + return success(BeanUtils.toBean(category, InfraCategoryRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得分类列表") + @PreAuthorize("@ss.hasPermission('infra:category:query')") + public CommonResult> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) { + List list = categoryService.getCategoryList(listReqVO); + return success(BeanUtils.toBean(list, InfraCategoryRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出分类 Excel") + @PreAuthorize("@ss.hasPermission('infra:category:export')") + @OperateLog(type = EXPORT) + public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO, + HttpServletResponse response) throws IOException { + List list = categoryService.getCategoryList(listReqVO); + // 导出 Excel + ExcelUtils.write(response, "分类.xls", "数据", InfraCategoryRespVO.class, + BeanUtils.toBean(list, InfraCategoryRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO new file mode 100644 index 0000000..9bf21c0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 分类 DO + * + * @author 芋道源码 + */ +@TableName("infra_category") +@KeySequence("infra_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraCategoryDO extends BaseDO { + + public static final Long PARENT_ID_ROOT = 0L; + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 父编号 + */ + private Long parentId; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO new file mode 100644 index 0000000..e5c6f18 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +@Schema(description = "管理后台 - 分类列表 Request VO") +@Data +public class InfraCategoryListReqVO { + + @Schema(description = "名字", example = "芋头") + private String name; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper new file mode 100644 index 0000000..9dadbf1 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 分类 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraCategoryMapper extends BaseMapperX { + + default List selectList(InfraCategoryListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(InfraCategoryDO::getName, reqVO.getName()) + .orderByDesc(InfraCategoryDO::getId)); + } + + default InfraCategoryDO selectByParentIdAndName(Long parentId, String name) { + return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name); + } + + default Long selectCountByParentId(Long parentId) { + return selectCount(InfraCategoryDO::getParentId, parentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO new file mode 100644 index 0000000..6325d86 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import com.alibaba.excel.annotation.*; + +@Schema(description = "管理后台 - 分类 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraCategoryRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @ExcelProperty("父编号") + private Long parentId; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO new file mode 100644 index 0000000..3c03b97 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; + +@Schema(description = "管理后台 - 分类新增/修改 Request VO") +@Data +public class InfraCategorySaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "父编号不能为空") + private Long parentId; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService new file mode 100644 index 0000000..9d0ae1a --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 分类 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraCategoryService { + + /** + * 创建分类 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCategory(@Valid InfraCategorySaveReqVO createReqVO); + + /** + * 更新分类 + * + * @param updateReqVO 更新信息 + */ + void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO); + + /** + * 删除分类 + * + * @param id 编号 + */ + void deleteCategory(Long id); + + /** + * 获得分类 + * + * @param id 编号 + * @return 分类 + */ + InfraCategoryDO getCategory(Long id); + + /** + * 获得分类列表 + * + * @param listReqVO 查询条件 + * @return 分类列表 + */ + List getCategoryList(InfraCategoryListReqVO listReqVO); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl new file mode 100644 index 0000000..351568b --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl @@ -0,0 +1,136 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 分类 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraCategoryServiceImpl implements InfraCategoryService { + + @Resource + private InfraCategoryMapper categoryMapper; + + @Override + public Long createCategory(InfraCategorySaveReqVO createReqVO) { + // 校验父编号的有效性 + validateParentCategory(null, createReqVO.getParentId()); + // 校验名字的唯一性 + validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName()); + + // 插入 + InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class); + categoryMapper.insert(category); + // 返回 + return category.getId(); + } + + @Override + public void updateCategory(InfraCategorySaveReqVO updateReqVO) { + // 校验存在 + validateCategoryExists(updateReqVO.getId()); + // 校验父编号的有效性 + validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId()); + // 校验名字的唯一性 + validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); + + // 更新 + InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class); + categoryMapper.updateById(updateObj); + } + + @Override + public void deleteCategory(Long id) { + // 校验存在 + validateCategoryExists(id); + // 校验是否有子分类 + if (categoryMapper.selectCountByParentId(id) > 0) { + throw exception(CATEGORY_EXITS_CHILDREN); + } + // 删除 + categoryMapper.deleteById(id); + } + + private void validateCategoryExists(Long id) { + if (categoryMapper.selectById(id) == null) { + throw exception(CATEGORY_NOT_EXISTS); + } + } + + private void validateParentCategory(Long id, Long parentId) { + if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { + return; + } + // 1. 不能设置自己为父分类 + if (Objects.equals(id, parentId)) { + throw exception(CATEGORY_PARENT_ERROR); + } + // 2. 父分类不存在 + CategoryDO parentCategory = categoryMapper.selectById(parentId); + if (parentCategory == null) { + throw exception(CATEGORY_PARENT_NOT_EXITS); + } + // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路 + if (id == null) { // id 为空,说明新增,不需要考虑环路 + return; + } + for (int i = 0; i < Short.MAX_VALUE; i++) { + // 3.1 校验环路 + parentId = parentCategory.getParentId(); + if (Objects.equals(id, parentId)) { + throw exception(CATEGORY_PARENT_IS_CHILD); + } + // 3.2 继续递归下一级父分类 + if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { + break; + } + parentCategory = categoryMapper.selectById(parentId); + if (parentCategory == null) { + break; + } + } + } + + private void validateCategoryNameUnique(Long id, Long parentId, String name) { + CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name); + if (category == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的分类 + if (id == null) { + throw exception(CATEGORY_NAME_DUPLICATE); + } + if (!Objects.equals(category.getId(), id)) { + throw exception(CATEGORY_NAME_DUPLICATE); + } + } + + @Override + public InfraCategoryDO getCategory(Long id) { + return categoryMapper.selectById(id); + } + + @Override + public List getCategoryList(InfraCategoryListReqVO listReqVO) { + return categoryMapper.selectList(listReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest new file mode 100644 index 0000000..efb70fd --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest @@ -0,0 +1,129 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraCategoryServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraCategoryServiceImpl.class) +public class InfraCategoryServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraCategoryServiceImpl categoryService; + + @Resource + private InfraCategoryMapper categoryMapper; + + @Test + public void testCreateCategory_success() { + // 准备参数 + InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null); + + // 调用 + Long categoryId = categoryService.createCategory(createReqVO); + // 断言 + assertNotNull(categoryId); + // 校验记录的属性是否正确 + InfraCategoryDO category = categoryMapper.selectById(categoryId); + assertPojoEquals(createReqVO, category, "id"); + } + + @Test + public void testUpdateCategory_success() { + // mock 数据 + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> { + o.setId(dbCategory.getId()); // 设置更新的 ID + }); + + // 调用 + categoryService.updateCategory(updateReqVO); + // 校验是否更新正确 + InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, category); + } + + @Test + public void testUpdateCategory_notExists() { + // 准备参数 + InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS); + } + + @Test + public void testDeleteCategory_success() { + // mock 数据 + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbCategory.getId(); + + // 调用 + categoryService.deleteCategory(id); + // 校验数据不存在了 + assertNull(categoryMapper.selectById(id)); + } + + @Test + public void testDeleteCategory_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetCategoryList() { + // mock 数据 + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到 + o.setName(null); + }); + categoryMapper.insert(dbCategory); + // 测试 name 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null))); + // 准备参数 + InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO(); + reqVO.setName(null); + + // 调用 + List list = categoryService.getCategoryList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbCategory, list.get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/js/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/js/index new file mode 100644 index 0000000..1e6ffdc --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/js/index @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +// 创建分类 +export function createCategory(data) { + return request({ + url: '/infra/category/create', + method: 'post', + data: data + }) +} + +// 更新分类 +export function updateCategory(data) { + return request({ + url: '/infra/category/update', + method: 'put', + data: data + }) +} + +// 删除分类 +export function deleteCategory(id) { + return request({ + url: '/infra/category/delete?id=' + id, + method: 'delete' + }) +} + +// 获得分类 +export function getCategory(id) { + return request({ + url: '/infra/category/get?id=' + id, + method: 'get' + }) +} + +// 获得分类列表 +export function getCategoryList(params) { + return request({ + url: '/infra/category/list', + method: 'get', + params + }) +} +// 导出分类 Excel +export function exportCategoryExcel(params) { + return request({ + url: '/infra/category/export-excel', + method: 'get', + params, + responseType: 'blob' + }) +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/h2 new file mode 100644 index 0000000..4141766 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/h2 @@ -0,0 +1,10 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_category" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" bigint NOT NULL, + PRIMARY KEY ("id") +) COMMENT '分类表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_category"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/sql new file mode 100644 index 0000000..8140948 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '分类管理', '', 2, 0, 888, + 'category', '', 'infra/demo/index', 0, 'InfraCategory' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类查询', 'infra:category:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类创建', 'infra:category:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类更新', 'infra:category:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类删除', 'infra:category:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类导出', 'infra:category:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm new file mode 100644 index 0000000..7fa06e8 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm @@ -0,0 +1,130 @@ + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/index new file mode 100644 index 0000000..88da682 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/vue/index @@ -0,0 +1,161 @@ + + + diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper new file mode 100644 index 0000000..025ac85 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/assert.json new file mode 100644 index 0000000..0937ba9 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/assert.json @@ -0,0 +1,73 @@ +[ { + "contentPath" : "java/InfraStudentPageReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" +}, { + "contentPath" : "java/InfraStudentRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" +}, { + "contentPath" : "java/InfraStudentSaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" +}, { + "contentPath" : "java/InfraStudentController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" +}, { + "contentPath" : "java/InfraStudentDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" +}, { + "contentPath" : "java/InfraStudentContactDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" +}, { + "contentPath" : "java/InfraStudentTeacherDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" +}, { + "contentPath" : "java/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" +}, { + "contentPath" : "java/InfraStudentContactMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" +}, { + "contentPath" : "java/InfraStudentTeacherMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" +}, { + "contentPath" : "xml/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" +}, { + "contentPath" : "java/InfraStudentServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" +}, { + "contentPath" : "java/InfraStudentService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" +}, { + "contentPath" : "java/InfraStudentServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" +}, { + "contentPath" : "vue/StudentForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue" +}, { + "contentPath" : "vue/StudentContactForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue" +}, { + "contentPath" : "vue/StudentTeacherForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue" +}, { + "contentPath" : "vue/StudentContactList", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactList.vue" +}, { + "contentPath" : "vue/StudentTeacherList", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherList.vue" +}, { + "contentPath" : "ts/index", + "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" +} ] \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作 new file mode 100644 index 0000000..d3201de --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作 @@ -0,0 +1,6 @@ +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! +// ========== 学生 TODO 补充编号 ========== +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); +ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在"); +ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在"); +ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在"); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO new file mode 100644 index 0000000..17c668e --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生联系人 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_contact") +@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentContactDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper new file mode 100644 index 0000000..ca662d1 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生联系人 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentContactMapper extends BaseMapperX { + + default PageResult selectPage(PageParam reqVO, Long studentId) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(InfraStudentContactDO::getStudentId, studentId) + .orderByDesc(InfraStudentContactDO::getId)); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentContactDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController new file mode 100644 index 0000000..d6f2018 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController @@ -0,0 +1,183 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; + +@Tag(name = "管理后台 - 学生") +@RestController +@RequestMapping("/infra/student") +@Validated +public class InfraStudentController { + + @Resource + private InfraStudentService studentService; + + @PostMapping("/create") + @Operation(summary = "创建学生") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { + return success(studentService.createStudent(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新学生") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { + studentService.updateStudent(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除学生") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudent(@RequestParam("id") Long id) { + studentService.deleteStudent(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得学生") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudent(@RequestParam("id") Long id) { + InfraStudentDO student = studentService.getStudent(id); + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得学生分页") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { + PageResult pageResult = studentService.getStudentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出学生 Excel") + @PreAuthorize("@ss.hasPermission('infra:student:export')") + @OperateLog(type = EXPORT) + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = studentService.getStudentPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, + BeanUtils.toBean(list, InfraStudentRespVO.class)); + } + + // ==================== 子表(学生联系人) ==================== + + @GetMapping("/student-contact/page") + @Operation(summary = "获得学生联系人分页") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentContactPage(PageParam pageReqVO, + @RequestParam("studentId") Long studentId) { + return success(studentService.getStudentContactPage(pageReqVO, studentId)); + } + + @PostMapping("/student-contact/create") + @Operation(summary = "创建学生联系人") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { + return success(studentService.createStudentContact(studentContact)); + } + + @PutMapping("/student-contact/update") + @Operation(summary = "更新学生联系人") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { + studentService.updateStudentContact(studentContact); + return success(true); + } + + @DeleteMapping("/student-contact/delete") + @Parameter(name = "id", description = "编号", required = true) + @Operation(summary = "删除学生联系人") + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudentContact(@RequestParam("id") Long id) { + studentService.deleteStudentContact(id); + return success(true); + } + + @GetMapping("/student-contact/get") + @Operation(summary = "获得学生联系人") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudentContact(@RequestParam("id") Long id) { + return success(studentService.getStudentContact(id)); + } + + // ==================== 子表(学生班主任) ==================== + + @GetMapping("/student-teacher/page") + @Operation(summary = "获得学生班主任分页") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentTeacherPage(PageParam pageReqVO, + @RequestParam("studentId") Long studentId) { + return success(studentService.getStudentTeacherPage(pageReqVO, studentId)); + } + + @PostMapping("/student-teacher/create") + @Operation(summary = "创建学生班主任") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { + return success(studentService.createStudentTeacher(studentTeacher)); + } + + @PutMapping("/student-teacher/update") + @Operation(summary = "更新学生班主任") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { + studentService.updateStudentTeacher(studentTeacher); + return success(true); + } + + @DeleteMapping("/student-teacher/delete") + @Parameter(name = "id", description = "编号", required = true) + @Operation(summary = "删除学生班主任") + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudentTeacher(@RequestParam("id") Long id) { + studentService.deleteStudentTeacher(id); + return success(true); + } + + @GetMapping("/student-teacher/get") + @Operation(summary = "获得学生班主任") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudentTeacher(@RequestParam("id") Long id) { + return success(studentService.getStudentTeacher(id)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO new file mode 100644 index 0000000..b0d4bd2 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生 DO + * + * @author 芋道源码 + */ +@TableName("infra_student") +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper new file mode 100644 index 0000000..34e70a0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 学生 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentMapper extends BaseMapperX { + + default PageResult selectPage(InfraStudentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(InfraStudentDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO new file mode 100644 index 0000000..41a3730 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 学生分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class InfraStudentPageReqVO extends PageParam { + + @Schema(description = "名字", example = "芋头") + private String name; + + @Schema(description = "出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "是否有效", example = "true") + private Boolean enabled; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO new file mode 100644 index 0000000..c41a550 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - 学生 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraStudentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @ExcelProperty("简介") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "性别", converter = DictConvert.class) + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "是否有效", converter = DictConvert.class) + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @ExcelProperty("头像") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @ExcelProperty("附件") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @ExcelProperty("备注") + private String memo; + + @Schema(description = "创建时间") + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO new file mode 100644 index 0000000..eaadf74 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; + +@Schema(description = "管理后台 - 学生新增/修改 Request VO") +@Data +public class InfraStudentSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @NotEmpty(message = "简介不能为空") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出生日期不能为空") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "性别不能为空") + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否有效不能为空") + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @NotEmpty(message = "头像不能为空") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @NotEmpty(message = "附件不能为空") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @NotEmpty(message = "备注不能为空") + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService new file mode 100644 index 0000000..7df090d --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService @@ -0,0 +1,139 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 学生 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraStudentService { + + /** + * 创建学生 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); + + /** + * 更新学生 + * + * @param updateReqVO 更新信息 + */ + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); + + /** + * 删除学生 + * + * @param id 编号 + */ + void deleteStudent(Long id); + + /** + * 获得学生 + * + * @param id 编号 + * @return 学生 + */ + InfraStudentDO getStudent(Long id); + + /** + * 获得学生分页 + * + * @param pageReqVO 分页查询 + * @return 学生分页 + */ + PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); + + // ==================== 子表(学生联系人) ==================== + + /** + * 获得学生联系人分页 + * + * @param pageReqVO 分页查询 + * @param studentId 学生编号 + * @return 学生联系人分页 + */ + PageResult getStudentContactPage(PageParam pageReqVO, Long studentId); + + /** + * 创建学生联系人 + * + * @param studentContact 创建信息 + * @return 编号 + */ + Long createStudentContact(@Valid InfraStudentContactDO studentContact); + + /** + * 更新学生联系人 + * + * @param studentContact 更新信息 + */ + void updateStudentContact(@Valid InfraStudentContactDO studentContact); + + /** + * 删除学生联系人 + * + * @param id 编号 + */ + void deleteStudentContact(Long id); + + /** + * 获得学生联系人 + * + * @param id 编号 + * @return 学生联系人 + */ + InfraStudentContactDO getStudentContact(Long id); + + // ==================== 子表(学生班主任) ==================== + + /** + * 获得学生班主任分页 + * + * @param pageReqVO 分页查询 + * @param studentId 学生编号 + * @return 学生班主任分页 + */ + PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId); + + /** + * 创建学生班主任 + * + * @param studentTeacher 创建信息 + * @return 编号 + */ + Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); + + /** + * 更新学生班主任 + * + * @param studentTeacher 更新信息 + */ + void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); + + /** + * 删除学生班主任 + * + * @param id 编号 + */ + void deleteStudentTeacher(Long id); + + /** + * 获得学生班主任 + * + * @param id 编号 + * @return 学生班主任 + */ + InfraStudentTeacherDO getStudentTeacher(Long id); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl new file mode 100644 index 0000000..793b2dd --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl @@ -0,0 +1,180 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 学生 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraStudentServiceImpl implements InfraStudentService { + + @Resource + private InfraStudentMapper studentMapper; + @Resource + private InfraStudentContactMapper studentContactMapper; + @Resource + private InfraStudentTeacherMapper studentTeacherMapper; + + @Override + public Long createStudent(InfraStudentSaveReqVO createReqVO) { + // 插入 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); + studentMapper.insert(student); + // 返回 + return student.getId(); + } + + @Override + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { + // 校验存在 + validateStudentExists(updateReqVO.getId()); + // 更新 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); + studentMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStudent(Long id) { + // 校验存在 + validateStudentExists(id); + // 删除 + studentMapper.deleteById(id); + + // 删除子表 + deleteStudentContactByStudentId(id); + deleteStudentTeacherByStudentId(id); + } + + private void validateStudentExists(Long id) { + if (studentMapper.selectById(id) == null) { + throw exception(STUDENT_NOT_EXISTS); + } + } + + @Override + public InfraStudentDO getStudent(Long id) { + return studentMapper.selectById(id); + } + + @Override + public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { + return studentMapper.selectPage(pageReqVO); + } + + // ==================== 子表(学生联系人) ==================== + + @Override + public PageResult getStudentContactPage(PageParam pageReqVO, Long studentId) { + return studentContactMapper.selectPage(pageReqVO, studentId); + } + + @Override + public Long createStudentContact(InfraStudentContactDO studentContact) { + studentContactMapper.insert(studentContact); + return studentContact.getId(); + } + + @Override + public void updateStudentContact(InfraStudentContactDO studentContact) { + // 校验存在 + validateStudentContactExists(studentContact.getId()); + // 更新 + studentContactMapper.updateById(studentContact); + } + + @Override + public void deleteStudentContact(Long id) { + // 校验存在 + validateStudentContactExists(id); + // 删除 + studentContactMapper.deleteById(id); + } + + @Override + public InfraStudentContactDO getStudentContact(Long id) { + return studentContactMapper.selectById(id); + } + + private void validateStudentContactExists(Long id) { + if (studentContactMapper.selectById(id) == null) { + throw exception(STUDENT_CONTACT_NOT_EXISTS); + } + } + + private void deleteStudentContactByStudentId(Long studentId) { + studentContactMapper.deleteByStudentId(studentId); + } + + // ==================== 子表(学生班主任) ==================== + + @Override + public PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId) { + return studentTeacherMapper.selectPage(pageReqVO, studentId); + } + + @Override + public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) { + // 校验是否已经存在 + if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) { + throw exception(STUDENT_TEACHER_EXISTS); + } + // 插入 + studentTeacherMapper.insert(studentTeacher); + return studentTeacher.getId(); + } + + @Override + public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) { + // 校验存在 + validateStudentTeacherExists(studentTeacher.getId()); + // 更新 + studentTeacherMapper.updateById(studentTeacher); + } + + @Override + public void deleteStudentTeacher(Long id) { + // 校验存在 + validateStudentTeacherExists(id); + // 删除 + studentTeacherMapper.deleteById(id); + } + + @Override + public InfraStudentTeacherDO getStudentTeacher(Long id) { + return studentTeacherMapper.selectById(id); + } + + private void validateStudentTeacherExists(Long id) { + if (studentTeacherMapper.selectById(id) == null) { + throw exception(STUDENT_TEACHER_NOT_EXISTS); + } + } + + private void deleteStudentTeacherByStudentId(Long studentId) { + studentTeacherMapper.deleteByStudentId(studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest new file mode 100644 index 0000000..b5f4bf0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraStudentServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraStudentServiceImpl.class) +public class InfraStudentServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraStudentServiceImpl studentService; + + @Resource + private InfraStudentMapper studentMapper; + + @Test + public void testCreateStudent_success() { + // 准备参数 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); + + // 调用 + Long studentId = studentService.createStudent(createReqVO); + // 断言 + assertNotNull(studentId); + // 校验记录的属性是否正确 + InfraStudentDO student = studentMapper.selectById(studentId); + assertPojoEquals(createReqVO, student, "id"); + } + + @Test + public void testUpdateStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { + o.setId(dbStudent.getId()); // 设置更新的 ID + }); + + // 调用 + studentService.updateStudent(updateReqVO); + // 校验是否更新正确 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, student); + } + + @Test + public void testUpdateStudent_notExists() { + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); + } + + @Test + public void testDeleteStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbStudent.getId(); + + // 调用 + studentService.deleteStudent(id); + // 校验数据不存在了 + assertNull(studentMapper.selectById(id)); + } + + @Test + public void testDeleteStudent_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetStudentPage() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 + o.setName(null); + o.setBirthday(null); + o.setSex(null); + o.setEnabled(null); + o.setCreateTime(null); + }); + studentMapper.insert(dbStudent); + // 测试 name 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); + // 测试 birthday 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); + // 测试 sex 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); + // 测试 enabled 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); + // 测试 createTime 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); + // 准备参数 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); + reqVO.setName(null); + reqVO.setBirthday(null); + reqVO.setSex(null); + reqVO.setEnabled(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = studentService.getStudentPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbStudent, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO new file mode 100644 index 0000000..c19cf9f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生班主任 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_teacher") +@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentTeacherDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper new file mode 100644 index 0000000..994212d --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生班主任 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentTeacherMapper extends BaseMapperX { + + default PageResult selectPage(PageParam reqVO, Long studentId) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eq(InfraStudentTeacherDO::getStudentId, studentId) + .orderByDesc(InfraStudentTeacherDO::getId)); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentTeacherDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2 new file mode 100644 index 0000000..6c1875f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2 @@ -0,0 +1,17 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_student" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "birthday" varchar NOT NULL, + "sex" int NOT NULL, + "enabled" bit NOT NULL, + "avatar" varchar NOT NULL, + "video" varchar NOT NULL, + "memo" varchar NOT NULL, + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id") +) COMMENT '学生表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql new file mode 100644 index 0000000..83df279 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '学生管理', '', 2, 0, 888, + 'student', '', 'infra/demo/index', 0, 'InfraStudent' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生查询', 'infra:student:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生创建', 'infra:student:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生更新', 'infra:student:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生删除', 'infra:student:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生导出', 'infra:student:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/ts/index new file mode 100644 index 0000000..2fe87b7 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/ts/index @@ -0,0 +1,95 @@ +import request from '@/config/axios' + +export interface StudentVO { + id: number + name: string + description: string + birthday: Date + sex: number + enabled: boolean + avatar: string + video: string + memo: string +} + +// 查询学生分页 +export const getStudentPage = async (params) => { + return await request.get({ url: `/infra/student/page`, params }) +} + +// 查询学生详情 +export const getStudent = async (id: number) => { + return await request.get({ url: `/infra/student/get?id=` + id }) +} + +// 新增学生 +export const createStudent = async (data: StudentVO) => { + return await request.post({ url: `/infra/student/create`, data }) +} + +// 修改学生 +export const updateStudent = async (data: StudentVO) => { + return await request.put({ url: `/infra/student/update`, data }) +} + +// 删除学生 +export const deleteStudent = async (id: number) => { + return await request.delete({ url: `/infra/student/delete?id=` + id }) +} + +// 导出学生 Excel +export const exportStudent = async (params) => { + return await request.download({ url: `/infra/student/export-excel`, params }) +} + +// ==================== 子表(学生联系人) ==================== + +// 获得学生联系人分页 +export const getStudentContactPage = async (params) => { + return await request.get({ url: `/infra/student/student-contact/page`, params }) +} +// 新增学生联系人 +export const createStudentContact = async (data) => { + return await request.post({ url: `/infra/student/student-contact/create`, data }) +} + +// 修改学生联系人 +export const updateStudentContact = async (data) => { + return await request.put({ url: `/infra/student/student-contact/update`, data }) +} + +// 删除学生联系人 +export const deleteStudentContact = async (id: number) => { + return await request.delete({ url: `/infra/student/student-contact/delete?id=` + id }) +} + +// 获得学生联系人 +export const getStudentContact = async (id: number) => { + return await request.get({ url: `/infra/student/student-contact/get?id=` + id }) +} + +// ==================== 子表(学生班主任) ==================== + +// 获得学生班主任分页 +export const getStudentTeacherPage = async (params) => { + return await request.get({ url: `/infra/student/student-teacher/page`, params }) +} +// 新增学生班主任 +export const createStudentTeacher = async (data) => { + return await request.post({ url: `/infra/student/student-teacher/create`, data }) +} + +// 修改学生班主任 +export const updateStudentTeacher = async (data) => { + return await request.put({ url: `/infra/student/student-teacher/update`, data }) +} + +// 删除学生班主任 +export const deleteStudentTeacher = async (id: number) => { + return await request.delete({ url: `/infra/student/student-teacher/delete?id=` + id }) +} + +// 获得学生班主任 +export const getStudentTeacher = async (id: number) => { + return await request.get({ url: `/infra/student/student-teacher/get?id=` + id }) +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm new file mode 100644 index 0000000..4a13935 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm @@ -0,0 +1,155 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList new file mode 100644 index 0000000..eada66a --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList @@ -0,0 +1,146 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm new file mode 100644 index 0000000..0dabcb5 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm @@ -0,0 +1,152 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm new file mode 100644 index 0000000..f93c21c --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm @@ -0,0 +1,155 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList new file mode 100644 index 0000000..1eba0a3 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList @@ -0,0 +1,146 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/index new file mode 100644 index 0000000..9d15146 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/vue/index @@ -0,0 +1,278 @@ + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper new file mode 100644 index 0000000..155aa5c --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/assert.json new file mode 100644 index 0000000..0937ba9 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/assert.json @@ -0,0 +1,73 @@ +[ { + "contentPath" : "java/InfraStudentPageReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" +}, { + "contentPath" : "java/InfraStudentRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" +}, { + "contentPath" : "java/InfraStudentSaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" +}, { + "contentPath" : "java/InfraStudentController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" +}, { + "contentPath" : "java/InfraStudentDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" +}, { + "contentPath" : "java/InfraStudentContactDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" +}, { + "contentPath" : "java/InfraStudentTeacherDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" +}, { + "contentPath" : "java/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" +}, { + "contentPath" : "java/InfraStudentContactMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" +}, { + "contentPath" : "java/InfraStudentTeacherMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" +}, { + "contentPath" : "xml/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" +}, { + "contentPath" : "java/InfraStudentServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" +}, { + "contentPath" : "java/InfraStudentService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" +}, { + "contentPath" : "java/InfraStudentServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" +}, { + "contentPath" : "vue/StudentForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue" +}, { + "contentPath" : "vue/StudentContactForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue" +}, { + "contentPath" : "vue/StudentTeacherForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue" +}, { + "contentPath" : "vue/StudentContactList", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactList.vue" +}, { + "contentPath" : "vue/StudentTeacherList", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherList.vue" +}, { + "contentPath" : "ts/index", + "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" +} ] \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作 new file mode 100644 index 0000000..f8be662 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作 @@ -0,0 +1,3 @@ +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! +// ========== 学生 TODO 补充编号 ========== +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO new file mode 100644 index 0000000..17c668e --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生联系人 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_contact") +@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentContactDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper new file mode 100644 index 0000000..35bbd53 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生联系人 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentContactMapper extends BaseMapperX { + + default List selectListByStudentId(Long studentId) { + return selectList(InfraStudentContactDO::getStudentId, studentId); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentContactDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController new file mode 100644 index 0000000..b9a587b --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; + +@Tag(name = "管理后台 - 学生") +@RestController +@RequestMapping("/infra/student") +@Validated +public class InfraStudentController { + + @Resource + private InfraStudentService studentService; + + @PostMapping("/create") + @Operation(summary = "创建学生") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { + return success(studentService.createStudent(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新学生") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { + studentService.updateStudent(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除学生") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudent(@RequestParam("id") Long id) { + studentService.deleteStudent(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得学生") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudent(@RequestParam("id") Long id) { + InfraStudentDO student = studentService.getStudent(id); + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得学生分页") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { + PageResult pageResult = studentService.getStudentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出学生 Excel") + @PreAuthorize("@ss.hasPermission('infra:student:export')") + @OperateLog(type = EXPORT) + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = studentService.getStudentPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, + BeanUtils.toBean(list, InfraStudentRespVO.class)); + } + + // ==================== 子表(学生联系人) ==================== + + @GetMapping("/student-contact/list-by-student-id") + @Operation(summary = "获得学生联系人列表") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { + return success(studentService.getStudentContactListByStudentId(studentId)); + } + + // ==================== 子表(学生班主任) ==================== + + @GetMapping("/student-teacher/get-by-student-id") + @Operation(summary = "获得学生班主任") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { + return success(studentService.getStudentTeacherByStudentId(studentId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO new file mode 100644 index 0000000..b0d4bd2 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生 DO + * + * @author 芋道源码 + */ +@TableName("infra_student") +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper new file mode 100644 index 0000000..34e70a0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 学生 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentMapper extends BaseMapperX { + + default PageResult selectPage(InfraStudentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(InfraStudentDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO new file mode 100644 index 0000000..41a3730 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 学生分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class InfraStudentPageReqVO extends PageParam { + + @Schema(description = "名字", example = "芋头") + private String name; + + @Schema(description = "出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "是否有效", example = "true") + private Boolean enabled; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO new file mode 100644 index 0000000..c41a550 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - 学生 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraStudentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @ExcelProperty("简介") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "性别", converter = DictConvert.class) + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "是否有效", converter = DictConvert.class) + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @ExcelProperty("头像") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @ExcelProperty("附件") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @ExcelProperty("备注") + private String memo; + + @Schema(description = "创建时间") + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO new file mode 100644 index 0000000..faa491d --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; + +@Schema(description = "管理后台 - 学生新增/修改 Request VO") +@Data +public class InfraStudentSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @NotEmpty(message = "简介不能为空") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出生日期不能为空") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "性别不能为空") + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否有效不能为空") + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @NotEmpty(message = "头像不能为空") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @NotEmpty(message = "附件不能为空") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @NotEmpty(message = "备注不能为空") + private String memo; + + @Schema(description = "学生联系人列表") + private List studentContacts; + + @Schema(description = "学生班主任") + private InfraStudentTeacherDO studentTeacher; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService new file mode 100644 index 0000000..afa7d22 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 学生 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraStudentService { + + /** + * 创建学生 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); + + /** + * 更新学生 + * + * @param updateReqVO 更新信息 + */ + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); + + /** + * 删除学生 + * + * @param id 编号 + */ + void deleteStudent(Long id); + + /** + * 获得学生 + * + * @param id 编号 + * @return 学生 + */ + InfraStudentDO getStudent(Long id); + + /** + * 获得学生分页 + * + * @param pageReqVO 分页查询 + * @return 学生分页 + */ + PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); + + // ==================== 子表(学生联系人) ==================== + + /** + * 获得学生联系人列表 + * + * @param studentId 学生编号 + * @return 学生联系人列表 + */ + List getStudentContactListByStudentId(Long studentId); + + // ==================== 子表(学生班主任) ==================== + + /** + * 获得学生班主任 + * + * @param studentId 学生编号 + * @return 学生班主任 + */ + InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl new file mode 100644 index 0000000..c57cba6 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl @@ -0,0 +1,147 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 学生 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraStudentServiceImpl implements InfraStudentService { + + @Resource + private InfraStudentMapper studentMapper; + @Resource + private InfraStudentContactMapper studentContactMapper; + @Resource + private InfraStudentTeacherMapper studentTeacherMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createStudent(InfraStudentSaveReqVO createReqVO) { + // 插入 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); + studentMapper.insert(student); + + // 插入子表 + createStudentContactList(student.getId(), createReqVO.getStudentContacts()); + createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); + // 返回 + return student.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { + // 校验存在 + validateStudentExists(updateReqVO.getId()); + // 更新 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); + studentMapper.updateById(updateObj); + + // 更新子表 + updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); + updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStudent(Long id) { + // 校验存在 + validateStudentExists(id); + // 删除 + studentMapper.deleteById(id); + + // 删除子表 + deleteStudentContactByStudentId(id); + deleteStudentTeacherByStudentId(id); + } + + private void validateStudentExists(Long id) { + if (studentMapper.selectById(id) == null) { + throw exception(STUDENT_NOT_EXISTS); + } + } + + @Override + public InfraStudentDO getStudent(Long id) { + return studentMapper.selectById(id); + } + + @Override + public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { + return studentMapper.selectPage(pageReqVO); + } + + // ==================== 子表(学生联系人) ==================== + + @Override + public List getStudentContactListByStudentId(Long studentId) { + return studentContactMapper.selectListByStudentId(studentId); + } + + private void createStudentContactList(Long studentId, List list) { + list.forEach(o -> o.setStudentId(studentId)); + studentContactMapper.insertBatch(list); + } + + private void updateStudentContactList(Long studentId, List list) { + deleteStudentContactByStudentId(studentId); + list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新 + createStudentContactList(studentId, list); + } + + private void deleteStudentContactByStudentId(Long studentId) { + studentContactMapper.deleteByStudentId(studentId); + } + + // ==================== 子表(学生班主任) ==================== + + @Override + public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { + return studentTeacherMapper.selectByStudentId(studentId); + } + + private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { + if (studentTeacher == null) { + return; + } + studentTeacher.setStudentId(studentId); + studentTeacherMapper.insert(studentTeacher); + } + + private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { + if (studentTeacher == null) { + return; + } + studentTeacher.setStudentId(studentId); + studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新 + studentTeacherMapper.insertOrUpdate(studentTeacher); + } + + private void deleteStudentTeacherByStudentId(Long studentId) { + studentTeacherMapper.deleteByStudentId(studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest new file mode 100644 index 0000000..b5f4bf0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraStudentServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraStudentServiceImpl.class) +public class InfraStudentServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraStudentServiceImpl studentService; + + @Resource + private InfraStudentMapper studentMapper; + + @Test + public void testCreateStudent_success() { + // 准备参数 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); + + // 调用 + Long studentId = studentService.createStudent(createReqVO); + // 断言 + assertNotNull(studentId); + // 校验记录的属性是否正确 + InfraStudentDO student = studentMapper.selectById(studentId); + assertPojoEquals(createReqVO, student, "id"); + } + + @Test + public void testUpdateStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { + o.setId(dbStudent.getId()); // 设置更新的 ID + }); + + // 调用 + studentService.updateStudent(updateReqVO); + // 校验是否更新正确 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, student); + } + + @Test + public void testUpdateStudent_notExists() { + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); + } + + @Test + public void testDeleteStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbStudent.getId(); + + // 调用 + studentService.deleteStudent(id); + // 校验数据不存在了 + assertNull(studentMapper.selectById(id)); + } + + @Test + public void testDeleteStudent_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetStudentPage() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 + o.setName(null); + o.setBirthday(null); + o.setSex(null); + o.setEnabled(null); + o.setCreateTime(null); + }); + studentMapper.insert(dbStudent); + // 测试 name 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); + // 测试 birthday 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); + // 测试 sex 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); + // 测试 enabled 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); + // 测试 createTime 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); + // 准备参数 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); + reqVO.setName(null); + reqVO.setBirthday(null); + reqVO.setSex(null); + reqVO.setEnabled(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = studentService.getStudentPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbStudent, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO new file mode 100644 index 0000000..c19cf9f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生班主任 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_teacher") +@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentTeacherDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper new file mode 100644 index 0000000..0521bba --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生班主任 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentTeacherMapper extends BaseMapperX { + + default InfraStudentTeacherDO selectByStudentId(Long studentId) { + return selectOne(InfraStudentTeacherDO::getStudentId, studentId); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentTeacherDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2 new file mode 100644 index 0000000..6c1875f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2 @@ -0,0 +1,17 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_student" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "birthday" varchar NOT NULL, + "sex" int NOT NULL, + "enabled" bit NOT NULL, + "avatar" varchar NOT NULL, + "video" varchar NOT NULL, + "memo" varchar NOT NULL, + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id") +) COMMENT '学生表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql new file mode 100644 index 0000000..83df279 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '学生管理', '', 2, 0, 888, + 'student', '', 'infra/demo/index', 0, 'InfraStudent' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生查询', 'infra:student:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生创建', 'infra:student:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生更新', 'infra:student:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生删除', 'infra:student:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生导出', 'infra:student:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/ts/index new file mode 100644 index 0000000..6112800 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/ts/index @@ -0,0 +1,57 @@ +import request from '@/config/axios' + +export interface StudentVO { + id: number + name: string + description: string + birthday: Date + sex: number + enabled: boolean + avatar: string + video: string + memo: string +} + +// 查询学生分页 +export const getStudentPage = async (params) => { + return await request.get({ url: `/infra/student/page`, params }) +} + +// 查询学生详情 +export const getStudent = async (id: number) => { + return await request.get({ url: `/infra/student/get?id=` + id }) +} + +// 新增学生 +export const createStudent = async (data: StudentVO) => { + return await request.post({ url: `/infra/student/create`, data }) +} + +// 修改学生 +export const updateStudent = async (data: StudentVO) => { + return await request.put({ url: `/infra/student/update`, data }) +} + +// 删除学生 +export const deleteStudent = async (id: number) => { + return await request.delete({ url: `/infra/student/delete?id=` + id }) +} + +// 导出学生 Excel +export const exportStudent = async (params) => { + return await request.download({ url: `/infra/student/export-excel`, params }) +} + +// ==================== 子表(学生联系人) ==================== + +// 获得学生联系人列表 +export const getStudentContactListByStudentId = async (studentId) => { + return await request.get({ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId }) +} + +// ==================== 子表(学生班主任) ==================== + +// 获得学生班主任 +export const getStudentTeacherByStudentId = async (studentId) => { + return await request.get({ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId }) +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm new file mode 100644 index 0000000..55ca994 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm @@ -0,0 +1,174 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList new file mode 100644 index 0000000..d0e89da --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList @@ -0,0 +1,72 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm new file mode 100644 index 0000000..d8e7bc3 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm @@ -0,0 +1,184 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm new file mode 100644 index 0000000..b22a480 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm @@ -0,0 +1,122 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList new file mode 100644 index 0000000..e510adc --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList @@ -0,0 +1,76 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/index new file mode 100644 index 0000000..ee7c05f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/vue/index @@ -0,0 +1,267 @@ + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper new file mode 100644 index 0000000..155aa5c --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/assert.json new file mode 100644 index 0000000..60e7f47 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/assert.json @@ -0,0 +1,67 @@ +[ { + "contentPath" : "java/InfraStudentPageReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" +}, { + "contentPath" : "java/InfraStudentRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" +}, { + "contentPath" : "java/InfraStudentSaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" +}, { + "contentPath" : "java/InfraStudentController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" +}, { + "contentPath" : "java/InfraStudentDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" +}, { + "contentPath" : "java/InfraStudentContactDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" +}, { + "contentPath" : "java/InfraStudentTeacherDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" +}, { + "contentPath" : "java/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" +}, { + "contentPath" : "java/InfraStudentContactMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" +}, { + "contentPath" : "java/InfraStudentTeacherMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" +}, { + "contentPath" : "xml/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" +}, { + "contentPath" : "java/InfraStudentServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" +}, { + "contentPath" : "java/InfraStudentService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" +}, { + "contentPath" : "java/InfraStudentServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" +}, { + "contentPath" : "vue/StudentForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue" +}, { + "contentPath" : "vue/StudentContactForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue" +}, { + "contentPath" : "vue/StudentTeacherForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue" +}, { + "contentPath" : "ts/index", + "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" +} ] \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作 new file mode 100644 index 0000000..f8be662 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作 @@ -0,0 +1,3 @@ +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! +// ========== 学生 TODO 补充编号 ========== +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO new file mode 100644 index 0000000..17c668e --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生联系人 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_contact") +@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentContactDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper new file mode 100644 index 0000000..35bbd53 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生联系人 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentContactMapper extends BaseMapperX { + + default List selectListByStudentId(Long studentId) { + return selectList(InfraStudentContactDO::getStudentId, studentId); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentContactDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController new file mode 100644 index 0000000..b9a587b --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; + +@Tag(name = "管理后台 - 学生") +@RestController +@RequestMapping("/infra/student") +@Validated +public class InfraStudentController { + + @Resource + private InfraStudentService studentService; + + @PostMapping("/create") + @Operation(summary = "创建学生") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { + return success(studentService.createStudent(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新学生") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { + studentService.updateStudent(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除学生") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudent(@RequestParam("id") Long id) { + studentService.deleteStudent(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得学生") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudent(@RequestParam("id") Long id) { + InfraStudentDO student = studentService.getStudent(id); + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得学生分页") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { + PageResult pageResult = studentService.getStudentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出学生 Excel") + @PreAuthorize("@ss.hasPermission('infra:student:export')") + @OperateLog(type = EXPORT) + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = studentService.getStudentPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, + BeanUtils.toBean(list, InfraStudentRespVO.class)); + } + + // ==================== 子表(学生联系人) ==================== + + @GetMapping("/student-contact/list-by-student-id") + @Operation(summary = "获得学生联系人列表") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { + return success(studentService.getStudentContactListByStudentId(studentId)); + } + + // ==================== 子表(学生班主任) ==================== + + @GetMapping("/student-teacher/get-by-student-id") + @Operation(summary = "获得学生班主任") + @Parameter(name = "studentId", description = "学生编号") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { + return success(studentService.getStudentTeacherByStudentId(studentId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO new file mode 100644 index 0000000..b0d4bd2 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生 DO + * + * @author 芋道源码 + */ +@TableName("infra_student") +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper new file mode 100644 index 0000000..34e70a0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 学生 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentMapper extends BaseMapperX { + + default PageResult selectPage(InfraStudentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(InfraStudentDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO new file mode 100644 index 0000000..41a3730 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 学生分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class InfraStudentPageReqVO extends PageParam { + + @Schema(description = "名字", example = "芋头") + private String name; + + @Schema(description = "出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "是否有效", example = "true") + private Boolean enabled; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO new file mode 100644 index 0000000..c41a550 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - 学生 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraStudentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @ExcelProperty("简介") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "性别", converter = DictConvert.class) + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "是否有效", converter = DictConvert.class) + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @ExcelProperty("头像") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @ExcelProperty("附件") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @ExcelProperty("备注") + private String memo; + + @Schema(description = "创建时间") + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO new file mode 100644 index 0000000..faa491d --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; + +@Schema(description = "管理后台 - 学生新增/修改 Request VO") +@Data +public class InfraStudentSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @NotEmpty(message = "简介不能为空") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出生日期不能为空") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "性别不能为空") + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否有效不能为空") + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @NotEmpty(message = "头像不能为空") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @NotEmpty(message = "附件不能为空") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @NotEmpty(message = "备注不能为空") + private String memo; + + @Schema(description = "学生联系人列表") + private List studentContacts; + + @Schema(description = "学生班主任") + private InfraStudentTeacherDO studentTeacher; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService new file mode 100644 index 0000000..afa7d22 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService @@ -0,0 +1,77 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 学生 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraStudentService { + + /** + * 创建学生 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); + + /** + * 更新学生 + * + * @param updateReqVO 更新信息 + */ + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); + + /** + * 删除学生 + * + * @param id 编号 + */ + void deleteStudent(Long id); + + /** + * 获得学生 + * + * @param id 编号 + * @return 学生 + */ + InfraStudentDO getStudent(Long id); + + /** + * 获得学生分页 + * + * @param pageReqVO 分页查询 + * @return 学生分页 + */ + PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); + + // ==================== 子表(学生联系人) ==================== + + /** + * 获得学生联系人列表 + * + * @param studentId 学生编号 + * @return 学生联系人列表 + */ + List getStudentContactListByStudentId(Long studentId); + + // ==================== 子表(学生班主任) ==================== + + /** + * 获得学生班主任 + * + * @param studentId 学生编号 + * @return 学生班主任 + */ + InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl new file mode 100644 index 0000000..c57cba6 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl @@ -0,0 +1,147 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 学生 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraStudentServiceImpl implements InfraStudentService { + + @Resource + private InfraStudentMapper studentMapper; + @Resource + private InfraStudentContactMapper studentContactMapper; + @Resource + private InfraStudentTeacherMapper studentTeacherMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createStudent(InfraStudentSaveReqVO createReqVO) { + // 插入 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); + studentMapper.insert(student); + + // 插入子表 + createStudentContactList(student.getId(), createReqVO.getStudentContacts()); + createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); + // 返回 + return student.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { + // 校验存在 + validateStudentExists(updateReqVO.getId()); + // 更新 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); + studentMapper.updateById(updateObj); + + // 更新子表 + updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); + updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteStudent(Long id) { + // 校验存在 + validateStudentExists(id); + // 删除 + studentMapper.deleteById(id); + + // 删除子表 + deleteStudentContactByStudentId(id); + deleteStudentTeacherByStudentId(id); + } + + private void validateStudentExists(Long id) { + if (studentMapper.selectById(id) == null) { + throw exception(STUDENT_NOT_EXISTS); + } + } + + @Override + public InfraStudentDO getStudent(Long id) { + return studentMapper.selectById(id); + } + + @Override + public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { + return studentMapper.selectPage(pageReqVO); + } + + // ==================== 子表(学生联系人) ==================== + + @Override + public List getStudentContactListByStudentId(Long studentId) { + return studentContactMapper.selectListByStudentId(studentId); + } + + private void createStudentContactList(Long studentId, List list) { + list.forEach(o -> o.setStudentId(studentId)); + studentContactMapper.insertBatch(list); + } + + private void updateStudentContactList(Long studentId, List list) { + deleteStudentContactByStudentId(studentId); + list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新 + createStudentContactList(studentId, list); + } + + private void deleteStudentContactByStudentId(Long studentId) { + studentContactMapper.deleteByStudentId(studentId); + } + + // ==================== 子表(学生班主任) ==================== + + @Override + public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { + return studentTeacherMapper.selectByStudentId(studentId); + } + + private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { + if (studentTeacher == null) { + return; + } + studentTeacher.setStudentId(studentId); + studentTeacherMapper.insert(studentTeacher); + } + + private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { + if (studentTeacher == null) { + return; + } + studentTeacher.setStudentId(studentId); + studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新 + studentTeacherMapper.insertOrUpdate(studentTeacher); + } + + private void deleteStudentTeacherByStudentId(Long studentId) { + studentTeacherMapper.deleteByStudentId(studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest new file mode 100644 index 0000000..b5f4bf0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraStudentServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraStudentServiceImpl.class) +public class InfraStudentServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraStudentServiceImpl studentService; + + @Resource + private InfraStudentMapper studentMapper; + + @Test + public void testCreateStudent_success() { + // 准备参数 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); + + // 调用 + Long studentId = studentService.createStudent(createReqVO); + // 断言 + assertNotNull(studentId); + // 校验记录的属性是否正确 + InfraStudentDO student = studentMapper.selectById(studentId); + assertPojoEquals(createReqVO, student, "id"); + } + + @Test + public void testUpdateStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { + o.setId(dbStudent.getId()); // 设置更新的 ID + }); + + // 调用 + studentService.updateStudent(updateReqVO); + // 校验是否更新正确 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, student); + } + + @Test + public void testUpdateStudent_notExists() { + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); + } + + @Test + public void testDeleteStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbStudent.getId(); + + // 调用 + studentService.deleteStudent(id); + // 校验数据不存在了 + assertNull(studentMapper.selectById(id)); + } + + @Test + public void testDeleteStudent_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetStudentPage() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 + o.setName(null); + o.setBirthday(null); + o.setSex(null); + o.setEnabled(null); + o.setCreateTime(null); + }); + studentMapper.insert(dbStudent); + // 测试 name 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); + // 测试 birthday 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); + // 测试 sex 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); + // 测试 enabled 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); + // 测试 createTime 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); + // 准备参数 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); + reqVO.setName(null); + reqVO.setBirthday(null); + reqVO.setSex(null); + reqVO.setEnabled(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = studentService.getStudentPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbStudent, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO new file mode 100644 index 0000000..c19cf9f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生班主任 DO + * + * @author 芋道源码 + */ +@TableName("infra_student_teacher") +@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentTeacherDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 学生编号 + */ + private Long studentId; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper new file mode 100644 index 0000000..0521bba --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 学生班主任 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentTeacherMapper extends BaseMapperX { + + default InfraStudentTeacherDO selectByStudentId(Long studentId) { + return selectOne(InfraStudentTeacherDO::getStudentId, studentId); + } + + default int deleteByStudentId(Long studentId) { + return delete(InfraStudentTeacherDO::getStudentId, studentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2 new file mode 100644 index 0000000..6c1875f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2 @@ -0,0 +1,17 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_student" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "birthday" varchar NOT NULL, + "sex" int NOT NULL, + "enabled" bit NOT NULL, + "avatar" varchar NOT NULL, + "video" varchar NOT NULL, + "memo" varchar NOT NULL, + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id") +) COMMENT '学生表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql new file mode 100644 index 0000000..83df279 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '学生管理', '', 2, 0, 888, + 'student', '', 'infra/demo/index', 0, 'InfraStudent' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生查询', 'infra:student:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生创建', 'infra:student:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生更新', 'infra:student:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生删除', 'infra:student:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生导出', 'infra:student:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/ts/index new file mode 100644 index 0000000..6112800 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/ts/index @@ -0,0 +1,57 @@ +import request from '@/config/axios' + +export interface StudentVO { + id: number + name: string + description: string + birthday: Date + sex: number + enabled: boolean + avatar: string + video: string + memo: string +} + +// 查询学生分页 +export const getStudentPage = async (params) => { + return await request.get({ url: `/infra/student/page`, params }) +} + +// 查询学生详情 +export const getStudent = async (id: number) => { + return await request.get({ url: `/infra/student/get?id=` + id }) +} + +// 新增学生 +export const createStudent = async (data: StudentVO) => { + return await request.post({ url: `/infra/student/create`, data }) +} + +// 修改学生 +export const updateStudent = async (data: StudentVO) => { + return await request.put({ url: `/infra/student/update`, data }) +} + +// 删除学生 +export const deleteStudent = async (id: number) => { + return await request.delete({ url: `/infra/student/delete?id=` + id }) +} + +// 导出学生 Excel +export const exportStudent = async (params) => { + return await request.download({ url: `/infra/student/export-excel`, params }) +} + +// ==================== 子表(学生联系人) ==================== + +// 获得学生联系人列表 +export const getStudentContactListByStudentId = async (studentId) => { + return await request.get({ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId }) +} + +// ==================== 子表(学生班主任) ==================== + +// 获得学生班主任 +export const getStudentTeacherByStudentId = async (studentId) => { + return await request.get({ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId }) +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm new file mode 100644 index 0000000..55ca994 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm @@ -0,0 +1,174 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm new file mode 100644 index 0000000..d8e7bc3 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm @@ -0,0 +1,184 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm new file mode 100644 index 0000000..b22a480 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm @@ -0,0 +1,122 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/index new file mode 100644 index 0000000..b115b13 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/vue/index @@ -0,0 +1,252 @@ + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper new file mode 100644 index 0000000..155aa5c --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/assert.json new file mode 100644 index 0000000..5a0eebd --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/assert.json @@ -0,0 +1,49 @@ +[ { + "contentPath" : "java/InfraStudentPageReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" +}, { + "contentPath" : "java/InfraStudentRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" +}, { + "contentPath" : "java/InfraStudentSaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" +}, { + "contentPath" : "java/InfraStudentController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" +}, { + "contentPath" : "java/InfraStudentDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" +}, { + "contentPath" : "java/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" +}, { + "contentPath" : "xml/InfraStudentMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" +}, { + "contentPath" : "java/InfraStudentServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" +}, { + "contentPath" : "java/InfraStudentService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" +}, { + "contentPath" : "java/InfraStudentServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" +}, { + "contentPath" : "vue/StudentForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue" +}, { + "contentPath" : "ts/index", + "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" +} ] \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作 new file mode 100644 index 0000000..f8be662 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作 @@ -0,0 +1,3 @@ +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! +// ========== 学生 TODO 补充编号 ========== +ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController new file mode 100644 index 0000000..3796982 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; + +@Tag(name = "管理后台 - 学生") +@RestController +@RequestMapping("/infra/student") +@Validated +public class InfraStudentController { + + @Resource + private InfraStudentService studentService; + + @PostMapping("/create") + @Operation(summary = "创建学生") + @PreAuthorize("@ss.hasPermission('infra:student:create')") + public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { + return success(studentService.createStudent(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新学生") + @PreAuthorize("@ss.hasPermission('infra:student:update')") + public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { + studentService.updateStudent(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除学生") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:student:delete')") + public CommonResult deleteStudent(@RequestParam("id") Long id) { + studentService.deleteStudent(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得学生") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult getStudent(@RequestParam("id") Long id) { + InfraStudentDO student = studentService.getStudent(id); + return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得学生分页") + @PreAuthorize("@ss.hasPermission('infra:student:query')") + public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { + PageResult pageResult = studentService.getStudentPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出学生 Excel") + @PreAuthorize("@ss.hasPermission('infra:student:export')") + @OperateLog(type = EXPORT) + public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = studentService.getStudentPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, + BeanUtils.toBean(list, InfraStudentRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO new file mode 100644 index 0000000..b0d4bd2 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import java.time.LocalDateTime; +import java.time.LocalDateTime; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 学生 DO + * + * @author 芋道源码 + */ +@TableName("infra_student") +@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraStudentDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 简介 + */ + private String description; + /** + * 出生日期 + */ + private LocalDateTime birthday; + /** + * 性别 + * + * 枚举 {@link TODO system_user_sex 对应的类} + */ + private Integer sex; + /** + * 是否有效 + * + * 枚举 {@link TODO infra_boolean_string 对应的类} + */ + private Boolean enabled; + /** + * 头像 + */ + private String avatar; + /** + * 附件 + */ + private String video; + /** + * 备注 + */ + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper new file mode 100644 index 0000000..34e70a0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 学生 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraStudentMapper extends BaseMapperX { + + default PageResult selectPage(InfraStudentPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) + .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) + .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) + .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) + .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(InfraStudentDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO new file mode 100644 index 0000000..41a3730 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 学生分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class InfraStudentPageReqVO extends PageParam { + + @Schema(description = "名字", example = "芋头") + private String name; + + @Schema(description = "出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", example = "1") + private Integer sex; + + @Schema(description = "是否有效", example = "true") + private Boolean enabled; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO new file mode 100644 index 0000000..c41a550 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; +import com.alibaba.excel.annotation.*; +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; + +@Schema(description = "管理后台 - 学生 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraStudentRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @ExcelProperty("简介") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("出生日期") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "性别", converter = DictConvert.class) + @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @ExcelProperty(value = "是否有效", converter = DictConvert.class) + @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @ExcelProperty("头像") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @ExcelProperty("附件") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @ExcelProperty("备注") + private String memo; + + @Schema(description = "创建时间") + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO new file mode 100644 index 0000000..43e7f14 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 学生新增/修改 Request VO") +@Data +public class InfraStudentSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") + @NotEmpty(message = "简介不能为空") + private String description; + + @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出生日期不能为空") + private LocalDateTime birthday; + + @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "性别不能为空") + private Integer sex; + + @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否有效不能为空") + private Boolean enabled; + + @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") + @NotEmpty(message = "头像不能为空") + private String avatar; + + @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") + @NotEmpty(message = "附件不能为空") + private String video; + + @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") + @NotEmpty(message = "备注不能为空") + private String memo; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService new file mode 100644 index 0000000..c4a0e17 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 学生 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraStudentService { + + /** + * 创建学生 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); + + /** + * 更新学生 + * + * @param updateReqVO 更新信息 + */ + void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); + + /** + * 删除学生 + * + * @param id 编号 + */ + void deleteStudent(Long id); + + /** + * 获得学生 + * + * @param id 编号 + * @return 学生 + */ + InfraStudentDO getStudent(Long id); + + /** + * 获得学生分页 + * + * @param pageReqVO 分页查询 + * @return 学生分页 + */ + PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl new file mode 100644 index 0000000..2292a66 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 学生 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraStudentServiceImpl implements InfraStudentService { + + @Resource + private InfraStudentMapper studentMapper; + + @Override + public Long createStudent(InfraStudentSaveReqVO createReqVO) { + // 插入 + InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); + studentMapper.insert(student); + // 返回 + return student.getId(); + } + + @Override + public void updateStudent(InfraStudentSaveReqVO updateReqVO) { + // 校验存在 + validateStudentExists(updateReqVO.getId()); + // 更新 + InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); + studentMapper.updateById(updateObj); + } + + @Override + public void deleteStudent(Long id) { + // 校验存在 + validateStudentExists(id); + // 删除 + studentMapper.deleteById(id); + } + + private void validateStudentExists(Long id) { + if (studentMapper.selectById(id) == null) { + throw exception(STUDENT_NOT_EXISTS); + } + } + + @Override + public InfraStudentDO getStudent(Long id) { + return studentMapper.selectById(id); + } + + @Override + public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { + return studentMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest new file mode 100644 index 0000000..b5f4bf0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraStudentServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraStudentServiceImpl.class) +public class InfraStudentServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraStudentServiceImpl studentService; + + @Resource + private InfraStudentMapper studentMapper; + + @Test + public void testCreateStudent_success() { + // 准备参数 + InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); + + // 调用 + Long studentId = studentService.createStudent(createReqVO); + // 断言 + assertNotNull(studentId); + // 校验记录的属性是否正确 + InfraStudentDO student = studentMapper.selectById(studentId); + assertPojoEquals(createReqVO, student, "id"); + } + + @Test + public void testUpdateStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { + o.setId(dbStudent.getId()); // 设置更新的 ID + }); + + // 调用 + studentService.updateStudent(updateReqVO); + // 校验是否更新正确 + InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, student); + } + + @Test + public void testUpdateStudent_notExists() { + // 准备参数 + InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); + } + + @Test + public void testDeleteStudent_success() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); + studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbStudent.getId(); + + // 调用 + studentService.deleteStudent(id); + // 校验数据不存在了 + assertNull(studentMapper.selectById(id)); + } + + @Test + public void testDeleteStudent_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetStudentPage() { + // mock 数据 + InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 + o.setName(null); + o.setBirthday(null); + o.setSex(null); + o.setEnabled(null); + o.setCreateTime(null); + }); + studentMapper.insert(dbStudent); + // 测试 name 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); + // 测试 birthday 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); + // 测试 sex 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); + // 测试 enabled 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); + // 测试 createTime 不匹配 + studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); + // 准备参数 + InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); + reqVO.setName(null); + reqVO.setBirthday(null); + reqVO.setSex(null); + reqVO.setEnabled(null); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = studentService.getStudentPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbStudent, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/h2 new file mode 100644 index 0000000..6c1875f --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/h2 @@ -0,0 +1,17 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_student" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "birthday" varchar NOT NULL, + "sex" int NOT NULL, + "enabled" bit NOT NULL, + "avatar" varchar NOT NULL, + "video" varchar NOT NULL, + "memo" varchar NOT NULL, + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY ("id") +) COMMENT '学生表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/sql new file mode 100644 index 0000000..83df279 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '学生管理', '', 2, 0, 888, + 'student', '', 'infra/demo/index', 0, 'InfraStudent' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生查询', 'infra:student:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生创建', 'infra:student:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生更新', 'infra:student:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生删除', 'infra:student:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '学生导出', 'infra:student:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/ts/index new file mode 100644 index 0000000..8cdf254 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/ts/index @@ -0,0 +1,43 @@ +import request from '@/config/axios' + +export interface StudentVO { + id: number + name: string + description: string + birthday: Date + sex: number + enabled: boolean + avatar: string + video: string + memo: string +} + +// 查询学生分页 +export const getStudentPage = async (params) => { + return await request.get({ url: `/infra/student/page`, params }) +} + +// 查询学生详情 +export const getStudent = async (id: number) => { + return await request.get({ url: `/infra/student/get?id=` + id }) +} + +// 新增学生 +export const createStudent = async (data: StudentVO) => { + return await request.post({ url: `/infra/student/create`, data }) +} + +// 修改学生 +export const updateStudent = async (data: StudentVO) => { + return await request.put({ url: `/infra/student/update`, data }) +} + +// 删除学生 +export const deleteStudent = async (id: number) => { + return await request.delete({ url: `/infra/student/delete?id=` + id }) +} + +// 导出学生 Excel +export const exportStudent = async (params) => { + return await request.download({ url: `/infra/student/export-excel`, params }) +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm new file mode 100644 index 0000000..0dabcb5 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm @@ -0,0 +1,152 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/index new file mode 100644 index 0000000..b115b13 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/vue/index @@ -0,0 +1,252 @@ + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper new file mode 100644 index 0000000..155aa5c --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/assert.json b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/assert.json new file mode 100644 index 0000000..357df00 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/assert.json @@ -0,0 +1,49 @@ +[ { + "contentPath" : "java/InfraCategoryListReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java" +}, { + "contentPath" : "java/InfraCategoryRespVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java" +}, { + "contentPath" : "java/InfraCategorySaveReqVO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java" +}, { + "contentPath" : "java/InfraCategoryController", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java" +}, { + "contentPath" : "java/InfraCategoryDO", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java" +}, { + "contentPath" : "java/InfraCategoryMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java" +}, { + "contentPath" : "xml/InfraCategoryMapper", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml" +}, { + "contentPath" : "java/InfraCategoryServiceImpl", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java" +}, { + "contentPath" : "java/InfraCategoryService", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java" +}, { + "contentPath" : "java/InfraCategoryServiceImplTest", + "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java" +}, { + "contentPath" : "java/ErrorCodeConstants_手动操作", + "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" +}, { + "contentPath" : "sql/sql", + "filePath" : "sql/sql.sql" +}, { + "contentPath" : "sql/h2", + "filePath" : "sql/h2.sql" +}, { + "contentPath" : "vue/index", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" +}, { + "contentPath" : "vue/CategoryForm", + "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/CategoryForm.vue" +}, { + "contentPath" : "ts/index", + "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" +} ] \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作 new file mode 100644 index 0000000..36df675 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作 @@ -0,0 +1,8 @@ +// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! +// ========== 分类 TODO 补充编号 ========== +ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, "分类不存在"); +ErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子分类,无法删除"); +ErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级分类不存在"); +ErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父分类"); +ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该名字的分类"); +ErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子InfraCategory为父InfraCategory"); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController new file mode 100644 index 0000000..a7b2f81 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController @@ -0,0 +1,94 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo; + +import org.springframework.web.bind.annotation.*; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.security.access.prepost.PreAuthorize; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; + +import javax.validation.constraints.*; +import javax.validation.*; +import javax.servlet.http.*; +import java.util.*; +import java.io.IOException; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; + +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; +import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService; + +@Tag(name = "管理后台 - 分类") +@RestController +@RequestMapping("/infra/category") +@Validated +public class InfraCategoryController { + + @Resource + private InfraCategoryService categoryService; + + @PostMapping("/create") + @Operation(summary = "创建分类") + @PreAuthorize("@ss.hasPermission('infra:category:create')") + public CommonResult createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) { + return success(categoryService.createCategory(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新分类") + @PreAuthorize("@ss.hasPermission('infra:category:update')") + public CommonResult updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) { + categoryService.updateCategory(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除分类") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('infra:category:delete')") + public CommonResult deleteCategory(@RequestParam("id") Long id) { + categoryService.deleteCategory(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得分类") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('infra:category:query')") + public CommonResult getCategory(@RequestParam("id") Long id) { + InfraCategoryDO category = categoryService.getCategory(id); + return success(BeanUtils.toBean(category, InfraCategoryRespVO.class)); + } + + @GetMapping("/list") + @Operation(summary = "获得分类列表") + @PreAuthorize("@ss.hasPermission('infra:category:query')") + public CommonResult> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) { + List list = categoryService.getCategoryList(listReqVO); + return success(BeanUtils.toBean(list, InfraCategoryRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出分类 Excel") + @PreAuthorize("@ss.hasPermission('infra:category:export')") + @OperateLog(type = EXPORT) + public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO, + HttpServletResponse response) throws IOException { + List list = categoryService.getCategoryList(listReqVO); + // 导出 Excel + ExcelUtils.write(response, "分类.xls", "数据", InfraCategoryRespVO.class, + BeanUtils.toBean(list, InfraCategoryRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO new file mode 100644 index 0000000..9bf21c0 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.infra.dal.dataobject.demo; + +import lombok.*; +import java.util.*; +import com.baomidou.mybatisplus.annotation.*; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; + +/** + * 分类 DO + * + * @author 芋道源码 + */ +@TableName("infra_category") +@KeySequence("infra_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class InfraCategoryDO extends BaseDO { + + public static final Long PARENT_ID_ROOT = 0L; + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 名字 + */ + private String name; + /** + * 父编号 + */ + private Long parentId; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO new file mode 100644 index 0000000..e5c6f18 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO @@ -0,0 +1,15 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +@Schema(description = "管理后台 - 分类列表 Request VO") +@Data +public class InfraCategoryListReqVO { + + @Schema(description = "名字", example = "芋头") + private String name; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper new file mode 100644 index 0000000..9dadbf1 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.infra.dal.mysql.demo; + +import java.util.*; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import org.apache.ibatis.annotations.Mapper; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; + +/** + * 分类 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface InfraCategoryMapper extends BaseMapperX { + + default List selectList(InfraCategoryListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(InfraCategoryDO::getName, reqVO.getName()) + .orderByDesc(InfraCategoryDO::getId)); + } + + default InfraCategoryDO selectByParentIdAndName(Long parentId, String name) { + return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name); + } + + default Long selectCountByParentId(Long parentId) { + return selectCount(InfraCategoryDO::getParentId, parentId); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO new file mode 100644 index 0000000..6325d86 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import java.util.*; +import com.alibaba.excel.annotation.*; + +@Schema(description = "管理后台 - 分类 Response VO") +@Data +@ExcelIgnoreUnannotated +public class InfraCategoryRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @ExcelProperty("名字") + private String name; + + @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @ExcelProperty("父编号") + private Long parentId; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO new file mode 100644 index 0000000..3c03b97 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import javax.validation.constraints.*; +import java.util.*; + +@Schema(description = "管理后台 - 分类新增/修改 Request VO") +@Data +public class InfraCategorySaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") + @NotEmpty(message = "名字不能为空") + private String name; + + @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "父编号不能为空") + private Long parentId; + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService new file mode 100644 index 0000000..9d0ae1a --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import java.util.*; +import javax.validation.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +/** + * 分类 Service 接口 + * + * @author 芋道源码 + */ +public interface InfraCategoryService { + + /** + * 创建分类 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCategory(@Valid InfraCategorySaveReqVO createReqVO); + + /** + * 更新分类 + * + * @param updateReqVO 更新信息 + */ + void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO); + + /** + * 删除分类 + * + * @param id 编号 + */ + void deleteCategory(Long id); + + /** + * 获得分类 + * + * @param id 编号 + * @return 分类 + */ + InfraCategoryDO getCategory(Long id); + + /** + * 获得分类列表 + * + * @param listReqVO 查询条件 + * @return 分类列表 + */ + List getCategoryList(InfraCategoryListReqVO listReqVO); + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl new file mode 100644 index 0000000..351568b --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl @@ -0,0 +1,136 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.springframework.stereotype.Service; +import javax.annotation.Resource; +import org.springframework.validation.annotation.Validated; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; + +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; + +/** + * 分类 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class InfraCategoryServiceImpl implements InfraCategoryService { + + @Resource + private InfraCategoryMapper categoryMapper; + + @Override + public Long createCategory(InfraCategorySaveReqVO createReqVO) { + // 校验父编号的有效性 + validateParentCategory(null, createReqVO.getParentId()); + // 校验名字的唯一性 + validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName()); + + // 插入 + InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class); + categoryMapper.insert(category); + // 返回 + return category.getId(); + } + + @Override + public void updateCategory(InfraCategorySaveReqVO updateReqVO) { + // 校验存在 + validateCategoryExists(updateReqVO.getId()); + // 校验父编号的有效性 + validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId()); + // 校验名字的唯一性 + validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); + + // 更新 + InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class); + categoryMapper.updateById(updateObj); + } + + @Override + public void deleteCategory(Long id) { + // 校验存在 + validateCategoryExists(id); + // 校验是否有子分类 + if (categoryMapper.selectCountByParentId(id) > 0) { + throw exception(CATEGORY_EXITS_CHILDREN); + } + // 删除 + categoryMapper.deleteById(id); + } + + private void validateCategoryExists(Long id) { + if (categoryMapper.selectById(id) == null) { + throw exception(CATEGORY_NOT_EXISTS); + } + } + + private void validateParentCategory(Long id, Long parentId) { + if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { + return; + } + // 1. 不能设置自己为父分类 + if (Objects.equals(id, parentId)) { + throw exception(CATEGORY_PARENT_ERROR); + } + // 2. 父分类不存在 + CategoryDO parentCategory = categoryMapper.selectById(parentId); + if (parentCategory == null) { + throw exception(CATEGORY_PARENT_NOT_EXITS); + } + // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路 + if (id == null) { // id 为空,说明新增,不需要考虑环路 + return; + } + for (int i = 0; i < Short.MAX_VALUE; i++) { + // 3.1 校验环路 + parentId = parentCategory.getParentId(); + if (Objects.equals(id, parentId)) { + throw exception(CATEGORY_PARENT_IS_CHILD); + } + // 3.2 继续递归下一级父分类 + if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { + break; + } + parentCategory = categoryMapper.selectById(parentId); + if (parentCategory == null) { + break; + } + } + } + + private void validateCategoryNameUnique(Long id, Long parentId, String name) { + CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name); + if (category == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的分类 + if (id == null) { + throw exception(CATEGORY_NAME_DUPLICATE); + } + if (!Objects.equals(category.getId(), id)) { + throw exception(CATEGORY_NAME_DUPLICATE); + } + } + + @Override + public InfraCategoryDO getCategory(Long id) { + return categoryMapper.selectById(id); + } + + @Override + public List getCategoryList(InfraCategoryListReqVO listReqVO) { + return categoryMapper.selectList(listReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest new file mode 100644 index 0000000..efb70fd --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest @@ -0,0 +1,129 @@ +package cn.iocoder.yudao.module.infra.service.demo; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; + +import javax.annotation.Resource; + +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; + +import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; +import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; +import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; +import cn.iocoder.yudao.framework.common.pojo.PageResult; + +import javax.annotation.Resource; +import org.springframework.context.annotation.Import; +import java.util.*; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link InfraCategoryServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(InfraCategoryServiceImpl.class) +public class InfraCategoryServiceImplTest extends BaseDbUnitTest { + + @Resource + private InfraCategoryServiceImpl categoryService; + + @Resource + private InfraCategoryMapper categoryMapper; + + @Test + public void testCreateCategory_success() { + // 准备参数 + InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null); + + // 调用 + Long categoryId = categoryService.createCategory(createReqVO); + // 断言 + assertNotNull(categoryId); + // 校验记录的属性是否正确 + InfraCategoryDO category = categoryMapper.selectById(categoryId); + assertPojoEquals(createReqVO, category, "id"); + } + + @Test + public void testUpdateCategory_success() { + // mock 数据 + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> { + o.setId(dbCategory.getId()); // 设置更新的 ID + }); + + // 调用 + categoryService.updateCategory(updateReqVO); + // 校验是否更新正确 + InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, category); + } + + @Test + public void testUpdateCategory_notExists() { + // 准备参数 + InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS); + } + + @Test + public void testDeleteCategory_success() { + // mock 数据 + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbCategory.getId(); + + // 调用 + categoryService.deleteCategory(id); + // 校验数据不存在了 + assertNull(categoryMapper.selectById(id)); + } + + @Test + public void testDeleteCategory_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); + } + + @Test + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 + public void testGetCategoryList() { + // mock 数据 + InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到 + o.setName(null); + }); + categoryMapper.insert(dbCategory); + // 测试 name 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null))); + // 准备参数 + InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO(); + reqVO.setName(null); + + // 调用 + List list = categoryService.getCategoryList(reqVO); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbCategory, list.get(0)); + } + +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/h2 b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/h2 new file mode 100644 index 0000000..4141766 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/h2 @@ -0,0 +1,10 @@ +-- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "infra_category" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "description" bigint NOT NULL, + PRIMARY KEY ("id") +) COMMENT '分类表'; + +-- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 +DELETE FROM "infra_category"; \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/sql b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/sql new file mode 100644 index 0000000..8140948 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/sql/sql @@ -0,0 +1,55 @@ +-- 菜单 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status, component_name +) +VALUES ( + '分类管理', '', 2, 0, 888, + 'category', '', 'infra/demo/index', 0, 'InfraCategory' +); + +-- 按钮父菜单ID +-- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类查询', 'infra:category:query', 3, 1, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类创建', 'infra:category:create', 3, 2, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类更新', 'infra:category:update', 3, 3, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类删除', 'infra:category:delete', 3, 4, @parentId, + '', '', '', 0 +); +INSERT INTO system_menu( + name, permission, type, sort, parent_id, + path, icon, component, status +) +VALUES ( + '分类导出', 'infra:category:export', 3, 5, @parentId, + '', '', '', 0 +); \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/ts/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/ts/index new file mode 100644 index 0000000..453c885 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/ts/index @@ -0,0 +1,37 @@ +import request from '@/config/axios' + +export interface CategoryVO { + id: number + name: string + parentId: number +} + +// 查询分类列表 +export const getCategoryList = async (params) => { + return await request.get({ url: `/infra/category/list`, params }) +} + +// 查询分类详情 +export const getCategory = async (id: number) => { + return await request.get({ url: `/infra/category/get?id=` + id }) +} + +// 新增分类 +export const createCategory = async (data: CategoryVO) => { + return await request.post({ url: `/infra/category/create`, data }) +} + +// 修改分类 +export const updateCategory = async (data: CategoryVO) => { + return await request.put({ url: `/infra/category/update`, data }) +} + +// 删除分类 +export const deleteCategory = async (id: number) => { + return await request.delete({ url: `/infra/category/delete?id=` + id }) +} + +// 导出分类 Excel +export const exportCategory = async (params) => { + return await request.download({ url: `/infra/category/export-excel`, params }) +} \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm new file mode 100644 index 0000000..8e139fb --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm @@ -0,0 +1,114 @@ + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/index b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/index new file mode 100644 index 0000000..46902e7 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/vue/index @@ -0,0 +1,185 @@ + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper new file mode 100644 index 0000000..025ac85 --- /dev/null +++ b/yudao-module-infra/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/yudao-module-infra/src/test/resources/logback.xml b/yudao-module-infra/src/test/resources/logback.xml new file mode 100644 index 0000000..daf756b --- /dev/null +++ b/yudao-module-infra/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/yudao-module-infra/src/test/resources/sql/clean.sql b/yudao-module-infra/src/test/resources/sql/clean.sql new file mode 100644 index 0000000..58345ed --- /dev/null +++ b/yudao-module-infra/src/test/resources/sql/clean.sql @@ -0,0 +1,11 @@ +DELETE FROM "infra_config"; +DELETE FROM "infra_file_config"; +DELETE FROM "infra_file"; +DELETE FROM "infra_job"; +DELETE FROM "infra_job_log"; +DELETE FROM "infra_api_access_log"; +DELETE FROM "infra_api_error_log"; +DELETE FROM "infra_file_config"; +DELETE FROM "infra_data_source_config"; +DELETE FROM "infra_codegen_table"; +DELETE FROM "infra_codegen_column"; diff --git a/yudao-module-infra/src/test/resources/sql/create_tables.sql b/yudao-module-infra/src/test/resources/sql/create_tables.sql new file mode 100644 index 0000000..d4b19c9 --- /dev/null +++ b/yudao-module-infra/src/test/resources/sql/create_tables.sql @@ -0,0 +1,216 @@ + +CREATE TABLE IF NOT EXISTS "infra_config" ( + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号', + "category" varchar(50) NOT NULL, + "type" tinyint NOT NULL, + "name" varchar(100) NOT NULL DEFAULT '' COMMENT '名字', + "config_key" varchar(100) NOT NULL DEFAULT '', + "value" varchar(500) NOT NULL DEFAULT '', + "visible" bit NOT NULL, + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '参数配置表'; + +CREATE TABLE IF NOT EXISTS "infra_file_config" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(63) NOT NULL, + "storage" tinyint NOT NULL, + "remark" varchar(255), + "master" bit(1) NOT NULL, + "config" varchar(4096) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '文件配置表'; + +CREATE TABLE IF NOT EXISTS "infra_file" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "config_id" bigint NOT NULL, + "name" varchar(256), + "path" varchar(512), + "url" varchar(1024), + "type" varchar(63) DEFAULT NULL, + "size" bigint NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '文件表'; + +CREATE TABLE IF NOT EXISTS "infra_job" ( + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '任务编号', + "name" varchar(32) NOT NULL COMMENT '任务名称', + "status" tinyint(4) NOT NULL COMMENT '任务状态', + "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字', + "handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数', + "cron_expression" varchar(32) NOT NULL COMMENT 'CRON 表达式', + "retry_count" int(11) NOT NULL DEFAULT '0' COMMENT '重试次数', + "retry_interval" int(11) NOT NULL DEFAULT '0' COMMENT '重试间隔', + "monitor_timeout" int(11) NOT NULL DEFAULT '0' COMMENT '监控超时时间', + "creator" varchar(64) DEFAULT '' COMMENT '创建者', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + "updater" varchar(64) DEFAULT '' COMMENT '更新者', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + "deleted" bit NOT NULL DEFAULT FALSE COMMENT '是否删除', + PRIMARY KEY ("id") +) COMMENT='定时任务表'; + +CREATE TABLE IF NOT EXISTS "infra_job_log" ( + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '日志编号', + "job_id" bigint(20) NOT NULL COMMENT '任务编号', + "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字', + "handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数', + "execute_index" tinyint(4) NOT NULL DEFAULT '1' COMMENT '第几次执行', + "begin_time" datetime NOT NULL COMMENT '开始执行时间', + "end_time" datetime DEFAULT NULL COMMENT '结束执行时间', + "duration" int(11) DEFAULT NULL COMMENT '执行时长', + "status" tinyint(4) NOT NULL COMMENT '任务状态', + "result" varchar(4000) DEFAULT '' COMMENT '结果数据', + "creator" varchar(64) DEFAULT '' COMMENT '创建者', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + "updater" varchar(64) DEFAULT '' COMMENT '更新者', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + "deleted" bit(1) NOT NULL DEFAULT FALSE COMMENT '是否删除', + PRIMARY KEY ("id") +)COMMENT='定时任务日志表'; + +CREATE TABLE IF NOT EXISTS "infra_api_access_log" ( + "id" bigint not null GENERATED BY DEFAULT AS IDENTITY, + "trace_id" varchar(64) not null default '', + "user_id" bigint not null default '0', + "user_type" tinyint not null default '0', + "application_name" varchar(50) not null, + "request_method" varchar(16) not null default '', + "request_url" varchar(255) not null default '', + "request_params" varchar(8000) not null default '', + "response_body" varchar(8000) not null default '', + "user_ip" varchar(50) not null, + "user_agent" varchar(512) not null, + `operate_module` varchar(50) NOT NULL, + `operate_name` varchar(50) NOT NULL, + `operate_type` bigint(4) NOT NULL DEFAULT '0', + "begin_time" timestamp not null, + "end_time" timestamp not null, + "duration" integer not null, + "result_code" integer not null default '0', + "result_msg" varchar(512) default '', + "creator" varchar(64) default '', + "create_time" timestamp not null default current_timestamp, + "updater" varchar(64) default '', + "update_time" timestamp not null default current_timestamp, + "deleted" bit not null default false, + "tenant_id" bigint not null default '0', + primary key ("id") +) COMMENT 'API 访问日志表'; + +CREATE TABLE IF NOT EXISTS "infra_api_error_log" ( + "id" bigint not null GENERATED BY DEFAULT AS IDENTITY, + "trace_id" varchar(64) not null, + "user_id" bigint not null default '0', + "user_type" tinyint not null default '0', + "application_name" varchar(50) not null, + "request_method" varchar(16) not null, + "request_url" varchar(255) not null, + "request_params" varchar(8000) not null, + "user_ip" varchar(50) not null, + "user_agent" varchar(512) not null, + "exception_time" timestamp not null, + "exception_name" varchar(128) not null default '', + "exception_message" clob not null, + "exception_root_cause_message" clob not null, + "exception_stack_trace" clob not null, + "exception_class_name" varchar(512) not null, + "exception_file_name" varchar(512) not null, + "exception_method_name" varchar(512) not null, + "exception_line_number" integer not null, + "process_status" tinyint not null, + "process_time" timestamp default null, + "process_user_id" bigint default '0', + "creator" varchar(64) default '', + "create_time" timestamp not null default current_timestamp, + "updater" varchar(64) default '', + "update_time" timestamp not null default current_timestamp, + "deleted" bit not null default false, + "tenant_id" bigint not null default '0', + primary key ("id") +) COMMENT '系统异常日志'; + +CREATE TABLE IF NOT EXISTS "infra_data_source_config" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(100) NOT NULL, + "url" varchar(1024) NOT NULL, + "username" varchar(255) NOT NULL, + "password" varchar(255) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '数据源配置表'; + +CREATE TABLE IF NOT EXISTS "infra_codegen_table" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "data_source_config_id" bigint not null, + "scene" tinyint not null DEFAULT 1, + "table_name" varchar(200) NOT NULL, + "table_comment" varchar(500) NOT NULL, + "remark" varchar(500) NOT NULL, + "module_name" varchar(30) NOT NULL, + "business_name" varchar(30) NOT NULL, + "class_name" varchar(100) NOT NULL, + "class_comment" varchar(50) NOT NULL, + "author" varchar(50) NOT NULL, + "template_type" tinyint not null DEFAULT 1, + "front_type" tinyint not null, + "parent_menu_id" bigint not null, + "master_table_id" bigint not null, + "sub_join_column_id" bigint not null, + "sub_join_many" bit not null, + "tree_parent_column_id" bigint not null, + "tree_name_column_id" bigint not null, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '代码生成表定义表'; + +CREATE TABLE IF NOT EXISTS "infra_codegen_column" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "table_id" bigint not null, + "column_name" varchar(200) NOT NULL, + "data_type" varchar(100) NOT NULL, + "column_comment" varchar(500) NOT NULL, + "nullable" tinyint not null, + "primary_key" tinyint not null, + "ordinal_position" int not null, + "java_type" varchar(32) NOT NULL, + "java_field" varchar(64) NOT NULL, + "dict_type" varchar(200) NOT NULL, + "example" varchar(64) NOT NULL, + "create_operation" bit not null, + "update_operation" bit not null, + "list_operation" bit not null, + "list_operation_condition" varchar(32) not null, + "list_operation_result" bit not null, + "html_type" varchar(32) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '代码生成表字段定义表'; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-api/pom.xml b/yudao-module-infra/yudao-module-infra-api/pom.xml deleted file mode 100644 index 168ea64..0000000 --- a/yudao-module-infra/yudao-module-infra-api/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - cn.iocoder.boot - yudao-module-infra - ${revision} - - 4.0.0 - yudao-module-infra-api - jar - - ${project.artifactId} - - infra 模块 API,暴露给其它模块调用 - - - - - cn.iocoder.boot - yudao-common - - - - - org.springframework.boot - spring-boot-starter-validation - true - - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/pom.xml b/yudao-module-infra/yudao-module-infra-biz/pom.xml deleted file mode 100644 index e6fba74..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/pom.xml +++ /dev/null @@ -1,126 +0,0 @@ - - - - cn.iocoder.boot - yudao-module-infra - ${revision} - - 4.0.0 - yudao-module-infra-biz - jar - - ${project.artifactId} - - infra 模块,主要提供两块能力: - 1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等 - 2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等 - - - - - cn.iocoder.boot - yudao-module-infra-api - ${revision} - - - - - cn.iocoder.boot - yudao-spring-boot-starter-biz-tenant - - - - - cn.iocoder.boot - yudao-spring-boot-starter-security - - - - cn.iocoder.boot - yudao-spring-boot-starter-websocket - - - - - cn.iocoder.boot - yudao-spring-boot-starter-mybatis - - - com.baomidou - mybatis-plus-generator - - - - cn.iocoder.boot - yudao-spring-boot-starter-redis - - - - - - - cn.iocoder.boot - yudao-spring-boot-starter-job - - - - - cn.iocoder.boot - yudao-spring-boot-starter-mq - - - - - cn.iocoder.boot - yudao-spring-boot-starter-test - test - - - - - - cn.iocoder.boot - yudao-spring-boot-starter-excel - - - - org.apache.velocity - velocity-engine-core - - - - - cn.iocoder.boot - yudao-spring-boot-starter-monitor - - - - de.codecentric - spring-boot-admin-starter-server - - - - - commons-net - commons-net - - - com.jcraft - jsch - - - - software.amazon.awssdk - s3 - - - - org.apache.tika - tika-core - - - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java deleted file mode 100644 index af01680..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/api/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package cn.iocoder.yudao.module.infra.api; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm deleted file mode 100644 index b7f2651..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/api/api.ts.vm +++ /dev/null @@ -1,32 +0,0 @@ -import { defHttp } from '@/utils/http/axios' -#set ($baseURL = "/${table.moduleName}/${simpleClassName_strikeCase}") - -// 查询${table.classComment}列表 -export function get${simpleClassName}Page(params) { - return defHttp.get({ url: '${baseURL}/page', params }) -} - -// 查询${table.classComment}详情 -export function get${simpleClassName}(id: number) { - return defHttp.get({ url: `${baseURL}/get?id=${id}` }) -} - -// 新增${table.classComment} -export function create${simpleClassName}(data) { - return defHttp.post({ url: '${baseURL}/create', data }) -} - -// 修改${table.classComment} -export function update${simpleClassName}(data) { - return defHttp.put({ url: '${baseURL}/update', data }) -} - -// 删除${table.classComment} -export function delete${simpleClassName}(id: number) { - return defHttp.delete({ url: `${baseURL}/delete?id=${id}` }) -} - -// 导出${table.classComment} Excel -export function export${simpleClassName}(params) { - return defHttp.download({ url: '${baseURL}/export-excel', params }, '${table.classComment}.xls') -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/form.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/form.vue.vm deleted file mode 100644 index 1804365..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/form.vue.vm +++ /dev/null @@ -1,58 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm deleted file mode 100644 index 9e59b12..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben/views/index.vue.vm +++ /dev/null @@ -1,92 +0,0 @@ - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm deleted file mode 100644 index c71e0b4..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue3_vben5_antd/schema/views/modules/form_sub_erp.vue.vm +++ /dev/null @@ -1,90 +0,0 @@ -#set ($subTable = $subTables.get($subIndex))##当前表 -#set ($subColumns = $subColumnsList.get($subIndex))##当前字段数组 -#set ($subJoinColumn = $subJoinColumns.get($subIndex))##当前 join 字段 -#set ($subSimpleClassName = $subSimpleClassNames.get($subIndex)) - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java deleted file mode 100644 index 8db47da..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/ftp/FtpFileClientTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.module.infra.framework.file.core.ftp; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.extra.ftp.FtpMode; -import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClient; -import cn.iocoder.yudao.module.infra.framework.file.core.client.ftp.FtpFileClientConfig; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - * {@link FtpFileClient} 集成测试 - * - * @author 芋道源码 - */ -public class FtpFileClientTest { - -// docker run -d \ -// -p 2121:21 -p 30000-30009:30000-30009 \ -// -e FTP_USER=foo \ -// -e FTP_PASS=pass \ -// -e PASV_ADDRESS=127.0.0.1 \ -// -e PASV_MIN_PORT=30000 \ -// -e PASV_MAX_PORT=30009 \ -// -v $(pwd)/ftp-data:/home/vsftpd \ -// fauria/vsftpd - - @Test - @Disabled - public void test() { - // 创建客户端 - FtpFileClientConfig config = new FtpFileClientConfig(); - config.setDomain("http://127.0.0.1:48080"); - config.setBasePath("/home/ftp"); - config.setHost("127.0.0.1"); - config.setPort(2121); - config.setUsername("foo"); - config.setPassword("pass"); - config.setMode(FtpMode.Passive.name()); - FtpFileClient client = new FtpFileClient(0L, config); - client.init(); - // 上传文件 - String path = IdUtil.fastSimpleUUID() + ".jpg"; - byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); - String fullPath = client.upload(content, path, "image/jpeg"); - System.out.println("访问地址:" + fullPath); - if (false) { - byte[] bytes = client.getContent(path); - System.out.println("文件内容:" + bytes); - } - if (false) { - client.delete(path); - } - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java deleted file mode 100644 index 7c622a5..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.infra.framework.file.core.local; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.IdUtil; -import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClient; -import cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -public class LocalFileClientTest { - - @Test - @Disabled - public void test() { - // 创建客户端 - LocalFileClientConfig config = new LocalFileClientConfig(); - config.setDomain("http://127.0.0.1:48080"); - config.setBasePath("/Users/yunai/file_test"); - LocalFileClient client = new LocalFileClient(0L, config); - client.init(); - // 上传文件 - String path = IdUtil.fastSimpleUUID() + ".jpg"; - byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); - String fullPath = client.upload(content, path, "image/jpeg"); - System.out.println("访问地址:" + fullPath); - client.delete(path); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java deleted file mode 100644 index 3c40ce2..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java +++ /dev/null @@ -1,118 +0,0 @@ -package cn.iocoder.yudao.module.infra.framework.file.core.s3; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.IdUtil; -import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; -import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClient; -import cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig; -import jakarta.validation.Validation; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -public class S3FileClientTest { - - @Test - @Disabled // MinIO,如果要集成测试,可以注释本行 - public void testMinIO() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 - config.setAccessKey("admin"); - config.setAccessSecret("password"); - config.setBucket("yudaoyuanma"); - config.setDomain(null); - // 默认 9000 endpoint - config.setEndpoint("http://127.0.0.1:9000"); - - // 执行上传 - testExecuteUpload(config); - } - - @Test - @Disabled // 阿里云 OSS,如果要集成测试,可以注释本行 - public void testAliyun() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 - config.setAccessKey(System.getenv("ALIYUN_ACCESS_KEY")); - config.setAccessSecret(System.getenv("ALIYUN_SECRET_KEY")); - config.setBucket("yunai-aoteman"); - config.setDomain(null); // 如果有自定义域名,则可以设置。http://ali-oss.iocoder.cn - // 默认北京的 endpoint - config.setEndpoint("oss-cn-beijing.aliyuncs.com"); - - // 执行上传 - testExecuteUpload(config); - } - - @Test - @Disabled // 腾讯云 COS,如果要集成测试,可以注释本行 - public void testQCloud() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 - config.setAccessKey(System.getenv("QCLOUD_ACCESS_KEY")); - config.setAccessSecret(System.getenv("QCLOUD_SECRET_KEY")); - config.setBucket("aoteman-1255880240"); - config.setDomain(null); // 如果有自定义域名,则可以设置。http://tengxun-oss.iocoder.cn - // 默认上海的 endpoint - config.setEndpoint("cos.ap-shanghai.myqcloud.com"); - - // 执行上传 - testExecuteUpload(config); - } - - @Test - @Disabled // 七牛云存储,如果要集成测试,可以注释本行 - public void testQiniu() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 -// config.setAccessKey(System.getenv("QINIU_ACCESS_KEY")); -// config.setAccessSecret(System.getenv("QINIU_SECRET_KEY")); - config.setAccessKey("b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8"); - config.setAccessSecret("kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP"); - config.setBucket("ruoyi-vue-pro"); - config.setDomain("http://test.yudao.iocoder.cn"); // 如果有自定义域名,则可以设置。http://static.yudao.iocoder.cn - // 默认上海的 endpoint - config.setEndpoint("s3-cn-south-1.qiniucs.com"); - - // 执行上传 - testExecuteUpload(config); - } - - @Test - @Disabled // 华为云存储,如果要集成测试,可以注释本行 - public void testHuaweiCloud() throws Exception { - S3FileClientConfig config = new S3FileClientConfig(); - // 配置成你自己的 -// config.setAccessKey(System.getenv("HUAWEI_CLOUD_ACCESS_KEY")); -// config.setAccessSecret(System.getenv("HUAWEI_CLOUD_SECRET_KEY")); - config.setBucket("yudao"); - config.setDomain(null); // 如果有自定义域名,则可以设置。 - // 默认上海的 endpoint - config.setEndpoint("obs.cn-east-3.myhuaweicloud.com"); - - // 执行上传 - testExecuteUpload(config); - } - - private void testExecuteUpload(S3FileClientConfig config) throws Exception { - // 校验配置 - ValidationUtils.validate(Validation.buildDefaultValidatorFactory().getValidator(), config); - // 创建 Client - S3FileClient client = new S3FileClient(0L, config); - client.init(); - // 上传文件 - String path = IdUtil.fastSimpleUUID() + ".jpg"; - byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); - String fullPath = client.upload(content, path, "image/jpeg"); - System.out.println("访问地址:" + fullPath); - // 读取文件 - if (true) { - byte[] bytes = client.getContent(path); - System.out.println("文件内容:" + bytes.length); - } - // 删除文件 - if (false) { - client.delete(path); - } - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java deleted file mode 100644 index 53c904e..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/sftp/SftpFileClientTest.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.infra.framework.file.core.sftp; - -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.util.IdUtil; -import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClient; -import cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - * {@link SftpFileClient} 集成测试 - * - * @author 芋道源码 - */ -public class SftpFileClientTest { - -// docker run -p 2222:22 -d \ -// -v $(pwd)/sftp-data:/home/foo/upload \ -// atmoz/sftp \ -// foo:pass:1001 - - @Test - @Disabled - public void test() { - // 创建客户端 - SftpFileClientConfig config = new SftpFileClientConfig(); - config.setDomain("http://127.0.0.1:48080"); - config.setBasePath("/upload"); // 注意,这个是相对路径,不是实际 linux 上的路径!!! - config.setHost("127.0.0.1"); - config.setPort(2222); - config.setUsername("foo"); - config.setPassword("pass"); - SftpFileClient client = new SftpFileClient(0L, config); - client.init(); - // 上传文件 - String path = IdUtil.fastSimpleUUID() + ".jpg"; - byte[] content = ResourceUtil.readBytes("file/erweima.jpg"); - String fullPath = client.upload(content, path, "image/jpeg"); - System.out.println("访问地址:" + fullPath); - if (false) { - byte[] bytes = client.getContent(path); - System.out.println("文件内容:" + bytes); - } - if (false) { - client.delete(path); - } - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java deleted file mode 100644 index a6de8d9..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/DefaultDatabaseQueryTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.infra.service; - -import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.generator.query.DefaultQuery; -import com.baomidou.mybatisplus.generator.config.DataSourceConfig; -import com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder; -import com.baomidou.mybatisplus.generator.config.po.TableInfo; - -import java.util.List; - -public class DefaultDatabaseQueryTest { - - public static void main(String[] args) { -// DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:oracle:thin:@127.0.0.1:1521:xe", -// "root", "123456").build(); - DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder("jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro", - "root", "123456").build(); -// StrategyConfig strategyConfig = new StrategyConfig.Builder().build(); - - ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, null, null, null, null); - - DefaultQuery query = new DefaultQuery(builder); - - long time = System.currentTimeMillis(); - List tableInfos = query.queryTables(); - for (TableInfo tableInfo : tableInfos) { - if (StrUtil.startWithAny(tableInfo.getName().toLowerCase(), "act_", "flw_", "qrtz_")) { - continue; - } - System.out.println(String.format("CREATE SEQUENCE %s_seq MINVALUE 1;", tableInfo.getName())); -// System.out.println(String.format("DELETE FROM %s WHERE deleted = '1';", tableInfo.getName())); - } - System.out.println(tableInfos.size()); - System.out.println(System.currentTimeMillis() - time); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java deleted file mode 100644 index 7a26ea9..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenBuilderTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.codegen.inner; - -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; -import com.baomidou.mybatisplus.generator.config.po.TableField; -import com.baomidou.mybatisplus.generator.config.po.TableInfo; -import com.baomidou.mybatisplus.generator.config.rules.IColumnType; -import org.apache.ibatis.type.JdbcType; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; - -import java.util.Collections; -import java.util.List; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class CodegenBuilderTest extends BaseMockitoUnitTest { - - @InjectMocks - private CodegenBuilder codegenBuilder; - - @Test - public void testBuildTable() { - // 准备参数 - TableInfo tableInfo = mock(TableInfo.class); - // mock 方法 - when(tableInfo.getName()).thenReturn("system_user"); - when(tableInfo.getComment()).thenReturn("用户"); - - // 调用 - CodegenTableDO table = codegenBuilder.buildTable(tableInfo); - // 断言 - assertEquals("system_user", table.getTableName()); - assertEquals("用户", table.getTableComment()); - assertEquals("system", table.getModuleName()); - assertEquals("user", table.getBusinessName()); - assertEquals("User", table.getClassName()); - assertEquals("用户", table.getClassComment()); - } - - @Test - public void testBuildColumns() { - // 准备参数 - Long tableId = randomLongId(); - TableField tableField = mock(TableField.class); - List tableFields = Collections.singletonList(tableField); - // mock 方法 - TableField.MetaInfo metaInfo = mock(TableField.MetaInfo.class); - when(tableField.getMetaInfo()).thenReturn(metaInfo); - when(metaInfo.getJdbcType()).thenReturn(JdbcType.BIGINT); - when(tableField.getComment()).thenReturn("编号"); - when(tableField.isKeyFlag()).thenReturn(true); - IColumnType columnType = mock(IColumnType.class); - when(tableField.getColumnType()).thenReturn(columnType); - when(columnType.getType()).thenReturn("Long"); - when(tableField.getName()).thenReturn("id2"); - when(tableField.getPropertyName()).thenReturn("id"); - - // 调用 - List columns = codegenBuilder.buildColumns(tableId, tableFields); - // 断言 - assertEquals(1, columns.size()); - CodegenColumnDO column = columns.get(0); - assertEquals(tableId, column.getTableId()); - assertEquals("id2", column.getColumnName()); - assertEquals("BIGINT", column.getDataType()); - assertEquals("编号", column.getColumnComment()); - assertFalse(column.getNullable()); - assertTrue(column.getPrimaryKey()); - assertEquals(1, column.getOrdinalPosition()); - assertEquals("Long", column.getJavaType()); - assertEquals("id", column.getJavaField()); - assertNull(column.getDictType()); - assertNotNull(column.getExample()); - assertFalse(column.getCreateOperation()); - assertTrue(column.getUpdateOperation()); - assertFalse(column.getListOperation()); - assertEquals("=", column.getListOperationCondition()); - assertTrue(column.getListOperationResult()); - assertEquals("input", column.getHtmlType()); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java deleted file mode 100644 index 3c7390a..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java +++ /dev/null @@ -1,138 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.codegen.inner; - -import cn.hutool.core.io.FileUtil; -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.io.resource.ResourceUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.ZipUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; -import cn.iocoder.yudao.module.infra.framework.codegen.config.CodegenProperties; -import org.junit.jupiter.api.BeforeEach; -import org.mockito.InjectMocks; -import org.mockito.Spy; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * {@link CodegenEngine} 的单元测试抽象基类 - * - * @author 芋道源码 - */ -public abstract class CodegenEngineAbstractTest extends BaseMockitoUnitTest { - - /** - * 测试文件资源目录 - */ - private String resourcesPath = ""; - - @InjectMocks - protected CodegenEngine codegenEngine; - - @Spy - protected CodegenProperties codegenProperties = new CodegenProperties() - .setBasePackage("cn.iocoder.yudao"); - - @BeforeEach - public void setUp() { - codegenEngine.setJakartaEnable(true); // 强制使用 jakarta,保证单测可以基于 jakarta 断言 - codegenEngine.initGlobalBindingMap(); - // 单测强制使用 - // 获取测试文件 resources 路径 - String absolutePath = FileUtil.getAbsolutePath("application-unit-test.yaml"); - // 系统不一样生成的文件也有差异,那就各自生成各自的 - resourcesPath = absolutePath.split("/target")[0] + "/src/test/resources/codegen/"; - } - - protected static CodegenTableDO getTable(String name) { - String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json"); - return JsonUtils.parseObject(content, "table", CodegenTableDO.class); - } - - protected static List getColumnList(String name) { - String content = ResourceUtil.readUtf8Str("codegen/table/" + name + ".json"); - List list = JsonUtils.parseArray(content, "columns", CodegenColumnDO.class); - list.forEach(column -> { - if (column.getNullable() == null) { - column.setNullable(false); - } - if (column.getCreateOperation() == null) { - column.setCreateOperation(false); - } - if (column.getUpdateOperation() == null) { - column.setUpdateOperation(false); - } - if (column.getListOperation() == null) { - column.setListOperation(false); - } - if (column.getListOperationResult() == null) { - column.setListOperationResult(false); - } - }); - return list; - } - - @SuppressWarnings("rawtypes") - protected static void assertResult(Map result, String path) { - String assertContent = ResourceUtil.readUtf8Str("codegen/" + path + "/assert.json"); - List asserts = JsonUtils.parseArray(assertContent, HashMap.class); - assertEquals(asserts.size(), result.size()); - // 校验每个文件 - asserts.forEach(assertMap -> { - String contentPath = (String) assertMap.get("contentPath"); - String filePath = (String) assertMap.get("filePath"); - String content = ResourceUtil.readUtf8Str("codegen/" + path + "/" + contentPath); - assertEquals(content, result.get(filePath), filePath + ":不匹配"); - }); - } - - // ==================== 调试专用 ==================== - - /** - * 【调试使用】将生成的代码,写入到文件 - * - * @param result 生成的代码 - * @param path 写入文件的路径 - */ - protected void writeFile(Map result, String path) { - // 生成压缩包 - String[] paths = result.keySet().toArray(new String[0]); - ByteArrayInputStream[] ins = result.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - ZipUtil.zip(outputStream, paths, ins); - // 写入文件 - FileUtil.writeBytes(outputStream.toByteArray(), path); - } - - /** - * 【调试使用】将生成的结果,写入到文件 - * - * @param result 生成的代码 - * @param basePath 写入文件的路径(绝对路径) - */ - protected void writeResult(Map result, String basePath) { - // 写入文件内容 - List> asserts = new ArrayList<>(); - result.forEach((filePath, fileContent) -> { - String lastFilePath = StrUtil.subAfter(filePath, '/', true); - String contentPath = StrUtil.subAfter(lastFilePath, '.', true) - + '/' + StrUtil.subBefore(lastFilePath, '.', true); - asserts.add(MapUtil.builder().put("filePath", filePath) - .put("contentPath", contentPath).build()); - FileUtil.writeUtf8String(fileContent, basePath + "/" + contentPath); - }); - // 写入 assert.json 文件 - FileUtil.writeUtf8String(JsonUtils.toJsonPrettyString(asserts), basePath + "/assert.json"); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java deleted file mode 100644 index a251484..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue2Test.java +++ /dev/null @@ -1,100 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.codegen.inner; - -import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; -import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; -import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** - * {@link CodegenEngine} 的 Vue2 + Element UI 单元测试 - * - * @author 芋道源码 - */ -@Disabled -public class CodegenEngineVue2Test extends CodegenEngineAbstractTest { - - @Test - public void testExecute_vue2_one() { - // 准备参数 - CodegenTableDO table = getTable("student") - .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) - .setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); - List columns = getColumnList("student"); - - // 调用 - Map result = codegenEngine.execute(table, columns, null, null); - // 生成测试文件 - //writeResult(result, resourcesPath + "/vue2_one"); - // 断言 - assertResult(result, "/vue2_one"); - } - - @Test - public void testExecute_vue2_tree() { - // 准备参数 - CodegenTableDO table = getTable("category") - .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) - .setTemplateType(CodegenTemplateTypeEnum.TREE.getType()); - List columns = getColumnList("category"); - - // 调用 - Map result = codegenEngine.execute(table, columns, null, null); - // 生成测试文件 - //writeResult(result, resourcesPath + "/vue2_tree"); - // 断言 - assertResult(result, "/vue2_tree"); -// writeFile(result, "/Users/yunai/test/demo66.zip"); - } - - @Test - public void testExecute_vue2_master_normal() { - testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vue2_master_normal"); - } - - @Test - public void testExecute_vue2_master_erp() { - testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vue2_master_erp"); - } - - @Test - public void testExecute_vue2_master_inner() { - testExecute_vue2_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vue2_master_inner"); - } - - private void testExecute_vue2_master(CodegenTemplateTypeEnum templateType, - String path) { - // 准备参数 - CodegenTableDO table = getTable("student") - .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) - .setTemplateType(templateType.getType()); - List columns = getColumnList("student"); - // 准备参数(子表) - CodegenTableDO contactTable = getTable("contact") - .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) - .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) - .setSubJoinColumnId(100L).setSubJoinMany(true); - List contactColumns = getColumnList("contact"); - // 准备参数(班主任) - CodegenTableDO teacherTable = getTable("teacher") - .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) - .setFrontType(CodegenFrontTypeEnum.VUE2_ELEMENT_UI.getType()) - .setSubJoinColumnId(200L).setSubJoinMany(false); - List teacherColumns = getColumnList("teacher"); - - // 调用 - Map result = codegenEngine.execute(table, columns, - Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns)); - // 生成测试文件 - //writeResult(result, resourcesPath + path); - // 断言 - assertResult(result, path); -// writeFile(result, "/Users/yunai/test/demo11.zip"); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue3Test.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue3Test.java deleted file mode 100644 index 7695dac..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineVue3Test.java +++ /dev/null @@ -1,100 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.codegen.inner; - -import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenColumnDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.codegen.CodegenTableDO; -import cn.iocoder.yudao.module.infra.enums.codegen.CodegenFrontTypeEnum; -import cn.iocoder.yudao.module.infra.enums.codegen.CodegenTemplateTypeEnum; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** - * {@link CodegenEngine} 的 Vue2 + Element Plus 单元测试 - * - * @author 芋道源码 - */ -@Disabled -public class CodegenEngineVue3Test extends CodegenEngineAbstractTest { - - @Test - public void testExecute_vue3_one() { - // 准备参数 - CodegenTableDO table = getTable("student") - .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) - .setTemplateType(CodegenTemplateTypeEnum.ONE.getType()); - List columns = getColumnList("student"); - - // 调用 - Map result = codegenEngine.execute(table, columns, null, null); - // 生成测试文件 - //writeResult(result, resourcesPath + "/vue3_one"); - // 断言 - assertResult(result, "/vue3_one"); - } - - @Test - public void testExecute_vue3_tree() { - // 准备参数 - CodegenTableDO table = getTable("category") - .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) - .setTemplateType(CodegenTemplateTypeEnum.TREE.getType()); - List columns = getColumnList("category"); - - // 调用 - Map result = codegenEngine.execute(table, columns, null, null); - // 生成测试文件 - //writeResult(result, resourcesPath + "/vue3_tree"); - // 断言 - assertResult(result, "/vue3_tree"); -// writeFile(result, "/Users/yunai/test/demo66.zip"); - } - - @Test - public void testExecute_vue3_master_normal() { - testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_NORMAL, "/vue3_master_normal"); - } - - @Test - public void testExecute_vue3_master_erp() { - testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_ERP, "/vue3_master_erp"); - } - - @Test - public void testExecute_vue3_master_inner() { - testExecute_vue3_master(CodegenTemplateTypeEnum.MASTER_INNER, "/vue3_master_inner"); - } - - private void testExecute_vue3_master(CodegenTemplateTypeEnum templateType, - String path) { - // 准备参数 - CodegenTableDO table = getTable("student") - .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) - .setTemplateType(templateType.getType()); - List columns = getColumnList("student"); - // 准备参数(子表) - CodegenTableDO contactTable = getTable("contact") - .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) - .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) - .setSubJoinColumnId(100L).setSubJoinMany(true); - List contactColumns = getColumnList("contact"); - // 准备参数(班主任) - CodegenTableDO teacherTable = getTable("teacher") - .setTemplateType(CodegenTemplateTypeEnum.SUB.getType()) - .setFrontType(CodegenFrontTypeEnum.VUE3_ELEMENT_PLUS.getType()) - .setSubJoinColumnId(200L).setSubJoinMany(false); - List teacherColumns = getColumnList("teacher"); - - // 调用 - Map result = codegenEngine.execute(table, columns, - Arrays.asList(contactTable, teacherTable), Arrays.asList(contactColumns, teacherColumns)); - // 生成测试文件 - //writeResult(result, resourcesPath + path); - // 断言 - assertResult(result, path); - // writeFile(result, "/Users/yunai/test/demo11.zip"); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImplTest.java deleted file mode 100644 index e7a5072..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImplTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.db; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.infra.dal.dataobject.db.DataSourceConfigDO; -import com.baomidou.mybatisplus.generator.config.po.TableField; -import com.baomidou.mybatisplus.generator.config.po.TableInfo; -import com.baomidou.mybatisplus.generator.config.rules.DbColumnType; -import org.apache.ibatis.type.JdbcType; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -@Import(DatabaseTableServiceImpl.class) -public class DatabaseTableServiceImplTest extends BaseDbUnitTest { - - @Resource - private DatabaseTableServiceImpl databaseTableService; - - @MockBean - private DataSourceConfigService dataSourceConfigService; - - @Test - public void testGetTableList() { - // 准备参数 - Long dataSourceConfigId = randomLongId(); - // mock 方法 - DataSourceConfigDO dataSourceConfig = new DataSourceConfigDO().setUsername("sa").setPassword("") - .setUrl("jdbc:h2:mem:testdb"); - when(dataSourceConfigService.getDataSourceConfig(eq(dataSourceConfigId))) - .thenReturn(dataSourceConfig); - - // 调用 - List tables = databaseTableService.getTableList(dataSourceConfigId, - "config", "参数"); - // 断言 - assertEquals(1, tables.size()); - assertTableInfo(tables.get(0)); - } - - @Test - public void testGetTable() { - // 准备参数 - Long dataSourceConfigId = randomLongId(); - // mock 方法 - DataSourceConfigDO dataSourceConfig = new DataSourceConfigDO().setUsername("sa").setPassword("") - .setUrl("jdbc:h2:mem:testdb"); - when(dataSourceConfigService.getDataSourceConfig(eq(dataSourceConfigId))) - .thenReturn(dataSourceConfig); - - // 调用 - TableInfo tableInfo = databaseTableService.getTable(dataSourceConfigId, "infra_config"); - // 断言 - assertTableInfo(tableInfo); - } - - private void assertTableInfo(TableInfo tableInfo) { - assertEquals("infra_config", tableInfo.getName()); - assertEquals("参数配置表", tableInfo.getComment()); - assertEquals(13, tableInfo.getFields().size()); - // id 字段 - TableField idField = tableInfo.getFields().get(0); - assertEquals("id", idField.getName()); - assertEquals(JdbcType.BIGINT, idField.getMetaInfo().getJdbcType()); - assertEquals("编号", idField.getComment()); - assertFalse(idField.getMetaInfo().isNullable()); - assertTrue(idField.isKeyFlag()); - assertTrue(idField.isKeyIdentityFlag()); - assertEquals(DbColumnType.LONG, idField.getColumnType()); - assertEquals("id", idField.getPropertyName()); - // name 字段 - TableField nameField = tableInfo.getFields().get(3); - assertEquals("name", nameField.getName()); - assertEquals(JdbcType.VARCHAR, nameField.getMetaInfo().getJdbcType()); - assertEquals("名字", nameField.getComment()); - assertFalse(nameField.getMetaInfo().isNullable()); - assertFalse(nameField.isKeyFlag()); - assertFalse(nameField.isKeyIdentityFlag()); - assertEquals(DbColumnType.STRING, nameField.getColumnType()); - assertEquals("name", nameField.getPropertyName()); - } -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImplTest.java deleted file mode 100644 index a6bd3c8..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/job/JobLogServiceImplTest.java +++ /dev/null @@ -1,172 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.job; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.infra.controller.admin.job.vo.log.JobLogPageReqVO; -import cn.iocoder.yudao.module.infra.dal.dataobject.job.JobLogDO; -import cn.iocoder.yudao.module.infra.dal.mysql.job.JobLogMapper; -import cn.iocoder.yudao.module.infra.enums.job.JobLogStatusEnum; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.time.Duration; -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@Import(JobLogServiceImpl.class) -public class JobLogServiceImplTest extends BaseDbUnitTest { - - @Resource - private JobLogServiceImpl jobLogService; - @Resource - private JobLogMapper jobLogMapper; - - @Test - public void testCreateJobLog() { - // 准备参数 - JobLogDO reqVO = randomPojo(JobLogDO.class, o -> o.setExecuteIndex(1)); - - // 调用 - Long id = jobLogService.createJobLog(reqVO.getJobId(), reqVO.getBeginTime(), - reqVO.getHandlerName(), reqVO.getHandlerParam(), reqVO.getExecuteIndex()); - // 断言 - assertNotNull(id); - // 校验记录的属性是否正确 - JobLogDO job = jobLogMapper.selectById(id); - assertEquals(JobLogStatusEnum.RUNNING.getStatus(), job.getStatus()); - } - - @Test - public void testUpdateJobLogResultAsync_success() { - // mock 数据 - JobLogDO log = randomPojo(JobLogDO.class, o -> { - o.setExecuteIndex(1); - o.setStatus(JobLogStatusEnum.RUNNING.getStatus()); - }); - jobLogMapper.insert(log); - // 准备参数 - Long logId = log.getId(); - LocalDateTime endTime = randomLocalDateTime(); - Integer duration = randomInteger(); - boolean success = true; - String result = randomString(); - - // 调用 - jobLogService.updateJobLogResultAsync(logId, endTime, duration, success, result); - // 校验记录的属性是否正确 - JobLogDO dbLog = jobLogMapper.selectById(log.getId()); - assertEquals(endTime, dbLog.getEndTime()); - assertEquals(duration, dbLog.getDuration()); - assertEquals(JobLogStatusEnum.SUCCESS.getStatus(), dbLog.getStatus()); - assertEquals(result, dbLog.getResult()); - } - - @Test - public void testUpdateJobLogResultAsync_failure() { - // mock 数据 - JobLogDO log = randomPojo(JobLogDO.class, o -> { - o.setExecuteIndex(1); - o.setStatus(JobLogStatusEnum.RUNNING.getStatus()); - }); - jobLogMapper.insert(log); - // 准备参数 - Long logId = log.getId(); - LocalDateTime endTime = randomLocalDateTime(); - Integer duration = randomInteger(); - boolean success = false; - String result = randomString(); - - // 调用 - jobLogService.updateJobLogResultAsync(logId, endTime, duration, success, result); - // 校验记录的属性是否正确 - JobLogDO dbLog = jobLogMapper.selectById(log.getId()); - assertEquals(endTime, dbLog.getEndTime()); - assertEquals(duration, dbLog.getDuration()); - assertEquals(JobLogStatusEnum.FAILURE.getStatus(), dbLog.getStatus()); - assertEquals(result, dbLog.getResult()); - } - - @Test - public void testCleanJobLog() { - // mock 数据 - JobLogDO log01 = randomPojo(JobLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3)))) - .setExecuteIndex(1); - jobLogMapper.insert(log01); - JobLogDO log02 = randomPojo(JobLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1)))) - .setExecuteIndex(1); - jobLogMapper.insert(log02); - // 准备参数 - Integer exceedDay = 2; - Integer deleteLimit = 1; - - // 调用 - Integer count = jobLogService.cleanJobLog(exceedDay, deleteLimit); - // 断言 - assertEquals(1, count); - List logs = jobLogMapper.selectList(); - assertEquals(1, logs.size()); - // TODO @芋艿:createTime updateTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(log02, logs.get(0), "createTime", "updateTime"); - } - - @Test - public void testGetJobLog() { - // mock 数据 - JobLogDO dbJobLog = randomPojo(JobLogDO.class, o -> o.setExecuteIndex(1)); - jobLogMapper.insert(dbJobLog); - // 准备参数 - Long id = dbJobLog.getId(); - - // 调用 - JobLogDO jobLog = jobLogService.getJobLog(id); - // 断言 - assertPojoEquals(dbJobLog, jobLog); - } - - @Test - public void testGetJobPage() { - // mock 数据 - JobLogDO dbJobLog = randomPojo(JobLogDO.class, o -> { - o.setExecuteIndex(1); - o.setHandlerName("handlerName 单元测试"); - o.setStatus(JobLogStatusEnum.SUCCESS.getStatus()); - o.setBeginTime(buildTime(2021, 1, 8)); - o.setEndTime(buildTime(2021, 1, 8)); - }); - jobLogMapper.insert(dbJobLog); - // 测试 jobId 不匹配 - jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setJobId(randomLongId()))); - // 测试 handlerName 不匹配 - jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setHandlerName(randomString()))); - // 测试 beginTime 不匹配 - jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setBeginTime(buildTime(2021, 1, 7)))); - // 测试 endTime 不匹配 - jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setEndTime(buildTime(2021, 1, 9)))); - // 测试 status 不匹配 - jobLogMapper.insert(cloneIgnoreId(dbJobLog, o -> o.setStatus(JobLogStatusEnum.FAILURE.getStatus()))); - // 准备参数 - JobLogPageReqVO reqVo = new JobLogPageReqVO(); - reqVo.setJobId(dbJobLog.getJobId()); - reqVo.setHandlerName("单元"); - reqVo.setBeginTime(dbJobLog.getBeginTime()); - reqVo.setEndTime(dbJobLog.getEndTime()); - reqVo.setStatus(JobLogStatusEnum.SUCCESS.getStatus()); - - // 调用 - PageResult pageResult = jobLogService.getJobLogPage(reqVo); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbJobLog, pageResult.getList().get(0)); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImplTest.java deleted file mode 100644 index b8da238..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImplTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.logger; - -import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiAccessLogCreateReqDTO; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO; -import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiAccessLogDO; -import cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiAccessLogMapper; -import jakarta.annotation.Resource; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import java.time.Duration; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Import(ApiAccessLogServiceImpl.class) -public class ApiAccessLogServiceImplTest extends BaseDbUnitTest { - - @Resource - private ApiAccessLogServiceImpl apiAccessLogService; - - @Resource - private ApiAccessLogMapper apiAccessLogMapper; - - @Test - public void testGetApiAccessLogPage() { - ApiAccessLogDO apiAccessLogDO = randomPojo(ApiAccessLogDO.class, o -> { - o.setUserId(2233L); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setApplicationName("yudao-test"); - o.setRequestUrl("foo"); - o.setBeginTime(buildTime(2021, 3, 13)); - o.setDuration(1000); - o.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()); - }); - apiAccessLogMapper.insert(apiAccessLogDO); - // 测试 userId 不匹配 - apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setUserId(3344L))); - // 测试 userType 不匹配 - apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 applicationName 不匹配 - apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setApplicationName("test"))); - // 测试 requestUrl 不匹配 - apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setRequestUrl("bar"))); - // 测试 beginTime 不匹配:构造一个早期时间 2021-02-06 00:00:00 - apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setBeginTime(buildTime(2021, 2, 6)))); - // 测试 duration 不匹配 - apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setDuration(100))); - // 测试 resultCode 不匹配 - apiAccessLogMapper.insert(cloneIgnoreId(apiAccessLogDO, o -> o.setResultCode(2))); - // 准备参数 - ApiAccessLogPageReqVO reqVO = new ApiAccessLogPageReqVO(); - reqVO.setUserId(2233L); - reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); - reqVO.setApplicationName("yudao-test"); - reqVO.setRequestUrl("foo"); - reqVO.setBeginTime(buildBetweenTime(2021, 3, 13, 2021, 3, 13)); - reqVO.setDuration(1000); - reqVO.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()); - - // 调用 - PageResult pageResult = apiAccessLogService.getApiAccessLogPage(reqVO); - // 断言,只查到了一条符合条件的 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(apiAccessLogDO, pageResult.getList().get(0)); - } - - @Test - public void testCleanJobLog() { - // mock 数据 - ApiAccessLogDO log01 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3)))); - apiAccessLogMapper.insert(log01); - ApiAccessLogDO log02 = randomPojo(ApiAccessLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1)))); - apiAccessLogMapper.insert(log02); - // 准备参数 - Integer exceedDay = 2; - Integer deleteLimit = 1; - - // 调用 - Integer count = apiAccessLogService.cleanAccessLog(exceedDay, deleteLimit); - // 断言 - assertEquals(1, count); - List logs = apiAccessLogMapper.selectList(); - assertEquals(1, logs.size()); - // TODO @芋艿:createTime updateTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(log02, logs.get(0), "createTime", "updateTime"); - } - - @Test - public void testCreateApiAccessLog() { - // 准备参数 - ApiAccessLogCreateReqDTO createDTO = randomPojo(ApiAccessLogCreateReqDTO.class); - - // 调用 - apiAccessLogService.createApiAccessLog(createDTO); - // 断言 - ApiAccessLogDO apiAccessLogDO = apiAccessLogMapper.selectOne(null); - assertPojoEquals(createDTO, apiAccessLogDO); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImplTest.java b/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImplTest.java deleted file mode 100644 index d4854e7..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImplTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.logger; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO; -import cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO; -import cn.iocoder.yudao.module.infra.dal.dataobject.logger.ApiErrorLogDO; -import cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper; -import cn.iocoder.yudao.module.infra.enums.logger.ApiErrorLogProcessStatusEnum; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.time.Duration; -import java.util.List; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_NOT_FOUND; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.API_ERROR_LOG_PROCESSED; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@Import(ApiErrorLogServiceImpl.class) -public class ApiErrorLogServiceImplTest extends BaseDbUnitTest { - - @Resource - private ApiErrorLogServiceImpl apiErrorLogService; - - @Resource - private ApiErrorLogMapper apiErrorLogMapper; - - @Test - public void testGetApiErrorLogPage() { - // mock 数据 - ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, o -> { - o.setUserId(2233L); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setApplicationName("yudao-test"); - o.setRequestUrl("foo"); - o.setExceptionTime(buildTime(2021, 3, 13)); - o.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); - }); - apiErrorLogMapper.insert(apiErrorLogDO); - // 测试 userId 不匹配 - apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setUserId(3344L))); - // 测试 userType 不匹配 - apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 applicationName 不匹配 - apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setApplicationName("test"))); - // 测试 requestUrl 不匹配 - apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setRequestUrl("bar"))); - // 测试 exceptionTime 不匹配:构造一个早期时间 2021-02-06 00:00:00 - apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, o -> o.setExceptionTime(buildTime(2021, 2, 6)))); - // 测试 progressStatus 不匹配 - apiErrorLogMapper.insert(cloneIgnoreId(apiErrorLogDO, logDO -> logDO.setProcessStatus(ApiErrorLogProcessStatusEnum.DONE.getStatus()))); - // 准备参数 - ApiErrorLogPageReqVO reqVO = new ApiErrorLogPageReqVO(); - reqVO.setUserId(2233L); - reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); - reqVO.setApplicationName("yudao-test"); - reqVO.setRequestUrl("foo"); - reqVO.setExceptionTime(buildBetweenTime(2021, 3, 1, 2021, 3, 31)); - reqVO.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); - - // 调用 - PageResult pageResult = apiErrorLogService.getApiErrorLogPage(reqVO); - // 断言,只查到了一条符合条件的 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(apiErrorLogDO, pageResult.getList().get(0)); - } - - @Test - public void testCreateApiErrorLog() { - // 准备参数 - ApiErrorLogCreateReqDTO createDTO = randomPojo(ApiErrorLogCreateReqDTO.class); - - // 调用 - apiErrorLogService.createApiErrorLog(createDTO); - // 断言 - ApiErrorLogDO apiErrorLogDO = apiErrorLogMapper.selectOne(null); - assertPojoEquals(createDTO, apiErrorLogDO); - assertEquals(ApiErrorLogProcessStatusEnum.INIT.getStatus(), apiErrorLogDO.getProcessStatus()); - } - - @Test - public void testUpdateApiErrorLogProcess_success() { - // 准备参数 - ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, - o -> o.setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus())); - apiErrorLogMapper.insert(apiErrorLogDO); - // 准备参数 - Long id = apiErrorLogDO.getId(); - Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus(); - Long processUserId = randomLongId(); - - // 调用 - apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId); - // 断言 - ApiErrorLogDO dbApiErrorLogDO = apiErrorLogMapper.selectById(apiErrorLogDO.getId()); - assertEquals(processStatus, dbApiErrorLogDO.getProcessStatus()); - assertEquals(processUserId, dbApiErrorLogDO.getProcessUserId()); - assertNotNull(dbApiErrorLogDO.getProcessTime()); - } - - @Test - public void testUpdateApiErrorLogProcess_processed() { - // 准备参数 - ApiErrorLogDO apiErrorLogDO = randomPojo(ApiErrorLogDO.class, - o -> o.setProcessStatus(ApiErrorLogProcessStatusEnum.DONE.getStatus())); - apiErrorLogMapper.insert(apiErrorLogDO); - // 准备参数 - Long id = apiErrorLogDO.getId(); - Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus(); - Long processUserId = randomLongId(); - - // 调用,并断言异常 - assertServiceException(() -> - apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId), - API_ERROR_LOG_PROCESSED); - } - - @Test - public void testUpdateApiErrorLogProcess_notFound() { - // 准备参数 - Long id = randomLongId(); - Integer processStatus = randomEle(ApiErrorLogProcessStatusEnum.values()).getStatus(); - Long processUserId = randomLongId(); - - // 调用,并断言异常 - assertServiceException(() -> - apiErrorLogService.updateApiErrorLogProcess(id, processStatus, processUserId), - API_ERROR_LOG_NOT_FOUND); - } - - @Test - public void testCleanJobLog() { - // mock 数据 - ApiErrorLogDO log01 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-3)))); - apiErrorLogMapper.insert(log01); - ApiErrorLogDO log02 = randomPojo(ApiErrorLogDO.class, o -> o.setCreateTime(addTime(Duration.ofDays(-1)))); - apiErrorLogMapper.insert(log02); - // 准备参数 - Integer exceedDay = 2; - Integer deleteLimit = 1; - - // 调用 - Integer count = apiErrorLogService.cleanErrorLog(exceedDay, deleteLimit); - // 断言 - assertEquals(1, count); - List logs = apiErrorLogMapper.selectList(); - assertEquals(1, logs.size()); - // TODO @芋艿:createTime updateTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(log02, logs.get(0), "createTime", "updateTime"); - } - -} diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/application-unit-test.yaml b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/application-unit-test.yaml deleted file mode 100644 index 5ce2d4b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/application-unit-test.yaml +++ /dev/null @@ -1,50 +0,0 @@ -spring: - main: - lazy-initialization: true # 开启懒加载,加快速度 - banner-mode: off # 单元测试,禁用 Banner - ---- #################### 数据库相关配置 #################### - -spring: - # 数据源配置项 - datasource: - name: ruoyi-vue-pro - url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 - driver-class-name: org.h2.Driver - username: sa - password: - druid: - async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 - initial-size: 1 # 单元测试,配置为 1,提升启动速度 - sql: - init: - schema-locations: classpath:/sql/create_tables.sql - encoding: UTF-8 - - # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 - data: - redis: - host: 127.0.0.1 # 地址 - port: 16379 # 端口(单元测试,使用 16379 端口) - database: 0 # 数据库索引 - -mybatis-plus: - lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 - type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject - ---- #################### 定时任务相关配置 #################### - ---- #################### 配置中心相关配置 #################### - ---- #################### 服务保障相关配置 #################### - -# Lock4j 配置项(单元测试,禁用 Lock4j) - ---- #################### 监控相关配置 #################### - ---- #################### 芋道相关配置 #################### - -# 芋道配置项,设置当前项目所有自定义的配置 -yudao: - info: - base-package: cn.iocoder.yudao diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/category.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/category.json deleted file mode 100644 index 033c048..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/category.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "table": { - "id": 10, - "scene" : 1, - "parentMenuId" : 888, - "tableName" : "infra_category", - "tableComment" : "分类表", - "moduleName" : "infra", - "businessName" : "demo", - "className" : "InfraCategory", - "classComment" : "分类", - "author" : "芋道源码", - "treeParentColumnId" : 22, - "treeNameColumnId" : 11 - }, - "columns": [ { - "columnName" : "id", - "dataType" : "BIGINT", - "columnComment" : "编号", - "primaryKey" : true, - "javaType" : "Long", - "javaField" : "id", - "example" : "1024", - "updateOperation" : true, - "listOperationResult" : true - }, { - "id" : 11, - "columnName" : "name", - "dataType" : "VARCHAR", - "columnComment" : "名字", - "javaType" : "String", - "javaField" : "name", - "example" : "芋头", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "LIKE", - "listOperationResult" : true, - "htmlType" : "input" - }, { - "id" : 22, - "columnName" : "description", - "dataType" : "VARCHAR", - "columnComment" : "父编号", - "javaType" : "Long", - "javaField" : "parentId", - "example" : "2048", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true - } ] -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/contact.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/contact.json deleted file mode 100644 index 74f92cd..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/contact.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "table": { - "scene" : 1, - "tableName" : "infra_student_contact", - "tableComment" : "学生联系人表", - "moduleName" : "infra", - "businessName" : "demo", - "className" : "InfraStudentContact", - "classComment" : "学生联系人", - "author" : "芋道源码" - }, - "columns": [ { - "columnName" : "id", - "dataType" : "BIGINT", - "columnComment" : "编号", - "primaryKey" : true, - "javaType" : "Long", - "javaField" : "id", - "example" : "1024", - "updateOperation" : true, - "listOperationResult" : true - }, { - "id" : 100, - "columnName" : "student_id", - "dataType" : "BIGINT", - "columnComment" : "学生编号", - "javaType" : "Long", - "javaField" : "studentId", - "example" : "2048", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true - }, { - "columnName" : "name", - "dataType" : "VARCHAR", - "columnComment" : "名字", - "javaType" : "String", - "javaField" : "name", - "example" : "芋头", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "LIKE", - "listOperationResult" : true, - "htmlType" : "input" - }, { - "columnName" : "description", - "dataType" : "VARCHAR", - "columnComment" : "简介", - "javaType" : "String", - "javaField" : "description", - "example" : "我是介绍", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "textarea" - }, { - "columnName" : "birthday", - "dataType" : "DATE", - "columnComment" : "出生日期", - "javaType" : "LocalDateTime", - "javaField" : "birthday", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "=", - "listOperationResult" : true, - "htmlType" : "datetime" - }, { - "columnName" : "sex", - "dataType" : "INTEGER", - "columnComment" : "性别", - "javaType" : "Integer", - "javaField" : "sex", - "dictType" : "system_user_sex", - "example" : "1", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "=", - "listOperationResult" : true, - "htmlType" : "select" - }, { - "columnName" : "enabled", - "dataType" : "BOOLEAN", - "columnComment" : "是否有效", - "javaType" : "Boolean", - "javaField" : "enabled", - "dictType" : "infra_boolean_string", - "example" : "true", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "=", - "listOperationResult" : true, - "htmlType" : "radio" - }, { - "columnName" : "avatar", - "dataType" : "VARCHAR", - "columnComment" : "头像", - "javaType" : "String", - "javaField" : "avatar", - "example" : "https://www.iocoder.cn/1.png", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "imageUpload" - }, { - "columnName" : "video", - "dataType" : "VARCHAR", - "columnComment" : "附件", - "nullable" : true, - "javaType" : "String", - "javaField" : "video", - "example" : "https://www.iocoder.cn/1.mp4", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "fileUpload" - }, { - "columnName" : "memo", - "dataType" : "VARCHAR", - "columnComment" : "备注", - "javaType" : "String", - "javaField" : "memo", - "example" : "我是备注", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "editor" - }, { - "columnName" : "create_time", - "dataType" : "DATE", - "columnComment" : "创建时间", - "nullable" : true, - "javaType" : "LocalDateTime", - "javaField" : "createTime", - "listOperation" : true, - "listOperationCondition" : "BETWEEN", - "listOperationResult" : true, - "htmlType" : "datetime" - } ] -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/student.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/student.json deleted file mode 100644 index efe8af4..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/student.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "table": { - "id": 1, - "scene" : 1, - "parentMenuId" : 888, - "tableName" : "infra_student", - "tableComment" : "学生表", - "moduleName" : "infra", - "businessName" : "demo", - "className" : "InfraStudent", - "classComment" : "学生", - "author" : "芋道源码" - }, - "columns": [ { - "id" : 100, - "columnName" : "id", - "dataType" : "BIGINT", - "columnComment" : "编号", - "primaryKey" : true, - "javaType" : "Long", - "javaField" : "id", - "example" : "1024", - "updateOperation" : true, - "listOperationResult" : true - }, { - "columnName" : "name", - "dataType" : "VARCHAR", - "columnComment" : "名字", - "javaType" : "String", - "javaField" : "name", - "example" : "芋头", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "LIKE", - "listOperationResult" : true, - "htmlType" : "input" - }, { - "columnName" : "description", - "dataType" : "VARCHAR", - "columnComment" : "简介", - "javaType" : "String", - "javaField" : "description", - "example" : "我是介绍", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "textarea" - }, { - "columnName" : "birthday", - "dataType" : "DATE", - "columnComment" : "出生日期", - "javaType" : "LocalDateTime", - "javaField" : "birthday", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "=", - "listOperationResult" : true, - "htmlType" : "datetime" - }, { - "columnName" : "sex", - "dataType" : "INTEGER", - "columnComment" : "性别", - "javaType" : "Integer", - "javaField" : "sex", - "dictType" : "system_user_sex", - "example" : "1", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "=", - "listOperationResult" : true, - "htmlType" : "select" - }, { - "columnName" : "enabled", - "dataType" : "BOOLEAN", - "columnComment" : "是否有效", - "javaType" : "Boolean", - "javaField" : "enabled", - "dictType" : "infra_boolean_string", - "example" : "true", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "=", - "listOperationResult" : true, - "htmlType" : "radio" - }, { - "columnName" : "avatar", - "dataType" : "VARCHAR", - "columnComment" : "头像", - "javaType" : "String", - "javaField" : "avatar", - "example" : "https://www.iocoder.cn/1.png", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "imageUpload" - }, { - "columnName" : "video", - "dataType" : "VARCHAR", - "columnComment" : "附件", - "javaType" : "String", - "javaField" : "video", - "example" : "https://www.iocoder.cn/1.mp4", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "fileUpload" - }, { - "columnName" : "memo", - "dataType" : "VARCHAR", - "columnComment" : "备注", - "javaType" : "String", - "javaField" : "memo", - "example" : "我是备注", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "editor" - }, { - "columnName" : "create_time", - "dataType" : "DATE", - "columnComment" : "创建时间", - "nullable" : true, - "javaType" : "LocalDateTime", - "javaField" : "createTime", - "listOperation" : true, - "listOperationCondition" : "BETWEEN", - "listOperationResult" : true, - "htmlType" : "datetime" - } ] -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/teacher.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/teacher.json deleted file mode 100644 index 4d93518..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/table/teacher.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "table": { - "scene" : 1, - "tableName" : "infra_student_teacher", - "tableComment" : "学生班主任表", - "moduleName" : "infra", - "businessName" : "demo", - "className" : "InfraStudentTeacher", - "classComment" : "学生班主任", - "author" : "芋道源码" - }, - "columns": [ { - "columnName" : "id", - "dataType" : "BIGINT", - "columnComment" : "编号", - "primaryKey" : true, - "javaType" : "Long", - "javaField" : "id", - "example" : "1024", - "updateOperation" : true, - "listOperationResult" : true - }, { - "id" : 200, - "columnName" : "student_id", - "dataType" : "BIGINT", - "columnComment" : "学生编号", - "javaType" : "Long", - "javaField" : "studentId", - "example" : "2048", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true - }, { - "columnName" : "name", - "dataType" : "VARCHAR", - "columnComment" : "名字", - "javaType" : "String", - "javaField" : "name", - "example" : "芋头", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "LIKE", - "listOperationResult" : true, - "htmlType" : "input" - }, { - "columnName" : "description", - "dataType" : "VARCHAR", - "columnComment" : "简介", - "javaType" : "String", - "javaField" : "description", - "example" : "我是介绍", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "textarea" - }, { - "columnName" : "birthday", - "dataType" : "DATE", - "columnComment" : "出生日期", - "javaType" : "LocalDateTime", - "javaField" : "birthday", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "=", - "listOperationResult" : true, - "htmlType" : "datetime" - }, { - "columnName" : "sex", - "dataType" : "INTEGER", - "columnComment" : "性别", - "javaType" : "Integer", - "javaField" : "sex", - "dictType" : "system_user_sex", - "example" : "1", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "=", - "listOperationResult" : true, - "htmlType" : "select" - }, { - "columnName" : "enabled", - "dataType" : "BOOLEAN", - "columnComment" : "是否有效", - "javaType" : "Boolean", - "javaField" : "enabled", - "dictType" : "infra_boolean_string", - "example" : "true", - "createOperation" : true, - "updateOperation" : true, - "listOperation" : true, - "listOperationCondition" : "=", - "listOperationResult" : true, - "htmlType" : "radio" - }, { - "columnName" : "avatar", - "dataType" : "VARCHAR", - "columnComment" : "头像", - "javaType" : "String", - "javaField" : "avatar", - "example" : "https://www.iocoder.cn/1.png", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "imageUpload" - }, { - "columnName" : "video", - "dataType" : "VARCHAR", - "columnComment" : "附件", - "nullable" : true, - "javaType" : "String", - "javaField" : "video", - "example" : "https://www.iocoder.cn/1.mp4", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "fileUpload" - }, { - "columnName" : "memo", - "dataType" : "VARCHAR", - "columnComment" : "备注", - "javaType" : "String", - "javaField" : "memo", - "example" : "我是备注", - "createOperation" : true, - "updateOperation" : true, - "listOperationResult" : true, - "htmlType" : "editor" - }, { - "columnName" : "create_time", - "dataType" : "DATE", - "columnComment" : "创建时间", - "nullable" : true, - "javaType" : "LocalDateTime", - "javaField" : "createTime", - "listOperation" : true, - "listOperationCondition" : "BETWEEN", - "listOperationResult" : true, - "htmlType" : "datetime" - } ] -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/assert.json deleted file mode 100644 index 8edb8e6..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/assert.json +++ /dev/null @@ -1,73 +0,0 @@ -[ { - "contentPath" : "java/InfraStudentPageReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" -}, { - "contentPath" : "java/InfraStudentRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" -}, { - "contentPath" : "java/InfraStudentSaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" -}, { - "contentPath" : "java/InfraStudentController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" -}, { - "contentPath" : "java/InfraStudentDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" -}, { - "contentPath" : "java/InfraStudentContactDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" -}, { - "contentPath" : "java/InfraStudentTeacherDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" -}, { - "contentPath" : "java/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" -}, { - "contentPath" : "java/InfraStudentContactMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" -}, { - "contentPath" : "java/InfraStudentTeacherMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" -}, { - "contentPath" : "xml/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" -}, { - "contentPath" : "java/InfraStudentServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" -}, { - "contentPath" : "java/InfraStudentService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" -}, { - "contentPath" : "java/InfraStudentServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" -}, { - "contentPath": "js/index", - "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" -}, { - "contentPath" : "vue/StudentForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" -}, { - "contentPath" : "vue/StudentContactForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue" -}, { - "contentPath" : "vue/StudentTeacherForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue" -}, { - "contentPath" : "vue/StudentContactList", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue" -}, { - "contentPath" : "vue/StudentTeacherList", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactDO deleted file mode 100644 index 17c668e..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生联系人 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_contact") -@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentContactDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactMapper deleted file mode 100644 index ca662d1..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentContactMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生联系人 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentContactMapper extends BaseMapperX { - - default PageResult selectPage(PageParam reqVO, Long studentId) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eq(InfraStudentContactDO::getStudentId, studentId) - .orderByDesc(InfraStudentContactDO::getId)); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentContactDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentController deleted file mode 100644 index d6f2018..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentController +++ /dev/null @@ -1,183 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; - -@Tag(name = "管理后台 - 学生") -@RestController -@RequestMapping("/infra/student") -@Validated -public class InfraStudentController { - - @Resource - private InfraStudentService studentService; - - @PostMapping("/create") - @Operation(summary = "创建学生") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { - return success(studentService.createStudent(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新学生") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { - studentService.updateStudent(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除学生") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudent(@RequestParam("id") Long id) { - studentService.deleteStudent(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得学生") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudent(@RequestParam("id") Long id) { - InfraStudentDO student = studentService.getStudent(id); - return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得学生分页") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { - PageResult pageResult = studentService.getStudentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出学生 Excel") - @PreAuthorize("@ss.hasPermission('infra:student:export')") - @OperateLog(type = EXPORT) - public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = studentService.getStudentPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, - BeanUtils.toBean(list, InfraStudentRespVO.class)); - } - - // ==================== 子表(学生联系人) ==================== - - @GetMapping("/student-contact/page") - @Operation(summary = "获得学生联系人分页") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentContactPage(PageParam pageReqVO, - @RequestParam("studentId") Long studentId) { - return success(studentService.getStudentContactPage(pageReqVO, studentId)); - } - - @PostMapping("/student-contact/create") - @Operation(summary = "创建学生联系人") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { - return success(studentService.createStudentContact(studentContact)); - } - - @PutMapping("/student-contact/update") - @Operation(summary = "更新学生联系人") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { - studentService.updateStudentContact(studentContact); - return success(true); - } - - @DeleteMapping("/student-contact/delete") - @Parameter(name = "id", description = "编号", required = true) - @Operation(summary = "删除学生联系人") - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudentContact(@RequestParam("id") Long id) { - studentService.deleteStudentContact(id); - return success(true); - } - - @GetMapping("/student-contact/get") - @Operation(summary = "获得学生联系人") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudentContact(@RequestParam("id") Long id) { - return success(studentService.getStudentContact(id)); - } - - // ==================== 子表(学生班主任) ==================== - - @GetMapping("/student-teacher/page") - @Operation(summary = "获得学生班主任分页") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentTeacherPage(PageParam pageReqVO, - @RequestParam("studentId") Long studentId) { - return success(studentService.getStudentTeacherPage(pageReqVO, studentId)); - } - - @PostMapping("/student-teacher/create") - @Operation(summary = "创建学生班主任") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { - return success(studentService.createStudentTeacher(studentTeacher)); - } - - @PutMapping("/student-teacher/update") - @Operation(summary = "更新学生班主任") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { - studentService.updateStudentTeacher(studentTeacher); - return success(true); - } - - @DeleteMapping("/student-teacher/delete") - @Parameter(name = "id", description = "编号", required = true) - @Operation(summary = "删除学生班主任") - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudentTeacher(@RequestParam("id") Long id) { - studentService.deleteStudentTeacher(id); - return success(true); - } - - @GetMapping("/student-teacher/get") - @Operation(summary = "获得学生班主任") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudentTeacher(@RequestParam("id") Long id) { - return success(studentService.getStudentTeacher(id)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentDO deleted file mode 100644 index b0d4bd2..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentDO +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生 DO - * - * @author 芋道源码 - */ -@TableName("infra_student") -@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentMapper deleted file mode 100644 index 34e70a0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 学生 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentMapper extends BaseMapperX { - - default PageResult selectPage(InfraStudentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) - .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) - .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) - .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) - .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(InfraStudentDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentPageReqVO deleted file mode 100644 index 41a3730..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentPageReqVO +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 学生分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class InfraStudentPageReqVO extends PageParam { - - @Schema(description = "名字", example = "芋头") - private String name; - - @Schema(description = "出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", example = "1") - private Integer sex; - - @Schema(description = "是否有效", example = "true") - private Boolean enabled; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO deleted file mode 100644 index c41a550..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentRespVO +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; - -@Schema(description = "管理后台 - 学生 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraStudentRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @ExcelProperty("简介") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "性别", converter = DictConvert.class) - @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @ExcelProperty(value = "是否有效", converter = DictConvert.class) - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @ExcelProperty("头像") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @ExcelProperty("附件") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @ExcelProperty("备注") - private String memo; - - @Schema(description = "创建时间") - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentSaveReqVO deleted file mode 100644 index eaadf74..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentSaveReqVO +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; - -@Schema(description = "管理后台 - 学生新增/修改 Request VO") -@Data -public class InfraStudentSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @NotEmpty(message = "简介不能为空") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "出生日期不能为空") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "性别不能为空") - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否有效不能为空") - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @NotEmpty(message = "头像不能为空") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @NotEmpty(message = "附件不能为空") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @NotEmpty(message = "备注不能为空") - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentService deleted file mode 100644 index 7df090d..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentService +++ /dev/null @@ -1,139 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 学生 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraStudentService { - - /** - * 创建学生 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); - - /** - * 更新学生 - * - * @param updateReqVO 更新信息 - */ - void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); - - /** - * 删除学生 - * - * @param id 编号 - */ - void deleteStudent(Long id); - - /** - * 获得学生 - * - * @param id 编号 - * @return 学生 - */ - InfraStudentDO getStudent(Long id); - - /** - * 获得学生分页 - * - * @param pageReqVO 分页查询 - * @return 学生分页 - */ - PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); - - // ==================== 子表(学生联系人) ==================== - - /** - * 获得学生联系人分页 - * - * @param pageReqVO 分页查询 - * @param studentId 学生编号 - * @return 学生联系人分页 - */ - PageResult getStudentContactPage(PageParam pageReqVO, Long studentId); - - /** - * 创建学生联系人 - * - * @param studentContact 创建信息 - * @return 编号 - */ - Long createStudentContact(@Valid InfraStudentContactDO studentContact); - - /** - * 更新学生联系人 - * - * @param studentContact 更新信息 - */ - void updateStudentContact(@Valid InfraStudentContactDO studentContact); - - /** - * 删除学生联系人 - * - * @param id 编号 - */ - void deleteStudentContact(Long id); - - /** - * 获得学生联系人 - * - * @param id 编号 - * @return 学生联系人 - */ - InfraStudentContactDO getStudentContact(Long id); - - // ==================== 子表(学生班主任) ==================== - - /** - * 获得学生班主任分页 - * - * @param pageReqVO 分页查询 - * @param studentId 学生编号 - * @return 学生班主任分页 - */ - PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId); - - /** - * 创建学生班主任 - * - * @param studentTeacher 创建信息 - * @return 编号 - */ - Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); - - /** - * 更新学生班主任 - * - * @param studentTeacher 更新信息 - */ - void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); - - /** - * 删除学生班主任 - * - * @param id 编号 - */ - void deleteStudentTeacher(Long id); - - /** - * 获得学生班主任 - * - * @param id 编号 - * @return 学生班主任 - */ - InfraStudentTeacherDO getStudentTeacher(Long id); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImpl deleted file mode 100644 index 793b2dd..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImpl +++ /dev/null @@ -1,180 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 学生 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraStudentServiceImpl implements InfraStudentService { - - @Resource - private InfraStudentMapper studentMapper; - @Resource - private InfraStudentContactMapper studentContactMapper; - @Resource - private InfraStudentTeacherMapper studentTeacherMapper; - - @Override - public Long createStudent(InfraStudentSaveReqVO createReqVO) { - // 插入 - InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); - studentMapper.insert(student); - // 返回 - return student.getId(); - } - - @Override - public void updateStudent(InfraStudentSaveReqVO updateReqVO) { - // 校验存在 - validateStudentExists(updateReqVO.getId()); - // 更新 - InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); - studentMapper.updateById(updateObj); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteStudent(Long id) { - // 校验存在 - validateStudentExists(id); - // 删除 - studentMapper.deleteById(id); - - // 删除子表 - deleteStudentContactByStudentId(id); - deleteStudentTeacherByStudentId(id); - } - - private void validateStudentExists(Long id) { - if (studentMapper.selectById(id) == null) { - throw exception(STUDENT_NOT_EXISTS); - } - } - - @Override - public InfraStudentDO getStudent(Long id) { - return studentMapper.selectById(id); - } - - @Override - public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { - return studentMapper.selectPage(pageReqVO); - } - - // ==================== 子表(学生联系人) ==================== - - @Override - public PageResult getStudentContactPage(PageParam pageReqVO, Long studentId) { - return studentContactMapper.selectPage(pageReqVO, studentId); - } - - @Override - public Long createStudentContact(InfraStudentContactDO studentContact) { - studentContactMapper.insert(studentContact); - return studentContact.getId(); - } - - @Override - public void updateStudentContact(InfraStudentContactDO studentContact) { - // 校验存在 - validateStudentContactExists(studentContact.getId()); - // 更新 - studentContactMapper.updateById(studentContact); - } - - @Override - public void deleteStudentContact(Long id) { - // 校验存在 - validateStudentContactExists(id); - // 删除 - studentContactMapper.deleteById(id); - } - - @Override - public InfraStudentContactDO getStudentContact(Long id) { - return studentContactMapper.selectById(id); - } - - private void validateStudentContactExists(Long id) { - if (studentContactMapper.selectById(id) == null) { - throw exception(STUDENT_CONTACT_NOT_EXISTS); - } - } - - private void deleteStudentContactByStudentId(Long studentId) { - studentContactMapper.deleteByStudentId(studentId); - } - - // ==================== 子表(学生班主任) ==================== - - @Override - public PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId) { - return studentTeacherMapper.selectPage(pageReqVO, studentId); - } - - @Override - public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) { - // 校验是否已经存在 - if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) { - throw exception(STUDENT_TEACHER_EXISTS); - } - // 插入 - studentTeacherMapper.insert(studentTeacher); - return studentTeacher.getId(); - } - - @Override - public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) { - // 校验存在 - validateStudentTeacherExists(studentTeacher.getId()); - // 更新 - studentTeacherMapper.updateById(studentTeacher); - } - - @Override - public void deleteStudentTeacher(Long id) { - // 校验存在 - validateStudentTeacherExists(id); - // 删除 - studentTeacherMapper.deleteById(id); - } - - @Override - public InfraStudentTeacherDO getStudentTeacher(Long id) { - return studentTeacherMapper.selectById(id); - } - - private void validateStudentTeacherExists(Long id) { - if (studentTeacherMapper.selectById(id) == null) { - throw exception(STUDENT_TEACHER_NOT_EXISTS); - } - } - - private void deleteStudentTeacherByStudentId(Long studentId) { - studentTeacherMapper.deleteByStudentId(studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImplTest deleted file mode 100644 index b5f4bf0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentServiceImplTest +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraStudentServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraStudentServiceImpl.class) -public class InfraStudentServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraStudentServiceImpl studentService; - - @Resource - private InfraStudentMapper studentMapper; - - @Test - public void testCreateStudent_success() { - // 准备参数 - InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); - - // 调用 - Long studentId = studentService.createStudent(createReqVO); - // 断言 - assertNotNull(studentId); - // 校验记录的属性是否正确 - InfraStudentDO student = studentMapper.selectById(studentId); - assertPojoEquals(createReqVO, student, "id"); - } - - @Test - public void testUpdateStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { - o.setId(dbStudent.getId()); // 设置更新的 ID - }); - - // 调用 - studentService.updateStudent(updateReqVO); - // 校验是否更新正确 - InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, student); - } - - @Test - public void testUpdateStudent_notExists() { - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); - } - - @Test - public void testDeleteStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbStudent.getId(); - - // 调用 - studentService.deleteStudent(id); - // 校验数据不存在了 - assertNull(studentMapper.selectById(id)); - } - - @Test - public void testDeleteStudent_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetStudentPage() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 - o.setName(null); - o.setBirthday(null); - o.setSex(null); - o.setEnabled(null); - o.setCreateTime(null); - }); - studentMapper.insert(dbStudent); - // 测试 name 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); - // 测试 birthday 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); - // 测试 sex 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); - // 测试 enabled 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); - // 测试 createTime 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); - // 准备参数 - InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); - reqVO.setName(null); - reqVO.setBirthday(null); - reqVO.setSex(null); - reqVO.setEnabled(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = studentService.getStudentPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbStudent, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherDO deleted file mode 100644 index c19cf9f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生班主任 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_teacher") -@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentTeacherDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherMapper deleted file mode 100644 index 994212d..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/java/InfraStudentTeacherMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生班主任 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentTeacherMapper extends BaseMapperX { - - default PageResult selectPage(PageParam reqVO, Long studentId) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eq(InfraStudentTeacherDO::getStudentId, studentId) - .orderByDesc(InfraStudentTeacherDO::getId)); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentTeacherDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/js/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/js/index deleted file mode 100644 index 211d95e..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/js/index +++ /dev/null @@ -1,141 +0,0 @@ -import request from '@/utils/request' - -// 创建学生 -export function createStudent(data) { - return request({ - url: '/infra/student/create', - method: 'post', - data: data - }) -} - -// 更新学生 -export function updateStudent(data) { - return request({ - url: '/infra/student/update', - method: 'put', - data: data - }) -} - -// 删除学生 -export function deleteStudent(id) { - return request({ - url: '/infra/student/delete?id=' + id, - method: 'delete' - }) -} - -// 获得学生 -export function getStudent(id) { - return request({ - url: '/infra/student/get?id=' + id, - method: 'get' - }) -} - -// 获得学生分页 -export function getStudentPage(params) { - return request({ - url: '/infra/student/page', - method: 'get', - params - }) -} -// 导出学生 Excel -export function exportStudentExcel(params) { - return request({ - url: '/infra/student/export-excel', - method: 'get', - params, - responseType: 'blob' - }) -} - -// ==================== 子表(学生联系人) ==================== - - // 获得学生联系人分页 - export function getStudentContactPage(params) { - return request({ - url: '/infra/student/student-contact/page', - method: 'get', - params - }) - } - // 新增学生联系人 - export function createStudentContact(data) { - return request({ - url: `/infra/student/student-contact/create`, - method: 'post', - data - }) - } - - // 修改学生联系人 - export function updateStudentContact(data) { - return request({ - url: `/infra/student/student-contact/update`, - method: 'post', - data - }) - } - - // 删除学生联系人 - export function deleteStudentContact(id) { - return request({ - url: `/infra/student/student-contact/delete?id=` + id, - method: 'delete' - }) - } - - // 获得学生联系人 - export function getStudentContact(id) { - return request({ - url: `/infra/student/student-contact/get?id=` + id, - method: 'get' - }) - } - -// ==================== 子表(学生班主任) ==================== - - // 获得学生班主任分页 - export function getStudentTeacherPage(params) { - return request({ - url: '/infra/student/student-teacher/page', - method: 'get', - params - }) - } - // 新增学生班主任 - export function createStudentTeacher(data) { - return request({ - url: `/infra/student/student-teacher/create`, - method: 'post', - data - }) - } - - // 修改学生班主任 - export function updateStudentTeacher(data) { - return request({ - url: `/infra/student/student-teacher/update`, - method: 'post', - data - }) - } - - // 删除学生班主任 - export function deleteStudentTeacher(id) { - return request({ - url: `/infra/student/student-teacher/delete?id=` + id, - method: 'delete' - }) - } - - // 获得学生班主任 - export function getStudentTeacher(id) { - return request({ - url: `/infra/student/student-teacher/get?id=` + id, - method: 'get' - }) - } \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/sql/h2 deleted file mode 100644 index 6c1875f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/sql/h2 +++ /dev/null @@ -1,17 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_student" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" varchar NOT NULL, - "birthday" varchar NOT NULL, - "sex" int NOT NULL, - "enabled" bit NOT NULL, - "avatar" varchar NOT NULL, - "video" varchar NOT NULL, - "memo" varchar NOT NULL, - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id") -) COMMENT '学生表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/sql/sql deleted file mode 100644 index 83df279..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '学生管理', '', 2, 0, 888, - 'student', '', 'infra/demo/index', 0, 'InfraStudent' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生查询', 'infra:student:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生创建', 'infra:student:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生更新', 'infra:student:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生删除', 'infra:student:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生导出', 'infra:student:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactForm deleted file mode 100644 index de3b0a7..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactForm +++ /dev/null @@ -1,151 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactList deleted file mode 100644 index 00f1ce0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentContactList +++ /dev/null @@ -1,129 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentForm deleted file mode 100644 index d89e506..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentForm +++ /dev/null @@ -1,149 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherForm deleted file mode 100644 index 874a03b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherForm +++ /dev/null @@ -1,151 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherList deleted file mode 100644 index 7d561a0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/StudentTeacherList +++ /dev/null @@ -1,129 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/index deleted file mode 100644 index 9c7588f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/vue/index +++ /dev/null @@ -1,233 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/xml/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/xml/InfraStudentMapper deleted file mode 100644 index 155aa5c..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_erp/xml/InfraStudentMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/assert.json deleted file mode 100644 index 8edb8e6..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/assert.json +++ /dev/null @@ -1,73 +0,0 @@ -[ { - "contentPath" : "java/InfraStudentPageReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" -}, { - "contentPath" : "java/InfraStudentRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" -}, { - "contentPath" : "java/InfraStudentSaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" -}, { - "contentPath" : "java/InfraStudentController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" -}, { - "contentPath" : "java/InfraStudentDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" -}, { - "contentPath" : "java/InfraStudentContactDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" -}, { - "contentPath" : "java/InfraStudentTeacherDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" -}, { - "contentPath" : "java/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" -}, { - "contentPath" : "java/InfraStudentContactMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" -}, { - "contentPath" : "java/InfraStudentTeacherMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" -}, { - "contentPath" : "xml/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" -}, { - "contentPath" : "java/InfraStudentServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" -}, { - "contentPath" : "java/InfraStudentService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" -}, { - "contentPath" : "java/InfraStudentServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" -}, { - "contentPath": "js/index", - "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" -}, { - "contentPath" : "vue/StudentForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" -}, { - "contentPath" : "vue/StudentContactForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue" -}, { - "contentPath" : "vue/StudentTeacherForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue" -}, { - "contentPath" : "vue/StudentContactList", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactList.vue" -}, { - "contentPath" : "vue/StudentTeacherList", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherList.vue" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactDO deleted file mode 100644 index 17c668e..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生联系人 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_contact") -@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentContactDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactMapper deleted file mode 100644 index 35bbd53..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentContactMapper +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生联系人 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentContactMapper extends BaseMapperX { - - default List selectListByStudentId(Long studentId) { - return selectList(InfraStudentContactDO::getStudentId, studentId); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentContactDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentController deleted file mode 100644 index b9a587b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentController +++ /dev/null @@ -1,117 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; - -@Tag(name = "管理后台 - 学生") -@RestController -@RequestMapping("/infra/student") -@Validated -public class InfraStudentController { - - @Resource - private InfraStudentService studentService; - - @PostMapping("/create") - @Operation(summary = "创建学生") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { - return success(studentService.createStudent(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新学生") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { - studentService.updateStudent(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除学生") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudent(@RequestParam("id") Long id) { - studentService.deleteStudent(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得学生") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudent(@RequestParam("id") Long id) { - InfraStudentDO student = studentService.getStudent(id); - return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得学生分页") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { - PageResult pageResult = studentService.getStudentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出学生 Excel") - @PreAuthorize("@ss.hasPermission('infra:student:export')") - @OperateLog(type = EXPORT) - public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = studentService.getStudentPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, - BeanUtils.toBean(list, InfraStudentRespVO.class)); - } - - // ==================== 子表(学生联系人) ==================== - - @GetMapping("/student-contact/list-by-student-id") - @Operation(summary = "获得学生联系人列表") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { - return success(studentService.getStudentContactListByStudentId(studentId)); - } - - // ==================== 子表(学生班主任) ==================== - - @GetMapping("/student-teacher/get-by-student-id") - @Operation(summary = "获得学生班主任") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { - return success(studentService.getStudentTeacherByStudentId(studentId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentDO deleted file mode 100644 index b0d4bd2..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentDO +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生 DO - * - * @author 芋道源码 - */ -@TableName("infra_student") -@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentMapper deleted file mode 100644 index 34e70a0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 学生 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentMapper extends BaseMapperX { - - default PageResult selectPage(InfraStudentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) - .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) - .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) - .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) - .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(InfraStudentDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentPageReqVO deleted file mode 100644 index 41a3730..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentPageReqVO +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 学生分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class InfraStudentPageReqVO extends PageParam { - - @Schema(description = "名字", example = "芋头") - private String name; - - @Schema(description = "出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", example = "1") - private Integer sex; - - @Schema(description = "是否有效", example = "true") - private Boolean enabled; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO deleted file mode 100644 index c41a550..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentRespVO +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; - -@Schema(description = "管理后台 - 学生 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraStudentRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @ExcelProperty("简介") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "性别", converter = DictConvert.class) - @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @ExcelProperty(value = "是否有效", converter = DictConvert.class) - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @ExcelProperty("头像") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @ExcelProperty("附件") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @ExcelProperty("备注") - private String memo; - - @Schema(description = "创建时间") - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentSaveReqVO deleted file mode 100644 index faa491d..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentSaveReqVO +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; - -@Schema(description = "管理后台 - 学生新增/修改 Request VO") -@Data -public class InfraStudentSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @NotEmpty(message = "简介不能为空") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "出生日期不能为空") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "性别不能为空") - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否有效不能为空") - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @NotEmpty(message = "头像不能为空") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @NotEmpty(message = "附件不能为空") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @NotEmpty(message = "备注不能为空") - private String memo; - - @Schema(description = "学生联系人列表") - private List studentContacts; - - @Schema(description = "学生班主任") - private InfraStudentTeacherDO studentTeacher; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentService deleted file mode 100644 index afa7d22..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentService +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 学生 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraStudentService { - - /** - * 创建学生 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); - - /** - * 更新学生 - * - * @param updateReqVO 更新信息 - */ - void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); - - /** - * 删除学生 - * - * @param id 编号 - */ - void deleteStudent(Long id); - - /** - * 获得学生 - * - * @param id 编号 - * @return 学生 - */ - InfraStudentDO getStudent(Long id); - - /** - * 获得学生分页 - * - * @param pageReqVO 分页查询 - * @return 学生分页 - */ - PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); - - // ==================== 子表(学生联系人) ==================== - - /** - * 获得学生联系人列表 - * - * @param studentId 学生编号 - * @return 学生联系人列表 - */ - List getStudentContactListByStudentId(Long studentId); - - // ==================== 子表(学生班主任) ==================== - - /** - * 获得学生班主任 - * - * @param studentId 学生编号 - * @return 学生班主任 - */ - InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImpl deleted file mode 100644 index c57cba6..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImpl +++ /dev/null @@ -1,147 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 学生 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraStudentServiceImpl implements InfraStudentService { - - @Resource - private InfraStudentMapper studentMapper; - @Resource - private InfraStudentContactMapper studentContactMapper; - @Resource - private InfraStudentTeacherMapper studentTeacherMapper; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createStudent(InfraStudentSaveReqVO createReqVO) { - // 插入 - InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); - studentMapper.insert(student); - - // 插入子表 - createStudentContactList(student.getId(), createReqVO.getStudentContacts()); - createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); - // 返回 - return student.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateStudent(InfraStudentSaveReqVO updateReqVO) { - // 校验存在 - validateStudentExists(updateReqVO.getId()); - // 更新 - InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); - studentMapper.updateById(updateObj); - - // 更新子表 - updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); - updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteStudent(Long id) { - // 校验存在 - validateStudentExists(id); - // 删除 - studentMapper.deleteById(id); - - // 删除子表 - deleteStudentContactByStudentId(id); - deleteStudentTeacherByStudentId(id); - } - - private void validateStudentExists(Long id) { - if (studentMapper.selectById(id) == null) { - throw exception(STUDENT_NOT_EXISTS); - } - } - - @Override - public InfraStudentDO getStudent(Long id) { - return studentMapper.selectById(id); - } - - @Override - public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { - return studentMapper.selectPage(pageReqVO); - } - - // ==================== 子表(学生联系人) ==================== - - @Override - public List getStudentContactListByStudentId(Long studentId) { - return studentContactMapper.selectListByStudentId(studentId); - } - - private void createStudentContactList(Long studentId, List list) { - list.forEach(o -> o.setStudentId(studentId)); - studentContactMapper.insertBatch(list); - } - - private void updateStudentContactList(Long studentId, List list) { - deleteStudentContactByStudentId(studentId); - list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新 - createStudentContactList(studentId, list); - } - - private void deleteStudentContactByStudentId(Long studentId) { - studentContactMapper.deleteByStudentId(studentId); - } - - // ==================== 子表(学生班主任) ==================== - - @Override - public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { - return studentTeacherMapper.selectByStudentId(studentId); - } - - private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { - if (studentTeacher == null) { - return; - } - studentTeacher.setStudentId(studentId); - studentTeacherMapper.insert(studentTeacher); - } - - private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { - if (studentTeacher == null) { - return; - } - studentTeacher.setStudentId(studentId); - studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新 - studentTeacherMapper.insertOrUpdate(studentTeacher); - } - - private void deleteStudentTeacherByStudentId(Long studentId) { - studentTeacherMapper.deleteByStudentId(studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImplTest deleted file mode 100644 index b5f4bf0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentServiceImplTest +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraStudentServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraStudentServiceImpl.class) -public class InfraStudentServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraStudentServiceImpl studentService; - - @Resource - private InfraStudentMapper studentMapper; - - @Test - public void testCreateStudent_success() { - // 准备参数 - InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); - - // 调用 - Long studentId = studentService.createStudent(createReqVO); - // 断言 - assertNotNull(studentId); - // 校验记录的属性是否正确 - InfraStudentDO student = studentMapper.selectById(studentId); - assertPojoEquals(createReqVO, student, "id"); - } - - @Test - public void testUpdateStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { - o.setId(dbStudent.getId()); // 设置更新的 ID - }); - - // 调用 - studentService.updateStudent(updateReqVO); - // 校验是否更新正确 - InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, student); - } - - @Test - public void testUpdateStudent_notExists() { - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); - } - - @Test - public void testDeleteStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbStudent.getId(); - - // 调用 - studentService.deleteStudent(id); - // 校验数据不存在了 - assertNull(studentMapper.selectById(id)); - } - - @Test - public void testDeleteStudent_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetStudentPage() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 - o.setName(null); - o.setBirthday(null); - o.setSex(null); - o.setEnabled(null); - o.setCreateTime(null); - }); - studentMapper.insert(dbStudent); - // 测试 name 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); - // 测试 birthday 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); - // 测试 sex 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); - // 测试 enabled 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); - // 测试 createTime 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); - // 准备参数 - InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); - reqVO.setName(null); - reqVO.setBirthday(null); - reqVO.setSex(null); - reqVO.setEnabled(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = studentService.getStudentPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbStudent, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherDO deleted file mode 100644 index c19cf9f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生班主任 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_teacher") -@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentTeacherDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherMapper deleted file mode 100644 index 0521bba..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/java/InfraStudentTeacherMapper +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生班主任 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentTeacherMapper extends BaseMapperX { - - default InfraStudentTeacherDO selectByStudentId(Long studentId) { - return selectOne(InfraStudentTeacherDO::getStudentId, studentId); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentTeacherDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/js/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/js/index deleted file mode 100644 index b4e6ac5..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/js/index +++ /dev/null @@ -1,74 +0,0 @@ -import request from '@/utils/request' - -// 创建学生 -export function createStudent(data) { - return request({ - url: '/infra/student/create', - method: 'post', - data: data - }) -} - -// 更新学生 -export function updateStudent(data) { - return request({ - url: '/infra/student/update', - method: 'put', - data: data - }) -} - -// 删除学生 -export function deleteStudent(id) { - return request({ - url: '/infra/student/delete?id=' + id, - method: 'delete' - }) -} - -// 获得学生 -export function getStudent(id) { - return request({ - url: '/infra/student/get?id=' + id, - method: 'get' - }) -} - -// 获得学生分页 -export function getStudentPage(params) { - return request({ - url: '/infra/student/page', - method: 'get', - params - }) -} -// 导出学生 Excel -export function exportStudentExcel(params) { - return request({ - url: '/infra/student/export-excel', - method: 'get', - params, - responseType: 'blob' - }) -} - -// ==================== 子表(学生联系人) ==================== - - // 获得学生联系人列表 - export function getStudentContactListByStudentId(studentId) { - return request({ - url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId, - method: 'get' - }) - } - -// ==================== 子表(学生班主任) ==================== - - // 获得学生班主任 - export function getStudentTeacherByStudentId(studentId) { - return request({ - url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId, - method: 'get' - }) - } - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2 deleted file mode 100644 index 6c1875f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/sql/h2 +++ /dev/null @@ -1,17 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_student" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" varchar NOT NULL, - "birthday" varchar NOT NULL, - "sex" int NOT NULL, - "enabled" bit NOT NULL, - "avatar" varchar NOT NULL, - "video" varchar NOT NULL, - "memo" varchar NOT NULL, - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id") -) COMMENT '学生表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql deleted file mode 100644 index 83df279..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '学生管理', '', 2, 0, 888, - 'student', '', 'infra/demo/index', 0, 'InfraStudent' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生查询', 'infra:student:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生创建', 'infra:student:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生更新', 'infra:student:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生删除', 'infra:student:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生导出', 'infra:student:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm deleted file mode 100644 index c953bfa..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactForm +++ /dev/null @@ -1,177 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList deleted file mode 100644 index c0a8710..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentContactList +++ /dev/null @@ -1,89 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm deleted file mode 100644 index 6d93b61..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentForm +++ /dev/null @@ -1,180 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm deleted file mode 100644 index 0dac19b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherForm +++ /dev/null @@ -1,127 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList deleted file mode 100644 index 9f57274..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/StudentTeacherList +++ /dev/null @@ -1,93 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/index deleted file mode 100644 index ddeafdf..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/vue/index +++ /dev/null @@ -1,222 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper deleted file mode 100644 index 155aa5c..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_inner/xml/InfraStudentMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/assert.json deleted file mode 100644 index 6f94535..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/assert.json +++ /dev/null @@ -1,67 +0,0 @@ -[ { - "contentPath" : "java/InfraStudentPageReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" -}, { - "contentPath" : "java/InfraStudentRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" -}, { - "contentPath" : "java/InfraStudentSaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" -}, { - "contentPath" : "java/InfraStudentController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" -}, { - "contentPath" : "java/InfraStudentDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" -}, { - "contentPath" : "java/InfraStudentContactDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" -}, { - "contentPath" : "java/InfraStudentTeacherDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" -}, { - "contentPath" : "java/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" -}, { - "contentPath" : "java/InfraStudentContactMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" -}, { - "contentPath" : "java/InfraStudentTeacherMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" -}, { - "contentPath" : "xml/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" -}, { - "contentPath" : "java/InfraStudentServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" -}, { - "contentPath" : "java/InfraStudentService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" -}, { - "contentPath" : "java/InfraStudentServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" -}, { - "contentPath": "js/index", - "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" -}, { - "contentPath" : "vue/StudentForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" -}, { - "contentPath" : "vue/StudentContactForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentContactForm.vue" -}, { - "contentPath" : "vue/StudentTeacherForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/components/StudentTeacherForm.vue" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO deleted file mode 100644 index 17c668e..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生联系人 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_contact") -@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentContactDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper deleted file mode 100644 index 35bbd53..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentContactMapper +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生联系人 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentContactMapper extends BaseMapperX { - - default List selectListByStudentId(Long studentId) { - return selectList(InfraStudentContactDO::getStudentId, studentId); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentContactDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController deleted file mode 100644 index b9a587b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentController +++ /dev/null @@ -1,117 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; - -@Tag(name = "管理后台 - 学生") -@RestController -@RequestMapping("/infra/student") -@Validated -public class InfraStudentController { - - @Resource - private InfraStudentService studentService; - - @PostMapping("/create") - @Operation(summary = "创建学生") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { - return success(studentService.createStudent(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新学生") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { - studentService.updateStudent(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除学生") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudent(@RequestParam("id") Long id) { - studentService.deleteStudent(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得学生") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudent(@RequestParam("id") Long id) { - InfraStudentDO student = studentService.getStudent(id); - return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得学生分页") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { - PageResult pageResult = studentService.getStudentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出学生 Excel") - @PreAuthorize("@ss.hasPermission('infra:student:export')") - @OperateLog(type = EXPORT) - public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = studentService.getStudentPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, - BeanUtils.toBean(list, InfraStudentRespVO.class)); - } - - // ==================== 子表(学生联系人) ==================== - - @GetMapping("/student-contact/list-by-student-id") - @Operation(summary = "获得学生联系人列表") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { - return success(studentService.getStudentContactListByStudentId(studentId)); - } - - // ==================== 子表(学生班主任) ==================== - - @GetMapping("/student-teacher/get-by-student-id") - @Operation(summary = "获得学生班主任") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { - return success(studentService.getStudentTeacherByStudentId(studentId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO deleted file mode 100644 index b0d4bd2..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentDO +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生 DO - * - * @author 芋道源码 - */ -@TableName("infra_student") -@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper deleted file mode 100644 index 34e70a0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 学生 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentMapper extends BaseMapperX { - - default PageResult selectPage(InfraStudentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) - .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) - .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) - .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) - .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(InfraStudentDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO deleted file mode 100644 index 41a3730..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentPageReqVO +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 学生分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class InfraStudentPageReqVO extends PageParam { - - @Schema(description = "名字", example = "芋头") - private String name; - - @Schema(description = "出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", example = "1") - private Integer sex; - - @Schema(description = "是否有效", example = "true") - private Boolean enabled; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO deleted file mode 100644 index c41a550..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentRespVO +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; - -@Schema(description = "管理后台 - 学生 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraStudentRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @ExcelProperty("简介") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "性别", converter = DictConvert.class) - @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @ExcelProperty(value = "是否有效", converter = DictConvert.class) - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @ExcelProperty("头像") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @ExcelProperty("附件") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @ExcelProperty("备注") - private String memo; - - @Schema(description = "创建时间") - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO deleted file mode 100644 index faa491d..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentSaveReqVO +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; - -@Schema(description = "管理后台 - 学生新增/修改 Request VO") -@Data -public class InfraStudentSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @NotEmpty(message = "简介不能为空") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "出生日期不能为空") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "性别不能为空") - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否有效不能为空") - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @NotEmpty(message = "头像不能为空") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @NotEmpty(message = "附件不能为空") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @NotEmpty(message = "备注不能为空") - private String memo; - - @Schema(description = "学生联系人列表") - private List studentContacts; - - @Schema(description = "学生班主任") - private InfraStudentTeacherDO studentTeacher; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService deleted file mode 100644 index afa7d22..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentService +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 学生 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraStudentService { - - /** - * 创建学生 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); - - /** - * 更新学生 - * - * @param updateReqVO 更新信息 - */ - void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); - - /** - * 删除学生 - * - * @param id 编号 - */ - void deleteStudent(Long id); - - /** - * 获得学生 - * - * @param id 编号 - * @return 学生 - */ - InfraStudentDO getStudent(Long id); - - /** - * 获得学生分页 - * - * @param pageReqVO 分页查询 - * @return 学生分页 - */ - PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); - - // ==================== 子表(学生联系人) ==================== - - /** - * 获得学生联系人列表 - * - * @param studentId 学生编号 - * @return 学生联系人列表 - */ - List getStudentContactListByStudentId(Long studentId); - - // ==================== 子表(学生班主任) ==================== - - /** - * 获得学生班主任 - * - * @param studentId 学生编号 - * @return 学生班主任 - */ - InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl deleted file mode 100644 index c57cba6..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImpl +++ /dev/null @@ -1,147 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 学生 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraStudentServiceImpl implements InfraStudentService { - - @Resource - private InfraStudentMapper studentMapper; - @Resource - private InfraStudentContactMapper studentContactMapper; - @Resource - private InfraStudentTeacherMapper studentTeacherMapper; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createStudent(InfraStudentSaveReqVO createReqVO) { - // 插入 - InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); - studentMapper.insert(student); - - // 插入子表 - createStudentContactList(student.getId(), createReqVO.getStudentContacts()); - createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); - // 返回 - return student.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateStudent(InfraStudentSaveReqVO updateReqVO) { - // 校验存在 - validateStudentExists(updateReqVO.getId()); - // 更新 - InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); - studentMapper.updateById(updateObj); - - // 更新子表 - updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); - updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteStudent(Long id) { - // 校验存在 - validateStudentExists(id); - // 删除 - studentMapper.deleteById(id); - - // 删除子表 - deleteStudentContactByStudentId(id); - deleteStudentTeacherByStudentId(id); - } - - private void validateStudentExists(Long id) { - if (studentMapper.selectById(id) == null) { - throw exception(STUDENT_NOT_EXISTS); - } - } - - @Override - public InfraStudentDO getStudent(Long id) { - return studentMapper.selectById(id); - } - - @Override - public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { - return studentMapper.selectPage(pageReqVO); - } - - // ==================== 子表(学生联系人) ==================== - - @Override - public List getStudentContactListByStudentId(Long studentId) { - return studentContactMapper.selectListByStudentId(studentId); - } - - private void createStudentContactList(Long studentId, List list) { - list.forEach(o -> o.setStudentId(studentId)); - studentContactMapper.insertBatch(list); - } - - private void updateStudentContactList(Long studentId, List list) { - deleteStudentContactByStudentId(studentId); - list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新 - createStudentContactList(studentId, list); - } - - private void deleteStudentContactByStudentId(Long studentId) { - studentContactMapper.deleteByStudentId(studentId); - } - - // ==================== 子表(学生班主任) ==================== - - @Override - public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { - return studentTeacherMapper.selectByStudentId(studentId); - } - - private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { - if (studentTeacher == null) { - return; - } - studentTeacher.setStudentId(studentId); - studentTeacherMapper.insert(studentTeacher); - } - - private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { - if (studentTeacher == null) { - return; - } - studentTeacher.setStudentId(studentId); - studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新 - studentTeacherMapper.insertOrUpdate(studentTeacher); - } - - private void deleteStudentTeacherByStudentId(Long studentId) { - studentTeacherMapper.deleteByStudentId(studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest deleted file mode 100644 index b5f4bf0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentServiceImplTest +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraStudentServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraStudentServiceImpl.class) -public class InfraStudentServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraStudentServiceImpl studentService; - - @Resource - private InfraStudentMapper studentMapper; - - @Test - public void testCreateStudent_success() { - // 准备参数 - InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); - - // 调用 - Long studentId = studentService.createStudent(createReqVO); - // 断言 - assertNotNull(studentId); - // 校验记录的属性是否正确 - InfraStudentDO student = studentMapper.selectById(studentId); - assertPojoEquals(createReqVO, student, "id"); - } - - @Test - public void testUpdateStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { - o.setId(dbStudent.getId()); // 设置更新的 ID - }); - - // 调用 - studentService.updateStudent(updateReqVO); - // 校验是否更新正确 - InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, student); - } - - @Test - public void testUpdateStudent_notExists() { - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); - } - - @Test - public void testDeleteStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbStudent.getId(); - - // 调用 - studentService.deleteStudent(id); - // 校验数据不存在了 - assertNull(studentMapper.selectById(id)); - } - - @Test - public void testDeleteStudent_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetStudentPage() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 - o.setName(null); - o.setBirthday(null); - o.setSex(null); - o.setEnabled(null); - o.setCreateTime(null); - }); - studentMapper.insert(dbStudent); - // 测试 name 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); - // 测试 birthday 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); - // 测试 sex 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); - // 测试 enabled 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); - // 测试 createTime 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); - // 准备参数 - InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); - reqVO.setName(null); - reqVO.setBirthday(null); - reqVO.setSex(null); - reqVO.setEnabled(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = studentService.getStudentPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbStudent, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO deleted file mode 100644 index c19cf9f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生班主任 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_teacher") -@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentTeacherDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper deleted file mode 100644 index 0521bba..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/java/InfraStudentTeacherMapper +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生班主任 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentTeacherMapper extends BaseMapperX { - - default InfraStudentTeacherDO selectByStudentId(Long studentId) { - return selectOne(InfraStudentTeacherDO::getStudentId, studentId); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentTeacherDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/js/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/js/index deleted file mode 100644 index b4e6ac5..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/js/index +++ /dev/null @@ -1,74 +0,0 @@ -import request from '@/utils/request' - -// 创建学生 -export function createStudent(data) { - return request({ - url: '/infra/student/create', - method: 'post', - data: data - }) -} - -// 更新学生 -export function updateStudent(data) { - return request({ - url: '/infra/student/update', - method: 'put', - data: data - }) -} - -// 删除学生 -export function deleteStudent(id) { - return request({ - url: '/infra/student/delete?id=' + id, - method: 'delete' - }) -} - -// 获得学生 -export function getStudent(id) { - return request({ - url: '/infra/student/get?id=' + id, - method: 'get' - }) -} - -// 获得学生分页 -export function getStudentPage(params) { - return request({ - url: '/infra/student/page', - method: 'get', - params - }) -} -// 导出学生 Excel -export function exportStudentExcel(params) { - return request({ - url: '/infra/student/export-excel', - method: 'get', - params, - responseType: 'blob' - }) -} - -// ==================== 子表(学生联系人) ==================== - - // 获得学生联系人列表 - export function getStudentContactListByStudentId(studentId) { - return request({ - url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId, - method: 'get' - }) - } - -// ==================== 子表(学生班主任) ==================== - - // 获得学生班主任 - export function getStudentTeacherByStudentId(studentId) { - return request({ - url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId, - method: 'get' - }) - } - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2 deleted file mode 100644 index 6c1875f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/sql/h2 +++ /dev/null @@ -1,17 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_student" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" varchar NOT NULL, - "birthday" varchar NOT NULL, - "sex" int NOT NULL, - "enabled" bit NOT NULL, - "avatar" varchar NOT NULL, - "video" varchar NOT NULL, - "memo" varchar NOT NULL, - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id") -) COMMENT '学生表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql deleted file mode 100644 index 83df279..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '学生管理', '', 2, 0, 888, - 'student', '', 'infra/demo/index', 0, 'InfraStudent' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生查询', 'infra:student:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生创建', 'infra:student:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生更新', 'infra:student:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生删除', 'infra:student:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生导出', 'infra:student:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm deleted file mode 100644 index c953bfa..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentContactForm +++ /dev/null @@ -1,177 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm deleted file mode 100644 index 6d93b61..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentForm +++ /dev/null @@ -1,180 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm deleted file mode 100644 index 0dac19b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/StudentTeacherForm +++ /dev/null @@ -1,127 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/index deleted file mode 100644 index 4607581..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/vue/index +++ /dev/null @@ -1,205 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper deleted file mode 100644 index 155aa5c..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_master_normal/xml/InfraStudentMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/assert.json deleted file mode 100644 index 5a37c37..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/assert.json +++ /dev/null @@ -1,49 +0,0 @@ -[ { - "contentPath" : "java/InfraStudentPageReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" -}, { - "contentPath" : "java/InfraStudentRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" -}, { - "contentPath" : "java/InfraStudentSaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" -}, { - "contentPath" : "java/InfraStudentController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" -}, { - "contentPath" : "java/InfraStudentDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" -}, { - "contentPath" : "java/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" -}, { - "contentPath" : "xml/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" -}, { - "contentPath" : "java/InfraStudentServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" -}, { - "contentPath" : "java/InfraStudentService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" -}, { - "contentPath" : "java/InfraStudentServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" -}, { - "contentPath": "js/index", - "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" -}, { - "contentPath" : "vue/StudentForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/StudentForm.vue" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/ErrorCodeConstants_手动操作 deleted file mode 100644 index f8be662..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/ErrorCodeConstants_手动操作 +++ /dev/null @@ -1,3 +0,0 @@ -// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! -// ========== 学生 TODO 补充编号 ========== -ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController deleted file mode 100644 index 3796982..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentController +++ /dev/null @@ -1,95 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; - -@Tag(name = "管理后台 - 学生") -@RestController -@RequestMapping("/infra/student") -@Validated -public class InfraStudentController { - - @Resource - private InfraStudentService studentService; - - @PostMapping("/create") - @Operation(summary = "创建学生") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { - return success(studentService.createStudent(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新学生") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { - studentService.updateStudent(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除学生") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudent(@RequestParam("id") Long id) { - studentService.deleteStudent(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得学生") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudent(@RequestParam("id") Long id) { - InfraStudentDO student = studentService.getStudent(id); - return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得学生分页") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { - PageResult pageResult = studentService.getStudentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出学生 Excel") - @PreAuthorize("@ss.hasPermission('infra:student:export')") - @OperateLog(type = EXPORT) - public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = studentService.getStudentPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, - BeanUtils.toBean(list, InfraStudentRespVO.class)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO deleted file mode 100644 index b0d4bd2..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentDO +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生 DO - * - * @author 芋道源码 - */ -@TableName("infra_student") -@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper deleted file mode 100644 index 34e70a0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 学生 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentMapper extends BaseMapperX { - - default PageResult selectPage(InfraStudentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) - .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) - .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) - .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) - .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(InfraStudentDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO deleted file mode 100644 index 41a3730..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentPageReqVO +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 学生分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class InfraStudentPageReqVO extends PageParam { - - @Schema(description = "名字", example = "芋头") - private String name; - - @Schema(description = "出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", example = "1") - private Integer sex; - - @Schema(description = "是否有效", example = "true") - private Boolean enabled; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO deleted file mode 100644 index c41a550..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentRespVO +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; - -@Schema(description = "管理后台 - 学生 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraStudentRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @ExcelProperty("简介") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "性别", converter = DictConvert.class) - @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @ExcelProperty(value = "是否有效", converter = DictConvert.class) - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @ExcelProperty("头像") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @ExcelProperty("附件") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @ExcelProperty("备注") - private String memo; - - @Schema(description = "创建时间") - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO deleted file mode 100644 index 43e7f14..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentSaveReqVO +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 学生新增/修改 Request VO") -@Data -public class InfraStudentSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @NotEmpty(message = "简介不能为空") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "出生日期不能为空") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "性别不能为空") - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否有效不能为空") - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @NotEmpty(message = "头像不能为空") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @NotEmpty(message = "附件不能为空") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @NotEmpty(message = "备注不能为空") - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService deleted file mode 100644 index c4a0e17..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentService +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 学生 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraStudentService { - - /** - * 创建学生 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); - - /** - * 更新学生 - * - * @param updateReqVO 更新信息 - */ - void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); - - /** - * 删除学生 - * - * @param id 编号 - */ - void deleteStudent(Long id); - - /** - * 获得学生 - * - * @param id 编号 - * @return 学生 - */ - InfraStudentDO getStudent(Long id); - - /** - * 获得学生分页 - * - * @param pageReqVO 分页查询 - * @return 学生分页 - */ - PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl deleted file mode 100644 index 2292a66..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImpl +++ /dev/null @@ -1,74 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 学生 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraStudentServiceImpl implements InfraStudentService { - - @Resource - private InfraStudentMapper studentMapper; - - @Override - public Long createStudent(InfraStudentSaveReqVO createReqVO) { - // 插入 - InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); - studentMapper.insert(student); - // 返回 - return student.getId(); - } - - @Override - public void updateStudent(InfraStudentSaveReqVO updateReqVO) { - // 校验存在 - validateStudentExists(updateReqVO.getId()); - // 更新 - InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); - studentMapper.updateById(updateObj); - } - - @Override - public void deleteStudent(Long id) { - // 校验存在 - validateStudentExists(id); - // 删除 - studentMapper.deleteById(id); - } - - private void validateStudentExists(Long id) { - if (studentMapper.selectById(id) == null) { - throw exception(STUDENT_NOT_EXISTS); - } - } - - @Override - public InfraStudentDO getStudent(Long id) { - return studentMapper.selectById(id); - } - - @Override - public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { - return studentMapper.selectPage(pageReqVO); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest deleted file mode 100644 index b5f4bf0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/java/InfraStudentServiceImplTest +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraStudentServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraStudentServiceImpl.class) -public class InfraStudentServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraStudentServiceImpl studentService; - - @Resource - private InfraStudentMapper studentMapper; - - @Test - public void testCreateStudent_success() { - // 准备参数 - InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); - - // 调用 - Long studentId = studentService.createStudent(createReqVO); - // 断言 - assertNotNull(studentId); - // 校验记录的属性是否正确 - InfraStudentDO student = studentMapper.selectById(studentId); - assertPojoEquals(createReqVO, student, "id"); - } - - @Test - public void testUpdateStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { - o.setId(dbStudent.getId()); // 设置更新的 ID - }); - - // 调用 - studentService.updateStudent(updateReqVO); - // 校验是否更新正确 - InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, student); - } - - @Test - public void testUpdateStudent_notExists() { - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); - } - - @Test - public void testDeleteStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbStudent.getId(); - - // 调用 - studentService.deleteStudent(id); - // 校验数据不存在了 - assertNull(studentMapper.selectById(id)); - } - - @Test - public void testDeleteStudent_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetStudentPage() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 - o.setName(null); - o.setBirthday(null); - o.setSex(null); - o.setEnabled(null); - o.setCreateTime(null); - }); - studentMapper.insert(dbStudent); - // 测试 name 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); - // 测试 birthday 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); - // 测试 sex 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); - // 测试 enabled 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); - // 测试 createTime 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); - // 准备参数 - InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); - reqVO.setName(null); - reqVO.setBirthday(null); - reqVO.setSex(null); - reqVO.setEnabled(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = studentService.getStudentPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbStudent, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/js/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/js/index deleted file mode 100644 index 44db468..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/js/index +++ /dev/null @@ -1,53 +0,0 @@ -import request from '@/utils/request' - -// 创建学生 -export function createStudent(data) { - return request({ - url: '/infra/student/create', - method: 'post', - data: data - }) -} - -// 更新学生 -export function updateStudent(data) { - return request({ - url: '/infra/student/update', - method: 'put', - data: data - }) -} - -// 删除学生 -export function deleteStudent(id) { - return request({ - url: '/infra/student/delete?id=' + id, - method: 'delete' - }) -} - -// 获得学生 -export function getStudent(id) { - return request({ - url: '/infra/student/get?id=' + id, - method: 'get' - }) -} - -// 获得学生分页 -export function getStudentPage(params) { - return request({ - url: '/infra/student/page', - method: 'get', - params - }) -} -// 导出学生 Excel -export function exportStudentExcel(params) { - return request({ - url: '/infra/student/export-excel', - method: 'get', - params, - responseType: 'blob' - }) -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/sql/h2 deleted file mode 100644 index 6c1875f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/sql/h2 +++ /dev/null @@ -1,17 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_student" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" varchar NOT NULL, - "birthday" varchar NOT NULL, - "sex" int NOT NULL, - "enabled" bit NOT NULL, - "avatar" varchar NOT NULL, - "video" varchar NOT NULL, - "memo" varchar NOT NULL, - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id") -) COMMENT '学生表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/sql/sql deleted file mode 100644 index 83df279..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '学生管理', '', 2, 0, 888, - 'student', '', 'infra/demo/index', 0, 'InfraStudent' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生查询', 'infra:student:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生创建', 'infra:student:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生更新', 'infra:student:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生删除', 'infra:student:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生导出', 'infra:student:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm deleted file mode 100644 index d89e506..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/vue/StudentForm +++ /dev/null @@ -1,149 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/vue/index deleted file mode 100644 index 4607581..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/vue/index +++ /dev/null @@ -1,205 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper deleted file mode 100644 index 155aa5c..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_one/xml/InfraStudentMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/assert.json deleted file mode 100644 index a7d2f56..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/assert.json +++ /dev/null @@ -1,49 +0,0 @@ -[ { - "contentPath" : "java/InfraCategoryListReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java" -}, { - "contentPath" : "java/InfraCategoryRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java" -}, { - "contentPath" : "java/InfraCategorySaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java" -}, { - "contentPath" : "java/InfraCategoryController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java" -}, { - "contentPath" : "java/InfraCategoryDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java" -}, { - "contentPath" : "java/InfraCategoryMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java" -}, { - "contentPath" : "xml/InfraCategoryMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml" -}, { - "contentPath" : "java/InfraCategoryServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java" -}, { - "contentPath" : "java/InfraCategoryService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java" -}, { - "contentPath" : "java/InfraCategoryServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/index.vue" -}, { - "contentPath": "js/index", - "filePath": "yudao-ui-admin-vue2/src/api/infra/demo/index.js" -}, { - "contentPath" : "vue/CategoryForm", - "filePath" : "yudao-ui-admin-vue2/src/views/infra/demo/CategoryForm.vue" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作 deleted file mode 100644 index 36df675..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/ErrorCodeConstants_手动操作 +++ /dev/null @@ -1,8 +0,0 @@ -// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! -// ========== 分类 TODO 补充编号 ========== -ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, "分类不存在"); -ErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子分类,无法删除"); -ErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级分类不存在"); -ErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父分类"); -ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该名字的分类"); -ErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子InfraCategory为父InfraCategory"); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController deleted file mode 100644 index a7b2f81..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryController +++ /dev/null @@ -1,94 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService; - -@Tag(name = "管理后台 - 分类") -@RestController -@RequestMapping("/infra/category") -@Validated -public class InfraCategoryController { - - @Resource - private InfraCategoryService categoryService; - - @PostMapping("/create") - @Operation(summary = "创建分类") - @PreAuthorize("@ss.hasPermission('infra:category:create')") - public CommonResult createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) { - return success(categoryService.createCategory(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新分类") - @PreAuthorize("@ss.hasPermission('infra:category:update')") - public CommonResult updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) { - categoryService.updateCategory(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除分类") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:category:delete')") - public CommonResult deleteCategory(@RequestParam("id") Long id) { - categoryService.deleteCategory(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得分类") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:category:query')") - public CommonResult getCategory(@RequestParam("id") Long id) { - InfraCategoryDO category = categoryService.getCategory(id); - return success(BeanUtils.toBean(category, InfraCategoryRespVO.class)); - } - - @GetMapping("/list") - @Operation(summary = "获得分类列表") - @PreAuthorize("@ss.hasPermission('infra:category:query')") - public CommonResult> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) { - List list = categoryService.getCategoryList(listReqVO); - return success(BeanUtils.toBean(list, InfraCategoryRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出分类 Excel") - @PreAuthorize("@ss.hasPermission('infra:category:export')") - @OperateLog(type = EXPORT) - public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO, - HttpServletResponse response) throws IOException { - List list = categoryService.getCategoryList(listReqVO); - // 导出 Excel - ExcelUtils.write(response, "分类.xls", "数据", InfraCategoryRespVO.class, - BeanUtils.toBean(list, InfraCategoryRespVO.class)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO deleted file mode 100644 index 9bf21c0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryDO +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 分类 DO - * - * @author 芋道源码 - */ -@TableName("infra_category") -@KeySequence("infra_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraCategoryDO extends BaseDO { - - public static final Long PARENT_ID_ROOT = 0L; - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 父编号 - */ - private Long parentId; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO deleted file mode 100644 index e5c6f18..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryListReqVO +++ /dev/null @@ -1,15 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -@Schema(description = "管理后台 - 分类列表 Request VO") -@Data -public class InfraCategoryListReqVO { - - @Schema(description = "名字", example = "芋头") - private String name; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper deleted file mode 100644 index 9dadbf1..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryMapper +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 分类 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraCategoryMapper extends BaseMapperX { - - default List selectList(InfraCategoryListReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .likeIfPresent(InfraCategoryDO::getName, reqVO.getName()) - .orderByDesc(InfraCategoryDO::getId)); - } - - default InfraCategoryDO selectByParentIdAndName(Long parentId, String name) { - return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name); - } - - default Long selectCountByParentId(Long parentId) { - return selectCount(InfraCategoryDO::getParentId, parentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO deleted file mode 100644 index 6325d86..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryRespVO +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import com.alibaba.excel.annotation.*; - -@Schema(description = "管理后台 - 分类 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraCategoryRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - @ExcelProperty("父编号") - private Long parentId; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO deleted file mode 100644 index 3c03b97..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategorySaveReqVO +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; - -@Schema(description = "管理后台 - 分类新增/修改 Request VO") -@Data -public class InfraCategorySaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - @NotNull(message = "父编号不能为空") - private Long parentId; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService deleted file mode 100644 index 9d0ae1a..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryService +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 分类 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraCategoryService { - - /** - * 创建分类 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createCategory(@Valid InfraCategorySaveReqVO createReqVO); - - /** - * 更新分类 - * - * @param updateReqVO 更新信息 - */ - void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO); - - /** - * 删除分类 - * - * @param id 编号 - */ - void deleteCategory(Long id); - - /** - * 获得分类 - * - * @param id 编号 - * @return 分类 - */ - InfraCategoryDO getCategory(Long id); - - /** - * 获得分类列表 - * - * @param listReqVO 查询条件 - * @return 分类列表 - */ - List getCategoryList(InfraCategoryListReqVO listReqVO); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl deleted file mode 100644 index 351568b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImpl +++ /dev/null @@ -1,136 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 分类 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraCategoryServiceImpl implements InfraCategoryService { - - @Resource - private InfraCategoryMapper categoryMapper; - - @Override - public Long createCategory(InfraCategorySaveReqVO createReqVO) { - // 校验父编号的有效性 - validateParentCategory(null, createReqVO.getParentId()); - // 校验名字的唯一性 - validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName()); - - // 插入 - InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class); - categoryMapper.insert(category); - // 返回 - return category.getId(); - } - - @Override - public void updateCategory(InfraCategorySaveReqVO updateReqVO) { - // 校验存在 - validateCategoryExists(updateReqVO.getId()); - // 校验父编号的有效性 - validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId()); - // 校验名字的唯一性 - validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); - - // 更新 - InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class); - categoryMapper.updateById(updateObj); - } - - @Override - public void deleteCategory(Long id) { - // 校验存在 - validateCategoryExists(id); - // 校验是否有子分类 - if (categoryMapper.selectCountByParentId(id) > 0) { - throw exception(CATEGORY_EXITS_CHILDREN); - } - // 删除 - categoryMapper.deleteById(id); - } - - private void validateCategoryExists(Long id) { - if (categoryMapper.selectById(id) == null) { - throw exception(CATEGORY_NOT_EXISTS); - } - } - - private void validateParentCategory(Long id, Long parentId) { - if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { - return; - } - // 1. 不能设置自己为父分类 - if (Objects.equals(id, parentId)) { - throw exception(CATEGORY_PARENT_ERROR); - } - // 2. 父分类不存在 - CategoryDO parentCategory = categoryMapper.selectById(parentId); - if (parentCategory == null) { - throw exception(CATEGORY_PARENT_NOT_EXITS); - } - // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路 - if (id == null) { // id 为空,说明新增,不需要考虑环路 - return; - } - for (int i = 0; i < Short.MAX_VALUE; i++) { - // 3.1 校验环路 - parentId = parentCategory.getParentId(); - if (Objects.equals(id, parentId)) { - throw exception(CATEGORY_PARENT_IS_CHILD); - } - // 3.2 继续递归下一级父分类 - if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { - break; - } - parentCategory = categoryMapper.selectById(parentId); - if (parentCategory == null) { - break; - } - } - } - - private void validateCategoryNameUnique(Long id, Long parentId, String name) { - CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name); - if (category == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的分类 - if (id == null) { - throw exception(CATEGORY_NAME_DUPLICATE); - } - if (!Objects.equals(category.getId(), id)) { - throw exception(CATEGORY_NAME_DUPLICATE); - } - } - - @Override - public InfraCategoryDO getCategory(Long id) { - return categoryMapper.selectById(id); - } - - @Override - public List getCategoryList(InfraCategoryListReqVO listReqVO) { - return categoryMapper.selectList(listReqVO); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest deleted file mode 100644 index efb70fd..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/java/InfraCategoryServiceImplTest +++ /dev/null @@ -1,129 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraCategoryServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraCategoryServiceImpl.class) -public class InfraCategoryServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraCategoryServiceImpl categoryService; - - @Resource - private InfraCategoryMapper categoryMapper; - - @Test - public void testCreateCategory_success() { - // 准备参数 - InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null); - - // 调用 - Long categoryId = categoryService.createCategory(createReqVO); - // 断言 - assertNotNull(categoryId); - // 校验记录的属性是否正确 - InfraCategoryDO category = categoryMapper.selectById(categoryId); - assertPojoEquals(createReqVO, category, "id"); - } - - @Test - public void testUpdateCategory_success() { - // mock 数据 - InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); - categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> { - o.setId(dbCategory.getId()); // 设置更新的 ID - }); - - // 调用 - categoryService.updateCategory(updateReqVO); - // 校验是否更新正确 - InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, category); - } - - @Test - public void testUpdateCategory_notExists() { - // 准备参数 - InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS); - } - - @Test - public void testDeleteCategory_success() { - // mock 数据 - InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); - categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbCategory.getId(); - - // 调用 - categoryService.deleteCategory(id); - // 校验数据不存在了 - assertNull(categoryMapper.selectById(id)); - } - - @Test - public void testDeleteCategory_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetCategoryList() { - // mock 数据 - InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到 - o.setName(null); - }); - categoryMapper.insert(dbCategory); - // 测试 name 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null))); - // 准备参数 - InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO(); - reqVO.setName(null); - - // 调用 - List list = categoryService.getCategoryList(reqVO); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbCategory, list.get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/js/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/js/index deleted file mode 100644 index 1e6ffdc..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/js/index +++ /dev/null @@ -1,53 +0,0 @@ -import request from '@/utils/request' - -// 创建分类 -export function createCategory(data) { - return request({ - url: '/infra/category/create', - method: 'post', - data: data - }) -} - -// 更新分类 -export function updateCategory(data) { - return request({ - url: '/infra/category/update', - method: 'put', - data: data - }) -} - -// 删除分类 -export function deleteCategory(id) { - return request({ - url: '/infra/category/delete?id=' + id, - method: 'delete' - }) -} - -// 获得分类 -export function getCategory(id) { - return request({ - url: '/infra/category/get?id=' + id, - method: 'get' - }) -} - -// 获得分类列表 -export function getCategoryList(params) { - return request({ - url: '/infra/category/list', - method: 'get', - params - }) -} -// 导出分类 Excel -export function exportCategoryExcel(params) { - return request({ - url: '/infra/category/export-excel', - method: 'get', - params, - responseType: 'blob' - }) -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/sql/h2 deleted file mode 100644 index 4141766..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/sql/h2 +++ /dev/null @@ -1,10 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_category" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" bigint NOT NULL, - PRIMARY KEY ("id") -) COMMENT '分类表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_category"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/sql/sql deleted file mode 100644 index 8140948..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '分类管理', '', 2, 0, 888, - 'category', '', 'infra/demo/index', 0, 'InfraCategory' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类查询', 'infra:category:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类创建', 'infra:category:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类更新', 'infra:category:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类删除', 'infra:category:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类导出', 'infra:category:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm deleted file mode 100644 index 7fa06e8..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/vue/CategoryForm +++ /dev/null @@ -1,130 +0,0 @@ - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/vue/index deleted file mode 100644 index 88da682..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/vue/index +++ /dev/null @@ -1,161 +0,0 @@ - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper deleted file mode 100644 index 025ac85..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue2_tree/xml/InfraCategoryMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/assert.json deleted file mode 100644 index 0937ba9..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/assert.json +++ /dev/null @@ -1,73 +0,0 @@ -[ { - "contentPath" : "java/InfraStudentPageReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" -}, { - "contentPath" : "java/InfraStudentRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" -}, { - "contentPath" : "java/InfraStudentSaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" -}, { - "contentPath" : "java/InfraStudentController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" -}, { - "contentPath" : "java/InfraStudentDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" -}, { - "contentPath" : "java/InfraStudentContactDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" -}, { - "contentPath" : "java/InfraStudentTeacherDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" -}, { - "contentPath" : "java/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" -}, { - "contentPath" : "java/InfraStudentContactMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" -}, { - "contentPath" : "java/InfraStudentTeacherMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" -}, { - "contentPath" : "xml/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" -}, { - "contentPath" : "java/InfraStudentServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" -}, { - "contentPath" : "java/InfraStudentService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" -}, { - "contentPath" : "java/InfraStudentServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" -}, { - "contentPath" : "vue/StudentForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue" -}, { - "contentPath" : "vue/StudentContactForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue" -}, { - "contentPath" : "vue/StudentTeacherForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue" -}, { - "contentPath" : "vue/StudentContactList", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactList.vue" -}, { - "contentPath" : "vue/StudentTeacherList", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherList.vue" -}, { - "contentPath" : "ts/index", - "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作 deleted file mode 100644 index d3201de..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/ErrorCodeConstants_手动操作 +++ /dev/null @@ -1,6 +0,0 @@ -// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! -// ========== 学生 TODO 补充编号 ========== -ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); -ErrorCode STUDENT_CONTACT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生联系人不存在"); -ErrorCode STUDENT_TEACHER_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任不存在"); -ErrorCode STUDENT_TEACHER_EXISTS = new ErrorCode(TODO 补充编号, "学生班主任已存在"); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO deleted file mode 100644 index 17c668e..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生联系人 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_contact") -@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentContactDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper deleted file mode 100644 index ca662d1..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentContactMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生联系人 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentContactMapper extends BaseMapperX { - - default PageResult selectPage(PageParam reqVO, Long studentId) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eq(InfraStudentContactDO::getStudentId, studentId) - .orderByDesc(InfraStudentContactDO::getId)); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentContactDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController deleted file mode 100644 index d6f2018..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentController +++ /dev/null @@ -1,183 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; - -@Tag(name = "管理后台 - 学生") -@RestController -@RequestMapping("/infra/student") -@Validated -public class InfraStudentController { - - @Resource - private InfraStudentService studentService; - - @PostMapping("/create") - @Operation(summary = "创建学生") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { - return success(studentService.createStudent(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新学生") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { - studentService.updateStudent(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除学生") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudent(@RequestParam("id") Long id) { - studentService.deleteStudent(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得学生") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudent(@RequestParam("id") Long id) { - InfraStudentDO student = studentService.getStudent(id); - return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得学生分页") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { - PageResult pageResult = studentService.getStudentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出学生 Excel") - @PreAuthorize("@ss.hasPermission('infra:student:export')") - @OperateLog(type = EXPORT) - public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = studentService.getStudentPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, - BeanUtils.toBean(list, InfraStudentRespVO.class)); - } - - // ==================== 子表(学生联系人) ==================== - - @GetMapping("/student-contact/page") - @Operation(summary = "获得学生联系人分页") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentContactPage(PageParam pageReqVO, - @RequestParam("studentId") Long studentId) { - return success(studentService.getStudentContactPage(pageReqVO, studentId)); - } - - @PostMapping("/student-contact/create") - @Operation(summary = "创建学生联系人") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { - return success(studentService.createStudentContact(studentContact)); - } - - @PutMapping("/student-contact/update") - @Operation(summary = "更新学生联系人") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudentContact(@Valid @RequestBody InfraStudentContactDO studentContact) { - studentService.updateStudentContact(studentContact); - return success(true); - } - - @DeleteMapping("/student-contact/delete") - @Parameter(name = "id", description = "编号", required = true) - @Operation(summary = "删除学生联系人") - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudentContact(@RequestParam("id") Long id) { - studentService.deleteStudentContact(id); - return success(true); - } - - @GetMapping("/student-contact/get") - @Operation(summary = "获得学生联系人") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudentContact(@RequestParam("id") Long id) { - return success(studentService.getStudentContact(id)); - } - - // ==================== 子表(学生班主任) ==================== - - @GetMapping("/student-teacher/page") - @Operation(summary = "获得学生班主任分页") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentTeacherPage(PageParam pageReqVO, - @RequestParam("studentId") Long studentId) { - return success(studentService.getStudentTeacherPage(pageReqVO, studentId)); - } - - @PostMapping("/student-teacher/create") - @Operation(summary = "创建学生班主任") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { - return success(studentService.createStudentTeacher(studentTeacher)); - } - - @PutMapping("/student-teacher/update") - @Operation(summary = "更新学生班主任") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudentTeacher(@Valid @RequestBody InfraStudentTeacherDO studentTeacher) { - studentService.updateStudentTeacher(studentTeacher); - return success(true); - } - - @DeleteMapping("/student-teacher/delete") - @Parameter(name = "id", description = "编号", required = true) - @Operation(summary = "删除学生班主任") - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudentTeacher(@RequestParam("id") Long id) { - studentService.deleteStudentTeacher(id); - return success(true); - } - - @GetMapping("/student-teacher/get") - @Operation(summary = "获得学生班主任") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudentTeacher(@RequestParam("id") Long id) { - return success(studentService.getStudentTeacher(id)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO deleted file mode 100644 index b0d4bd2..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentDO +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生 DO - * - * @author 芋道源码 - */ -@TableName("infra_student") -@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper deleted file mode 100644 index 34e70a0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 学生 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentMapper extends BaseMapperX { - - default PageResult selectPage(InfraStudentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) - .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) - .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) - .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) - .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(InfraStudentDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO deleted file mode 100644 index 41a3730..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentPageReqVO +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 学生分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class InfraStudentPageReqVO extends PageParam { - - @Schema(description = "名字", example = "芋头") - private String name; - - @Schema(description = "出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", example = "1") - private Integer sex; - - @Schema(description = "是否有效", example = "true") - private Boolean enabled; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO deleted file mode 100644 index c41a550..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentRespVO +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; - -@Schema(description = "管理后台 - 学生 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraStudentRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @ExcelProperty("简介") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "性别", converter = DictConvert.class) - @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @ExcelProperty(value = "是否有效", converter = DictConvert.class) - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @ExcelProperty("头像") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @ExcelProperty("附件") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @ExcelProperty("备注") - private String memo; - - @Schema(description = "创建时间") - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO deleted file mode 100644 index eaadf74..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentSaveReqVO +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; - -@Schema(description = "管理后台 - 学生新增/修改 Request VO") -@Data -public class InfraStudentSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @NotEmpty(message = "简介不能为空") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "出生日期不能为空") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "性别不能为空") - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否有效不能为空") - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @NotEmpty(message = "头像不能为空") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @NotEmpty(message = "附件不能为空") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @NotEmpty(message = "备注不能为空") - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService deleted file mode 100644 index 7df090d..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentService +++ /dev/null @@ -1,139 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 学生 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraStudentService { - - /** - * 创建学生 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); - - /** - * 更新学生 - * - * @param updateReqVO 更新信息 - */ - void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); - - /** - * 删除学生 - * - * @param id 编号 - */ - void deleteStudent(Long id); - - /** - * 获得学生 - * - * @param id 编号 - * @return 学生 - */ - InfraStudentDO getStudent(Long id); - - /** - * 获得学生分页 - * - * @param pageReqVO 分页查询 - * @return 学生分页 - */ - PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); - - // ==================== 子表(学生联系人) ==================== - - /** - * 获得学生联系人分页 - * - * @param pageReqVO 分页查询 - * @param studentId 学生编号 - * @return 学生联系人分页 - */ - PageResult getStudentContactPage(PageParam pageReqVO, Long studentId); - - /** - * 创建学生联系人 - * - * @param studentContact 创建信息 - * @return 编号 - */ - Long createStudentContact(@Valid InfraStudentContactDO studentContact); - - /** - * 更新学生联系人 - * - * @param studentContact 更新信息 - */ - void updateStudentContact(@Valid InfraStudentContactDO studentContact); - - /** - * 删除学生联系人 - * - * @param id 编号 - */ - void deleteStudentContact(Long id); - - /** - * 获得学生联系人 - * - * @param id 编号 - * @return 学生联系人 - */ - InfraStudentContactDO getStudentContact(Long id); - - // ==================== 子表(学生班主任) ==================== - - /** - * 获得学生班主任分页 - * - * @param pageReqVO 分页查询 - * @param studentId 学生编号 - * @return 学生班主任分页 - */ - PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId); - - /** - * 创建学生班主任 - * - * @param studentTeacher 创建信息 - * @return 编号 - */ - Long createStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); - - /** - * 更新学生班主任 - * - * @param studentTeacher 更新信息 - */ - void updateStudentTeacher(@Valid InfraStudentTeacherDO studentTeacher); - - /** - * 删除学生班主任 - * - * @param id 编号 - */ - void deleteStudentTeacher(Long id); - - /** - * 获得学生班主任 - * - * @param id 编号 - * @return 学生班主任 - */ - InfraStudentTeacherDO getStudentTeacher(Long id); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl deleted file mode 100644 index 793b2dd..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImpl +++ /dev/null @@ -1,180 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 学生 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraStudentServiceImpl implements InfraStudentService { - - @Resource - private InfraStudentMapper studentMapper; - @Resource - private InfraStudentContactMapper studentContactMapper; - @Resource - private InfraStudentTeacherMapper studentTeacherMapper; - - @Override - public Long createStudent(InfraStudentSaveReqVO createReqVO) { - // 插入 - InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); - studentMapper.insert(student); - // 返回 - return student.getId(); - } - - @Override - public void updateStudent(InfraStudentSaveReqVO updateReqVO) { - // 校验存在 - validateStudentExists(updateReqVO.getId()); - // 更新 - InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); - studentMapper.updateById(updateObj); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteStudent(Long id) { - // 校验存在 - validateStudentExists(id); - // 删除 - studentMapper.deleteById(id); - - // 删除子表 - deleteStudentContactByStudentId(id); - deleteStudentTeacherByStudentId(id); - } - - private void validateStudentExists(Long id) { - if (studentMapper.selectById(id) == null) { - throw exception(STUDENT_NOT_EXISTS); - } - } - - @Override - public InfraStudentDO getStudent(Long id) { - return studentMapper.selectById(id); - } - - @Override - public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { - return studentMapper.selectPage(pageReqVO); - } - - // ==================== 子表(学生联系人) ==================== - - @Override - public PageResult getStudentContactPage(PageParam pageReqVO, Long studentId) { - return studentContactMapper.selectPage(pageReqVO, studentId); - } - - @Override - public Long createStudentContact(InfraStudentContactDO studentContact) { - studentContactMapper.insert(studentContact); - return studentContact.getId(); - } - - @Override - public void updateStudentContact(InfraStudentContactDO studentContact) { - // 校验存在 - validateStudentContactExists(studentContact.getId()); - // 更新 - studentContactMapper.updateById(studentContact); - } - - @Override - public void deleteStudentContact(Long id) { - // 校验存在 - validateStudentContactExists(id); - // 删除 - studentContactMapper.deleteById(id); - } - - @Override - public InfraStudentContactDO getStudentContact(Long id) { - return studentContactMapper.selectById(id); - } - - private void validateStudentContactExists(Long id) { - if (studentContactMapper.selectById(id) == null) { - throw exception(STUDENT_CONTACT_NOT_EXISTS); - } - } - - private void deleteStudentContactByStudentId(Long studentId) { - studentContactMapper.deleteByStudentId(studentId); - } - - // ==================== 子表(学生班主任) ==================== - - @Override - public PageResult getStudentTeacherPage(PageParam pageReqVO, Long studentId) { - return studentTeacherMapper.selectPage(pageReqVO, studentId); - } - - @Override - public Long createStudentTeacher(InfraStudentTeacherDO studentTeacher) { - // 校验是否已经存在 - if (studentTeacherMapper.selectByStudentId(studentTeacher.getStudentId()) != null) { - throw exception(STUDENT_TEACHER_EXISTS); - } - // 插入 - studentTeacherMapper.insert(studentTeacher); - return studentTeacher.getId(); - } - - @Override - public void updateStudentTeacher(InfraStudentTeacherDO studentTeacher) { - // 校验存在 - validateStudentTeacherExists(studentTeacher.getId()); - // 更新 - studentTeacherMapper.updateById(studentTeacher); - } - - @Override - public void deleteStudentTeacher(Long id) { - // 校验存在 - validateStudentTeacherExists(id); - // 删除 - studentTeacherMapper.deleteById(id); - } - - @Override - public InfraStudentTeacherDO getStudentTeacher(Long id) { - return studentTeacherMapper.selectById(id); - } - - private void validateStudentTeacherExists(Long id) { - if (studentTeacherMapper.selectById(id) == null) { - throw exception(STUDENT_TEACHER_NOT_EXISTS); - } - } - - private void deleteStudentTeacherByStudentId(Long studentId) { - studentTeacherMapper.deleteByStudentId(studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest deleted file mode 100644 index b5f4bf0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentServiceImplTest +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraStudentServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraStudentServiceImpl.class) -public class InfraStudentServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraStudentServiceImpl studentService; - - @Resource - private InfraStudentMapper studentMapper; - - @Test - public void testCreateStudent_success() { - // 准备参数 - InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); - - // 调用 - Long studentId = studentService.createStudent(createReqVO); - // 断言 - assertNotNull(studentId); - // 校验记录的属性是否正确 - InfraStudentDO student = studentMapper.selectById(studentId); - assertPojoEquals(createReqVO, student, "id"); - } - - @Test - public void testUpdateStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { - o.setId(dbStudent.getId()); // 设置更新的 ID - }); - - // 调用 - studentService.updateStudent(updateReqVO); - // 校验是否更新正确 - InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, student); - } - - @Test - public void testUpdateStudent_notExists() { - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); - } - - @Test - public void testDeleteStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbStudent.getId(); - - // 调用 - studentService.deleteStudent(id); - // 校验数据不存在了 - assertNull(studentMapper.selectById(id)); - } - - @Test - public void testDeleteStudent_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetStudentPage() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 - o.setName(null); - o.setBirthday(null); - o.setSex(null); - o.setEnabled(null); - o.setCreateTime(null); - }); - studentMapper.insert(dbStudent); - // 测试 name 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); - // 测试 birthday 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); - // 测试 sex 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); - // 测试 enabled 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); - // 测试 createTime 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); - // 准备参数 - InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); - reqVO.setName(null); - reqVO.setBirthday(null); - reqVO.setSex(null); - reqVO.setEnabled(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = studentService.getStudentPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbStudent, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO deleted file mode 100644 index c19cf9f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生班主任 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_teacher") -@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentTeacherDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper deleted file mode 100644 index 994212d..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/java/InfraStudentTeacherMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生班主任 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentTeacherMapper extends BaseMapperX { - - default PageResult selectPage(PageParam reqVO, Long studentId) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eq(InfraStudentTeacherDO::getStudentId, studentId) - .orderByDesc(InfraStudentTeacherDO::getId)); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentTeacherDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2 deleted file mode 100644 index 6c1875f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/sql/h2 +++ /dev/null @@ -1,17 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_student" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" varchar NOT NULL, - "birthday" varchar NOT NULL, - "sex" int NOT NULL, - "enabled" bit NOT NULL, - "avatar" varchar NOT NULL, - "video" varchar NOT NULL, - "memo" varchar NOT NULL, - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id") -) COMMENT '学生表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql deleted file mode 100644 index 83df279..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '学生管理', '', 2, 0, 888, - 'student', '', 'infra/demo/index', 0, 'InfraStudent' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生查询', 'infra:student:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生创建', 'infra:student:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生更新', 'infra:student:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生删除', 'infra:student:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生导出', 'infra:student:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/ts/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/ts/index deleted file mode 100644 index 2fe87b7..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/ts/index +++ /dev/null @@ -1,95 +0,0 @@ -import request from '@/config/axios' - -export interface StudentVO { - id: number - name: string - description: string - birthday: Date - sex: number - enabled: boolean - avatar: string - video: string - memo: string -} - -// 查询学生分页 -export const getStudentPage = async (params) => { - return await request.get({ url: `/infra/student/page`, params }) -} - -// 查询学生详情 -export const getStudent = async (id: number) => { - return await request.get({ url: `/infra/student/get?id=` + id }) -} - -// 新增学生 -export const createStudent = async (data: StudentVO) => { - return await request.post({ url: `/infra/student/create`, data }) -} - -// 修改学生 -export const updateStudent = async (data: StudentVO) => { - return await request.put({ url: `/infra/student/update`, data }) -} - -// 删除学生 -export const deleteStudent = async (id: number) => { - return await request.delete({ url: `/infra/student/delete?id=` + id }) -} - -// 导出学生 Excel -export const exportStudent = async (params) => { - return await request.download({ url: `/infra/student/export-excel`, params }) -} - -// ==================== 子表(学生联系人) ==================== - -// 获得学生联系人分页 -export const getStudentContactPage = async (params) => { - return await request.get({ url: `/infra/student/student-contact/page`, params }) -} -// 新增学生联系人 -export const createStudentContact = async (data) => { - return await request.post({ url: `/infra/student/student-contact/create`, data }) -} - -// 修改学生联系人 -export const updateStudentContact = async (data) => { - return await request.put({ url: `/infra/student/student-contact/update`, data }) -} - -// 删除学生联系人 -export const deleteStudentContact = async (id: number) => { - return await request.delete({ url: `/infra/student/student-contact/delete?id=` + id }) -} - -// 获得学生联系人 -export const getStudentContact = async (id: number) => { - return await request.get({ url: `/infra/student/student-contact/get?id=` + id }) -} - -// ==================== 子表(学生班主任) ==================== - -// 获得学生班主任分页 -export const getStudentTeacherPage = async (params) => { - return await request.get({ url: `/infra/student/student-teacher/page`, params }) -} -// 新增学生班主任 -export const createStudentTeacher = async (data) => { - return await request.post({ url: `/infra/student/student-teacher/create`, data }) -} - -// 修改学生班主任 -export const updateStudentTeacher = async (data) => { - return await request.put({ url: `/infra/student/student-teacher/update`, data }) -} - -// 删除学生班主任 -export const deleteStudentTeacher = async (id: number) => { - return await request.delete({ url: `/infra/student/student-teacher/delete?id=` + id }) -} - -// 获得学生班主任 -export const getStudentTeacher = async (id: number) => { - return await request.get({ url: `/infra/student/student-teacher/get?id=` + id }) -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm deleted file mode 100644 index 4a13935..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactForm +++ /dev/null @@ -1,155 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList deleted file mode 100644 index eada66a..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentContactList +++ /dev/null @@ -1,146 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm deleted file mode 100644 index 0dabcb5..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentForm +++ /dev/null @@ -1,152 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm deleted file mode 100644 index f93c21c..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherForm +++ /dev/null @@ -1,155 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList deleted file mode 100644 index 1eba0a3..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/StudentTeacherList +++ /dev/null @@ -1,146 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/index deleted file mode 100644 index 9d15146..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/vue/index +++ /dev/null @@ -1,278 +0,0 @@ - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper deleted file mode 100644 index 155aa5c..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_erp/xml/InfraStudentMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/assert.json deleted file mode 100644 index 0937ba9..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/assert.json +++ /dev/null @@ -1,73 +0,0 @@ -[ { - "contentPath" : "java/InfraStudentPageReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" -}, { - "contentPath" : "java/InfraStudentRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" -}, { - "contentPath" : "java/InfraStudentSaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" -}, { - "contentPath" : "java/InfraStudentController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" -}, { - "contentPath" : "java/InfraStudentDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" -}, { - "contentPath" : "java/InfraStudentContactDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" -}, { - "contentPath" : "java/InfraStudentTeacherDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" -}, { - "contentPath" : "java/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" -}, { - "contentPath" : "java/InfraStudentContactMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" -}, { - "contentPath" : "java/InfraStudentTeacherMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" -}, { - "contentPath" : "xml/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" -}, { - "contentPath" : "java/InfraStudentServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" -}, { - "contentPath" : "java/InfraStudentService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" -}, { - "contentPath" : "java/InfraStudentServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" -}, { - "contentPath" : "vue/StudentForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue" -}, { - "contentPath" : "vue/StudentContactForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue" -}, { - "contentPath" : "vue/StudentTeacherForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue" -}, { - "contentPath" : "vue/StudentContactList", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactList.vue" -}, { - "contentPath" : "vue/StudentTeacherList", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherList.vue" -}, { - "contentPath" : "ts/index", - "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作 deleted file mode 100644 index f8be662..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/ErrorCodeConstants_手动操作 +++ /dev/null @@ -1,3 +0,0 @@ -// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! -// ========== 学生 TODO 补充编号 ========== -ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO deleted file mode 100644 index 17c668e..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生联系人 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_contact") -@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentContactDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper deleted file mode 100644 index 35bbd53..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentContactMapper +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生联系人 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentContactMapper extends BaseMapperX { - - default List selectListByStudentId(Long studentId) { - return selectList(InfraStudentContactDO::getStudentId, studentId); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentContactDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController deleted file mode 100644 index b9a587b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentController +++ /dev/null @@ -1,117 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; - -@Tag(name = "管理后台 - 学生") -@RestController -@RequestMapping("/infra/student") -@Validated -public class InfraStudentController { - - @Resource - private InfraStudentService studentService; - - @PostMapping("/create") - @Operation(summary = "创建学生") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { - return success(studentService.createStudent(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新学生") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { - studentService.updateStudent(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除学生") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudent(@RequestParam("id") Long id) { - studentService.deleteStudent(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得学生") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudent(@RequestParam("id") Long id) { - InfraStudentDO student = studentService.getStudent(id); - return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得学生分页") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { - PageResult pageResult = studentService.getStudentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出学生 Excel") - @PreAuthorize("@ss.hasPermission('infra:student:export')") - @OperateLog(type = EXPORT) - public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = studentService.getStudentPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, - BeanUtils.toBean(list, InfraStudentRespVO.class)); - } - - // ==================== 子表(学生联系人) ==================== - - @GetMapping("/student-contact/list-by-student-id") - @Operation(summary = "获得学生联系人列表") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { - return success(studentService.getStudentContactListByStudentId(studentId)); - } - - // ==================== 子表(学生班主任) ==================== - - @GetMapping("/student-teacher/get-by-student-id") - @Operation(summary = "获得学生班主任") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { - return success(studentService.getStudentTeacherByStudentId(studentId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO deleted file mode 100644 index b0d4bd2..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentDO +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生 DO - * - * @author 芋道源码 - */ -@TableName("infra_student") -@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper deleted file mode 100644 index 34e70a0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 学生 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentMapper extends BaseMapperX { - - default PageResult selectPage(InfraStudentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) - .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) - .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) - .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) - .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(InfraStudentDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO deleted file mode 100644 index 41a3730..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentPageReqVO +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 学生分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class InfraStudentPageReqVO extends PageParam { - - @Schema(description = "名字", example = "芋头") - private String name; - - @Schema(description = "出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", example = "1") - private Integer sex; - - @Schema(description = "是否有效", example = "true") - private Boolean enabled; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO deleted file mode 100644 index c41a550..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentRespVO +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; - -@Schema(description = "管理后台 - 学生 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraStudentRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @ExcelProperty("简介") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "性别", converter = DictConvert.class) - @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @ExcelProperty(value = "是否有效", converter = DictConvert.class) - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @ExcelProperty("头像") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @ExcelProperty("附件") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @ExcelProperty("备注") - private String memo; - - @Schema(description = "创建时间") - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO deleted file mode 100644 index faa491d..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentSaveReqVO +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; - -@Schema(description = "管理后台 - 学生新增/修改 Request VO") -@Data -public class InfraStudentSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @NotEmpty(message = "简介不能为空") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "出生日期不能为空") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "性别不能为空") - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否有效不能为空") - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @NotEmpty(message = "头像不能为空") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @NotEmpty(message = "附件不能为空") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @NotEmpty(message = "备注不能为空") - private String memo; - - @Schema(description = "学生联系人列表") - private List studentContacts; - - @Schema(description = "学生班主任") - private InfraStudentTeacherDO studentTeacher; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService deleted file mode 100644 index afa7d22..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentService +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 学生 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraStudentService { - - /** - * 创建学生 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); - - /** - * 更新学生 - * - * @param updateReqVO 更新信息 - */ - void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); - - /** - * 删除学生 - * - * @param id 编号 - */ - void deleteStudent(Long id); - - /** - * 获得学生 - * - * @param id 编号 - * @return 学生 - */ - InfraStudentDO getStudent(Long id); - - /** - * 获得学生分页 - * - * @param pageReqVO 分页查询 - * @return 学生分页 - */ - PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); - - // ==================== 子表(学生联系人) ==================== - - /** - * 获得学生联系人列表 - * - * @param studentId 学生编号 - * @return 学生联系人列表 - */ - List getStudentContactListByStudentId(Long studentId); - - // ==================== 子表(学生班主任) ==================== - - /** - * 获得学生班主任 - * - * @param studentId 学生编号 - * @return 学生班主任 - */ - InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl deleted file mode 100644 index c57cba6..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImpl +++ /dev/null @@ -1,147 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 学生 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraStudentServiceImpl implements InfraStudentService { - - @Resource - private InfraStudentMapper studentMapper; - @Resource - private InfraStudentContactMapper studentContactMapper; - @Resource - private InfraStudentTeacherMapper studentTeacherMapper; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createStudent(InfraStudentSaveReqVO createReqVO) { - // 插入 - InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); - studentMapper.insert(student); - - // 插入子表 - createStudentContactList(student.getId(), createReqVO.getStudentContacts()); - createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); - // 返回 - return student.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateStudent(InfraStudentSaveReqVO updateReqVO) { - // 校验存在 - validateStudentExists(updateReqVO.getId()); - // 更新 - InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); - studentMapper.updateById(updateObj); - - // 更新子表 - updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); - updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteStudent(Long id) { - // 校验存在 - validateStudentExists(id); - // 删除 - studentMapper.deleteById(id); - - // 删除子表 - deleteStudentContactByStudentId(id); - deleteStudentTeacherByStudentId(id); - } - - private void validateStudentExists(Long id) { - if (studentMapper.selectById(id) == null) { - throw exception(STUDENT_NOT_EXISTS); - } - } - - @Override - public InfraStudentDO getStudent(Long id) { - return studentMapper.selectById(id); - } - - @Override - public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { - return studentMapper.selectPage(pageReqVO); - } - - // ==================== 子表(学生联系人) ==================== - - @Override - public List getStudentContactListByStudentId(Long studentId) { - return studentContactMapper.selectListByStudentId(studentId); - } - - private void createStudentContactList(Long studentId, List list) { - list.forEach(o -> o.setStudentId(studentId)); - studentContactMapper.insertBatch(list); - } - - private void updateStudentContactList(Long studentId, List list) { - deleteStudentContactByStudentId(studentId); - list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新 - createStudentContactList(studentId, list); - } - - private void deleteStudentContactByStudentId(Long studentId) { - studentContactMapper.deleteByStudentId(studentId); - } - - // ==================== 子表(学生班主任) ==================== - - @Override - public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { - return studentTeacherMapper.selectByStudentId(studentId); - } - - private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { - if (studentTeacher == null) { - return; - } - studentTeacher.setStudentId(studentId); - studentTeacherMapper.insert(studentTeacher); - } - - private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { - if (studentTeacher == null) { - return; - } - studentTeacher.setStudentId(studentId); - studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新 - studentTeacherMapper.insertOrUpdate(studentTeacher); - } - - private void deleteStudentTeacherByStudentId(Long studentId) { - studentTeacherMapper.deleteByStudentId(studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest deleted file mode 100644 index b5f4bf0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentServiceImplTest +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraStudentServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraStudentServiceImpl.class) -public class InfraStudentServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraStudentServiceImpl studentService; - - @Resource - private InfraStudentMapper studentMapper; - - @Test - public void testCreateStudent_success() { - // 准备参数 - InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); - - // 调用 - Long studentId = studentService.createStudent(createReqVO); - // 断言 - assertNotNull(studentId); - // 校验记录的属性是否正确 - InfraStudentDO student = studentMapper.selectById(studentId); - assertPojoEquals(createReqVO, student, "id"); - } - - @Test - public void testUpdateStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { - o.setId(dbStudent.getId()); // 设置更新的 ID - }); - - // 调用 - studentService.updateStudent(updateReqVO); - // 校验是否更新正确 - InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, student); - } - - @Test - public void testUpdateStudent_notExists() { - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); - } - - @Test - public void testDeleteStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbStudent.getId(); - - // 调用 - studentService.deleteStudent(id); - // 校验数据不存在了 - assertNull(studentMapper.selectById(id)); - } - - @Test - public void testDeleteStudent_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetStudentPage() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 - o.setName(null); - o.setBirthday(null); - o.setSex(null); - o.setEnabled(null); - o.setCreateTime(null); - }); - studentMapper.insert(dbStudent); - // 测试 name 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); - // 测试 birthday 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); - // 测试 sex 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); - // 测试 enabled 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); - // 测试 createTime 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); - // 准备参数 - InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); - reqVO.setName(null); - reqVO.setBirthday(null); - reqVO.setSex(null); - reqVO.setEnabled(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = studentService.getStudentPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbStudent, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO deleted file mode 100644 index c19cf9f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生班主任 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_teacher") -@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentTeacherDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper deleted file mode 100644 index 0521bba..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/java/InfraStudentTeacherMapper +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生班主任 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentTeacherMapper extends BaseMapperX { - - default InfraStudentTeacherDO selectByStudentId(Long studentId) { - return selectOne(InfraStudentTeacherDO::getStudentId, studentId); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentTeacherDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2 deleted file mode 100644 index 6c1875f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/sql/h2 +++ /dev/null @@ -1,17 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_student" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" varchar NOT NULL, - "birthday" varchar NOT NULL, - "sex" int NOT NULL, - "enabled" bit NOT NULL, - "avatar" varchar NOT NULL, - "video" varchar NOT NULL, - "memo" varchar NOT NULL, - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id") -) COMMENT '学生表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql deleted file mode 100644 index 83df279..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '学生管理', '', 2, 0, 888, - 'student', '', 'infra/demo/index', 0, 'InfraStudent' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生查询', 'infra:student:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生创建', 'infra:student:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生更新', 'infra:student:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生删除', 'infra:student:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生导出', 'infra:student:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/ts/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/ts/index deleted file mode 100644 index 6112800..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/ts/index +++ /dev/null @@ -1,57 +0,0 @@ -import request from '@/config/axios' - -export interface StudentVO { - id: number - name: string - description: string - birthday: Date - sex: number - enabled: boolean - avatar: string - video: string - memo: string -} - -// 查询学生分页 -export const getStudentPage = async (params) => { - return await request.get({ url: `/infra/student/page`, params }) -} - -// 查询学生详情 -export const getStudent = async (id: number) => { - return await request.get({ url: `/infra/student/get?id=` + id }) -} - -// 新增学生 -export const createStudent = async (data: StudentVO) => { - return await request.post({ url: `/infra/student/create`, data }) -} - -// 修改学生 -export const updateStudent = async (data: StudentVO) => { - return await request.put({ url: `/infra/student/update`, data }) -} - -// 删除学生 -export const deleteStudent = async (id: number) => { - return await request.delete({ url: `/infra/student/delete?id=` + id }) -} - -// 导出学生 Excel -export const exportStudent = async (params) => { - return await request.download({ url: `/infra/student/export-excel`, params }) -} - -// ==================== 子表(学生联系人) ==================== - -// 获得学生联系人列表 -export const getStudentContactListByStudentId = async (studentId) => { - return await request.get({ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId }) -} - -// ==================== 子表(学生班主任) ==================== - -// 获得学生班主任 -export const getStudentTeacherByStudentId = async (studentId) => { - return await request.get({ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId }) -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm deleted file mode 100644 index 55ca994..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactForm +++ /dev/null @@ -1,174 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList deleted file mode 100644 index d0e89da..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentContactList +++ /dev/null @@ -1,72 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm deleted file mode 100644 index d8e7bc3..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentForm +++ /dev/null @@ -1,184 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm deleted file mode 100644 index b22a480..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherForm +++ /dev/null @@ -1,122 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList deleted file mode 100644 index e510adc..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/StudentTeacherList +++ /dev/null @@ -1,76 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/index deleted file mode 100644 index ee7c05f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/vue/index +++ /dev/null @@ -1,267 +0,0 @@ - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper deleted file mode 100644 index 155aa5c..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_inner/xml/InfraStudentMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/assert.json deleted file mode 100644 index 60e7f47..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/assert.json +++ /dev/null @@ -1,67 +0,0 @@ -[ { - "contentPath" : "java/InfraStudentPageReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" -}, { - "contentPath" : "java/InfraStudentRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" -}, { - "contentPath" : "java/InfraStudentSaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" -}, { - "contentPath" : "java/InfraStudentController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" -}, { - "contentPath" : "java/InfraStudentDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" -}, { - "contentPath" : "java/InfraStudentContactDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentContactDO.java" -}, { - "contentPath" : "java/InfraStudentTeacherDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentTeacherDO.java" -}, { - "contentPath" : "java/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" -}, { - "contentPath" : "java/InfraStudentContactMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentContactMapper.java" -}, { - "contentPath" : "java/InfraStudentTeacherMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentTeacherMapper.java" -}, { - "contentPath" : "xml/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" -}, { - "contentPath" : "java/InfraStudentServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" -}, { - "contentPath" : "java/InfraStudentService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" -}, { - "contentPath" : "java/InfraStudentServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" -}, { - "contentPath" : "vue/StudentForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue" -}, { - "contentPath" : "vue/StudentContactForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentContactForm.vue" -}, { - "contentPath" : "vue/StudentTeacherForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/components/StudentTeacherForm.vue" -}, { - "contentPath" : "ts/index", - "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作 deleted file mode 100644 index f8be662..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/ErrorCodeConstants_手动操作 +++ /dev/null @@ -1,3 +0,0 @@ -// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! -// ========== 学生 TODO 补充编号 ========== -ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO deleted file mode 100644 index 17c668e..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生联系人 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_contact") -@KeySequence("infra_student_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentContactDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper deleted file mode 100644 index 35bbd53..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentContactMapper +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生联系人 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentContactMapper extends BaseMapperX { - - default List selectListByStudentId(Long studentId) { - return selectList(InfraStudentContactDO::getStudentId, studentId); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentContactDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController deleted file mode 100644 index b9a587b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentController +++ /dev/null @@ -1,117 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; - -@Tag(name = "管理后台 - 学生") -@RestController -@RequestMapping("/infra/student") -@Validated -public class InfraStudentController { - - @Resource - private InfraStudentService studentService; - - @PostMapping("/create") - @Operation(summary = "创建学生") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { - return success(studentService.createStudent(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新学生") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { - studentService.updateStudent(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除学生") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudent(@RequestParam("id") Long id) { - studentService.deleteStudent(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得学生") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudent(@RequestParam("id") Long id) { - InfraStudentDO student = studentService.getStudent(id); - return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得学生分页") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { - PageResult pageResult = studentService.getStudentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出学生 Excel") - @PreAuthorize("@ss.hasPermission('infra:student:export')") - @OperateLog(type = EXPORT) - public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = studentService.getStudentPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, - BeanUtils.toBean(list, InfraStudentRespVO.class)); - } - - // ==================== 子表(学生联系人) ==================== - - @GetMapping("/student-contact/list-by-student-id") - @Operation(summary = "获得学生联系人列表") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentContactListByStudentId(@RequestParam("studentId") Long studentId) { - return success(studentService.getStudentContactListByStudentId(studentId)); - } - - // ==================== 子表(学生班主任) ==================== - - @GetMapping("/student-teacher/get-by-student-id") - @Operation(summary = "获得学生班主任") - @Parameter(name = "studentId", description = "学生编号") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudentTeacherByStudentId(@RequestParam("studentId") Long studentId) { - return success(studentService.getStudentTeacherByStudentId(studentId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO deleted file mode 100644 index b0d4bd2..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentDO +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生 DO - * - * @author 芋道源码 - */ -@TableName("infra_student") -@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper deleted file mode 100644 index 34e70a0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 学生 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentMapper extends BaseMapperX { - - default PageResult selectPage(InfraStudentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) - .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) - .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) - .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) - .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(InfraStudentDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO deleted file mode 100644 index 41a3730..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentPageReqVO +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 学生分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class InfraStudentPageReqVO extends PageParam { - - @Schema(description = "名字", example = "芋头") - private String name; - - @Schema(description = "出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", example = "1") - private Integer sex; - - @Schema(description = "是否有效", example = "true") - private Boolean enabled; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO deleted file mode 100644 index c41a550..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentRespVO +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; - -@Schema(description = "管理后台 - 学生 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraStudentRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @ExcelProperty("简介") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "性别", converter = DictConvert.class) - @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @ExcelProperty(value = "是否有效", converter = DictConvert.class) - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @ExcelProperty("头像") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @ExcelProperty("附件") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @ExcelProperty("备注") - private String memo; - - @Schema(description = "创建时间") - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO deleted file mode 100644 index faa491d..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentSaveReqVO +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; - -@Schema(description = "管理后台 - 学生新增/修改 Request VO") -@Data -public class InfraStudentSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @NotEmpty(message = "简介不能为空") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "出生日期不能为空") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "性别不能为空") - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否有效不能为空") - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @NotEmpty(message = "头像不能为空") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @NotEmpty(message = "附件不能为空") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @NotEmpty(message = "备注不能为空") - private String memo; - - @Schema(description = "学生联系人列表") - private List studentContacts; - - @Schema(description = "学生班主任") - private InfraStudentTeacherDO studentTeacher; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService deleted file mode 100644 index afa7d22..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentService +++ /dev/null @@ -1,77 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 学生 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraStudentService { - - /** - * 创建学生 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); - - /** - * 更新学生 - * - * @param updateReqVO 更新信息 - */ - void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); - - /** - * 删除学生 - * - * @param id 编号 - */ - void deleteStudent(Long id); - - /** - * 获得学生 - * - * @param id 编号 - * @return 学生 - */ - InfraStudentDO getStudent(Long id); - - /** - * 获得学生分页 - * - * @param pageReqVO 分页查询 - * @return 学生分页 - */ - PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); - - // ==================== 子表(学生联系人) ==================== - - /** - * 获得学生联系人列表 - * - * @param studentId 学生编号 - * @return 学生联系人列表 - */ - List getStudentContactListByStudentId(Long studentId); - - // ==================== 子表(学生班主任) ==================== - - /** - * 获得学生班主任 - * - * @param studentId 学生编号 - * @return 学生班主任 - */ - InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl deleted file mode 100644 index c57cba6..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImpl +++ /dev/null @@ -1,147 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentContactDO; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentContactMapper; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentTeacherMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 学生 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraStudentServiceImpl implements InfraStudentService { - - @Resource - private InfraStudentMapper studentMapper; - @Resource - private InfraStudentContactMapper studentContactMapper; - @Resource - private InfraStudentTeacherMapper studentTeacherMapper; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createStudent(InfraStudentSaveReqVO createReqVO) { - // 插入 - InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); - studentMapper.insert(student); - - // 插入子表 - createStudentContactList(student.getId(), createReqVO.getStudentContacts()); - createStudentTeacher(student.getId(), createReqVO.getStudentTeacher()); - // 返回 - return student.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateStudent(InfraStudentSaveReqVO updateReqVO) { - // 校验存在 - validateStudentExists(updateReqVO.getId()); - // 更新 - InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); - studentMapper.updateById(updateObj); - - // 更新子表 - updateStudentContactList(updateReqVO.getId(), updateReqVO.getStudentContacts()); - updateStudentTeacher(updateReqVO.getId(), updateReqVO.getStudentTeacher()); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteStudent(Long id) { - // 校验存在 - validateStudentExists(id); - // 删除 - studentMapper.deleteById(id); - - // 删除子表 - deleteStudentContactByStudentId(id); - deleteStudentTeacherByStudentId(id); - } - - private void validateStudentExists(Long id) { - if (studentMapper.selectById(id) == null) { - throw exception(STUDENT_NOT_EXISTS); - } - } - - @Override - public InfraStudentDO getStudent(Long id) { - return studentMapper.selectById(id); - } - - @Override - public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { - return studentMapper.selectPage(pageReqVO); - } - - // ==================== 子表(学生联系人) ==================== - - @Override - public List getStudentContactListByStudentId(Long studentId) { - return studentContactMapper.selectListByStudentId(studentId); - } - - private void createStudentContactList(Long studentId, List list) { - list.forEach(o -> o.setStudentId(studentId)); - studentContactMapper.insertBatch(list); - } - - private void updateStudentContactList(Long studentId, List list) { - deleteStudentContactByStudentId(studentId); - list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下:1)id 冲突;2)updateTime 不更新 - createStudentContactList(studentId, list); - } - - private void deleteStudentContactByStudentId(Long studentId) { - studentContactMapper.deleteByStudentId(studentId); - } - - // ==================== 子表(学生班主任) ==================== - - @Override - public InfraStudentTeacherDO getStudentTeacherByStudentId(Long studentId) { - return studentTeacherMapper.selectByStudentId(studentId); - } - - private void createStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { - if (studentTeacher == null) { - return; - } - studentTeacher.setStudentId(studentId); - studentTeacherMapper.insert(studentTeacher); - } - - private void updateStudentTeacher(Long studentId, InfraStudentTeacherDO studentTeacher) { - if (studentTeacher == null) { - return; - } - studentTeacher.setStudentId(studentId); - studentTeacher.setUpdater(null).setUpdateTime(null); // 解决更新情况下:updateTime 不更新 - studentTeacherMapper.insertOrUpdate(studentTeacher); - } - - private void deleteStudentTeacherByStudentId(Long studentId) { - studentTeacherMapper.deleteByStudentId(studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest deleted file mode 100644 index b5f4bf0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentServiceImplTest +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraStudentServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraStudentServiceImpl.class) -public class InfraStudentServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraStudentServiceImpl studentService; - - @Resource - private InfraStudentMapper studentMapper; - - @Test - public void testCreateStudent_success() { - // 准备参数 - InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); - - // 调用 - Long studentId = studentService.createStudent(createReqVO); - // 断言 - assertNotNull(studentId); - // 校验记录的属性是否正确 - InfraStudentDO student = studentMapper.selectById(studentId); - assertPojoEquals(createReqVO, student, "id"); - } - - @Test - public void testUpdateStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { - o.setId(dbStudent.getId()); // 设置更新的 ID - }); - - // 调用 - studentService.updateStudent(updateReqVO); - // 校验是否更新正确 - InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, student); - } - - @Test - public void testUpdateStudent_notExists() { - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); - } - - @Test - public void testDeleteStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbStudent.getId(); - - // 调用 - studentService.deleteStudent(id); - // 校验数据不存在了 - assertNull(studentMapper.selectById(id)); - } - - @Test - public void testDeleteStudent_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetStudentPage() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 - o.setName(null); - o.setBirthday(null); - o.setSex(null); - o.setEnabled(null); - o.setCreateTime(null); - }); - studentMapper.insert(dbStudent); - // 测试 name 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); - // 测试 birthday 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); - // 测试 sex 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); - // 测试 enabled 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); - // 测试 createTime 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); - // 准备参数 - InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); - reqVO.setName(null); - reqVO.setBirthday(null); - reqVO.setSex(null); - reqVO.setEnabled(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = studentService.getStudentPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbStudent, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO deleted file mode 100644 index c19cf9f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherDO +++ /dev/null @@ -1,71 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生班主任 DO - * - * @author 芋道源码 - */ -@TableName("infra_student_teacher") -@KeySequence("infra_student_teacher_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentTeacherDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 学生编号 - */ - private Long studentId; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper deleted file mode 100644 index 0521bba..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/java/InfraStudentTeacherMapper +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentTeacherDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 学生班主任 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentTeacherMapper extends BaseMapperX { - - default InfraStudentTeacherDO selectByStudentId(Long studentId) { - return selectOne(InfraStudentTeacherDO::getStudentId, studentId); - } - - default int deleteByStudentId(Long studentId) { - return delete(InfraStudentTeacherDO::getStudentId, studentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2 deleted file mode 100644 index 6c1875f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/sql/h2 +++ /dev/null @@ -1,17 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_student" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" varchar NOT NULL, - "birthday" varchar NOT NULL, - "sex" int NOT NULL, - "enabled" bit NOT NULL, - "avatar" varchar NOT NULL, - "video" varchar NOT NULL, - "memo" varchar NOT NULL, - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id") -) COMMENT '学生表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql deleted file mode 100644 index 83df279..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '学生管理', '', 2, 0, 888, - 'student', '', 'infra/demo/index', 0, 'InfraStudent' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生查询', 'infra:student:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生创建', 'infra:student:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生更新', 'infra:student:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生删除', 'infra:student:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生导出', 'infra:student:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/ts/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/ts/index deleted file mode 100644 index 6112800..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/ts/index +++ /dev/null @@ -1,57 +0,0 @@ -import request from '@/config/axios' - -export interface StudentVO { - id: number - name: string - description: string - birthday: Date - sex: number - enabled: boolean - avatar: string - video: string - memo: string -} - -// 查询学生分页 -export const getStudentPage = async (params) => { - return await request.get({ url: `/infra/student/page`, params }) -} - -// 查询学生详情 -export const getStudent = async (id: number) => { - return await request.get({ url: `/infra/student/get?id=` + id }) -} - -// 新增学生 -export const createStudent = async (data: StudentVO) => { - return await request.post({ url: `/infra/student/create`, data }) -} - -// 修改学生 -export const updateStudent = async (data: StudentVO) => { - return await request.put({ url: `/infra/student/update`, data }) -} - -// 删除学生 -export const deleteStudent = async (id: number) => { - return await request.delete({ url: `/infra/student/delete?id=` + id }) -} - -// 导出学生 Excel -export const exportStudent = async (params) => { - return await request.download({ url: `/infra/student/export-excel`, params }) -} - -// ==================== 子表(学生联系人) ==================== - -// 获得学生联系人列表 -export const getStudentContactListByStudentId = async (studentId) => { - return await request.get({ url: `/infra/student/student-contact/list-by-student-id?studentId=` + studentId }) -} - -// ==================== 子表(学生班主任) ==================== - -// 获得学生班主任 -export const getStudentTeacherByStudentId = async (studentId) => { - return await request.get({ url: `/infra/student/student-teacher/get-by-student-id?studentId=` + studentId }) -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm deleted file mode 100644 index 55ca994..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentContactForm +++ /dev/null @@ -1,174 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm deleted file mode 100644 index d8e7bc3..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentForm +++ /dev/null @@ -1,184 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm deleted file mode 100644 index b22a480..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/StudentTeacherForm +++ /dev/null @@ -1,122 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/index deleted file mode 100644 index b115b13..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/vue/index +++ /dev/null @@ -1,252 +0,0 @@ - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper deleted file mode 100644 index 155aa5c..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_master_normal/xml/InfraStudentMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/assert.json deleted file mode 100644 index 5a0eebd..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/assert.json +++ /dev/null @@ -1,49 +0,0 @@ -[ { - "contentPath" : "java/InfraStudentPageReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentPageReqVO.java" -}, { - "contentPath" : "java/InfraStudentRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentRespVO.java" -}, { - "contentPath" : "java/InfraStudentSaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraStudentSaveReqVO.java" -}, { - "contentPath" : "java/InfraStudentController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraStudentController.java" -}, { - "contentPath" : "java/InfraStudentDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraStudentDO.java" -}, { - "contentPath" : "java/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraStudentMapper.java" -}, { - "contentPath" : "xml/InfraStudentMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraStudentMapper.xml" -}, { - "contentPath" : "java/InfraStudentServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImpl.java" -}, { - "contentPath" : "java/InfraStudentService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentService.java" -}, { - "contentPath" : "java/InfraStudentServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraStudentServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" -}, { - "contentPath" : "vue/StudentForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/StudentForm.vue" -}, { - "contentPath" : "ts/index", - "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作 deleted file mode 100644 index f8be662..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/ErrorCodeConstants_手动操作 +++ /dev/null @@ -1,3 +0,0 @@ -// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! -// ========== 学生 TODO 补充编号 ========== -ErrorCode STUDENT_NOT_EXISTS = new ErrorCode(TODO 补充编号, "学生不存在"); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController deleted file mode 100644 index 3796982..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentController +++ /dev/null @@ -1,95 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraStudentService; - -@Tag(name = "管理后台 - 学生") -@RestController -@RequestMapping("/infra/student") -@Validated -public class InfraStudentController { - - @Resource - private InfraStudentService studentService; - - @PostMapping("/create") - @Operation(summary = "创建学生") - @PreAuthorize("@ss.hasPermission('infra:student:create')") - public CommonResult createStudent(@Valid @RequestBody InfraStudentSaveReqVO createReqVO) { - return success(studentService.createStudent(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新学生") - @PreAuthorize("@ss.hasPermission('infra:student:update')") - public CommonResult updateStudent(@Valid @RequestBody InfraStudentSaveReqVO updateReqVO) { - studentService.updateStudent(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除学生") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:student:delete')") - public CommonResult deleteStudent(@RequestParam("id") Long id) { - studentService.deleteStudent(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得学生") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult getStudent(@RequestParam("id") Long id) { - InfraStudentDO student = studentService.getStudent(id); - return success(BeanUtils.toBean(student, InfraStudentRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得学生分页") - @PreAuthorize("@ss.hasPermission('infra:student:query')") - public CommonResult> getStudentPage(@Valid InfraStudentPageReqVO pageReqVO) { - PageResult pageResult = studentService.getStudentPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, InfraStudentRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出学生 Excel") - @PreAuthorize("@ss.hasPermission('infra:student:export')") - @OperateLog(type = EXPORT) - public void exportStudentExcel(@Valid InfraStudentPageReqVO pageReqVO, - HttpServletResponse response) throws IOException { - pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = studentService.getStudentPage(pageReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "学生.xls", "数据", InfraStudentRespVO.class, - BeanUtils.toBean(list, InfraStudentRespVO.class)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO deleted file mode 100644 index b0d4bd2..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentDO +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import java.time.LocalDateTime; -import java.time.LocalDateTime; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 学生 DO - * - * @author 芋道源码 - */ -@TableName("infra_student") -@KeySequence("infra_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraStudentDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 简介 - */ - private String description; - /** - * 出生日期 - */ - private LocalDateTime birthday; - /** - * 性别 - * - * 枚举 {@link TODO system_user_sex 对应的类} - */ - private Integer sex; - /** - * 是否有效 - * - * 枚举 {@link TODO infra_boolean_string 对应的类} - */ - private Boolean enabled; - /** - * 头像 - */ - private String avatar; - /** - * 附件 - */ - private String video; - /** - * 备注 - */ - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper deleted file mode 100644 index 34e70a0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentMapper +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 学生 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraStudentMapper extends BaseMapperX { - - default PageResult selectPage(InfraStudentPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(InfraStudentDO::getName, reqVO.getName()) - .eqIfPresent(InfraStudentDO::getBirthday, reqVO.getBirthday()) - .eqIfPresent(InfraStudentDO::getSex, reqVO.getSex()) - .eqIfPresent(InfraStudentDO::getEnabled, reqVO.getEnabled()) - .betweenIfPresent(InfraStudentDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(InfraStudentDO::getId)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO deleted file mode 100644 index 41a3730..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentPageReqVO +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 学生分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class InfraStudentPageReqVO extends PageParam { - - @Schema(description = "名字", example = "芋头") - private String name; - - @Schema(description = "出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", example = "1") - private Integer sex; - - @Schema(description = "是否有效", example = "true") - private Boolean enabled; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO deleted file mode 100644 index c41a550..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentRespVO +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; -import com.alibaba.excel.annotation.*; -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; - -@Schema(description = "管理后台 - 学生 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraStudentRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @ExcelProperty("简介") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("出生日期") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "性别", converter = DictConvert.class) - @DictFormat("system_user_sex") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @ExcelProperty(value = "是否有效", converter = DictConvert.class) - @DictFormat("infra_boolean_string") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中 - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @ExcelProperty("头像") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @ExcelProperty("附件") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @ExcelProperty("备注") - private String memo; - - @Schema(description = "创建时间") - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO deleted file mode 100644 index 43e7f14..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentSaveReqVO +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; -import org.springframework.format.annotation.DateTimeFormat; -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 学生新增/修改 Request VO") -@Data -public class InfraStudentSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是介绍") - @NotEmpty(message = "简介不能为空") - private String description; - - @Schema(description = "出生日期", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "出生日期不能为空") - private LocalDateTime birthday; - - @Schema(description = "性别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "性别不能为空") - private Integer sex; - - @Schema(description = "是否有效", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否有效不能为空") - private Boolean enabled; - - @Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png") - @NotEmpty(message = "头像不能为空") - private String avatar; - - @Schema(description = "附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.mp4") - @NotEmpty(message = "附件不能为空") - private String video; - - @Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是备注") - @NotEmpty(message = "备注不能为空") - private String memo; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService deleted file mode 100644 index c4a0e17..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentService +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 学生 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraStudentService { - - /** - * 创建学生 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createStudent(@Valid InfraStudentSaveReqVO createReqVO); - - /** - * 更新学生 - * - * @param updateReqVO 更新信息 - */ - void updateStudent(@Valid InfraStudentSaveReqVO updateReqVO); - - /** - * 删除学生 - * - * @param id 编号 - */ - void deleteStudent(Long id); - - /** - * 获得学生 - * - * @param id 编号 - * @return 学生 - */ - InfraStudentDO getStudent(Long id); - - /** - * 获得学生分页 - * - * @param pageReqVO 分页查询 - * @return 学生分页 - */ - PageResult getStudentPage(InfraStudentPageReqVO pageReqVO); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl deleted file mode 100644 index 2292a66..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImpl +++ /dev/null @@ -1,74 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 学生 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraStudentServiceImpl implements InfraStudentService { - - @Resource - private InfraStudentMapper studentMapper; - - @Override - public Long createStudent(InfraStudentSaveReqVO createReqVO) { - // 插入 - InfraStudentDO student = BeanUtils.toBean(createReqVO, InfraStudentDO.class); - studentMapper.insert(student); - // 返回 - return student.getId(); - } - - @Override - public void updateStudent(InfraStudentSaveReqVO updateReqVO) { - // 校验存在 - validateStudentExists(updateReqVO.getId()); - // 更新 - InfraStudentDO updateObj = BeanUtils.toBean(updateReqVO, InfraStudentDO.class); - studentMapper.updateById(updateObj); - } - - @Override - public void deleteStudent(Long id) { - // 校验存在 - validateStudentExists(id); - // 删除 - studentMapper.deleteById(id); - } - - private void validateStudentExists(Long id) { - if (studentMapper.selectById(id) == null) { - throw exception(STUDENT_NOT_EXISTS); - } - } - - @Override - public InfraStudentDO getStudent(Long id) { - return studentMapper.selectById(id); - } - - @Override - public PageResult getStudentPage(InfraStudentPageReqVO pageReqVO) { - return studentMapper.selectPage(pageReqVO); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest deleted file mode 100644 index b5f4bf0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/java/InfraStudentServiceImplTest +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraStudentDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraStudentMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraStudentServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraStudentServiceImpl.class) -public class InfraStudentServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraStudentServiceImpl studentService; - - @Resource - private InfraStudentMapper studentMapper; - - @Test - public void testCreateStudent_success() { - // 准备参数 - InfraStudentSaveReqVO createReqVO = randomPojo(InfraStudentSaveReqVO.class).setId(null); - - // 调用 - Long studentId = studentService.createStudent(createReqVO); - // 断言 - assertNotNull(studentId); - // 校验记录的属性是否正确 - InfraStudentDO student = studentMapper.selectById(studentId); - assertPojoEquals(createReqVO, student, "id"); - } - - @Test - public void testUpdateStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class, o -> { - o.setId(dbStudent.getId()); // 设置更新的 ID - }); - - // 调用 - studentService.updateStudent(updateReqVO); - // 校验是否更新正确 - InfraStudentDO student = studentMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, student); - } - - @Test - public void testUpdateStudent_notExists() { - // 准备参数 - InfraStudentSaveReqVO updateReqVO = randomPojo(InfraStudentSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.updateStudent(updateReqVO), STUDENT_NOT_EXISTS); - } - - @Test - public void testDeleteStudent_success() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class); - studentMapper.insert(dbStudent);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbStudent.getId(); - - // 调用 - studentService.deleteStudent(id); - // 校验数据不存在了 - assertNull(studentMapper.selectById(id)); - } - - @Test - public void testDeleteStudent_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> studentService.deleteStudent(id), STUDENT_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetStudentPage() { - // mock 数据 - InfraStudentDO dbStudent = randomPojo(InfraStudentDO.class, o -> { // 等会查询到 - o.setName(null); - o.setBirthday(null); - o.setSex(null); - o.setEnabled(null); - o.setCreateTime(null); - }); - studentMapper.insert(dbStudent); - // 测试 name 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setName(null))); - // 测试 birthday 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setBirthday(null))); - // 测试 sex 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setSex(null))); - // 测试 enabled 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setEnabled(null))); - // 测试 createTime 不匹配 - studentMapper.insert(cloneIgnoreId(dbStudent, o -> o.setCreateTime(null))); - // 准备参数 - InfraStudentPageReqVO reqVO = new InfraStudentPageReqVO(); - reqVO.setName(null); - reqVO.setBirthday(null); - reqVO.setSex(null); - reqVO.setEnabled(null); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = studentService.getStudentPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbStudent, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/sql/h2 deleted file mode 100644 index 6c1875f..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/sql/h2 +++ /dev/null @@ -1,17 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_student" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" varchar NOT NULL, - "birthday" varchar NOT NULL, - "sex" int NOT NULL, - "enabled" bit NOT NULL, - "avatar" varchar NOT NULL, - "video" varchar NOT NULL, - "memo" varchar NOT NULL, - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY ("id") -) COMMENT '学生表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_student"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/sql/sql deleted file mode 100644 index 83df279..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '学生管理', '', 2, 0, 888, - 'student', '', 'infra/demo/index', 0, 'InfraStudent' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生查询', 'infra:student:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生创建', 'infra:student:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生更新', 'infra:student:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生删除', 'infra:student:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '学生导出', 'infra:student:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/ts/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/ts/index deleted file mode 100644 index 8cdf254..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/ts/index +++ /dev/null @@ -1,43 +0,0 @@ -import request from '@/config/axios' - -export interface StudentVO { - id: number - name: string - description: string - birthday: Date - sex: number - enabled: boolean - avatar: string - video: string - memo: string -} - -// 查询学生分页 -export const getStudentPage = async (params) => { - return await request.get({ url: `/infra/student/page`, params }) -} - -// 查询学生详情 -export const getStudent = async (id: number) => { - return await request.get({ url: `/infra/student/get?id=` + id }) -} - -// 新增学生 -export const createStudent = async (data: StudentVO) => { - return await request.post({ url: `/infra/student/create`, data }) -} - -// 修改学生 -export const updateStudent = async (data: StudentVO) => { - return await request.put({ url: `/infra/student/update`, data }) -} - -// 删除学生 -export const deleteStudent = async (id: number) => { - return await request.delete({ url: `/infra/student/delete?id=` + id }) -} - -// 导出学生 Excel -export const exportStudent = async (params) => { - return await request.download({ url: `/infra/student/export-excel`, params }) -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm deleted file mode 100644 index 0dabcb5..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/vue/StudentForm +++ /dev/null @@ -1,152 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/vue/index deleted file mode 100644 index b115b13..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/vue/index +++ /dev/null @@ -1,252 +0,0 @@ - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper deleted file mode 100644 index 155aa5c..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_one/xml/InfraStudentMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/assert.json b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/assert.json deleted file mode 100644 index 357df00..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/assert.json +++ /dev/null @@ -1,49 +0,0 @@ -[ { - "contentPath" : "java/InfraCategoryListReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryListReqVO.java" -}, { - "contentPath" : "java/InfraCategoryRespVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategoryRespVO.java" -}, { - "contentPath" : "java/InfraCategorySaveReqVO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/vo/InfraCategorySaveReqVO.java" -}, { - "contentPath" : "java/InfraCategoryController", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/demo/InfraCategoryController.java" -}, { - "contentPath" : "java/InfraCategoryDO", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/demo/InfraCategoryDO.java" -}, { - "contentPath" : "java/InfraCategoryMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/demo/InfraCategoryMapper.java" -}, { - "contentPath" : "xml/InfraCategoryMapper", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/resources/mapper/demo/InfraCategoryMapper.xml" -}, { - "contentPath" : "java/InfraCategoryServiceImpl", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImpl.java" -}, { - "contentPath" : "java/InfraCategoryService", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryService.java" -}, { - "contentPath" : "java/InfraCategoryServiceImplTest", - "filePath" : "yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/demo/InfraCategoryServiceImplTest.java" -}, { - "contentPath" : "java/ErrorCodeConstants_手动操作", - "filePath" : "yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants_手动操作.java" -}, { - "contentPath" : "sql/sql", - "filePath" : "sql/sql.sql" -}, { - "contentPath" : "sql/h2", - "filePath" : "sql/h2.sql" -}, { - "contentPath" : "vue/index", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/index.vue" -}, { - "contentPath" : "vue/CategoryForm", - "filePath" : "yudao-ui-admin-vue3/src/views/infra/demo/CategoryForm.vue" -}, { - "contentPath" : "ts/index", - "filePath" : "yudao-ui-admin-vue3/src/api/infra/demo/index.ts" -} ] \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作 deleted file mode 100644 index 36df675..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/ErrorCodeConstants_手动操作 +++ /dev/null @@ -1,8 +0,0 @@ -// TODO 待办:请将下面的错误码复制到 yudao-module-infra-api 模块的 ErrorCodeConstants 类中。注意,请给“TODO 补充编号”设置一个错误码编号!!! -// ========== 分类 TODO 补充编号 ========== -ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(TODO 补充编号, "分类不存在"); -ErrorCode CATEGORY_EXITS_CHILDREN = new ErrorCode(TODO 补充编号, "存在存在子分类,无法删除"); -ErrorCode CATEGORY_PARENT_NOT_EXITS = new ErrorCode(TODO 补充编号,"父级分类不存在"); -ErrorCode CATEGORY_PARENT_ERROR = new ErrorCode(TODO 补充编号, "不能设置自己为父分类"); -ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(TODO 补充编号, "已经存在该名字的分类"); -ErrorCode CATEGORY_PARENT_IS_CHILD = new ErrorCode(TODO 补充编号, "不能设置自己的子InfraCategory为父InfraCategory"); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController deleted file mode 100644 index a7b2f81..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryController +++ /dev/null @@ -1,94 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo; - -import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.security.access.prepost.PreAuthorize; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; - -import javax.validation.constraints.*; -import javax.validation.*; -import javax.servlet.http.*; -import java.util.*; -import java.io.IOException; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; - -import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; -import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.*; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import cn.iocoder.yudao.module.infra.service.demo.InfraCategoryService; - -@Tag(name = "管理后台 - 分类") -@RestController -@RequestMapping("/infra/category") -@Validated -public class InfraCategoryController { - - @Resource - private InfraCategoryService categoryService; - - @PostMapping("/create") - @Operation(summary = "创建分类") - @PreAuthorize("@ss.hasPermission('infra:category:create')") - public CommonResult createCategory(@Valid @RequestBody InfraCategorySaveReqVO createReqVO) { - return success(categoryService.createCategory(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新分类") - @PreAuthorize("@ss.hasPermission('infra:category:update')") - public CommonResult updateCategory(@Valid @RequestBody InfraCategorySaveReqVO updateReqVO) { - categoryService.updateCategory(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除分类") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('infra:category:delete')") - public CommonResult deleteCategory(@RequestParam("id") Long id) { - categoryService.deleteCategory(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得分类") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('infra:category:query')") - public CommonResult getCategory(@RequestParam("id") Long id) { - InfraCategoryDO category = categoryService.getCategory(id); - return success(BeanUtils.toBean(category, InfraCategoryRespVO.class)); - } - - @GetMapping("/list") - @Operation(summary = "获得分类列表") - @PreAuthorize("@ss.hasPermission('infra:category:query')") - public CommonResult> getCategoryList(@Valid InfraCategoryListReqVO listReqVO) { - List list = categoryService.getCategoryList(listReqVO); - return success(BeanUtils.toBean(list, InfraCategoryRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出分类 Excel") - @PreAuthorize("@ss.hasPermission('infra:category:export')") - @OperateLog(type = EXPORT) - public void exportCategoryExcel(@Valid InfraCategoryListReqVO listReqVO, - HttpServletResponse response) throws IOException { - List list = categoryService.getCategoryList(listReqVO); - // 导出 Excel - ExcelUtils.write(response, "分类.xls", "数据", InfraCategoryRespVO.class, - BeanUtils.toBean(list, InfraCategoryRespVO.class)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO deleted file mode 100644 index 9bf21c0..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryDO +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.dataobject.demo; - -import lombok.*; -import java.util.*; -import com.baomidou.mybatisplus.annotation.*; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; - -/** - * 分类 DO - * - * @author 芋道源码 - */ -@TableName("infra_category") -@KeySequence("infra_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class InfraCategoryDO extends BaseDO { - - public static final Long PARENT_ID_ROOT = 0L; - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 名字 - */ - private String name; - /** - * 父编号 - */ - private Long parentId; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO deleted file mode 100644 index e5c6f18..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryListReqVO +++ /dev/null @@ -1,15 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import lombok.*; -import java.util.*; -import io.swagger.v3.oas.annotations.media.Schema; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -@Schema(description = "管理后台 - 分类列表 Request VO") -@Data -public class InfraCategoryListReqVO { - - @Schema(description = "名字", example = "芋头") - private String name; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper deleted file mode 100644 index 9dadbf1..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryMapper +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.infra.dal.mysql.demo; - -import java.util.*; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import org.apache.ibatis.annotations.Mapper; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; - -/** - * 分类 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface InfraCategoryMapper extends BaseMapperX { - - default List selectList(InfraCategoryListReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .likeIfPresent(InfraCategoryDO::getName, reqVO.getName()) - .orderByDesc(InfraCategoryDO::getId)); - } - - default InfraCategoryDO selectByParentIdAndName(Long parentId, String name) { - return selectOne(InfraCategoryDO::getParentId, parentId, InfraCategoryDO::getName, name); - } - - default Long selectCountByParentId(Long parentId) { - return selectCount(InfraCategoryDO::getParentId, parentId); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO deleted file mode 100644 index 6325d86..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryRespVO +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import java.util.*; -import com.alibaba.excel.annotation.*; - -@Schema(description = "管理后台 - 分类 Response VO") -@Data -@ExcelIgnoreUnannotated -public class InfraCategoryRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @ExcelProperty("名字") - private String name; - - @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - @ExcelProperty("父编号") - private Long parentId; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO deleted file mode 100644 index 3c03b97..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategorySaveReqVO +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.infra.controller.admin.demo.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import java.util.*; -import javax.validation.constraints.*; -import java.util.*; - -@Schema(description = "管理后台 - 分类新增/修改 Request VO") -@Data -public class InfraCategorySaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋头") - @NotEmpty(message = "名字不能为空") - private String name; - - @Schema(description = "父编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - @NotNull(message = "父编号不能为空") - private Long parentId; - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService deleted file mode 100644 index 9d0ae1a..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryService +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import java.util.*; -import javax.validation.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -/** - * 分类 Service 接口 - * - * @author 芋道源码 - */ -public interface InfraCategoryService { - - /** - * 创建分类 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createCategory(@Valid InfraCategorySaveReqVO createReqVO); - - /** - * 更新分类 - * - * @param updateReqVO 更新信息 - */ - void updateCategory(@Valid InfraCategorySaveReqVO updateReqVO); - - /** - * 删除分类 - * - * @param id 编号 - */ - void deleteCategory(Long id); - - /** - * 获得分类 - * - * @param id 编号 - * @return 分类 - */ - InfraCategoryDO getCategory(Long id); - - /** - * 获得分类列表 - * - * @param listReqVO 查询条件 - * @return 分类列表 - */ - List getCategoryList(InfraCategoryListReqVO listReqVO); - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl deleted file mode 100644 index 351568b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImpl +++ /dev/null @@ -1,136 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.springframework.stereotype.Service; -import javax.annotation.Resource; -import org.springframework.validation.annotation.Validated; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; - -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; - -/** - * 分类 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class InfraCategoryServiceImpl implements InfraCategoryService { - - @Resource - private InfraCategoryMapper categoryMapper; - - @Override - public Long createCategory(InfraCategorySaveReqVO createReqVO) { - // 校验父编号的有效性 - validateParentCategory(null, createReqVO.getParentId()); - // 校验名字的唯一性 - validateCategoryNameUnique(null, createReqVO.getParentId(), createReqVO.getName()); - - // 插入 - InfraCategoryDO category = BeanUtils.toBean(createReqVO, InfraCategoryDO.class); - categoryMapper.insert(category); - // 返回 - return category.getId(); - } - - @Override - public void updateCategory(InfraCategorySaveReqVO updateReqVO) { - // 校验存在 - validateCategoryExists(updateReqVO.getId()); - // 校验父编号的有效性 - validateParentCategory(updateReqVO.getId(), updateReqVO.getParentId()); - // 校验名字的唯一性 - validateCategoryNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); - - // 更新 - InfraCategoryDO updateObj = BeanUtils.toBean(updateReqVO, InfraCategoryDO.class); - categoryMapper.updateById(updateObj); - } - - @Override - public void deleteCategory(Long id) { - // 校验存在 - validateCategoryExists(id); - // 校验是否有子分类 - if (categoryMapper.selectCountByParentId(id) > 0) { - throw exception(CATEGORY_EXITS_CHILDREN); - } - // 删除 - categoryMapper.deleteById(id); - } - - private void validateCategoryExists(Long id) { - if (categoryMapper.selectById(id) == null) { - throw exception(CATEGORY_NOT_EXISTS); - } - } - - private void validateParentCategory(Long id, Long parentId) { - if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { - return; - } - // 1. 不能设置自己为父分类 - if (Objects.equals(id, parentId)) { - throw exception(CATEGORY_PARENT_ERROR); - } - // 2. 父分类不存在 - CategoryDO parentCategory = categoryMapper.selectById(parentId); - if (parentCategory == null) { - throw exception(CATEGORY_PARENT_NOT_EXITS); - } - // 3. 递归校验父分类,如果父分类是自己的子分类,则报错,避免形成环路 - if (id == null) { // id 为空,说明新增,不需要考虑环路 - return; - } - for (int i = 0; i < Short.MAX_VALUE; i++) { - // 3.1 校验环路 - parentId = parentCategory.getParentId(); - if (Objects.equals(id, parentId)) { - throw exception(CATEGORY_PARENT_IS_CHILD); - } - // 3.2 继续递归下一级父分类 - if (parentId == null || CategoryDO.PARENT_ID_ROOT.equals(parentId)) { - break; - } - parentCategory = categoryMapper.selectById(parentId); - if (parentCategory == null) { - break; - } - } - } - - private void validateCategoryNameUnique(Long id, Long parentId, String name) { - CategoryDO category = categoryMapper.selectByParentIdAndName(parentId, name); - if (category == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的分类 - if (id == null) { - throw exception(CATEGORY_NAME_DUPLICATE); - } - if (!Objects.equals(category.getId(), id)) { - throw exception(CATEGORY_NAME_DUPLICATE); - } - } - - @Override - public InfraCategoryDO getCategory(Long id) { - return categoryMapper.selectById(id); - } - - @Override - public List getCategoryList(InfraCategoryListReqVO listReqVO) { - return categoryMapper.selectList(listReqVO); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest deleted file mode 100644 index efb70fd..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/java/InfraCategoryServiceImplTest +++ /dev/null @@ -1,129 +0,0 @@ -package cn.iocoder.yudao.module.infra.service.demo; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; - -import javax.annotation.Resource; - -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; - -import cn.iocoder.yudao.module.infra.controller.admin.demo.vo.*; -import cn.iocoder.yudao.module.infra.dal.dataobject.demo.InfraCategoryDO; -import cn.iocoder.yudao.module.infra.dal.mysql.demo.InfraCategoryMapper; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - -import javax.annotation.Resource; -import org.springframework.context.annotation.Import; -import java.util.*; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link InfraCategoryServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(InfraCategoryServiceImpl.class) -public class InfraCategoryServiceImplTest extends BaseDbUnitTest { - - @Resource - private InfraCategoryServiceImpl categoryService; - - @Resource - private InfraCategoryMapper categoryMapper; - - @Test - public void testCreateCategory_success() { - // 准备参数 - InfraCategorySaveReqVO createReqVO = randomPojo(InfraCategorySaveReqVO.class).setId(null); - - // 调用 - Long categoryId = categoryService.createCategory(createReqVO); - // 断言 - assertNotNull(categoryId); - // 校验记录的属性是否正确 - InfraCategoryDO category = categoryMapper.selectById(categoryId); - assertPojoEquals(createReqVO, category, "id"); - } - - @Test - public void testUpdateCategory_success() { - // mock 数据 - InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); - categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 - // 准备参数 - InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class, o -> { - o.setId(dbCategory.getId()); // 设置更新的 ID - }); - - // 调用 - categoryService.updateCategory(updateReqVO); - // 校验是否更新正确 - InfraCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, category); - } - - @Test - public void testUpdateCategory_notExists() { - // 准备参数 - InfraCategorySaveReqVO updateReqVO = randomPojo(InfraCategorySaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS); - } - - @Test - public void testDeleteCategory_success() { - // mock 数据 - InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class); - categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbCategory.getId(); - - // 调用 - categoryService.deleteCategory(id); - // 校验数据不存在了 - assertNull(categoryMapper.selectById(id)); - } - - @Test - public void testDeleteCategory_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); - } - - @Test - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解 - public void testGetCategoryList() { - // mock 数据 - InfraCategoryDO dbCategory = randomPojo(InfraCategoryDO.class, o -> { // 等会查询到 - o.setName(null); - }); - categoryMapper.insert(dbCategory); - // 测试 name 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName(null))); - // 准备参数 - InfraCategoryListReqVO reqVO = new InfraCategoryListReqVO(); - reqVO.setName(null); - - // 调用 - List list = categoryService.getCategoryList(reqVO); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbCategory, list.get(0)); - } - -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/sql/h2 b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/sql/h2 deleted file mode 100644 index 4141766..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/sql/h2 +++ /dev/null @@ -1,10 +0,0 @@ --- 将该建表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "infra_category" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "description" bigint NOT NULL, - PRIMARY KEY ("id") -) COMMENT '分类表'; - --- 将该删表 SQL 语句,添加到 yudao-module-infra-biz 模块的 test/resources/sql/clean.sql 文件里 -DELETE FROM "infra_category"; \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/sql/sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/sql/sql deleted file mode 100644 index 8140948..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/sql/sql +++ /dev/null @@ -1,55 +0,0 @@ --- 菜单 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status, component_name -) -VALUES ( - '分类管理', '', 2, 0, 888, - 'category', '', 'infra/demo/index', 0, 'InfraCategory' -); - --- 按钮父菜单ID --- 暂时只支持 MySQL。如果你是 Oracle、PostgreSQL、SQLServer 的话,需要手动修改 @parentId 的部分的代码 -SELECT @parentId := LAST_INSERT_ID(); - --- 按钮 SQL -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类查询', 'infra:category:query', 3, 1, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类创建', 'infra:category:create', 3, 2, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类更新', 'infra:category:update', 3, 3, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类删除', 'infra:category:delete', 3, 4, @parentId, - '', '', '', 0 -); -INSERT INTO system_menu( - name, permission, type, sort, parent_id, - path, icon, component, status -) -VALUES ( - '分类导出', 'infra:category:export', 3, 5, @parentId, - '', '', '', 0 -); \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/ts/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/ts/index deleted file mode 100644 index 453c885..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/ts/index +++ /dev/null @@ -1,37 +0,0 @@ -import request from '@/config/axios' - -export interface CategoryVO { - id: number - name: string - parentId: number -} - -// 查询分类列表 -export const getCategoryList = async (params) => { - return await request.get({ url: `/infra/category/list`, params }) -} - -// 查询分类详情 -export const getCategory = async (id: number) => { - return await request.get({ url: `/infra/category/get?id=` + id }) -} - -// 新增分类 -export const createCategory = async (data: CategoryVO) => { - return await request.post({ url: `/infra/category/create`, data }) -} - -// 修改分类 -export const updateCategory = async (data: CategoryVO) => { - return await request.put({ url: `/infra/category/update`, data }) -} - -// 删除分类 -export const deleteCategory = async (id: number) => { - return await request.delete({ url: `/infra/category/delete?id=` + id }) -} - -// 导出分类 Excel -export const exportCategory = async (params) => { - return await request.download({ url: `/infra/category/export-excel`, params }) -} \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm deleted file mode 100644 index 8e139fb..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/vue/CategoryForm +++ /dev/null @@ -1,114 +0,0 @@ - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/vue/index b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/vue/index deleted file mode 100644 index 46902e7..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/vue/index +++ /dev/null @@ -1,185 +0,0 @@ - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper deleted file mode 100644 index 025ac85..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/codegen/windows10/vue3_tree/xml/InfraCategoryMapper +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/logback.xml b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/logback.xml deleted file mode 100644 index daf756b..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/logback.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/clean.sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/clean.sql deleted file mode 100644 index 58345ed..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/clean.sql +++ /dev/null @@ -1,11 +0,0 @@ -DELETE FROM "infra_config"; -DELETE FROM "infra_file_config"; -DELETE FROM "infra_file"; -DELETE FROM "infra_job"; -DELETE FROM "infra_job_log"; -DELETE FROM "infra_api_access_log"; -DELETE FROM "infra_api_error_log"; -DELETE FROM "infra_file_config"; -DELETE FROM "infra_data_source_config"; -DELETE FROM "infra_codegen_table"; -DELETE FROM "infra_codegen_column"; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql b/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql deleted file mode 100644 index d4b19c9..0000000 --- a/yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql +++ /dev/null @@ -1,216 +0,0 @@ - -CREATE TABLE IF NOT EXISTS "infra_config" ( - "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '编号', - "category" varchar(50) NOT NULL, - "type" tinyint NOT NULL, - "name" varchar(100) NOT NULL DEFAULT '' COMMENT '名字', - "config_key" varchar(100) NOT NULL DEFAULT '', - "value" varchar(500) NOT NULL DEFAULT '', - "visible" bit NOT NULL, - "remark" varchar(500) DEFAULT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '参数配置表'; - -CREATE TABLE IF NOT EXISTS "infra_file_config" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(63) NOT NULL, - "storage" tinyint NOT NULL, - "remark" varchar(255), - "master" bit(1) NOT NULL, - "config" varchar(4096) NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '文件配置表'; - -CREATE TABLE IF NOT EXISTS "infra_file" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "config_id" bigint NOT NULL, - "name" varchar(256), - "path" varchar(512), - "url" varchar(1024), - "type" varchar(63) DEFAULT NULL, - "size" bigint NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY ("id") -) COMMENT '文件表'; - -CREATE TABLE IF NOT EXISTS "infra_job" ( - "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '任务编号', - "name" varchar(32) NOT NULL COMMENT '任务名称', - "status" tinyint(4) NOT NULL COMMENT '任务状态', - "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字', - "handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数', - "cron_expression" varchar(32) NOT NULL COMMENT 'CRON 表达式', - "retry_count" int(11) NOT NULL DEFAULT '0' COMMENT '重试次数', - "retry_interval" int(11) NOT NULL DEFAULT '0' COMMENT '重试间隔', - "monitor_timeout" int(11) NOT NULL DEFAULT '0' COMMENT '监控超时时间', - "creator" varchar(64) DEFAULT '' COMMENT '创建者', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - "updater" varchar(64) DEFAULT '' COMMENT '更新者', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - "deleted" bit NOT NULL DEFAULT FALSE COMMENT '是否删除', - PRIMARY KEY ("id") -) COMMENT='定时任务表'; - -CREATE TABLE IF NOT EXISTS "infra_job_log" ( - "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '日志编号', - "job_id" bigint(20) NOT NULL COMMENT '任务编号', - "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字', - "handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数', - "execute_index" tinyint(4) NOT NULL DEFAULT '1' COMMENT '第几次执行', - "begin_time" datetime NOT NULL COMMENT '开始执行时间', - "end_time" datetime DEFAULT NULL COMMENT '结束执行时间', - "duration" int(11) DEFAULT NULL COMMENT '执行时长', - "status" tinyint(4) NOT NULL COMMENT '任务状态', - "result" varchar(4000) DEFAULT '' COMMENT '结果数据', - "creator" varchar(64) DEFAULT '' COMMENT '创建者', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - "updater" varchar(64) DEFAULT '' COMMENT '更新者', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - "deleted" bit(1) NOT NULL DEFAULT FALSE COMMENT '是否删除', - PRIMARY KEY ("id") -)COMMENT='定时任务日志表'; - -CREATE TABLE IF NOT EXISTS "infra_api_access_log" ( - "id" bigint not null GENERATED BY DEFAULT AS IDENTITY, - "trace_id" varchar(64) not null default '', - "user_id" bigint not null default '0', - "user_type" tinyint not null default '0', - "application_name" varchar(50) not null, - "request_method" varchar(16) not null default '', - "request_url" varchar(255) not null default '', - "request_params" varchar(8000) not null default '', - "response_body" varchar(8000) not null default '', - "user_ip" varchar(50) not null, - "user_agent" varchar(512) not null, - `operate_module` varchar(50) NOT NULL, - `operate_name` varchar(50) NOT NULL, - `operate_type` bigint(4) NOT NULL DEFAULT '0', - "begin_time" timestamp not null, - "end_time" timestamp not null, - "duration" integer not null, - "result_code" integer not null default '0', - "result_msg" varchar(512) default '', - "creator" varchar(64) default '', - "create_time" timestamp not null default current_timestamp, - "updater" varchar(64) default '', - "update_time" timestamp not null default current_timestamp, - "deleted" bit not null default false, - "tenant_id" bigint not null default '0', - primary key ("id") -) COMMENT 'API 访问日志表'; - -CREATE TABLE IF NOT EXISTS "infra_api_error_log" ( - "id" bigint not null GENERATED BY DEFAULT AS IDENTITY, - "trace_id" varchar(64) not null, - "user_id" bigint not null default '0', - "user_type" tinyint not null default '0', - "application_name" varchar(50) not null, - "request_method" varchar(16) not null, - "request_url" varchar(255) not null, - "request_params" varchar(8000) not null, - "user_ip" varchar(50) not null, - "user_agent" varchar(512) not null, - "exception_time" timestamp not null, - "exception_name" varchar(128) not null default '', - "exception_message" clob not null, - "exception_root_cause_message" clob not null, - "exception_stack_trace" clob not null, - "exception_class_name" varchar(512) not null, - "exception_file_name" varchar(512) not null, - "exception_method_name" varchar(512) not null, - "exception_line_number" integer not null, - "process_status" tinyint not null, - "process_time" timestamp default null, - "process_user_id" bigint default '0', - "creator" varchar(64) default '', - "create_time" timestamp not null default current_timestamp, - "updater" varchar(64) default '', - "update_time" timestamp not null default current_timestamp, - "deleted" bit not null default false, - "tenant_id" bigint not null default '0', - primary key ("id") -) COMMENT '系统异常日志'; - -CREATE TABLE IF NOT EXISTS "infra_data_source_config" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(100) NOT NULL, - "url" varchar(1024) NOT NULL, - "username" varchar(255) NOT NULL, - "password" varchar(255) NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '数据源配置表'; - -CREATE TABLE IF NOT EXISTS "infra_codegen_table" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "data_source_config_id" bigint not null, - "scene" tinyint not null DEFAULT 1, - "table_name" varchar(200) NOT NULL, - "table_comment" varchar(500) NOT NULL, - "remark" varchar(500) NOT NULL, - "module_name" varchar(30) NOT NULL, - "business_name" varchar(30) NOT NULL, - "class_name" varchar(100) NOT NULL, - "class_comment" varchar(50) NOT NULL, - "author" varchar(50) NOT NULL, - "template_type" tinyint not null DEFAULT 1, - "front_type" tinyint not null, - "parent_menu_id" bigint not null, - "master_table_id" bigint not null, - "sub_join_column_id" bigint not null, - "sub_join_many" bit not null, - "tree_parent_column_id" bigint not null, - "tree_name_column_id" bigint not null, - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '代码生成表定义表'; - -CREATE TABLE IF NOT EXISTS "infra_codegen_column" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "table_id" bigint not null, - "column_name" varchar(200) NOT NULL, - "data_type" varchar(100) NOT NULL, - "column_comment" varchar(500) NOT NULL, - "nullable" tinyint not null, - "primary_key" tinyint not null, - "ordinal_position" int not null, - "java_type" varchar(32) NOT NULL, - "java_field" varchar(64) NOT NULL, - "dict_type" varchar(200) NOT NULL, - "example" varchar(64) NOT NULL, - "create_operation" bit not null, - "update_operation" bit not null, - "list_operation" bit not null, - "list_operation_condition" varchar(32) not null, - "list_operation_result" bit not null, - "html_type" varchar(32) NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '代码生成表字段定义表'; \ No newline at end of file diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/AppSocialUserController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/AppSocialUserController.java index 5795e8f..fccf10b 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/AppSocialUserController.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/social/AppSocialUserController.java @@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.member.controller.app.social.vo.*; import cn.iocoder.yudao.module.system.api.social.SocialClientApi; import cn.iocoder.yudao.module.system.api.social.SocialUserApi; -import cn.iocoder.yudao.module.system.api.social.dto.*; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; diff --git a/yudao-module-system/pom.xml b/yudao-module-system/pom.xml index c2cc5a9..348133d 100644 --- a/yudao-module-system/pom.xml +++ b/yudao-module-system/pom.xml @@ -8,12 +8,8 @@ ${revision} 4.0.0 - - yudao-module-system-api - yudao-module-system-biz - yudao-module-system - pom + jar ${project.artifactId} @@ -21,4 +17,108 @@ 例如说:用户、部门、权限、数据字典等等 + + + cn.iocoder.boot + yudao-module-infra + ${revision} + + + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-data-permission + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-tenant + + + cn.iocoder.boot + yudao-spring-boot-starter-biz-ip + + + + + cn.iocoder.boot + yudao-spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter-validation + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mybatis + + + + cn.iocoder.boot + yudao-spring-boot-starter-redis + + + + + cn.iocoder.boot + yudao-spring-boot-starter-job + + + + + cn.iocoder.boot + yudao-spring-boot-starter-mq + + + + + cn.iocoder.boot + yudao-spring-boot-starter-test + test + + + + + cn.iocoder.boot + yudao-spring-boot-starter-excel + + + + org.springframework.boot + spring-boot-starter-mail + + + + + me.zhyd.oauth + JustAuth + + + com.xkcoding.justauth + justauth-spring-boot-starter + + + + com.github.binarywang + wx-java-mp-spring-boot-starter + + + com.github.binarywang + wx-java-miniapp-spring-boot-starter + + + + com.anji-plus + captcha-spring-boot-starter + + + + org.dromara.hutool + hutool-extra + + + + diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java new file mode 100644 index 0000000..bdc3ba5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.system.api.dept; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 部门 API 接口 + * + * @author 芋道源码 + */ +public interface DeptApi { + + /** + * 获得部门信息 + * + * @param id 部门编号 + * @return 部门信息 + */ + DeptRespDTO getDept(Long id); + + /** + * 获得部门信息数组 + * + * @param ids 部门编号数组 + * @return 部门信息数组 + */ + List getDeptList(Collection ids); + + /** + * 校验部门们是否有效。如下情况,视为无效: + * 1. 部门编号不存在 + * 2. 部门被禁用 + * + * @param ids 角色编号数组 + */ + void validateDeptList(Collection ids); + + /** + * 获得指定编号的部门 Map + * + * @param ids 部门编号数组 + * @return 部门 Map + */ + default Map getDeptMap(Collection ids) { + List list = getDeptList(ids); + return CollectionUtils.convertMap(list, DeptRespDTO::getId); + } + + /** + * 获得指定部门的所有子部门 + * + * @param id 部门编号 + * @return 子部门列表 + */ + List getChildDeptList(Long id); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java new file mode 100644 index 0000000..a37dc1b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.system.api.dept; + +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.List; + +/** + * 部门 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class DeptApiImpl implements DeptApi { + + @Resource + private DeptService deptService; + + @Override + public DeptRespDTO getDept(Long id) { + DeptDO dept = deptService.getDept(id); + return BeanUtils.toBean(dept, DeptRespDTO.class); + } + + @Override + public List getDeptList(Collection ids) { + List depts = deptService.getDeptList(ids); + return BeanUtils.toBean(depts, DeptRespDTO.class); + } + + @Override + public void validateDeptList(Collection ids) { + deptService.validateDeptList(ids); + } + + @Override + public List getChildDeptList(Long id) { + List childDeptList = deptService.getChildDeptList(id); + return BeanUtils.toBean(childDeptList, DeptRespDTO.class); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java new file mode 100644 index 0000000..c38f4cd --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.api.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 岗位 API 接口 + * + * @author 芋道源码 + */ +public interface PostApi { + + /** + * 校验岗位们是否有效。如下情况,视为无效: + * 1. 岗位编号不存在 + * 2. 岗位被禁用 + * + * @param ids 岗位编号数组 + */ + void validPostList(Collection ids); + + List getPostList(Collection ids); + + default Map getPostMap(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return MapUtil.empty(); + } + + List list = getPostList(ids); + return CollectionUtils.convertMap(list, PostRespDTO::getId); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java new file mode 100644 index 0000000..708ea0b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.api.dept; + +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.service.dept.PostService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.List; + +/** + * 岗位 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class PostApiImpl implements PostApi { + + @Resource + private PostService postService; + + @Override + public void validPostList(Collection ids) { + postService.validatePostList(ids); + } + + @Override + public List getPostList(Collection ids) { + List list = postService.getPostList(ids); + return BeanUtils.toBean(list, PostRespDTO.class); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java new file mode 100644 index 0000000..d3e66fd --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.system.api.dept.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import lombok.Data; + +/** + * 部门 Response DTO + * + * @author 芋道源码 + */ +@Data +public class DeptRespDTO { + + /** + * 部门编号 + */ + private Long id; + /** + * 部门名称 + */ + private String name; + /** + * 父部门编号 + */ + private Long parentId; + /** + * 负责人的用户编号 + */ + private Long leaderUserId; + /** + * 部门状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java new file mode 100644 index 0000000..cf2cc25 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.system.api.dept.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import lombok.Data; + +/** + * 岗位 Response DTO + * + * @author 芋道源码 + */ +@Data +public class PostRespDTO { + + /** + * 岗位序号 + */ + private Long id; + /** + * 岗位名称 + */ + private String name; + /** + * 岗位编码 + */ + private String code; + /** + * 岗位排序 + */ + private Integer sort; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java new file mode 100644 index 0000000..239f1fe --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.system.api.dict; + +import cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi; + +import java.util.Collection; + +/** + * 字典数据 API 接口 + * + * @author 芋道源码 + */ +public interface DictDataApi extends DictDataCommonApi { + + /** + * 校验字典数据们是否有效。如下情况,视为无效: + * 1. 字典数据不存在 + * 2. 字典数据被禁用 + * + * @param dictType 字典类型 + * @param values 字典数据值的数组 + */ + void validateDictDataList(String dictType, Collection values); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java new file mode 100644 index 0000000..8f46358 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.api.dict; + +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.biz.system.dict.dto.DictDataRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; +import cn.iocoder.yudao.module.system.service.dict.DictDataService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * 字典数据 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class DictDataApiImpl implements DictDataApi { + + @Resource + private DictDataService dictDataService; + + @Override + public void validateDictDataList(String dictType, Collection values) { + dictDataService.validateDictDataList(dictType, values); + } + + @Override + public List getDictDataList(String dictType) { + List list = dictDataService.getDictDataListByDictType(dictType); + return BeanUtils.toBean(list, DictDataRespDTO.class); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApi.java new file mode 100644 index 0000000..f24d154 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApi.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.api.logger; + +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; + +import jakarta.validation.Valid; + +/** + * 登录日志的 API 接口 + * + * @author 芋道源码 + */ +public interface LoginLogApi { + + /** + * 创建登录日志 + * + * @param reqDTO 日志信息 + */ + void createLoginLog(@Valid LoginLogCreateReqDTO reqDTO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApiImpl.java new file mode 100644 index 0000000..3d8dc92 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApiImpl.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.api.logger; + +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; +import cn.iocoder.yudao.module.system.service.logger.LoginLogService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; + +/** + * 登录日志的 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class LoginLogApiImpl implements LoginLogApi { + + @Resource + private LoginLogService loginLogService; + + @Override + public void createLoginLog(LoginLogCreateReqDTO reqDTO) { + loginLogService.createLoginLog(reqDTO); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java new file mode 100644 index 0000000..e6894a9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.api.logger; + +import cn.iocoder.yudao.framework.common.biz.system.logger.OperateLogCommonApi; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO; + +/** + * 操作日志 API 接口 + * + * @author 芋道源码 + */ +public interface OperateLogApi extends OperateLogCommonApi { + + /** + * 获取指定模块的指定数据的操作日志分页 + * + * @param pageReqDTO 请求 + * @return 操作日志分页 + */ + PageResult getOperateLogPage(OperateLogPageReqDTO pageReqDTO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java new file mode 100644 index 0000000..61fdf9f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.api.logger; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; +import cn.iocoder.yudao.module.system.service.logger.OperateLogService; +import com.fhs.core.trans.anno.TransMethodResult; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +/** + * 操作日志 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class OperateLogApiImpl implements OperateLogApi { + + @Resource + private OperateLogService operateLogService; + + @Override + public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { + operateLogService.createOperateLog(createReqDTO); + } + + @Override + @TransMethodResult + public PageResult getOperateLogPage(OperateLogPageReqDTO pageReqDTO) { + PageResult operateLogPage = operateLogService.getOperateLogPage(pageReqDTO); + return BeanUtils.toBean(operateLogPage, OperateLogRespDTO.class); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/LoginLogCreateReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/LoginLogCreateReqDTO.java new file mode 100644 index 0000000..539c8ee --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/LoginLogCreateReqDTO.java @@ -0,0 +1,62 @@ +package cn.iocoder.yudao.module.system.api.logger.dto; + +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +/** + * 登录日志创建 Request DTO + * + * @author 芋道源码 + */ +@Data +public class LoginLogCreateReqDTO { + + /** + * 日志类型 + */ + @NotNull(message = "日志类型不能为空") + private Integer logType; + /** + * 链路追踪编号 + */ + private String traceId; + + /** + * 用户编号 + */ + private Long userId; + /** + * 用户类型 + */ + @NotNull(message = "用户类型不能为空") + private Integer userType; + /** + * 用户账号 + * + * 不再强制校验 username 非空,因为 Member 社交登录时,此时暂时没有 username(mobile)! + */ + private String username; + + /** + * 登录结果 + */ + @NotNull(message = "登录结果不能为空") + private Integer result; + + /** + * 用户 IP + */ + @NotEmpty(message = "用户 IP 不能为空") + private String userIp; + /** + * 浏览器 UserAgent + * + * 允许空,原因:Job 过期登出时,是无法传递 UserAgent 的 + */ + private String userAgent; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogPageReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogPageReqDTO.java new file mode 100644 index 0000000..80e52cd --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogPageReqDTO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.system.api.logger.dto; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import lombok.Data; + +/** + * 操作日志分页 Request DTO + * + * @author HUIHUI + */ +@Data +public class OperateLogPageReqDTO extends PageParam { + + /** + * 模块类型 + */ + private String type; + /** + * 模块数据编号 + */ + private Long bizId; + + /** + * 用户编号 + */ + private Long userId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogRespDTO.java new file mode 100644 index 0000000..41ff437 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogRespDTO.java @@ -0,0 +1,83 @@ +package cn.iocoder.yudao.module.system.api.logger.dto; + +import com.fhs.core.trans.anno.Trans; +import com.fhs.core.trans.constant.TransType; +import com.fhs.core.trans.vo.VO; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 系统操作日志 Resp DTO + * + * @author HUIHUI + */ +@Data +public class OperateLogRespDTO implements VO { + + /** + * 日志编号 + */ + private Long id; + /** + * 链路追踪编号 + */ + private String traceId; + /** + * 用户编号 + */ + @Trans(type = TransType.SIMPLE, targetClassName = "cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO", + fields = "nickname", ref = "userName") + private Long userId; + /** + * 用户名称 + */ + private String userName; + /** + * 用户类型 + */ + private Integer userType; + /** + * 操作模块类型 + */ + private String type; + /** + * 操作名 + */ + private String subType; + /** + * 操作模块业务编号 + */ + private Long bizId; + /** + * 操作内容 + */ + private String action; + /** + * 拓展字段 + */ + private String extra; + + /** + * 请求方法名 + */ + private String requestMethod; + /** + * 请求地址 + */ + private String requestUrl; + /** + * 用户 IP + */ + private String userIp; + /** + * 浏览器 UA + */ + private String userAgent; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java new file mode 100644 index 0000000..9fb4b0e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.api.mail; + +import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleToUserReqDTO; + +import jakarta.validation.Valid; + +/** + * 邮箱发送 API 接口 + * + * @author 芋道源码 + */ +public interface MailSendApi { + + /** + * 发送单条邮箱给 Admin 用户 + * + * 在 mail 为空时,使用 userId 加载对应 Admin 的邮箱 + * + * @param reqDTO 发送请求 + * @return 发送日志编号 + */ + Long sendSingleMailToAdmin(@Valid MailSendSingleToUserReqDTO reqDTO); + + /** + * 发送单条邮箱给 Member 用户 + * + * 在 mail 为空时,使用 userId 加载对应 Member 的邮箱 + * + * @param reqDTO 发送请求 + * @return 发送日志编号 + */ + Long sendSingleMailToMember(@Valid MailSendSingleToUserReqDTO reqDTO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java new file mode 100644 index 0000000..0a8910e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.api.mail; + +import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleToUserReqDTO; +import cn.iocoder.yudao.module.system.service.mail.MailSendService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; + +/** + * 邮件发送 API 实现类 + * + * @author wangjingyi + */ +@Service +@Validated +public class MailSendApiImpl implements MailSendApi { + + @Resource + private MailSendService mailSendService; + + @Override + public Long sendSingleMailToAdmin(MailSendSingleToUserReqDTO reqDTO) { + return mailSendService.sendSingleMailToAdmin(reqDTO.getMail(), reqDTO.getUserId(), + reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); + } + + @Override + public Long sendSingleMailToMember(MailSendSingleToUserReqDTO reqDTO) { + return mailSendService.sendSingleMailToMember(reqDTO.getMail(), reqDTO.getUserId(), + reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendSingleToUserReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendSingleToUserReqDTO.java new file mode 100644 index 0000000..0481d59 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendSingleToUserReqDTO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.system.api.mail.dto; + +import lombok.Data; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotNull; +import java.util.Map; + +/** + * 邮件发送 Request DTO + * + * @author wangjingqi + */ +@Data +public class MailSendSingleToUserReqDTO { + + /** + * 用户编号 + */ + private Long userId; + /** + * 邮箱 + */ + @Email + private String mail; + + /** + * 邮件模板编号 + */ + @NotNull(message = "邮件模板编号不能为空") + private String templateCode; + /** + * 邮件模板参数 + */ + private Map templateParams; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java new file mode 100644 index 0000000..ca8e0c9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.api.notify; + +import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; + +import jakarta.validation.Valid; + +/** + * 站内信发送 API 接口 + * + * @author xrcoder + */ +public interface NotifyMessageSendApi { + + /** + * 发送单条站内信给 Admin 用户 + * + * @param reqDTO 发送请求 + * @return 发送消息 ID + */ + Long sendSingleMessageToAdmin(@Valid NotifySendSingleToUserReqDTO reqDTO); + + /** + * 发送单条站内信给 Member 用户 + * + * @param reqDTO 发送请求 + * @return 发送消息 ID + */ + Long sendSingleMessageToMember(@Valid NotifySendSingleToUserReqDTO reqDTO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApiImpl.java new file mode 100644 index 0000000..3fee347 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApiImpl.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.system.api.notify; + +import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; +import cn.iocoder.yudao.module.system.service.notify.NotifySendService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; + +/** + * 站内信发送 API 实现类 + * + * @author xrcoder + */ +@Service +public class NotifyMessageSendApiImpl implements NotifyMessageSendApi { + + @Resource + private NotifySendService notifySendService; + + @Override + public Long sendSingleMessageToAdmin(NotifySendSingleToUserReqDTO reqDTO) { + return notifySendService.sendSingleNotifyToAdmin(reqDTO.getUserId(), + reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); + } + + @Override + public Long sendSingleMessageToMember(NotifySendSingleToUserReqDTO reqDTO) { + return notifySendService.sendSingleNotifyToMember(reqDTO.getUserId(), + reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifySendSingleToUserReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifySendSingleToUserReqDTO.java new file mode 100644 index 0000000..635cc16 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifySendSingleToUserReqDTO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.api.notify.dto; + +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.Map; + +/** + * 站内信发送给 Admin 或者 Member 用户 + * + * @author xrcoder + */ +@Data +public class NotifySendSingleToUserReqDTO { + + /** + * 用户编号 + */ + @NotNull(message = "用户编号不能为空") + private Long userId; + + /** + * 站内信模板编号 + */ + @NotEmpty(message = "站内信模板编号不能为空") + private String templateCode; + + /** + * 站内信模板参数 + */ + private Map templateParams; +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifyTemplateReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifyTemplateReqDTO.java new file mode 100644 index 0000000..688e091 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifyTemplateReqDTO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.api.notify.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Data +public class NotifyTemplateReqDTO { + + @NotEmpty(message = "模版名称不能为空") + private String name; + + @NotNull(message = "模版编码不能为空") + private String code; + + @NotNull(message = "模版类型不能为空") + private Integer type; + + @NotEmpty(message = "发送人名称不能为空") + private String nickname; + + @NotEmpty(message = "模版内容不能为空") + private String content; + + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") + private Integer status; + + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java new file mode 100644 index 0000000..94fed14 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.system.api.oauth2; + +import cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO; +import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO; +import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; + +/** + * OAuth2.0 Token API 实现类 + * + * @author 芋道源码 + */ +@Service +public class OAuth2TokenApiImpl implements OAuth2TokenCommonApi { + + @Resource + private OAuth2TokenService oauth2TokenService; + + @Override + public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) { + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken( + reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId(), reqDTO.getScopes()); + return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class); + } + + @Override + public OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken) { + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.checkAccessToken(accessToken); + return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenCheckRespDTO.class); + } + + @Override + public OAuth2AccessTokenRespDTO removeAccessToken(String accessToken) { + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(accessToken); + return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class); + } + + @Override + public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, String clientId) { + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId); + return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/package-info.java new file mode 100644 index 0000000..be0607b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/package-info.java @@ -0,0 +1,4 @@ +/** + * system API 包,定义并实现提供给其它模块的 API + */ +package cn.iocoder.yudao.module.system.api; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApi.java new file mode 100644 index 0000000..41d8837 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApi.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.api.permission; + +import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi; + +import java.util.Collection; +import java.util.Set; + +/** + * 权限 API 接口 + * + * @author 芋道源码 + */ +public interface PermissionApi extends PermissionCommonApi { + + /** + * 获得拥有多个角色的用户编号集合 + * + * @param roleIds 角色编号集合 + * @return 用户编号集合 + */ + Set getUserRoleIdListByRoleIds(Collection roleIds); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java new file mode 100644 index 0000000..273669d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.system.api.permission; + +import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.Set; + +/** + * 权限 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class PermissionApiImpl implements PermissionApi { + + @Resource + private PermissionService permissionService; + + @Override + public Set getUserRoleIdListByRoleIds(Collection roleIds) { + return permissionService.getUserRoleIdListByRoleId(roleIds); + } + + @Override + public boolean hasAnyPermissions(Long userId, String... permissions) { + return permissionService.hasAnyPermissions(userId, permissions); + } + + @Override + public boolean hasAnyRoles(Long userId, String... roles) { + return permissionService.hasAnyRoles(userId, roles); + } + + @Override + public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) { + return permissionService.getDeptDataPermission(userId); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApi.java new file mode 100644 index 0000000..309c9ef --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApi.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.api.permission; + +import java.util.Collection; + +/** + * 角色 API 接口 + * + * @author 芋道源码 + */ +public interface RoleApi { + + /** + * 校验角色们是否有效。如下情况,视为无效: + * 1. 角色编号不存在 + * 2. 角色被禁用 + * + * @param ids 角色编号数组 + */ + void validRoleList(Collection ids); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApiImpl.java new file mode 100644 index 0000000..6b75606 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApiImpl.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.system.api.permission; + +import cn.iocoder.yudao.module.system.service.permission.RoleService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.Collection; + +/** + * 角色 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class RoleApiImpl implements RoleApi { + + @Resource + private RoleService roleService; + + @Override + public void validRoleList(Collection ids) { + roleService.validateRoleList(ids); + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java new file mode 100644 index 0000000..eefe9da --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.system.api.sms; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; + +import jakarta.validation.Valid; + +/** + * 短信验证码 API 接口 + * + * @author 芋道源码 + */ +public interface SmsCodeApi { + + /** + * 创建短信验证码,并进行发送 + * + * @param reqDTO 发送请求 + */ + void sendSmsCode(@Valid SmsCodeSendReqDTO reqDTO); + + /** + * 验证短信验证码,并进行使用 + * 如果正确,则将验证码标记成已使用 + * 如果错误,则抛出 {@link ServiceException} 异常 + * + * @param reqDTO 使用请求 + */ + void useSmsCode(@Valid SmsCodeUseReqDTO reqDTO); + + /** + * 检查验证码是否有效 + * + * @param reqDTO 校验请求 + */ + void validateSmsCode(@Valid SmsCodeValidateReqDTO reqDTO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApiImpl.java new file mode 100644 index 0000000..79dae8b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApiImpl.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.api.sms; + +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import cn.iocoder.yudao.module.system.service.sms.SmsCodeService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; + +/** + * 短信验证码 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class SmsCodeApiImpl implements SmsCodeApi { + + @Resource + private SmsCodeService smsCodeService; + + @Override + public void sendSmsCode(SmsCodeSendReqDTO reqDTO) { + smsCodeService.sendSmsCode(reqDTO); + } + + @Override + public void useSmsCode(SmsCodeUseReqDTO reqDTO) { + smsCodeService.useSmsCode(reqDTO); + } + + @Override + public void validateSmsCode(SmsCodeValidateReqDTO reqDTO) { + smsCodeService.validateSmsCode(reqDTO); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApi.java new file mode 100644 index 0000000..21f34c8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApi.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.api.sms; + +import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; + +import jakarta.validation.Valid; + +/** + * 短信发送 API 接口 + * + * @author 芋道源码 + */ +public interface SmsSendApi { + + /** + * 发送单条短信给 Admin 用户 + * + * 在 mobile 为空时,使用 userId 加载对应 Admin 的手机号 + * + * @param reqDTO 发送请求 + * @return 发送日志编号 + */ + Long sendSingleSmsToAdmin(@Valid SmsSendSingleToUserReqDTO reqDTO); + + /** + * 发送单条短信给 Member 用户 + * + * 在 mobile 为空时,使用 userId 加载对应 Member 的手机号 + * + * @param reqDTO 发送请求 + * @return 发送日志编号 + */ + Long sendSingleSmsToMember(@Valid SmsSendSingleToUserReqDTO reqDTO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApiImpl.java new file mode 100644 index 0000000..09c5b65 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApiImpl.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.api.sms; + +import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; +import cn.iocoder.yudao.module.system.service.sms.SmsSendService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; + +/** + * 短信发送 API 接口 + * + * @author 芋道源码 + */ +@Service +@Validated +public class SmsSendApiImpl implements SmsSendApi { + + @Resource + private SmsSendService smsSendService; + + @Override + public Long sendSingleSmsToAdmin(SmsSendSingleToUserReqDTO reqDTO) { + return smsSendService.sendSingleSmsToAdmin(reqDTO.getMobile(), reqDTO.getUserId(), + reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); + } + + @Override + public Long sendSingleSmsToMember(SmsSendSingleToUserReqDTO reqDTO) { + return smsSendService.sendSingleSmsToMember(reqDTO.getMobile(), reqDTO.getUserId(), + reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeSendReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeSendReqDTO.java new file mode 100644 index 0000000..31a253f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeSendReqDTO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.system.api.sms.dto.code; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * 短信验证码的发送 Request DTO + * + * @author 芋道源码 + */ +@Data +public class SmsCodeSendReqDTO { + + /** + * 手机号 + */ + @Mobile + @NotEmpty(message = "手机号不能为空") + private String mobile; + /** + * 发送场景 + */ + @NotNull(message = "发送场景不能为空") + @InEnum(SmsSceneEnum.class) + private Integer scene; + /** + * 发送 IP + */ + @NotEmpty(message = "发送 IP 不能为空") + private String createIp; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeUseReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeUseReqDTO.java new file mode 100644 index 0000000..c700fa4 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeUseReqDTO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.system.api.sms.dto.code; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * 短信验证码的使用 Request DTO + * + * @author 芋道源码 + */ +@Data +public class SmsCodeUseReqDTO { + + /** + * 手机号 + */ + @Mobile + @NotEmpty(message = "手机号不能为空") + private String mobile; + /** + * 发送场景 + */ + @NotNull(message = "发送场景不能为空") + @InEnum(SmsSceneEnum.class) + private Integer scene; + /** + * 验证码 + */ + @NotEmpty(message = "验证码") + private String code; + /** + * 使用 IP + */ + @NotEmpty(message = "使用 IP 不能为空") + private String usedIp; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeValidateReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeValidateReqDTO.java new file mode 100644 index 0000000..b388e6b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeValidateReqDTO.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.system.api.sms.dto.code; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * 短信验证码的校验 Request DTO + * + * @author 芋道源码 + */ +@Data +public class SmsCodeValidateReqDTO { + + /** + * 手机号 + */ + @Mobile + @NotEmpty(message = "手机号不能为空") + private String mobile; + /** + * 发送场景 + */ + @NotNull(message = "发送场景不能为空") + @InEnum(SmsSceneEnum.class) + private Integer scene; + /** + * 验证码 + */ + @NotEmpty(message = "验证码") + private String code; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java new file mode 100644 index 0000000..d566a48 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.system.api.sms.dto.send; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import java.util.Map; + +/** + * 短信发送给 Admin 或者 Member 用户 + * + * @author 芋道源码 + */ +@Data +public class SmsSendSingleToUserReqDTO { + + /** + * 用户编号 + */ + private Long userId; + /** + * 手机号 + */ + @Mobile + private String mobile; + /** + * 短信模板编号 + */ + @NotEmpty(message = "短信模板编号不能为空") + private String templateCode; + /** + * 短信模板参数 + */ + private Map templateParams; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java new file mode 100644 index 0000000..7306310 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.system.api.social; + +import cn.iocoder.yudao.module.system.api.social.dto.*; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * 社交应用的 API 接口 + * + * @author 芋道源码 + */ +public interface SocialClientApi { + + /** + * 获得社交平台的授权 URL + * + * @param socialType 社交平台的类型 {@link SocialTypeEnum} + * @param userType 用户类型 + * @param redirectUri 重定向 URL + * @return 社交平台的授权 URL + */ + String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri); + + /** + * 创建微信公众号 JS SDK 初始化所需的签名 + * + * @param userType 用户类型 + * @param url 访问的 URL 地址 + * @return 签名 + */ + SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url); + + //======================= 微信小程序独有 ======================= + + /** + * 获得微信小程序的手机信息 + * + * @param userType 用户类型 + * @param phoneCode 手机授权码 + * @return 手机信息 + */ + SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode); + + /** + * 获得小程序二维码 + * + * @param reqVO 请求信息 + * @return 小程序二维码 + */ + byte[] getWxaQrcode(@Valid SocialWxQrcodeReqDTO reqVO); + + /** + * 获得微信小程订阅模板 + * + * @return 小程序订阅消息模版 + */ + List getWxaSubscribeTemplateList(Integer userType); + + /** + * 发送微信小程序订阅消息 + * + * @param reqDTO 请求 + */ + void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO); + + /** + * 上传订单发货到微信小程序 + * + * @param userType 用户类型 + * @param reqDTO 请求 + */ + void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO); + + /** + * 通知订单收货到微信小程序 + * + * @param userType 用户类型 + * @param reqDTO 请求 + */ + void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java new file mode 100644 index 0000000..d2eb00d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java @@ -0,0 +1,107 @@ +package cn.iocoder.yudao.module.system.api.social; + +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.social.dto.*; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import cn.iocoder.yudao.module.system.service.social.SocialClientService; +import cn.iocoder.yudao.module.system.service.social.SocialUserService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.List; + +import static cn.hutool.core.collection.CollUtil.findOne; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 社交应用的 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class SocialClientApiImpl implements SocialClientApi { + + @Resource + private SocialClientService socialClientService; + @Resource + private SocialUserService socialUserService; + + @Override + public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) { + return socialClientService.getAuthorizeUrl(socialType, userType, redirectUri); + } + + @Override + public SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url) { + WxJsapiSignature signature = socialClientService.createWxMpJsapiSignature(userType, url); + return BeanUtils.toBean(signature, SocialWxJsapiSignatureRespDTO.class); + } + + //======================= 微信小程序独有 ======================= + + @Override + public SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode) { + WxMaPhoneNumberInfo info = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode); + return BeanUtils.toBean(info, SocialWxPhoneNumberInfoRespDTO.class); + } + + @Override + public byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO) { + return socialClientService.getWxaQrcode(reqVO); + } + + @Override + public List getWxaSubscribeTemplateList(Integer userType) { + List list = socialClientService.getSubscribeTemplateList(userType); + return convertList(list, item -> BeanUtils.toBean(item, SocialWxaSubscribeTemplateRespDTO.class).setId(item.getPriTmplId())); + } + + @Override + public void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO) { + // 1.1 获得订阅模版列表 + List templateList = socialClientService.getSubscribeTemplateList(reqDTO.getUserType()); + if (CollUtil.isEmpty(templateList)) { + log.warn("[sendSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO); + return; + } + // 1.2 获得需要使用的模版 + TemplateInfo template = findOne(templateList, item -> + ObjUtil.equal(item.getTitle(), reqDTO.getTemplateTitle())); + if (template == null) { + log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO); + return; + } + + // 2. 获得社交用户 + SocialUserRespDTO socialUser = socialUserService.getSocialUserByUserId(reqDTO.getUserType(), reqDTO.getUserId(), + SocialTypeEnum.WECHAT_MINI_PROGRAM.getType()); + if (StrUtil.isBlankIfStr(socialUser.getOpenid())) { + log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:会员 openid 缺失]", reqDTO); + return; + } + + // 3. 发送订阅消息 + socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid()); + } + + @Override + public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) { + socialClientService.uploadWxaOrderShippingInfo(userType, reqDTO); + } + + @Override + public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) { + socialClientService.notifyWxaOrderConfirmReceive(userType, reqDTO); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java new file mode 100644 index 0000000..3ddeb05 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.system.api.social; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; +import jakarta.validation.Valid; + +/** + * 社交用户的 API 接口 + * + * @author 芋道源码 + */ +public interface SocialUserApi { + + /** + * 绑定社交用户 + * + * @param reqDTO 绑定信息 + * @return 社交用户 openid + */ + String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO); + + /** + * 取消绑定社交用户 + * + * @param reqDTO 解绑 + */ + void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO); + + /** + * 获得社交用户,基于 userId + * + * @param userType 用户类型 + * @param userId 用户编号 + * @param socialType 社交平台的类型 + * @return 社交用户 + */ + SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType); + + /** + * 获得社交用户 + * + * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 + * + * @param userType 用户类型 + * @param socialType 社交平台的类型 + * @param code 授权码 + * @param state state + * @return 社交用户 + */ + SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java new file mode 100644 index 0000000..eb83316 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.system.api.social; + +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; +import cn.iocoder.yudao.module.system.service.social.SocialUserService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; + +/** + * 社交用户的 API 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class SocialUserApiImpl implements SocialUserApi { + + @Resource + private SocialUserService socialUserService; + + @Override + public String bindSocialUser(SocialUserBindReqDTO reqDTO) { + return socialUserService.bindSocialUser(reqDTO); + } + + @Override + public void unbindSocialUser(SocialUserUnbindReqDTO reqDTO) { + socialUserService.unbindSocialUser(reqDTO.getUserId(), reqDTO.getUserType(), + reqDTO.getSocialType(), reqDTO.getOpenid()); + } + + @Override + public SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType) { + return socialUserService.getSocialUserByUserId(userType, userId, socialType); + } + + @Override + public SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state) { + return socialUserService.getSocialUserByCode(userType, socialType, code, state); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java new file mode 100644 index 0000000..0ad3952 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * 取消绑定社交用户 Request DTO + * + * @author 芋道源码 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SocialUserBindReqDTO { + + /** + * 用户编号 + */ + @NotNull(message = "用户编号不能为空") + private Long userId; + /** + * 用户类型 + */ + @InEnum(UserTypeEnum.class) + @NotNull(message = "用户类型不能为空") + private Integer userType; + + /** + * 社交平台的类型 + */ + @InEnum(SocialTypeEnum.class) + @NotNull(message = "社交平台的类型不能为空") + private Integer socialType; + /** + * 授权码 + */ + @NotEmpty(message = "授权码不能为空") + private String code; + /** + * state + */ + @NotNull(message = "state 不能为空") + private String state; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java new file mode 100644 index 0000000..29fef01 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 社交用户 Response DTO + * + * @author 芋道源码 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SocialUserRespDTO { + + /** + * 社交用户的 openid + */ + private String openid; + /** + * 社交用户的昵称 + */ + private String nickname; + /** + * 社交用户的头像 + */ + private String avatar; + + /** + * 关联的用户编号 + */ + private Long userId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserUnbindReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserUnbindReqDTO.java new file mode 100644 index 0000000..7e559d8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserUnbindReqDTO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.NoArgsConstructor; + +/** + * 社交绑定 Request DTO,使用 code 授权码 + * + * @author 芋道源码 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SocialUserUnbindReqDTO { + + /** + * 用户编号 + */ + @NotNull(message = "用户编号不能为空") + private Long userId; + /** + * 用户类型 + */ + @InEnum(UserTypeEnum.class) + @NotNull(message = "用户类型不能为空") + private Integer userType; + + /** + * 社交平台的类型 + */ + @InEnum(SocialTypeEnum.class) + @NotNull(message = "社交平台的类型不能为空") + private Integer socialType; + + /** + * 社交平台的 openid + */ + @NotEmpty(message = "社交平台的 openid 不能为空") + private String openid; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxJsapiSignatureRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxJsapiSignatureRespDTO.java new file mode 100644 index 0000000..7d332e9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxJsapiSignatureRespDTO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import lombok.Data; + +/** + * 微信公众号 JSAPI 签名 Response DTO + * + * @author 芋道源码 + */ +@Data +public class SocialWxJsapiSignatureRespDTO { + + /** + * 微信公众号的 appId + */ + private String appId; + /** + * 匿名串 + */ + private String nonceStr; + /** + * 时间戳 + */ + private Long timestamp; + /** + * URL + */ + private String url; + /** + * 签名 + */ + private String signature; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxPhoneNumberInfoRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxPhoneNumberInfoRespDTO.java new file mode 100644 index 0000000..9d404b3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxPhoneNumberInfoRespDTO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import lombok.Data; + +/** + * 微信小程序的手机信息 Response DTO + * + * @author 芋道源码 + */ +@Data +public class SocialWxPhoneNumberInfoRespDTO { + + /** + * 用户绑定的手机号(国外手机号会有区号) + */ + private String phoneNumber; + + /** + * 没有区号的手机号 + */ + private String purePhoneNumber; + /** + * 区号 + */ + private String countryCode; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxQrcodeReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxQrcodeReqDTO.java new file mode 100644 index 0000000..6f4b96f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxQrcodeReqDTO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +/** + * 获取小程序码 Request DTO + * + * @author HUIHUI + * @see 获取不限制的小程序码 + */ +@Data +public class SocialWxQrcodeReqDTO { + + /** + * 页面路径不能携带参数(参数请放在scene字段里) + */ + public static final String SCENE = ""; + /** + * 二维码宽度 + */ + public static final Integer WIDTH = 430; + /** + * 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 + */ + public static final Boolean AUTO_COLOR = true; + /** + * 检查 page 是否存在 + */ + public static final Boolean CHECK_PATH = true; + /** + * 是否需要透明底色 + * + * hyaline 为 true 时,生成透明底色的小程序码 + */ + public static final Boolean HYALINE = true; + + /** + * 场景 + */ + @NotEmpty(message = "场景不能为空") + private String scene; + /** + * 页面路径 + */ + @NotEmpty(message = "页面路径不能为空") + private String path; + /** + * 二维码宽度 + */ + private Integer width; + + /** + * 是否需要透明底色 + */ + private Boolean autoColor; + /** + * 是否检查 page 是否存在 + */ + private Boolean checkPath; + /** + * 是否需要透明底色 + */ + private Boolean hyaline; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java new file mode 100644 index 0000000..d2bf62c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 小程序订单上传购物详情 + * + * @see 上传购物详情 + * @author 芋道源码 + */ +@Data +public class SocialWxaOrderNotifyConfirmReceiveReqDTO { + + /** + * 原支付交易对应的微信订单号 + */ + @NotEmpty(message = "原支付交易对应的微信订单号不能为空") + private String transactionId; + + /** + * 快递签收时间 + */ + @NotNull(message = "快递签收时间不能为空") + private LocalDateTime receivedTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java new file mode 100644 index 0000000..555da62 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * 小程序订单上传购物详情 + * + * @see 上传购物详情 + * @author 芋道源码 + */ +@Data +public class SocialWxaOrderUploadShippingInfoReqDTO { + + /** + * 物流模式 - 实体物流配送采用快递公司进行实体物流配送形式 + */ + public static final Integer LOGISTICS_TYPE_EXPRESS = 1; + /** + * 物流模式 - 虚拟商品,虚拟商品,例如话费充值,点卡等,无实体配送形式 + */ + public static final Integer LOGISTICS_TYPE_VIRTUAL = 3; + /** + * 物流模式 - 用户自提 + */ + public static final Integer LOGISTICS_TYPE_PICK_UP = 4; + + /** + * 支付者,支付者信息(openid) + */ + @NotEmpty(message = "支付者,支付者信息(openid)不能为空") + private String openid; + + /** + * 原支付交易对应的微信订单号 + */ + @NotEmpty(message = "原支付交易对应的微信订单号不能为空") + private String transactionId; + + /** + * 物流模式 + */ + @NotNull(message = "物流模式不能为空") + private Integer logisticsType; + /** + * 物流发货单号 + */ + private String logisticsNo; + /** + * 物流公司编号 + * + * @see 物流查询插件简介 + */ + private String expressCompany; + /** + * 商品信息 + */ + @NotEmpty(message = "商品信息不能为空") + private String itemDesc; + /** + * 收件人手机号 + */ + @NotEmpty(message = "收件人手机号") + private String receiverContact; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeMessageSendReqDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeMessageSendReqDTO.java new file mode 100644 index 0000000..2677a34 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeMessageSendReqDTO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; + +/** + * 微信小程序订阅消息发送 Request DTO + * + * @author HUIHUI + */ +@Data +public class SocialWxaSubscribeMessageSendReqDTO { + + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 编号 + * 关联 AdminUserDO 的 id 编号 + */ + @NotNull(message = "用户编号不能为空") + private Long userId; + /** + * 用户类型 + * + * 关联 {@link UserTypeEnum} + */ + @NotNull(message = "用户类型不能为空") + private Integer userType; + + /** + * 消息模版标题 + */ + @NotEmpty(message = "消息模版标题不能为空") + private String templateTitle; + + /** + * 点击模板卡片后的跳转页面,仅限本小程序内的页面 + * + * 支持带参数,(示例 index?foo=bar )。该字段不填则模板无跳转。 + */ + private String page; + + /** + * 模板内容的参数 + */ + private Map messages; + + public SocialWxaSubscribeMessageSendReqDTO addMessage(String key, String value) { + if (messages == null) { + messages = new HashMap<>(); + } + messages.put(key, value); + return this; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeTemplateRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeTemplateRespDTO.java new file mode 100644 index 0000000..ae14cc5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeTemplateRespDTO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.system.api.social.dto; + +import lombok.Data; + + +/** + * 小程序订阅消息模版 Response DTO + * + * @author HUIHUI + */ +@Data +public class SocialWxaSubscribeTemplateRespDTO { + + /** + * 模版编号 + */ + private String id; + + /** + * 模版标题 + */ + private String title; + + /** + * 模版内容 + */ + private String content; + + /** + * 模板内容示例 + */ + private String example; + + /** + * 模版类型 + * + * 2:为一次性订阅 + * 3:为长期订阅 + */ + private Integer type; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/tenant/TenantApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/tenant/TenantApiImpl.java new file mode 100644 index 0000000..73367c0 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/tenant/TenantApiImpl.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.system.api.tenant; + +import cn.iocoder.yudao.framework.common.biz.system.tenant.TenantCommonApi; +import cn.iocoder.yudao.module.system.service.tenant.TenantService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + * 多租户的 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class TenantApiImpl implements TenantCommonApi { + + @Resource + private TenantService tenantService; + + @Override + public List getTenantIdList() { + return tenantService.getTenantIdList(); + } + + @Override + public void validateTenant(Long id) { + tenantService.validTenant(id); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java new file mode 100644 index 0000000..507fb4b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.system.api.user; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Admin 用户 API 接口 + * + * @author 芋道源码 + */ +public interface AdminUserApi { + + /** + * 通过用户 ID 查询用户 + * + * @param id 用户ID + * @return 用户对象信息 + */ + AdminUserRespDTO getUser(Long id); + + /** + * 通过用户 ID 查询用户下属 + * + * @param id 用户编号 + * @return 用户下属用户列表 + */ + List getUserListBySubordinate(Long id); + + /** + * 通过用户 ID 查询用户们 + * + * @param ids 用户 ID 们 + * @return 用户对象信息 + */ + List getUserList(Collection ids); + + /** + * 获得指定部门的用户数组 + * + * @param deptIds 部门数组 + * @return 用户数组 + */ + List getUserListByDeptIds(Collection deptIds); + + /** + * 获得指定岗位的用户数组 + * + * @param postIds 岗位数组 + * @return 用户数组 + */ + List getUserListByPostIds(Collection postIds); + + /** + * 获得用户 Map + * + * @param ids 用户编号数组 + * @return 用户 Map + */ + default Map getUserMap(Collection ids) { + List users = getUserList(ids); + return CollectionUtils.convertMap(users, AdminUserRespDTO::getId); + } + + /** + * 校验用户是否有效。如下情况,视为无效: + * 1. 用户编号不存在 + * 2. 用户被禁用 + * + * @param id 用户编号 + */ + default void validateUser(Long id) { + validateUserList(Collections.singleton(id)); + } + + /** + * 校验用户们是否有效。如下情况,视为无效: + * 1. 用户编号不存在 + * 2. 用户被禁用 + * + * @param ids 用户编号数组 + */ + void validateUserList(Collection ids); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java new file mode 100644 index 0000000..4f7b4e4 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.system.api.user; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils; +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * Admin 用户 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class AdminUserApiImpl implements AdminUserApi { + + @Resource + private AdminUserService userService; + @Resource + private DeptService deptService; + + @Override + public AdminUserRespDTO getUser(Long id) { + AdminUserDO user = userService.getUser(id); + return BeanUtils.toBean(user, AdminUserRespDTO.class); + } + + @Override + public List getUserListBySubordinate(Long id) { + // 1.1 获取用户负责的部门 + List depts = deptService.getDeptListByLeaderUserId(id); + if (CollUtil.isEmpty(depts)) { + return Collections.emptyList(); + } + // 1.2 获取所有子部门 + Set deptIds = convertSet(depts, DeptDO::getId); + List childDeptList = deptService.getChildDeptList(deptIds); + if (CollUtil.isNotEmpty(childDeptList)) { + deptIds.addAll(convertSet(childDeptList, DeptDO::getId)); + } + + // 2. 获取部门对应的用户信息 + List users = userService.getUserListByDeptIds(deptIds); + users.removeIf(item -> ObjUtil.equal(item.getId(), id)); // 排除自己 + return BeanUtils.toBean(users, AdminUserRespDTO.class); + } + + @Override + public List getUserList(Collection ids) { + return DataPermissionUtils.executeIgnore(() -> { // 禁用数据权限。原因是,一般基于指定 id 的 API 查询,都是数据拼接为主 + List users = userService.getUserList(ids); + return BeanUtils.toBean(users, AdminUserRespDTO.class); + }); + } + + @Override + public List getUserListByDeptIds(Collection deptIds) { + List users = userService.getUserListByDeptIds(deptIds); + return BeanUtils.toBean(users, AdminUserRespDTO.class); + } + + @Override + public List getUserListByPostIds(Collection postIds) { + List users = userService.getUserListByPostIds(postIds); + return BeanUtils.toBean(users, AdminUserRespDTO.class); + } + + @Override + public void validateUserList(Collection ids) { + userService.validateUserList(ids); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java new file mode 100644 index 0000000..d86f3e5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.system.api.user.dto; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import lombok.Data; + +import java.util.Set; + +/** + * Admin 用户 Response DTO + * + * @author 芋道源码 + */ +@Data +public class AdminUserRespDTO { + + /** + * 用户ID + */ + private Long id; + /** + * 用户昵称 + */ + private String nickname; + /** + * 帐号状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + /** + * 部门ID + */ + private Long deptId; + /** + * 岗位编号数组 + */ + private Set postIds; + /** + * 手机号码 + */ + private String mobile; + /** + * 用户头像 + */ + private String avatar; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http new file mode 100644 index 0000000..f42dfcd --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http @@ -0,0 +1,33 @@ +### 请求 /login 接口 => 成功 +POST {{baseUrl}}/system/auth/login +Content-Type: application/json +tenant-id: {{adminTenantId}} +tag: Yunai.local + +{ + "username": "admin", + "password": "admin123", + "uuid": "3acd87a09a4f48fb9118333780e94883", + "code": "1024" +} + +### 请求 /login 接口 => 成功(无验证码) +POST {{baseUrl}}/system/auth/login +Content-Type: application/json +tenant-id: {{adminTenantId}} + +{ + "username": "admin", + "password": "admin123" +} + +### 请求 /get-permission-info 接口 => 成功 +GET {{baseUrl}}/system/auth/get-permission-info +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +### 请求 /list-menus 接口 => 成功 +GET {{baseUrl}}/system/list-menus +Authorization: Bearer {{token}} +#Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java new file mode 100644 index 0000000..d926947 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -0,0 +1,172 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.security.config.SecurityProperties; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; +import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; +import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; +import cn.iocoder.yudao.module.system.service.permission.MenuService; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import cn.iocoder.yudao.module.system.service.permission.RoleService; +import cn.iocoder.yudao.module.system.service.social.SocialClientService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +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.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 认证") +@RestController +@RequestMapping("/system/auth") +@Validated +@Slf4j +public class AuthController { + + @Resource + private AdminAuthService authService; + @Resource + private AdminUserService userService; + @Resource + private RoleService roleService; + @Resource + private MenuService menuService; + @Resource + private PermissionService permissionService; + @Resource + private SocialClientService socialClientService; + + @Resource + private SecurityProperties securityProperties; + + @PostMapping("/login") + @PermitAll + @Operation(summary = "使用账号密码登录") + public CommonResult login(@RequestBody @Valid AuthLoginReqVO reqVO) { + return success(authService.login(reqVO)); + } + + @PostMapping("/logout") + @PermitAll + @Operation(summary = "登出系统") + public CommonResult logout(HttpServletRequest request) { + String token = SecurityFrameworkUtils.obtainAuthorization(request, + securityProperties.getTokenHeader(), securityProperties.getTokenParameter()); + if (StrUtil.isNotBlank(token)) { + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); + } + return success(true); + } + + @PostMapping("/refresh-token") + @PermitAll + @Operation(summary = "刷新令牌") + @Parameter(name = "refreshToken", description = "刷新令牌", required = true) + public CommonResult refreshToken(@RequestParam("refreshToken") String refreshToken) { + return success(authService.refreshToken(refreshToken)); + } + + @GetMapping("/get-permission-info") + @Operation(summary = "获取登录用户的权限信息") + public CommonResult getPermissionInfo() { + // 1.1 获得用户信息 + AdminUserDO user = userService.getUser(getLoginUserId()); + if (user == null) { + return success(null); + } + + // 1.2 获得角色列表 + Set roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId()); + if (CollUtil.isEmpty(roleIds)) { + return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList())); + } + List roles = roleService.getRoleList(roleIds); + roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色 + + // 1.3 获得菜单列表 + Set menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId)); + List menuList = menuService.getMenuList(menuIds); + menuList = menuService.filterDisableMenus(menuList); + + // 2. 拼接结果返回 + return success(AuthConvert.INSTANCE.convert(user, roles, menuList)); + } + + @PostMapping("/register") + @PermitAll + @Operation(summary = "注册用户") + public CommonResult register(@RequestBody @Valid AuthRegisterReqVO registerReqVO) { + return success(authService.register(registerReqVO)); + } + + // ========== 短信登录相关 ========== + + @PostMapping("/sms-login") + @PermitAll + @Operation(summary = "使用短信验证码登录") + public CommonResult smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) { + return success(authService.smsLogin(reqVO)); + } + + @PostMapping("/send-sms-code") + @PermitAll + @Operation(summary = "发送手机验证码") + public CommonResult sendLoginSmsCode(@RequestBody @Valid AuthSmsSendReqVO reqVO) { + authService.sendSmsCode(reqVO); + return success(true); + } + + @PostMapping("/reset-password") + @PermitAll + @Operation(summary = "重置密码") + public CommonResult resetPassword(@RequestBody @Valid AuthResetPasswordReqVO reqVO) { + authService.resetPassword(reqVO); + return success(true); + } + + // ========== 社交登录相关 ========== + + @GetMapping("/social-auth-redirect") + @PermitAll + @Operation(summary = "社交授权的跳转") + @Parameters({ + @Parameter(name = "type", description = "社交类型", required = true), + @Parameter(name = "redirectUri", description = "回调路径") + }) + public CommonResult socialLogin(@RequestParam("type") Integer type, + @RequestParam("redirectUri") String redirectUri) { + return success(socialClientService.getAuthorizeUrl( + type, UserTypeEnum.ADMIN.getValue(), redirectUri)); + } + + @PostMapping("/social-login") + @PermitAll + @Operation(summary = "社交快捷登录,使用 code 授权码", description = "适合未登录的用户,但是社交账号已绑定用户") + public CommonResult socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) { + return success(authService.socialLogin(reqVO)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java new file mode 100644 index 0000000..a29660e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.Pattern; + +@Schema(description = "管理后台 - 账号密码登录 Request VO,如果登录并绑定社交用户,需要传递 social 开头的参数") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthLoginReqVO extends CaptchaVerificationReqVO { + + @Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma") + @NotEmpty(message = "登录账号不能为空") + @Length(min = 4, max = 16, message = "账号长度为 4-16 位") + @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") + private String username; + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao") + @NotEmpty(message = "密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + + // ========== 绑定社交登录时,需要传递如下参数 ========== + + @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(SocialTypeEnum.class) + private Integer socialType; + + @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String socialCode; + + @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + private String socialState; + + @AssertTrue(message = "授权码不能为空") + public boolean isSocialCodeValid() { + return socialType == null || StrUtil.isNotEmpty(socialCode); + } + + @AssertTrue(message = "授权 state 不能为空") + public boolean isSocialState() { + return socialType == null || StrUtil.isNotEmpty(socialState); + } + +} \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginRespVO.java new file mode 100644 index 0000000..1720d07 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginRespVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 登录 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthLoginRespVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long userId; + + @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "happy") + private String accessToken; + + @Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice") + private String refreshToken; + + @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime expiresTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthMenuRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthMenuRespVO.java new file mode 100644 index 0000000..d310bde --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthMenuRespVO.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Schema(description = "管理后台 - 登录用户的菜单信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthMenuRespVO { + + @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private Long id; + + @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long parentId; + + @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") + private String path; + + @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") + private String component; + + @Schema(description = "组件名", example = "SystemUser") + private String componentName; + + @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") + private String icon; + + @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + private Boolean visible; + + @Schema(description = "是否缓存", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + private Boolean keepAlive; + + @Schema(description = "是否总是显示", example = "false") + private Boolean alwaysShow; + + /** + * 子路由 + */ + private List children; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java new file mode 100644 index 0000000..a86ceda --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Set; + +@Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthPermissionInfoRespVO { + + @Schema(description = "用户信息", requiredMode = Schema.RequiredMode.REQUIRED) + private UserVO user; + + @Schema(description = "角色标识数组", requiredMode = Schema.RequiredMode.REQUIRED) + private Set roles; + + @Schema(description = "操作权限数组", requiredMode = Schema.RequiredMode.REQUIRED) + private Set permissions; + + @Schema(description = "菜单树", requiredMode = Schema.RequiredMode.REQUIRED) + private List menus; + + @Schema(description = "用户信息 VO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class UserVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String nickname; + + @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.jpg") + private String avatar; + + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private Long deptId; + + @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + private String username; + + @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") + private String email; + + } + + @Schema(description = "管理后台 - 登录用户的菜单信息 Response VO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class MenuVO { + + @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private Long id; + + @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long parentId; + + @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") + private String path; + + @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") + private String component; + + @Schema(description = "组件名", example = "SystemUser") + private String componentName; + + @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") + private String icon; + + @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + private Boolean visible; + + @Schema(description = "是否缓存", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + private Boolean keepAlive; + + @Schema(description = "是否总是显示", example = "false") + private Boolean alwaysShow; + + /** + * 子路由 + */ + private List children; + + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java new file mode 100644 index 0000000..bcfc3ef --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.*; + +@Schema(description = "管理后台 - Register Request VO") +@Data +public class AuthRegisterReqVO extends CaptchaVerificationReqVO { + + @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @NotBlank(message = "用户账号不能为空") + @Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成") + @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符") + private String username; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @NotBlank(message = "用户昵称不能为空") + @Size(max = 30, message = "用户昵称长度不能超过 30 个字符") + private String nickname; + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @NotEmpty(message = "密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; +} \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java new file mode 100644 index 0000000..16337cc --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +@Schema(description = "管理后台 - 短信重置账号密码 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthResetPasswordReqVO { + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234") + @NotEmpty(message = "密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13312341234") + @NotEmpty(message = "手机号不能为空") + @Mobile + private String mobile; + + @Schema(description = "手机短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @NotEmpty(message = "手机手机短信验证码不能为空") + private String code; +} \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsLoginReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsLoginReqVO.java new file mode 100644 index 0000000..e5730d2 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsLoginReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotEmpty; + +@Schema(description = "管理后台 - 短信验证码的登录 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthSmsLoginReqVO { + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma") + @NotEmpty(message = "手机号不能为空") + @Mobile + private String mobile; + + @Schema(description = "短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "验证码不能为空") + private String code; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java new file mode 100644 index 0000000..4efd98f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 发送手机验证码 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthSmsSendReqVO extends CaptchaVerificationReqVO { + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma") + @NotEmpty(message = "手机号不能为空") + @Mobile + private String mobile; + + @Schema(description = "短信场景", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "发送场景不能为空") + @InEnum(SmsSceneEnum.class) + private Integer scene; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialLoginReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialLoginReqVO.java new file mode 100644 index 0000000..da22470 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialLoginReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 社交绑定登录 Request VO,使用 code 授权码 + 账号密码") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AuthSocialLoginReqVO { + + @Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(SocialTypeEnum.class) + @NotNull(message = "社交平台的类型不能为空") + private Integer type; + + @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "授权码不能为空") + private String code; + + @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + @NotEmpty(message = "state 不能为空") + private String state; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java new file mode 100644 index 0000000..b080cb5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.system.controller.admin.auth.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 验证码 Request VO") +@Data +public class CaptchaVerificationReqVO { + + // ========== 图片验证码相关 ========== + @Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED, + example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==") + @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class) + private String captchaVerification; + + /** + * 开启验证码的 Group + */ + public interface CodeEnableGroup { + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java new file mode 100644 index 0000000..f4e5541 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.system.controller.admin.captcha; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import com.anji.captcha.model.common.ResponseModel; +import com.anji.captcha.model.vo.CaptchaVO; +import com.anji.captcha.service.CaptchaService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.servlet.http.HttpServletRequest; +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; + +@Tag(name = "管理后台 - 验证码") +@RestController("adminCaptchaController") +@RequestMapping("/system/captcha") +public class CaptchaController { + + @Resource + private CaptchaService captchaService; + + @PostMapping({"/get"}) + @Operation(summary = "获得验证码") + @PermitAll + @TenantIgnore + public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) { + assert request.getRemoteHost() != null; + data.setBrowserInfo(getRemoteId(request)); + return captchaService.get(data); + } + + @PostMapping("/check") + @Operation(summary = "校验验证码") + @PermitAll + @TenantIgnore + public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) { + data.setBrowserInfo(getRemoteId(request)); + return captchaService.check(data); + } + + public static String getRemoteId(HttpServletRequest request) { + String ip = ServletUtils.getClientIP(request); + String ua = request.getHeader("user-agent"); + if (StrUtil.isNotBlank(ip)) { + return ip + ua; + } + return request.getRemoteAddr() + ua; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java new file mode 100644 index 0000000..7873d00 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptRespVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSimpleRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 部门") +@RestController +@RequestMapping("/system/dept") +@Validated +public class DeptController { + + @Resource + private DeptService deptService; + + @PostMapping("create") + @Operation(summary = "创建部门") + @PreAuthorize("@ss.hasPermission('system:dept:create')") + public CommonResult createDept(@Valid @RequestBody DeptSaveReqVO createReqVO) { + Long deptId = deptService.createDept(createReqVO); + return success(deptId); + } + + @PutMapping("update") + @Operation(summary = "更新部门") + @PreAuthorize("@ss.hasPermission('system:dept:update')") + public CommonResult updateDept(@Valid @RequestBody DeptSaveReqVO updateReqVO) { + deptService.updateDept(updateReqVO); + return success(true); + } + + @DeleteMapping("delete") + @Operation(summary = "删除部门") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:dept:delete')") + public CommonResult deleteDept(@RequestParam("id") Long id) { + deptService.deleteDept(id); + return success(true); + } + + @GetMapping("/list") + @Operation(summary = "获取部门列表") + @PreAuthorize("@ss.hasPermission('system:dept:query')") + public CommonResult> getDeptList(DeptListReqVO reqVO) { + List list = deptService.getDeptList(reqVO); + return success(BeanUtils.toBean(list, DeptRespVO.class)); + } + + @GetMapping(value = {"/list-all-simple", "/simple-list"}) + @Operation(summary = "获取部门精简信息列表", description = "只包含被开启的部门,主要用于前端的下拉选项") + public CommonResult> getSimpleDeptList() { + List list = deptService.getDeptList( + new DeptListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus())); + return success(BeanUtils.toBean(list, DeptSimpleRespVO.class)); + } + + @GetMapping("/get") + @Operation(summary = "获得部门信息") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:dept:query')") + public CommonResult getDept(@RequestParam("id") Long id) { + DeptDO dept = deptService.getDept(id); + return success(BeanUtils.toBean(dept, DeptRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java new file mode 100644 index 0000000..897cea2 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java @@ -0,0 +1,106 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostRespVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSimpleRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.service.dept.PostService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 岗位") +@RestController +@RequestMapping("/system/post") +@Validated +public class PostController { + + @Resource + private PostService postService; + + @PostMapping("/create") + @Operation(summary = "创建岗位") + @PreAuthorize("@ss.hasPermission('system:post:create')") + public CommonResult createPost(@Valid @RequestBody PostSaveReqVO createReqVO) { + Long postId = postService.createPost(createReqVO); + return success(postId); + } + + @PutMapping("/update") + @Operation(summary = "修改岗位") + @PreAuthorize("@ss.hasPermission('system:post:update')") + public CommonResult updatePost(@Valid @RequestBody PostSaveReqVO updateReqVO) { + postService.updatePost(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除岗位") + @PreAuthorize("@ss.hasPermission('system:post:delete')") + public CommonResult deletePost(@RequestParam("id") Long id) { + postService.deletePost(id); + return success(true); + } + + @GetMapping(value = "/get") + @Operation(summary = "获得岗位信息") + @Parameter(name = "id", description = "岗位编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:post:query')") + public CommonResult getPost(@RequestParam("id") Long id) { + PostDO post = postService.getPost(id); + return success(BeanUtils.toBean(post, PostRespVO.class)); + } + + @GetMapping(value = {"/list-all-simple", "simple-list"}) + @Operation(summary = "获取岗位全列表", description = "只包含被开启的岗位,主要用于前端的下拉选项") + public CommonResult> getSimplePostList() { + // 获得岗位列表,只要开启状态的 + List list = postService.getPostList(null, Collections.singleton(CommonStatusEnum.ENABLE.getStatus())); + // 排序后,返回给前端 + list.sort(Comparator.comparing(PostDO::getSort)); + return success(BeanUtils.toBean(list, PostSimpleRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得岗位分页列表") + @PreAuthorize("@ss.hasPermission('system:post:query')") + public CommonResult> getPostPage(@Validated PostPageReqVO pageReqVO) { + PageResult pageResult = postService.getPostPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, PostRespVO.class)); + } + + @GetMapping("/export") + @Operation(summary = "岗位管理") + @PreAuthorize("@ss.hasPermission('system:post:export')") + @ApiAccessLog(operateType = EXPORT) + public void export(HttpServletResponse response, @Validated PostPageReqVO reqVO) throws IOException { + reqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = postService.getPostPage(reqVO).getList(); + // 输出 + ExcelUtils.write(response, "岗位数据.xls", "岗位列表", PostRespVO.class, + BeanUtils.toBean(list, PostRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptListReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptListReqVO.java new file mode 100644 index 0000000..131686f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptListReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 部门列表 Request VO") +@Data +public class DeptListReqVO { + + @Schema(description = "部门名称,模糊匹配", example = "芋道") + private String name; + + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java new file mode 100644 index 0000000..83e6ed0 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 部门信息 Response VO") +@Data +public class DeptRespVO { + + @Schema(description = "部门编号", example = "1024") + private Long id; + + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "父部门 ID", example = "1024") + private Long parentId; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Integer sort; + + @Schema(description = "负责人的用户编号", example = "2048") + private Long leaderUserId; + + @Schema(description = "联系电话", example = "15601691000") + private String phone; + + @Schema(description = "邮箱", example = "yudao@iocoder.cn") + private String email; + + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java new file mode 100644 index 0000000..4d25b7c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +@Schema(description = "管理后台 - 部门创建/修改 Request VO") +@Data +public class DeptSaveReqVO { + + @Schema(description = "部门编号", example = "1024") + private Long id; + + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @NotBlank(message = "部门名称不能为空") + @Size(max = 30, message = "部门名称长度不能超过 30 个字符") + private String name; + + @Schema(description = "父部门 ID", example = "1024") + private Long parentId; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "显示顺序不能为空") + private Integer sort; + + @Schema(description = "负责人的用户编号", example = "2048") + private Long leaderUserId; + + @Schema(description = "联系电话", example = "15601691000") + @Size(max = 11, message = "联系电话长度不能超过11个字符") + private String phone; + + @Schema(description = "邮箱", example = "yudao@iocoder.cn") + @Email(message = "邮箱格式不正确") + @Size(max = 50, message = "邮箱长度不能超过 50 个字符") + private String email; + + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java new file mode 100644 index 0000000..31c15d5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - 部门精简信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DeptSimpleRespVO { + + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "父部门 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long parentId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostPageReqVO.java new file mode 100644 index 0000000..78f306e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostPageReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - 岗位分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class PostPageReqVO extends PageParam { + + @Schema(description = "岗位编码,模糊匹配", example = "yudao") + private String code; + + @Schema(description = "岗位名称,模糊匹配", example = "芋道") + private String name; + + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java new file mode 100644 index 0000000..dde6f95 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 岗位信息 Response VO") +@Data +@ExcelIgnoreUnannotated +public class PostRespVO { + + @Schema(description = "岗位序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("岗位序号") + private Long id; + + @Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆") + @ExcelProperty("岗位名称") + private String name; + + @Schema(description = "岗位编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @ExcelProperty("岗位编码") + private String code; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("岗位排序") + private Integer sort; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "备注", example = "快乐的备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java new file mode 100644 index 0000000..5ac2be3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +@Schema(description = "管理后台 - 岗位创建/修改 Request VO") +@Data +public class PostSaveReqVO { + + @Schema(description = "岗位编号", example = "1024") + private Long id; + + @Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆") + @NotBlank(message = "岗位名称不能为空") + @Size(max = 50, message = "岗位名称长度不能超过 50 个字符") + private String name; + + @Schema(description = "岗位编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @NotBlank(message = "岗位编码不能为空") + @Size(max = 64, message = "岗位编码长度不能超过64个字符") + private String code; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "显示顺序不能为空") + private Integer sort; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "备注", example = "快乐的备注") + private String remark; + +} \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java new file mode 100644 index 0000000..72852a1 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 岗位信息的精简 Response VO") +@Data +public class PostSimpleRespVO { + + @Schema(description = "岗位序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("岗位序号") + private Long id; + + @Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆") + @ExcelProperty("岗位名称") + private String name; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.http new file mode 100644 index 0000000..5a7ce8e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.http @@ -0,0 +1,4 @@ +### 请求 /menu/list 接口 => 成功 +GET {{baseUrl}}/system/dict-data/list-all-simple +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java new file mode 100644 index 0000000..c05de99 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java @@ -0,0 +1,104 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataRespVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; +import cn.iocoder.yudao.module.system.service.dict.DictDataService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 字典数据") +@RestController +@RequestMapping("/system/dict-data") +@Validated +public class DictDataController { + + @Resource + private DictDataService dictDataService; + + @PostMapping("/create") + @Operation(summary = "新增字典数据") + @PreAuthorize("@ss.hasPermission('system:dict:create')") + public CommonResult createDictData(@Valid @RequestBody DictDataSaveReqVO createReqVO) { + Long dictDataId = dictDataService.createDictData(createReqVO); + return success(dictDataId); + } + + @PutMapping("/update") + @Operation(summary = "修改字典数据") + @PreAuthorize("@ss.hasPermission('system:dict:update')") + public CommonResult updateDictData(@Valid @RequestBody DictDataSaveReqVO updateReqVO) { + dictDataService.updateDictData(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除字典数据") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:dict:delete')") + public CommonResult deleteDictData(Long id) { + dictDataService.deleteDictData(id); + return success(true); + } + + @GetMapping(value = {"/list-all-simple", "simple-list"}) + @Operation(summary = "获得全部字典数据列表", description = "一般用于管理后台缓存字典数据在本地") + // 无需添加权限认证,因为前端全局都需要 + public CommonResult> getSimpleDictDataList() { + List list = dictDataService.getDictDataList( + CommonStatusEnum.ENABLE.getStatus(), null); + return success(BeanUtils.toBean(list, DictDataSimpleRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "/获得字典类型的分页列表") + @PreAuthorize("@ss.hasPermission('system:dict:query')") + public CommonResult> getDictTypePage(@Valid DictDataPageReqVO pageReqVO) { + PageResult pageResult = dictDataService.getDictDataPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, DictDataRespVO.class)); + } + + @GetMapping(value = "/get") + @Operation(summary = "/查询字典数据详细") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:dict:query')") + public CommonResult getDictData(@RequestParam("id") Long id) { + DictDataDO dictData = dictDataService.getDictData(id); + return success(BeanUtils.toBean(dictData, DictDataRespVO.class)); + } + + @GetMapping("/export") + @Operation(summary = "导出字典数据") + @PreAuthorize("@ss.hasPermission('system:dict:export')") + @ApiAccessLog(operateType = EXPORT) + public void export(HttpServletResponse response, @Valid DictDataPageReqVO exportReqVO) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = dictDataService.getDictDataPage(exportReqVO).getList(); + // 输出 + ExcelUtils.write(response, "字典数据.xls", "数据", DictDataRespVO.class, + BeanUtils.toBean(list, DictDataRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java new file mode 100644 index 0000000..c40980c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java @@ -0,0 +1,102 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeRespVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSimpleRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; +import cn.iocoder.yudao.module.system.service.dict.DictTypeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 字典类型") +@RestController +@RequestMapping("/system/dict-type") +@Validated +public class DictTypeController { + + @Resource + private DictTypeService dictTypeService; + + @PostMapping("/create") + @Operation(summary = "创建字典类型") + @PreAuthorize("@ss.hasPermission('system:dict:create')") + public CommonResult createDictType(@Valid @RequestBody DictTypeSaveReqVO createReqVO) { + Long dictTypeId = dictTypeService.createDictType(createReqVO); + return success(dictTypeId); + } + + @PutMapping("/update") + @Operation(summary = "修改字典类型") + @PreAuthorize("@ss.hasPermission('system:dict:update')") + public CommonResult updateDictType(@Valid @RequestBody DictTypeSaveReqVO updateReqVO) { + dictTypeService.updateDictType(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除字典类型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:dict:delete')") + public CommonResult deleteDictType(Long id) { + dictTypeService.deleteDictType(id); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得字典类型的分页列表") + @PreAuthorize("@ss.hasPermission('system:dict:query')") + public CommonResult> pageDictTypes(@Valid DictTypePageReqVO pageReqVO) { + PageResult pageResult = dictTypeService.getDictTypePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, DictTypeRespVO.class)); + } + + @Operation(summary = "/查询字典类型详细") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @GetMapping(value = "/get") + @PreAuthorize("@ss.hasPermission('system:dict:query')") + public CommonResult getDictType(@RequestParam("id") Long id) { + DictTypeDO dictType = dictTypeService.getDictType(id); + return success(BeanUtils.toBean(dictType, DictTypeRespVO.class)); + } + + @GetMapping(value = {"/list-all-simple", "simple-list"}) + @Operation(summary = "获得全部字典类型列表", description = "包括开启 + 禁用的字典类型,主要用于前端的下拉选项") + // 无需添加权限认证,因为前端全局都需要 + public CommonResult> getSimpleDictTypeList() { + List list = dictTypeService.getDictTypeList(); + return success(BeanUtils.toBean(list, DictTypeSimpleRespVO.class)); + } + + @Operation(summary = "导出数据类型") + @GetMapping("/export") + @PreAuthorize("@ss.hasPermission('system:dict:query')") + @ApiAccessLog(operateType = EXPORT) + public void export(HttpServletResponse response, @Valid DictTypePageReqVO exportReqVO) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = dictTypeService.getDictTypePage(exportReqVO).getList(); + // 导出 + ExcelUtils.write(response, "字典类型.xls", "数据", DictTypeRespVO.class, + BeanUtils.toBean(list, DictTypeRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataPageReqVO.java new file mode 100644 index 0000000..90c7f39 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataPageReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import jakarta.validation.constraints.Size; + +@Schema(description = "管理后台 - 字典类型分页列表 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class DictDataPageReqVO extends PageParam { + + @Schema(description = "字典标签", example = "芋道") + @Size(max = 100, message = "字典标签长度不能超过100个字符") + private String label; + + @Schema(description = "字典类型,模糊匹配", example = "sys_common_sex") + @Size(max = 100, message = "字典类型类型长度不能超过100个字符") + private String dictType; + + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java new file mode 100644 index 0000000..8857a70 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 字典数据信息 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DictDataRespVO { + + @Schema(description = "字典数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("字典编码") + private Long id; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("字典排序") + private Integer sort; + + @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @ExcelProperty("字典标签") + private String label; + + @Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder") + @ExcelProperty("字典键值") + private String value; + + @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") + @ExcelProperty("字典类型") + private String dictType; + + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default") + private String colorType; + + @Schema(description = "css 样式", example = "btn-visible") + private String cssClass; + + @Schema(description = "备注", example = "我是一个角色") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java new file mode 100644 index 0000000..ca71572 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +@Schema(description = "管理后台 - 字典数据创建/修改 Request VO") +@Data +public class DictDataSaveReqVO { + + @Schema(description = "字典数据编号", example = "1024") + private Long id; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "显示顺序不能为空") + private Integer sort; + + @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @NotBlank(message = "字典标签不能为空") + @Size(max = 100, message = "字典标签长度不能超过100个字符") + private String label; + + @Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder") + @NotBlank(message = "字典键值不能为空") + @Size(max = 100, message = "字典键值长度不能超过100个字符") + private String value; + + @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") + @NotBlank(message = "字典类型不能为空") + @Size(max = 100, message = "字典类型长度不能超过100个字符") + private String dictType; + + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") + private Integer status; + + @Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default") + private String colorType; + + @Schema(description = "css 样式", example = "btn-visible") + private String cssClass; + + @Schema(description = "备注", example = "我是一个角色") + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java new file mode 100644 index 0000000..d1497c3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 数据字典精简 Response VO") +@Data +public class DictDataSimpleRespVO { + + @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "gender") + private String dictType; + + @Schema(description = "字典键值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String value; + + @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "男") + private String label; + + @Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default") + private String colorType; + + @Schema(description = "css 样式", example = "btn-visible") + private String cssClass; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypePageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypePageReqVO.java new file mode 100644 index 0000000..2650dd8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypePageReqVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict.vo.type; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.format.annotation.DateTimeFormat; + +import jakarta.validation.constraints.Size; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 字典类型分页列表 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class DictTypePageReqVO extends PageParam { + + @Schema(description = "字典类型名称,模糊匹配", example = "芋道") + private String name; + + @Schema(description = "字典类型,模糊匹配", example = "sys_common_sex") + @Size(max = 100, message = "字典类型类型长度不能超过100个字符") + private String type; + + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") + private Integer status; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java new file mode 100644 index 0000000..6ddd47b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict.vo.type; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 字典类型信息 Response VO") +@Data +@ExcelIgnoreUnannotated +public class DictTypeRespVO { + + @Schema(description = "字典类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("字典主键") + private Long id; + + @Schema(description = "字典名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "性别") + @ExcelProperty("字典名称") + private String name; + + @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") + @ExcelProperty("字典类型") + private String type; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "备注", example = "快乐的备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSaveReqVO.java new file mode 100644 index 0000000..48a1494 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSaveReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict.vo.type; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +@Schema(description = "管理后台 - 字典类型创建/修改 Request VO") +@Data +public class DictTypeSaveReqVO { + + @Schema(description = "字典类型编号", example = "1024") + private Long id; + + @Schema(description = "字典名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "性别") + @NotBlank(message = "字典名称不能为空") + @Size(max = 100, message = "字典类型名称长度不能超过100个字符") + private String name; + + @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") + @NotNull(message = "字典类型不能为空") + @Size(max = 100, message = "字典类型类型长度不能超过 100 个字符") + private String type; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + + @Schema(description = "备注", example = "快乐的备注") + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSimpleRespVO.java new file mode 100644 index 0000000..f3e717d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSimpleRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.system.controller.admin.dict.vo.type; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 字典类型精简信息 Response VO") +@Data +public class DictTypeSimpleRespVO { + + @Schema(description = "字典类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "字典类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") + private String type; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http new file mode 100644 index 0000000..1416561 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http @@ -0,0 +1,5 @@ +### 获得地区树 +GET {{baseUrl}}/system/area/tree +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java new file mode 100644 index 0000000..b2f95d6 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.system.controller.admin.ip; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.ip.core.Area; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.framework.ip.core.utils.IPUtils; +import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeRespVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 地区") +@RestController +@RequestMapping("/system/area") +@Validated +public class AreaController { + + @GetMapping("/tree") + @Operation(summary = "获得地区树") + public CommonResult> getAreaTree() { + Area area = AreaUtils.getArea(Area.ID_CHINA); + Assert.notNull(area, "获取不到中国"); + return success(BeanUtils.toBean(area.getChildren(), AreaNodeRespVO.class)); + } + + @GetMapping("/get-by-ip") + @Operation(summary = "获得 IP 对应的地区名") + @Parameter(name = "ip", description = "IP", required = true) + public CommonResult getAreaByIp(@RequestParam("ip") String ip) { + // 获得城市 + Area area = IPUtils.getArea(ip); + if (area == null) { + return success("未知"); + } + // 格式化返回 + return success(AreaUtils.format(area.getId())); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/vo/AreaNodeRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/vo/AreaNodeRespVO.java new file mode 100644 index 0000000..1360ec6 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/vo/AreaNodeRespVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.controller.admin.ip.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 地区节点 Response VO") +@Data +public class AreaNodeRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "110000") + private Integer id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "北京") + private String name; + + /** + * 子节点 + */ + private List children; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java new file mode 100644 index 0000000..f2ff3a4 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.system.controller.admin.logger; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; +import cn.iocoder.yudao.module.system.service.logger.LoginLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 登录日志") +@RestController +@RequestMapping("/system/login-log") +@Validated +public class LoginLogController { + + @Resource + private LoginLogService loginLogService; + + @GetMapping("/page") + @Operation(summary = "获得登录日志分页列表") + @PreAuthorize("@ss.hasPermission('system:login-log:query')") + public CommonResult> getLoginLogPage(@Valid LoginLogPageReqVO pageReqVO) { + PageResult pageResult = loginLogService.getLoginLogPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, LoginLogRespVO.class)); + } + + @GetMapping("/export") + @Operation(summary = "导出登录日志 Excel") + @PreAuthorize("@ss.hasPermission('system:login-log:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportLoginLog(HttpServletResponse response, @Valid LoginLogPageReqVO exportReqVO) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = loginLogService.getLoginLogPage(exportReqVO).getList(); + // 输出 + ExcelUtils.write(response, "登录日志.xls", "数据列表", LoginLogRespVO.class, + BeanUtils.toBean(list, LoginLogRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http new file mode 100644 index 0000000..be3102e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http @@ -0,0 +1,4 @@ +### 请求 /system/operate-log/page 接口 => 成功 +GET {{baseUrl}}/system/operate-log/page +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java new file mode 100644 index 0000000..57b9b61 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.system.controller.admin.logger; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.translate.core.TranslateUtils; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; +import cn.iocoder.yudao.module.system.service.logger.OperateLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 操作日志") +@RestController +@RequestMapping("/system/operate-log") +@Validated +public class OperateLogController { + + @Resource + private OperateLogService operateLogService; + + @GetMapping("/page") + @Operation(summary = "查看操作日志分页列表") + @PreAuthorize("@ss.hasPermission('system:operate-log:query')") + public CommonResult> pageOperateLog(@Valid OperateLogPageReqVO pageReqVO) { + PageResult pageResult = operateLogService.getOperateLogPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, OperateLogRespVO.class)); + } + + @Operation(summary = "导出操作日志") + @GetMapping("/export") + @PreAuthorize("@ss.hasPermission('system:operate-log:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportOperateLog(HttpServletResponse response, @Valid OperateLogPageReqVO exportReqVO) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = operateLogService.getOperateLogPage(exportReqVO).getList(); + ExcelUtils.write(response, "操作日志.xls", "数据列表", OperateLogRespVO.class, + TranslateUtils.translate(BeanUtils.toBean(list, OperateLogRespVO.class))); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogPageReqVO.java new file mode 100644 index 0000000..40e12a8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogPageReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 登录日志分页列表 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class LoginLogPageReqVO extends PageParam { + + @Schema(description = "用户 IP,模拟匹配", example = "127.0.0.1") + private String userIp; + + @Schema(description = "用户账号,模拟匹配", example = "芋道") + private String username; + + @Schema(description = "操作状态", example = "true") + private Boolean status; + + @Schema(description = "登录时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java new file mode 100644 index 0000000..7b9cd16 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java @@ -0,0 +1,57 @@ +package cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 登录日志 Response VO") +@Data +@ExcelIgnoreUnannotated +public class LoginLogRespVO { + + @Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("日志主键") + private Long id; + + @Schema(description = "日志类型,参见 LoginLogTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "日志类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.LOGIN_TYPE) + private Integer logType; + + @Schema(description = "用户编号", example = "666") + private Long userId; + + @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer userType; + + @Schema(description = "链路追踪编号", example = "89aca178-a370-411c-ae02-3f0d672be4ab") + private String traceId; + + @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @ExcelProperty("用户账号") + private String username; + + @Schema(description = "登录结果,参见 LoginResultEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "登录结果", converter = DictConvert.class) + @DictFormat(DictTypeConstants.LOGIN_RESULT) + private Integer result; + + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") + @ExcelProperty("登录 IP") + private String userIp; + + @Schema(description = "浏览器 UserAgent", example = "Mozilla/5.0") + @ExcelProperty("浏览器 UA") + private String userAgent; + + @Schema(description = "登录时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("登录时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java new file mode 100644 index 0000000..1061f6d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 操作日志分页列表 Request VO") +@Data +public class OperateLogPageReqVO extends PageParam { + + @Schema(description = "用户编号", example = "芋道") + private Long userId; + + @Schema(description = "操作模块业务编号", example = "1") + private Long bizId; + + @Schema(description = "操作模块,模拟匹配", example = "订单") + private String type; + + @Schema(description = "操作名,模拟匹配", example = "创建订单") + private String subType; + + @Schema(description = "操作明细,模拟匹配", example = "修改编号为 1 的用户信息") + private String action; + + @Schema(description = "开始时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java new file mode 100644 index 0000000..e017ba7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog; + +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.fhs.core.trans.anno.Trans; +import com.fhs.core.trans.constant.TransType; +import com.fhs.core.trans.vo.VO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 操作日志 Response VO") +@Data +@ExcelIgnoreUnannotated +public class OperateLogRespVO implements VO { + + @Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("日志编号") + private Long id; + + @Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "89aca178-a370-411c-ae02-3f0d672be4ab") + private String traceId; + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @Trans(type = TransType.SIMPLE, target = AdminUserDO.class, fields = "nickname", ref = "userName") + private Long userId; + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @ExcelProperty("操作人") + private String userName; + + @Schema(description = "操作模块类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单") + @ExcelProperty("操作模块类型") + private String type; + + @Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建订单") + @ExcelProperty("操作名") + private String subType; + + @Schema(description = "操作模块业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("操作模块业务编号") + private Long bizId; + + @Schema(description = "操作明细", example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。") + private String action; + + @Schema(description = "拓展字段", example = "{'orderId': 1}") + private String extra; + + @Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET") + @NotEmpty(message = "请求方法名不能为空") + private String requestMethod; + + @Schema(description = "请求地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "/xxx/yyy") + private String requestUrl; + + @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") + private String userIp; + + @Schema(description = "浏览器 UserAgent", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0") + private String userAgent; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailAccountController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailAccountController.java new file mode 100644 index 0000000..4cd7c40 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailAccountController.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail; + + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountRespVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSimpleRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; +import cn.iocoder.yudao.module.system.service.mail.MailAccountService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 邮箱账号") +@RestController +@RequestMapping("/system/mail-account") +public class MailAccountController { + + @Resource + private MailAccountService mailAccountService; + + @PostMapping("/create") + @Operation(summary = "创建邮箱账号") + @PreAuthorize("@ss.hasPermission('system:mail-account:create')") + public CommonResult createMailAccount(@Valid @RequestBody MailAccountSaveReqVO createReqVO) { + return success(mailAccountService.createMailAccount(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改邮箱账号") + @PreAuthorize("@ss.hasPermission('system:mail-account:update')") + public CommonResult updateMailAccount(@Valid @RequestBody MailAccountSaveReqVO updateReqVO) { + mailAccountService.updateMailAccount(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除邮箱账号") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('system:mail-account:delete')") + public CommonResult deleteMailAccount(@RequestParam Long id) { + mailAccountService.deleteMailAccount(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得邮箱账号") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:mail-account:query')") + public CommonResult getMailAccount(@RequestParam("id") Long id) { + MailAccountDO account = mailAccountService.getMailAccount(id); + return success(BeanUtils.toBean(account, MailAccountRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得邮箱账号分页") + @PreAuthorize("@ss.hasPermission('system:mail-account:query')") + public CommonResult> getMailAccountPage(@Valid MailAccountPageReqVO pageReqVO) { + PageResult pageResult = mailAccountService.getMailAccountPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, MailAccountRespVO.class)); + } + + @GetMapping({"/list-all-simple", "simple-list"}) + @Operation(summary = "获得邮箱账号精简列表") + public CommonResult> getSimpleMailAccountList() { + List list = mailAccountService.getMailAccountList(); + return success(BeanUtils.toBean(list, MailAccountSimpleRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailLogController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailLogController.java new file mode 100644 index 0000000..1a78d43 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailLogController.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; +import cn.iocoder.yudao.module.system.service.mail.MailLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 邮件日志") +@RestController +@RequestMapping("/system/mail-log") +public class MailLogController { + + @Resource + private MailLogService mailLogService; + + @GetMapping("/page") + @Operation(summary = "获得邮箱日志分页") + @PreAuthorize("@ss.hasPermission('system:mail-log:query')") + public CommonResult> getMailLogPage(@Valid MailLogPageReqVO pageVO) { + PageResult pageResult = mailLogService.getMailLogPage(pageVO); + return success(BeanUtils.toBean(pageResult, MailLogRespVO.class)); + } + + @GetMapping("/get") + @Operation(summary = "获得邮箱日志") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:mail-log:query')") + public CommonResult getMailTemplate(@RequestParam("id") Long id) { + MailLogDO log = mailLogService.getMailLog(id); + return success(BeanUtils.toBean(log, MailLogRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http new file mode 100644 index 0000000..9ad2ed2 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http @@ -0,0 +1,14 @@ +### 请求 /system/mail-template/send-mail 接口 => 成功 +POST {{baseUrl}}/system/mail-template/send-mail +Authorization: Bearer {{token}} +Content-Type: application/json +tenant-id: {{adminTenantId}} + +{ + "templateCode": "test_01", + "mail": "7685413@qq.com", + "templateParams": { + "key01": "value01", + "key02": "value02" + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java new file mode 100644 index 0000000..22e2658 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.*; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; +import cn.iocoder.yudao.module.system.service.mail.MailSendService; +import cn.iocoder.yudao.module.system.service.mail.MailTemplateService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 邮件模版") +@RestController +@RequestMapping("/system/mail-template") +public class MailTemplateController { + + @Resource + private MailTemplateService mailTempleService; + @Resource + private MailSendService mailSendService; + + @PostMapping("/create") + @Operation(summary = "创建邮件模版") + @PreAuthorize("@ss.hasPermission('system:mail-template:create')") + public CommonResult createMailTemplate(@Valid @RequestBody MailTemplateSaveReqVO createReqVO){ + return success(mailTempleService.createMailTemplate(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改邮件模版") + @PreAuthorize("@ss.hasPermission('system:mail-template:update')") + public CommonResult updateMailTemplate(@Valid @RequestBody MailTemplateSaveReqVO updateReqVO){ + mailTempleService.updateMailTemplate(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除邮件模版") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:mail-template:delete')") + public CommonResult deleteMailTemplate(@RequestParam("id") Long id) { + mailTempleService.deleteMailTemplate(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得邮件模版") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:mail-template:query')") + public CommonResult getMailTemplate(@RequestParam("id") Long id) { + MailTemplateDO template = mailTempleService.getMailTemplate(id); + return success(BeanUtils.toBean(template, MailTemplateRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得邮件模版分页") + @PreAuthorize("@ss.hasPermission('system:mail-template:query')") + public CommonResult> getMailTemplatePage(@Valid MailTemplatePageReqVO pageReqVO) { + PageResult pageResult = mailTempleService.getMailTemplatePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, MailTemplateRespVO.class)); + } + + @GetMapping({"/list-all-simple", "simple-list"}) + @Operation(summary = "获得邮件模版精简列表") + public CommonResult> getSimpleTemplateList() { + List list = mailTempleService.getMailTemplateList(); + return success(BeanUtils.toBean(list, MailTemplateSimpleRespVO.class)); + } + + @PostMapping("/send-mail") + @Operation(summary = "发送短信") + @PreAuthorize("@ss.hasPermission('system:mail-template:send-mail')") + public CommonResult sendMail(@Valid @RequestBody MailTemplateSendReqVO sendReqVO) { + return success(mailSendService.sendSingleMailToAdmin(sendReqVO.getMail(), getLoginUserId(), + sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountPageReqVO.java new file mode 100644 index 0000000..9de195d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountPageReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 邮箱账号分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MailAccountPageReqVO extends PageParam { + + @Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma@123.com") + private String mail; + + @Schema(description = "用户名" , requiredMode = Schema.RequiredMode.REQUIRED , example = "yudao") + private String username; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountRespVO.java new file mode 100644 index 0000000..15232b2 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountRespVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 邮箱账号 Response VO") +@Data +public class MailAccountRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma@123.com") + private String mail; + + @Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + private String username; + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + private String password; + + @Schema(description = "SMTP 服务器域名", requiredMode = Schema.RequiredMode.REQUIRED, example = "www.iocoder.cn") + private String host; + + @Schema(description = "SMTP 服务器端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") + private Integer port; + + @Schema(description = "是否开启 ssl", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean sslEnable; + + @Schema(description = "是否开启 starttls", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean starttlsEnable; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSaveReqVO.java new file mode 100644 index 0000000..5bac2c9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSaveReqVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 邮箱账号创建/修改 Request VO") +@Data +public class MailAccountSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma@123.com") + @NotNull(message = "邮箱不能为空") + @Email(message = "必须是 Email 格式") + private String mail; + + @Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @NotNull(message = "用户名不能为空") + private String username; + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @NotNull(message = "密码必填") + private String password; + + @Schema(description = "SMTP 服务器域名", requiredMode = Schema.RequiredMode.REQUIRED, example = "www.iocoder.cn") + @NotNull(message = "SMTP 服务器域名不能为空") + private String host; + + @Schema(description = "SMTP 服务器端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") + @NotNull(message = "SMTP 服务器端口不能为空") + private Integer port; + + @Schema(description = "是否开启 ssl", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否开启 ssl 必填") + private Boolean sslEnable; + + @Schema(description = "是否开启 starttls", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否开启 starttls 必填") + private Boolean starttlsEnable; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSimpleRespVO.java new file mode 100644 index 0000000..0600633 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSimpleRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 邮箱账号的精简 Response VO") +@Data +public class MailAccountSimpleRespVO { + + @Schema(description = "邮箱编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "768541388@qq.com") + private String mail; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogPageReqVO.java new file mode 100644 index 0000000..5b3ad72 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogPageReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.log; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 邮箱日志分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MailLogPageReqVO extends PageParam { + + @Schema(description = "用户编号", example = "30883") + private Long userId; + + @Schema(description = "用户类型,参见 UserTypeEnum 枚举", example = "2") + private Integer userType; + + @Schema(description = "接收邮箱地址,模糊匹配", example = "76854@qq.com") + private String toMail; + + @Schema(description = "邮箱账号编号", example = "18107") + private Long accountId; + + @Schema(description = "模板编号", example = "5678") + private Long templateId; + + @Schema(description = "发送状态,参见 MailSendStatusEnum 枚举", example = "1") + private Integer sendStatus; + + @Schema(description = "发送时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] sendTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java new file mode 100755 index 0000000..49edfff --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.log; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 邮件日志 Response VO") +@Data +public class MailLogRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31020") + private Long id; + + @Schema(description = "用户编号", example = "30883") + private Long userId; + + @Schema(description = "用户类型,参见 UserTypeEnum 枚举", example = "2") + private Byte userType; + + @Schema(description = "接收邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "76854@qq.com") + private String toMail; + + @Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18107") + private Long accountId; + + @Schema(description = "发送邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "85757@qq.com") + private String fromMail; + + @Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5678") + private Long templateId; + + @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") + private String templateCode; + + @Schema(description = "模版发送人名称", example = "李四") + private String templateNickname; + + @Schema(description = "邮件标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试标题") + private String templateTitle; + + @Schema(description = "邮件内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试内容") + private String templateContent; + + @Schema(description = "邮件参数", requiredMode = Schema.RequiredMode.REQUIRED) + private Map templateParams; + + @Schema(description = "发送状态,参见 MailSendStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Byte sendStatus; + + @Schema(description = "发送时间") + private LocalDateTime sendTime; + + @Schema(description = "发送返回的消息 ID", example = "28568") + private String sendMessageId; + + @Schema(description = "发送异常") + private String sendException; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java new file mode 100644 index 0000000..2a9afd7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 邮件模版分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class MailTemplatePageReqVO extends PageParam { + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", example = "1") + private Integer status; + + @Schema(description = "标识,模糊匹配", example = "code_1024") + private String code; + + @Schema(description = "名称,模糊匹配", example = "芋头") + private String name; + + @Schema(description = "账号编号", example = "2048") + private Long accountId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java new file mode 100644 index 0000000..58a9b61 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 邮件末班 Response VO") +@Data +public class MailTemplateRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试名字") + private String name; + + @Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") + private String code; + + @Schema(description = "发送的邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long accountId; + + @Schema(description = "发送人名称", example = "芋头") + private String nickname; + + @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "注册成功") + private String title; + + @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,注册成功啦") + private String content; + + @Schema(description = "参数数组", example = "name,code") + private List params; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "备注", example = "奥特曼") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSaveReqVO.java new file mode 100644 index 0000000..fd6a60a --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSaveReqVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 邮件模版创建/修改 Request VO") +@Data +public class MailTemplateSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试名字") + @NotNull(message = "名称不能为空") + private String name; + + @Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") + @NotNull(message = "模版编号不能为空") + private String code; + + @Schema(description = "发送的邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "发送的邮箱账号编号不能为空") + private Long accountId; + + @Schema(description = "发送人名称", example = "芋头") + private String nickname; + + @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "注册成功") + @NotEmpty(message = "标题不能为空") + private String title; + + @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,注册成功啦") + @NotEmpty(message = "内容不能为空") + private String content; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + + @Schema(description = "备注", example = "奥特曼") + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSendReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSendReqVO.java new file mode 100644 index 0000000..b76b7ff --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSendReqVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.Map; + +@Schema(description = "管理后台 - 邮件发送 Req VO") +@Data +public class MailTemplateSendReqVO { + + @Schema(description = "接收邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "7685413@qq.com") + @NotEmpty(message = "接收邮箱不能为空") + private String mail; + + @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") + @NotNull(message = "模板编码不能为空") + private String templateCode; + + @Schema(description = "模板参数") + private Map templateParams; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSimpleRespVO.java new file mode 100644 index 0000000..29833ff --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSimpleRespVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 邮件模版的精简 Response VO") +@Data +public class MailTemplateSimpleRespVO { + + @Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "模版名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "哒哒哒") + private String name; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/NoticeController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/NoticeController.java new file mode 100644 index 0000000..95fc92d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/NoticeController.java @@ -0,0 +1,92 @@ +package cn.iocoder.yudao.module.system.controller.admin.notice; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeRespVO; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; +import cn.iocoder.yudao.module.system.service.notice.NoticeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 通知公告") +@RestController +@RequestMapping("/system/notice") +@Validated +public class NoticeController { + + @Resource + private NoticeService noticeService; + + @Resource + private WebSocketSenderApi webSocketSenderApi; + + @PostMapping("/create") + @Operation(summary = "创建通知公告") + @PreAuthorize("@ss.hasPermission('system:notice:create')") + public CommonResult createNotice(@Valid @RequestBody NoticeSaveReqVO createReqVO) { + Long noticeId = noticeService.createNotice(createReqVO); + return success(noticeId); + } + + @PutMapping("/update") + @Operation(summary = "修改通知公告") + @PreAuthorize("@ss.hasPermission('system:notice:update')") + public CommonResult updateNotice(@Valid @RequestBody NoticeSaveReqVO updateReqVO) { + noticeService.updateNotice(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除通知公告") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:notice:delete')") + public CommonResult deleteNotice(@RequestParam("id") Long id) { + noticeService.deleteNotice(id); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获取通知公告列表") + @PreAuthorize("@ss.hasPermission('system:notice:query')") + public CommonResult> getNoticePage(@Validated NoticePageReqVO pageReqVO) { + PageResult pageResult = noticeService.getNoticePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, NoticeRespVO.class)); + } + + @GetMapping("/get") + @Operation(summary = "获得通知公告") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:notice:query')") + public CommonResult getNotice(@RequestParam("id") Long id) { + NoticeDO notice = noticeService.getNotice(id); + return success(BeanUtils.toBean(notice, NoticeRespVO.class)); + } + + @PostMapping("/push") + @Operation(summary = "推送通知公告", description = "只发送给 websocket 连接在线的用户") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:notice:update')") + public CommonResult push(@RequestParam("id") Long id) { + NoticeDO notice = noticeService.getNotice(id); + Assert.notNull(notice, "公告不能为空"); + // 通过 websocket 推送给在线的用户 + webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), "notice-push", notice); + return success(true); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticePageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticePageReqVO.java new file mode 100644 index 0000000..e398042 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticePageReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.system.controller.admin.notice.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - 通知公告分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class NoticePageReqVO extends PageParam { + + @Schema(description = "通知公告名称,模糊匹配", example = "芋道") + private String title; + + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeRespVO.java new file mode 100644 index 0000000..543f588 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeRespVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.controller.admin.notice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 通知公告信息 Response VO") +@Data +public class NoticeRespVO { + + @Schema(description = "通知公告序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主") + private String title; + + @Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主") + private Integer type; + + @Schema(description = "公告内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "半生编码") + private String content; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java new file mode 100644 index 0000000..78794ed --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.system.controller.admin.notice.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +@Schema(description = "管理后台 - 通知公告创建/修改 Request VO") +@Data +public class NoticeSaveReqVO { + + @Schema(description = "岗位公告编号", example = "1024") + private Long id; + + @Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主") + @NotBlank(message = "公告标题不能为空") + @Size(max = 50, message = "公告标题不能超过50个字符") + private String title; + + @Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主") + @NotNull(message = "公告类型不能为空") + private Integer type; + + @Schema(description = "公告内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "半生编码") + private String content; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java new file mode 100644 index 0000000..752ad28 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java @@ -0,0 +1,98 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; +import cn.iocoder.yudao.module.system.service.notify.NotifyMessageService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 我的站内信") +@RestController +@RequestMapping("/system/notify-message") +@Validated +public class NotifyMessageController { + + @Resource + private NotifyMessageService notifyMessageService; + + // ========== 管理所有的站内信 ========== + + @GetMapping("/get") + @Operation(summary = "获得站内信") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:notify-message:query')") + public CommonResult getNotifyMessage(@RequestParam("id") Long id) { + NotifyMessageDO message = notifyMessageService.getNotifyMessage(id); + return success(BeanUtils.toBean(message, NotifyMessageRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得站内信分页") + @PreAuthorize("@ss.hasPermission('system:notify-message:query')") + public CommonResult> getNotifyMessagePage(@Valid NotifyMessagePageReqVO pageVO) { + PageResult pageResult = notifyMessageService.getNotifyMessagePage(pageVO); + return success(BeanUtils.toBean(pageResult, NotifyMessageRespVO.class)); + } + + // ========== 查看自己的站内信 ========== + + @GetMapping("/my-page") + @Operation(summary = "获得我的站内信分页") + public CommonResult> getMyMyNotifyMessagePage(@Valid NotifyMessageMyPageReqVO pageVO) { + PageResult pageResult = notifyMessageService.getMyMyNotifyMessagePage(pageVO, + getLoginUserId(), UserTypeEnum.ADMIN.getValue()); + return success(BeanUtils.toBean(pageResult, NotifyMessageRespVO.class)); + } + + @PutMapping("/update-read") + @Operation(summary = "标记站内信为已读") + @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048") + public CommonResult updateNotifyMessageRead(@RequestParam("ids") List ids) { + notifyMessageService.updateNotifyMessageRead(ids, getLoginUserId(), UserTypeEnum.ADMIN.getValue()); + return success(Boolean.TRUE); + } + + @PutMapping("/update-all-read") + @Operation(summary = "标记所有站内信为已读") + public CommonResult updateAllNotifyMessageRead() { + notifyMessageService.updateAllNotifyMessageRead(getLoginUserId(), UserTypeEnum.ADMIN.getValue()); + return success(Boolean.TRUE); + } + + @GetMapping("/get-unread-list") + @Operation(summary = "获取当前用户的最新站内信列表,默认 10 条") + @Parameter(name = "size", description = "10") + public CommonResult> getUnreadNotifyMessageList( + @RequestParam(name = "size", defaultValue = "10") Integer size) { + List list = notifyMessageService.getUnreadNotifyMessageList( + getLoginUserId(), UserTypeEnum.ADMIN.getValue(), size); + return success(BeanUtils.toBean(list, NotifyMessageRespVO.class)); + } + + @GetMapping("/get-unread-count") + @Operation(summary = "获得当前用户的未读站内信数量") + @ApiAccessLog(enable = false) // 由于前端会不断轮询该接口,记录日志没有意义 + public CommonResult getUnreadNotifyMessageCount() { + return success(notifyMessageService.getUnreadNotifyMessageCount( + getLoginUserId(), UserTypeEnum.ADMIN.getValue())); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java new file mode 100644 index 0000000..57c35db --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.*; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; +import cn.iocoder.yudao.module.system.service.notify.NotifySendService; +import cn.iocoder.yudao.module.system.service.notify.NotifyTemplateService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 站内信模版") +@RestController +@RequestMapping("/system/notify-template") +@Validated +public class NotifyTemplateController { + + @Resource + private NotifyTemplateService notifyTemplateService; + + @Resource + private NotifySendService notifySendService; + + @PostMapping("/create") + @Operation(summary = "创建站内信模版") + @PreAuthorize("@ss.hasPermission('system:notify-template:create')") + public CommonResult createNotifyTemplate(@Valid @RequestBody NotifyTemplateSaveReqVO createReqVO) { + return success(notifyTemplateService.createNotifyTemplate(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新站内信模版") + @PreAuthorize("@ss.hasPermission('system:notify-template:update')") + public CommonResult updateNotifyTemplate(@Valid @RequestBody NotifyTemplateSaveReqVO updateReqVO) { + notifyTemplateService.updateNotifyTemplate(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除站内信模版") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('system:notify-template:delete')") + public CommonResult deleteNotifyTemplate(@RequestParam("id") Long id) { + notifyTemplateService.deleteNotifyTemplate(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得站内信模版") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:notify-template:query')") + public CommonResult getNotifyTemplate(@RequestParam("id") Long id) { + NotifyTemplateDO template = notifyTemplateService.getNotifyTemplate(id); + return success(BeanUtils.toBean(template, NotifyTemplateRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得站内信模版分页") + @PreAuthorize("@ss.hasPermission('system:notify-template:query')") + public CommonResult> getNotifyTemplatePage(@Valid NotifyTemplatePageReqVO pageVO) { + PageResult pageResult = notifyTemplateService.getNotifyTemplatePage(pageVO); + return success(BeanUtils.toBean(pageResult, NotifyTemplateRespVO.class)); + } + + @PostMapping("/send-notify") + @Operation(summary = "发送站内信") + @PreAuthorize("@ss.hasPermission('system:notify-template:send-notify')") + public CommonResult sendNotify(@Valid @RequestBody NotifyTemplateSendReqVO sendReqVO) { + if (UserTypeEnum.MEMBER.getValue().equals(sendReqVO.getUserType())) { + return success(notifySendService.sendSingleNotifyToMember(sendReqVO.getUserId(), + sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); + } else { + return success(notifySendService.sendSingleNotifyToAdmin(sendReqVO.getUserId(), + sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); + } + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageMyPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageMyPageReqVO.java new file mode 100644 index 0000000..213d6e1 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageMyPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 站内信分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class NotifyMessageMyPageReqVO extends PageParam { + + @Schema(description = "是否已读", example = "true") + private Boolean readStatus; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessagePageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessagePageReqVO.java new file mode 100644 index 0000000..4e3aea5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessagePageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 站内信分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class NotifyMessagePageReqVO extends PageParam { + + @Schema(description = "用户编号", example = "25025") + private Long userId; + + @Schema(description = "用户类型", example = "1") + private Integer userType; + + @Schema(description = "模板编码", example = "test_01") + private String templateCode; + + @Schema(description = "模版类型", example = "2") + private Integer templateType; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageRespVO.java new file mode 100644 index 0000000..774ed2a --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageRespVO.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Map; + +@Schema(description = "管理后台 - 站内信 Response VO") +@Data +public class NotifyMessageRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25025") + private Long userId; + + @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Byte userType; + + @Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13013") + private Long templateId; + + @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") + private String templateCode; + + @Schema(description = "模版发送人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String templateNickname; + + @Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试内容") + private String templateContent; + + @Schema(description = "模版类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer templateType; + + @Schema(description = "模版参数", requiredMode = Schema.RequiredMode.REQUIRED) + private Map templateParams; + + @Schema(description = "是否已读", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + private Boolean readStatus; + + @Schema(description = "阅读时间") + private LocalDateTime readTime; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplatePageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplatePageReqVO.java new file mode 100644 index 0000000..0557297 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplatePageReqVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 站内信模版分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class NotifyTemplatePageReqVO extends PageParam { + + @Schema(description = "模版编码", example = "test_01") + private String code; + + @Schema(description = "模版名称", example = "我是名称") + private String name; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举类", example = "1") + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateRespVO.java new file mode 100644 index 0000000..8d362b8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateRespVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 站内信模版 Response VO") +@Data +public class NotifyTemplateRespVO { + + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试模版") + private String name; + + @Schema(description = "模版编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "SEND_TEST") + private String code; + + @Schema(description = "模版类型,对应 system_notify_template_type 字典", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "发送人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") + private String nickname; + + @Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是模版内容") + private String content; + + @Schema(description = "参数数组", example = "name,code") + private List params; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "备注", example = "我是备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSaveReqVO.java new file mode 100644 index 0000000..364c125 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSaveReqVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 站内信模版创建/修改 Request VO") +@Data +public class NotifyTemplateSaveReqVO { + + @Schema(description = "ID", example = "1024") + private Long id; + + @Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试模版") + @NotEmpty(message = "模版名称不能为空") + private String name; + + @Schema(description = "模版编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "SEND_TEST") + @NotNull(message = "模版编码不能为空") + private String code; + + @Schema(description = "模版类型,对应 system_notify_template_type 字典", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "模版类型不能为空") + private Integer type; + + @Schema(description = "发送人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") + @NotEmpty(message = "发送人名称不能为空") + private String nickname; + + @Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是模版内容") + @NotEmpty(message = "模版内容不能为空") + private String content; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") + private Integer status; + + @Schema(description = "备注", example = "我是备注") + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSendReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSendReqVO.java new file mode 100644 index 0000000..e7fc1a7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSendReqVO.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.Map; + +@Schema(description = "管理后台 - 站内信模板的发送 Request VO") +@Data +public class NotifyTemplateSendReqVO { + + @Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "01") + @NotNull(message = "用户id不能为空") + private Long userId; + + @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "用户类型不能为空") + private Integer userType; + + @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "01") + @NotEmpty(message = "模板编码不能为空") + private String templateCode; + + @Schema(description = "模板参数") + private Map templateParams; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http new file mode 100644 index 0000000..f8d7b89 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http @@ -0,0 +1,23 @@ +### 请求 /login 接口 => 成功 +POST {{baseUrl}}/system/oauth2-client/create +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +{ + "id": "1", + "secret": "admin123", + "name": "芋道源码", + "logo": "https://www.iocoder.cn/images/favicon.ico", + "description": "我是描述", + "status": 0, + "accessTokenValiditySeconds": 180, + "refreshTokenValiditySeconds": 8640, + "redirectUris": ["https://www.iocoder.cn"], + "autoApprove": true, + "authorizedGrantTypes": ["password"], + "scopes": ["user_info"], + "authorities": ["system:user:query"], + "resource_ids": ["1024"], + "additionalInformation": "{}" +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java new file mode 100755 index 0000000..d3f4aed --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - OAuth2 客户端") +@RestController +@RequestMapping("/system/oauth2-client") +@Validated +public class OAuth2ClientController { + + @Resource + private OAuth2ClientService oAuth2ClientService; + + @PostMapping("/create") + @Operation(summary = "创建 OAuth2 客户端") + @PreAuthorize("@ss.hasPermission('system:oauth2-client:create')") + public CommonResult createOAuth2Client(@Valid @RequestBody OAuth2ClientSaveReqVO createReqVO) { + return success(oAuth2ClientService.createOAuth2Client(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新 OAuth2 客户端") + @PreAuthorize("@ss.hasPermission('system:oauth2-client:update')") + public CommonResult updateOAuth2Client(@Valid @RequestBody OAuth2ClientSaveReqVO updateReqVO) { + oAuth2ClientService.updateOAuth2Client(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除 OAuth2 客户端") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('system:oauth2-client:delete')") + public CommonResult deleteOAuth2Client(@RequestParam("id") Long id) { + oAuth2ClientService.deleteOAuth2Client(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得 OAuth2 客户端") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:oauth2-client:query')") + public CommonResult getOAuth2Client(@RequestParam("id") Long id) { + OAuth2ClientDO client = oAuth2ClientService.getOAuth2Client(id); + return success(BeanUtils.toBean(client, OAuth2ClientRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得 OAuth2 客户端分页") + @PreAuthorize("@ss.hasPermission('system:oauth2-client:query')") + public CommonResult> getOAuth2ClientPage(@Valid OAuth2ClientPageReqVO pageVO) { + PageResult pageResult = oAuth2ClientService.getOAuth2ClientPage(pageVO); + return success(BeanUtils.toBean(pageResult, OAuth2ClientRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http new file mode 100644 index 0000000..f770ed9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http @@ -0,0 +1,54 @@ +### 请求 /system/oauth2/authorize 接口 => 成功 +GET {{baseUrl}}/system/oauth2/authorize?clientId=default +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +### 请求 /system/oauth2/authorize + token 接口 => 成功 +POST {{baseUrl}}/system/oauth2/authorize +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true + +### 请求 /system/oauth2/authorize + code 接口 => 成功 +POST {{baseUrl}}/system/oauth2/authorize +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=false + +### 请求 /system/oauth2/token + code 接口 => 成功 +POST {{baseUrl}}/system/oauth2/token +Content-Type: application/x-www-form-urlencoded +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenantId}} + +grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07a174588a97157eabef2f93a + +### 请求 /system/oauth2/token + password 接口 => 成功 +POST {{baseUrl}}/system/oauth2/token +Content-Type: application/x-www-form-urlencoded +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenantId}} + +grant_type=password&username=admin&password=admin123&scope=user.read + +### 请求 /system/oauth2/token + refresh_token 接口 => 成功 +POST {{baseUrl}}/system/oauth2/token +Content-Type: application/x-www-form-urlencoded +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenantId}} + +grant_type=refresh_token&refresh_token=00895465d6994f72a9d926ceeed0f588 + +### 请求 /system/oauth2/token + DELETE 接口 => 成功 +DELETE {{baseUrl}}/system/oauth2/token?token=ca8a188f464441d6949c51493a2b7596 +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenantId}} + +### 请求 /system/oauth2/check-token 接口 => 成功 +POST {{baseUrl}}/system/oauth2/check-token?token=620d307c5b4148df8a98dd6c6c547106 +Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java new file mode 100644 index 0000000..fd37be8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java @@ -0,0 +1,297 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; +import cn.iocoder.yudao.module.system.convert.oauth2.OAuth2OpenConvert; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2GrantTypeEnum; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; +import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.servlet.http.HttpServletRequest; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +/** + * 提供给外部应用调用为主 + * + * 一般来说,管理后台的 /system-api/* 是不直接提供给外部应用使用,主要是外部应用能够访问的数据与接口是有限的,而管理后台的 RBAC 无法很好的控制。 + * 参考大量的开放平台,都是独立的一套 OpenAPI,对应到【本系统】就是在 Controller 下新建 open 包,实现 /open-api/* 接口,然后通过 scope 进行控制。 + * 另外,一个公司如果有多个管理后台,它们 client_id 产生的 access token 相互之间是无法互通的,即无法访问它们系统的 API 接口,直到两个 client_id 产生信任授权。 + * + * 考虑到【本系统】暂时不想做的过于复杂,默认只有获取到 access token 之后,可以访问【本系统】管理后台的 /system-api/* 所有接口,除非手动添加 scope 控制。 + * scope 的使用示例,可见 {@link OAuth2UserController} 类 + * + * @author 芋道源码 + */ +@Tag(name = "管理后台 - OAuth2.0 授权") +@RestController +@RequestMapping("/system/oauth2") +@Validated +@Slf4j +public class OAuth2OpenController { + + @Resource + private OAuth2GrantService oauth2GrantService; + @Resource + private OAuth2ClientService oauth2ClientService; + @Resource + private OAuth2ApproveService oauth2ApproveService; + @Resource + private OAuth2TokenService oauth2TokenService; + + /** + * 对应 Spring Security OAuth 的 TokenEndpoint 类的 postAccessToken 方法 + * + * 授权码 authorization_code 模式时:code + redirectUri + state 参数 + * 密码 password 模式时:username + password + scope 参数 + * 刷新 refresh_token 模式时:refreshToken 参数 + * 客户端 client_credentials 模式:scope 参数 + * 简化 implicit 模式时:不支持 + * + * 注意,默认需要传递 client_id + client_secret 参数 + */ + @PostMapping("/token") + @PermitAll + @Operation(summary = "获得访问令牌", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【获取】调用") + @Parameters({ + @Parameter(name = "grant_type", required = true, description = "授权类型", example = "code"), + @Parameter(name = "code", description = "授权范围", example = "userinfo.read"), + @Parameter(name = "redirect_uri", description = "重定向 URI", example = "https://www.iocoder.cn"), + @Parameter(name = "state", description = "状态", example = "1"), + @Parameter(name = "username", example = "tudou"), + @Parameter(name = "password", example = "cai"), // 多个使用空格分隔 + @Parameter(name = "scope", example = "user_info"), + @Parameter(name = "refresh_token", example = "123424233"), + }) + public CommonResult postAccessToken(HttpServletRequest request, + @RequestParam("grant_type") String grantType, + @RequestParam(value = "code", required = false) String code, // 授权码模式 + @RequestParam(value = "redirect_uri", required = false) String redirectUri, // 授权码模式 + @RequestParam(value = "state", required = false) String state, // 授权码模式 + @RequestParam(value = "username", required = false) String username, // 密码模式 + @RequestParam(value = "password", required = false) String password, // 密码模式 + @RequestParam(value = "scope", required = false) String scope, // 密码模式 + @RequestParam(value = "refresh_token", required = false) String refreshToken) { // 刷新模式 + List scopes = OAuth2Utils.buildScopes(scope); + // 1.1 校验授权类型 + OAuth2GrantTypeEnum grantTypeEnum = OAuth2GrantTypeEnum.getByGrantType(grantType); + if (grantTypeEnum == null) { + throw exception0(BAD_REQUEST.getCode(), StrUtil.format("未知授权类型({})", grantType)); + } + if (grantTypeEnum == OAuth2GrantTypeEnum.IMPLICIT) { + throw exception0(BAD_REQUEST.getCode(), "Token 接口不支持 implicit 授权模式"); + } + + // 1.2 校验客户端 + String[] clientIdAndSecret = obtainBasicAuthorization(request); + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], + grantType, scopes, redirectUri); + + // 2. 根据授权模式,获取访问令牌 + OAuth2AccessTokenDO accessTokenDO; + switch (grantTypeEnum) { + case AUTHORIZATION_CODE: + accessTokenDO = oauth2GrantService.grantAuthorizationCodeForAccessToken(client.getClientId(), code, redirectUri, state); + break; + case PASSWORD: + accessTokenDO = oauth2GrantService.grantPassword(username, password, client.getClientId(), scopes); + break; + case CLIENT_CREDENTIALS: + accessTokenDO = oauth2GrantService.grantClientCredentials(client.getClientId(), scopes); + break; + case REFRESH_TOKEN: + accessTokenDO = oauth2GrantService.grantRefreshToken(refreshToken, client.getClientId()); + break; + default: + throw new IllegalArgumentException("未知授权类型:" + grantType); + } + Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 + return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO)); + } + + @DeleteMapping("/token") + @PermitAll + @Operation(summary = "删除访问令牌") + @Parameter(name = "token", required = true, description = "访问令牌", example = "biu") + public CommonResult revokeToken(HttpServletRequest request, + @RequestParam("token") String token) { + // 校验客户端 + String[] clientIdAndSecret = obtainBasicAuthorization(request); + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], + null, null, null); + + // 删除访问令牌 + return success(oauth2GrantService.revokeToken(client.getClientId(), token)); + } + + /** + * 对应 Spring Security OAuth 的 CheckTokenEndpoint 类的 checkToken 方法 + */ + @PostMapping("/check-token") + @PermitAll + @Operation(summary = "校验访问令牌") + @Parameter(name = "token", required = true, description = "访问令牌", example = "biu") + public CommonResult checkToken(HttpServletRequest request, + @RequestParam("token") String token) { + // 校验客户端 + String[] clientIdAndSecret = obtainBasicAuthorization(request); + oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], + null, null, null); + + // 校验令牌 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.checkAccessToken(token); + Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 + return success(OAuth2OpenConvert.INSTANCE.convert2(accessTokenDO)); + } + + /** + * 对应 Spring Security OAuth 的 AuthorizationEndpoint 类的 authorize 方法 + */ + @GetMapping("/authorize") + @Operation(summary = "获得授权信息", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【获取】调用") + @Parameter(name = "clientId", required = true, description = "客户端编号", example = "tudou") + public CommonResult authorize(@RequestParam("clientId") String clientId) { + // 0. 校验用户已经登录。通过 Spring Security 实现 + + // 1. 获得 Client 客户端的信息 + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId); + // 2. 获得用户已经授权的信息 + List approves = oauth2ApproveService.getApproveList(getLoginUserId(), getUserType(), clientId); + // 拼接返回 + return success(OAuth2OpenConvert.INSTANCE.convert(client, approves)); + } + + /** + * 对应 Spring Security OAuth 的 AuthorizationEndpoint 类的 approveOrDeny 方法 + * + * 场景一:【自动授权 autoApprove = true】 + * 刚进入 sso.vue 界面,调用该接口,用户历史已经给该应用做过对应的授权,或者 OAuth2Client 支持该 scope 的自动授权 + * 场景二:【手动授权 autoApprove = false】 + * 在 sso.vue 界面,用户选择好 scope 授权范围,调用该接口,进行授权。此时,approved 为 true 或者 false + * + * 因为前后端分离,Axios 无法很好的处理 302 重定向,所以和 Spring Security OAuth 略有不同,返回结果是重定向的 URL,剩余交给前端处理 + */ + @PostMapping("/authorize") + @Operation(summary = "申请授权", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【提交】调用") + @Parameters({ + @Parameter(name = "response_type", required = true, description = "响应类型", example = "code"), + @Parameter(name = "client_id", required = true, description = "客户端编号", example = "tudou"), + @Parameter(name = "scope", description = "授权范围", example = "userinfo.read"), // 使用 Map 格式,Spring MVC 暂时不支持这么接收参数 + @Parameter(name = "redirect_uri", required = true, description = "重定向 URI", example = "https://www.iocoder.cn"), + @Parameter(name = "auto_approve", required = true, description = "用户是否接受", example = "true"), + @Parameter(name = "state", example = "1") + }) + public CommonResult approveOrDeny(@RequestParam("response_type") String responseType, + @RequestParam("client_id") String clientId, + @RequestParam(value = "scope", required = false) String scope, + @RequestParam("redirect_uri") String redirectUri, + @RequestParam(value = "auto_approve") Boolean autoApprove, + @RequestParam(value = "state", required = false) String state) { + @SuppressWarnings("unchecked") + Map scopes = JsonUtils.parseObject(scope, Map.class); + scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap()); + // 0. 校验用户已经登录。通过 Spring Security 实现 + + // 1.1 校验 responseType 是否满足 code 或者 token 值 + OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType); + // 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内 + OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null, + grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri); + + // 2.1 假设 approved 为 null,说明是场景一 + if (Boolean.TRUE.equals(autoApprove)) { + // 如果无法自动授权通过,则返回空 url,前端不进行跳转 + if (!oauth2ApproveService.checkForPreApproval(getLoginUserId(), getUserType(), clientId, scopes.keySet())) { + return success(null); + } + } else { // 2.2 假设 approved 非 null,说明是场景二 + // 如果计算后不通过,则跳转一个错误链接 + if (!oauth2ApproveService.updateAfterApproval(getLoginUserId(), getUserType(), clientId, scopes)) { + return success(OAuth2Utils.buildUnsuccessfulRedirect(redirectUri, responseType, state, + "access_denied", "User denied access")); + } + } + + // 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 + List approveScopes = convertList(scopes.entrySet(), Map.Entry::getKey, Map.Entry::getValue); + if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) { + return success(getAuthorizationCodeRedirect(getLoginUserId(), client, approveScopes, redirectUri, state)); + } + // 3.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向 + return success(getImplicitGrantRedirect(getLoginUserId(), client, approveScopes, redirectUri, state)); + } + + private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) { + if (StrUtil.equals(responseType, "code")) { + return OAuth2GrantTypeEnum.AUTHORIZATION_CODE; + } + if (StrUtil.equalsAny(responseType, "token")) { + return OAuth2GrantTypeEnum.IMPLICIT; + } + throw exception0(BAD_REQUEST.getCode(), "response_type 参数值只允许 code 和 token"); + } + + private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client, + List scopes, String redirectUri, String state) { + // 1. 创建 access token 访问令牌 + OAuth2AccessTokenDO accessTokenDO = oauth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes); + Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 + // 2. 拼接重定向的 URL + // noinspection unchecked + return OAuth2Utils.buildImplicitRedirectUri(redirectUri, accessTokenDO.getAccessToken(), state, accessTokenDO.getExpiresTime(), + scopes, JsonUtils.parseObject(client.getAdditionalInformation(), Map.class)); + } + + private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client, + List scopes, String redirectUri, String state) { + // 1. 创建 code 授权码 + String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId, getUserType(), client.getClientId(), scopes, + redirectUri, state); + // 2. 拼接重定向的 URL + return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); + } + + private Integer getUserType() { + return UserTypeEnum.ADMIN.getValue(); + } + + private String[] obtainBasicAuthorization(HttpServletRequest request) { + String[] clientIdAndSecret = HttpUtils.obtainBasicAuthorization(request); + if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) { + throw exception0(BAD_REQUEST.getCode(), "client_id 或 client_secret 未正确传递"); + } + return clientIdAndSecret; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java new file mode 100644 index 0000000..cafcb23 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; +import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - OAuth2.0 令牌") +@RestController +@RequestMapping("/system/oauth2-token") +public class OAuth2TokenController { + + @Resource + private OAuth2TokenService oauth2TokenService; + @Resource + private AdminAuthService authService; + + @GetMapping("/page") + @Operation(summary = "获得访问令牌分页", description = "只返回有效期内的") + @PreAuthorize("@ss.hasPermission('system:oauth2-token:page')") + public CommonResult> getAccessTokenPage(@Valid OAuth2AccessTokenPageReqVO reqVO) { + PageResult pageResult = oauth2TokenService.getAccessTokenPage(reqVO); + return success(BeanUtils.toBean(pageResult, OAuth2AccessTokenRespVO.class)); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除访问令牌") + @Parameter(name = "accessToken", description = "访问令牌", required = true, example = "tudou") + @PreAuthorize("@ss.hasPermission('system:oauth2-token:delete')") + public CommonResult deleteAccessToken(@RequestParam("accessToken") String accessToken) { + authService.logout(accessToken, LoginLogTypeEnum.LOGOUT_DELETE.getType()); + return success(true); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http new file mode 100644 index 0000000..9e76c7c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http @@ -0,0 +1,14 @@ +### 请求 /system/oauth2/user/get 接口 => 成功 +GET {{baseUrl}}/system/oauth2/user/get +Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d +tenant-id: {{adminTenantId}} + +### 请求 /system/oauth2/user/update 接口 => 成功 +PUT {{baseUrl}}/system/oauth2/user/update +Content-Type: application/json +Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d +tenant-id: {{adminTenantId}} + +{ + "nickname": "芋道源码" +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.java new file mode 100644 index 0000000..5b3942d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.user.OAuth2UserInfoRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.user.OAuth2UserUpdateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import cn.iocoder.yudao.module.system.service.dept.PostService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +/** + * 提供给外部应用调用为主 + * + * 1. 在 getUserInfo 方法上,添加 @PreAuthorize("@ss.hasScope('user.read')") 注解,声明需要满足 scope = user.read + * 2. 在 updateUserInfo 方法上,添加 @PreAuthorize("@ss.hasScope('user.write')") 注解,声明需要满足 scope = user.write + * + * @author 芋道源码 + */ +@Tag(name = "管理后台 - OAuth2.0 用户") +@RestController +@RequestMapping("/system/oauth2/user") +@Validated +@Slf4j +public class OAuth2UserController { + + @Resource + private AdminUserService userService; + @Resource + private DeptService deptService; + @Resource + private PostService postService; + + @GetMapping("/get") + @Operation(summary = "获得用户基本信息") + @PreAuthorize("@ss.hasScope('user.read')") // + public CommonResult getUserInfo() { + // 获得用户基本信息 + AdminUserDO user = userService.getUser(getLoginUserId()); + OAuth2UserInfoRespVO resp = BeanUtils.toBean(user, OAuth2UserInfoRespVO.class); + // 获得部门信息 + if (user.getDeptId() != null) { + DeptDO dept = deptService.getDept(user.getDeptId()); + resp.setDept(BeanUtils.toBean(dept, OAuth2UserInfoRespVO.Dept.class)); + } + // 获得岗位信息 + if (CollUtil.isNotEmpty(user.getPostIds())) { + List posts = postService.getPostList(user.getPostIds()); + resp.setPosts(BeanUtils.toBean(posts, OAuth2UserInfoRespVO.Post.class)); + } + return success(resp); + } + + @PutMapping("/update") + @Operation(summary = "更新用户基本信息") + @PreAuthorize("@ss.hasScope('user.write')") + public CommonResult updateUserInfo(@Valid @RequestBody OAuth2UserUpdateReqVO reqVO) { + // 这里将 UserProfileUpdateReqVO =》UserProfileUpdateReqVO 对象,实现接口的复用。 + // 主要是,AdminUserService 没有自己的 BO 对象,所以复用只能这么做 + userService.updateUserProfile(getLoginUserId(), BeanUtils.toBean(reqVO, UserProfileUpdateReqVO.class)); + return success(true); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java new file mode 100755 index 0000000..f10f15a --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +@Schema(description = "管理后台 - OAuth2 客户端分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class OAuth2ClientPageReqVO extends PageParam { + + @Schema(description = "应用名,模糊匹配", example = "土豆") + private String name; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", example = "1") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java new file mode 100755 index 0000000..acd26c7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - OAuth2 客户端 Response VO") +@Data +public class OAuth2ClientRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") + private String clientId; + + @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "fan") + private String secret; + + @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") + private String name; + + @Schema(description = "应用图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png") + private String logo; + + @Schema(description = "应用描述", example = "我是一个应用") + private String description; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "访问令牌的有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "8640") + private Integer accessTokenValiditySeconds; + + @Schema(description = "刷新令牌的有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "8640000") + private Integer refreshTokenValiditySeconds; + + @Schema(description = "可重定向的 URI 地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") + private List redirectUris; + + @Schema(description = "授权类型,参见 OAuth2GrantTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "password") + private List authorizedGrantTypes; + + @Schema(description = "授权范围", example = "user_info") + private List scopes; + + @Schema(description = "自动通过的授权范围", example = "user_info") + private List autoApproveScopes; + + @Schema(description = "权限", example = "system:user:query") + private List authorities; + + @Schema(description = "资源", example = "1024") + private List resourceIds; + + @Schema(description = "附加信息", example = "{yunai: true}") + private String additionalInformation; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientSaveReqVO.java new file mode 100755 index 0000000..184d81d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientSaveReqVO.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +@Schema(description = "管理后台 - OAuth2 客户端创建/修改 Request VO") +@Data +public class OAuth2ClientSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") + @NotNull(message = "客户端编号不能为空") + private String clientId; + + @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "fan") + @NotNull(message = "客户端密钥不能为空") + private String secret; + + @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") + @NotNull(message = "应用名不能为空") + private String name; + + @Schema(description = "应用图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png") + @NotNull(message = "应用图标不能为空") + @URL(message = "应用图标的地址不正确") + private String logo; + + @Schema(description = "应用描述", example = "我是一个应用") + private String description; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + + @Schema(description = "访问令牌的有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "8640") + @NotNull(message = "访问令牌的有效期不能为空") + private Integer accessTokenValiditySeconds; + + @Schema(description = "刷新令牌的有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "8640000") + @NotNull(message = "刷新令牌的有效期不能为空") + private Integer refreshTokenValiditySeconds; + + @Schema(description = "可重定向的 URI 地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") + @NotNull(message = "可重定向的 URI 地址不能为空") + private List<@NotEmpty(message = "重定向的 URI 不能为空") @URL(message = "重定向的 URI 格式不正确") String> redirectUris; + + @Schema(description = "授权类型,参见 OAuth2GrantTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "password") + @NotNull(message = "授权类型不能为空") + private List authorizedGrantTypes; + + @Schema(description = "授权范围", example = "user_info") + private List scopes; + + @Schema(description = "自动通过的授权范围", example = "user_info") + private List autoApproveScopes; + + @Schema(description = "权限", example = "system:user:query") + private List authorities; + + @Schema(description = "资源", example = "1024") + private List resourceIds; + + @Schema(description = "附加信息", example = "{yunai: true}") + private String additionalInformation; + + @AssertTrue(message = "附加信息必须是 JSON 格式") + public boolean isAdditionalInformationJson() { + return StrUtil.isEmpty(additionalInformation) || JsonUtils.isJson(additionalInformation); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java new file mode 100644 index 0000000..1cdc6cb --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - 【开放接口】访问令牌 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2OpenAccessTokenRespVO { + + @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") + @JsonProperty("access_token") + private String accessToken; + + @Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice") + @JsonProperty("refresh_token") + private String refreshToken; + + @Schema(description = "令牌类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "bearer") + @JsonProperty("token_type") + private String tokenType; + + @Schema(description = "过期时间,单位:秒", requiredMode = Schema.RequiredMode.REQUIRED, example = "42430") + @JsonProperty("expires_in") + private Long expiresIn; + + @Schema(description = "授权范围,如果多个授权范围,使用空格分隔", example = "user_info") + private String scope; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java new file mode 100644 index 0000000..2aa3d77 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open; + +import cn.iocoder.yudao.framework.common.core.KeyValue; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Schema(description = "管理后台 - 授权页的信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2OpenAuthorizeInfoRespVO { + + /** + * 客户端 + */ + private Client client; + + @Schema(description = "scope 的选中信息,使用 List 保证有序性,Key 是 scope,Value 为是否选中", requiredMode = Schema.RequiredMode.REQUIRED) + private List> scopes; + + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class Client { + + @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") + private String name; + + @Schema(description = "应用图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png") + private String logo; + + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java new file mode 100644 index 0000000..cbee613 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Schema(description = "管理后台 - 【开放接口】校验令牌 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2OpenCheckTokenRespVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") + @JsonProperty("user_id") + private Long userId; + @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @JsonProperty("user_type") + private Integer userType; + @Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @JsonProperty("tenant_id") + private Long tenantId; + + @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "car") + @JsonProperty("client_id") + private String clientId; + @Schema(description = "授权范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_info") + private List scopes; + + @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") + @JsonProperty("access_token") + private String accessToken; + + @Schema(description = "过期时间,时间戳 / 1000,即单位:秒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1593092157") + private Long exp; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenPageReqVO.java new file mode 100644 index 0000000..0f932ee --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenPageReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Schema(description = "管理后台 - 访问令牌分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class OAuth2AccessTokenPageReqVO extends PageParam { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") + private Long userId; + + @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer userType; + + @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private String clientId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenRespVO.java new file mode 100644 index 0000000..362a460 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenRespVO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 访问令牌 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2AccessTokenRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") + private String accessToken; + + @Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice") + private String refreshToken; + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") + private Long userId; + + @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer userType; + + @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private String clientId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime expiresTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserInfoRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserInfoRespVO.java new file mode 100644 index 0000000..69a905d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserInfoRespVO.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Schema(description = "管理后台 - OAuth2 获得用户基本信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2UserInfoRespVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String username; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String nickname; + + @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") + private String email; + @Schema(description = "手机号码", example = "15601691300") + private String mobile; + + @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") + private Integer sex; + + @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") + private String avatar; + + /** + * 所在部门 + */ + private Dept dept; + + /** + * 所属岗位数组 + */ + private List posts; + + @Schema(description = "部门") + @Data + public static class Dept { + + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部") + private String name; + + } + + @Schema(description = "岗位") + @Data + public static class Post { + + @Schema(description = "岗位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "开发") + private String name; + + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserUpdateReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserUpdateReqVO.java new file mode 100644 index 0000000..e51ab20 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserUpdateReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Size; + +@Schema(description = "管理后台 - OAuth2 更新用户基本信息 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2UserUpdateReqVO { + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @Size(max = 30, message = "用户昵称长度不能超过 30 个字符") + private String nickname; + + @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") + @Email(message = "邮箱格式不正确") + @Size(max = 50, message = "邮箱长度不能超过 50 个字符") + private String email; + + @Schema(description = "手机号码", example = "15601691300") + @Length(min = 11, max = 11, message = "手机号长度必须 11 位") + private String mobile; + + @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") + private Integer sex; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.http index 5a7ce8e..fbaff1e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.http +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.http @@ -1,4 +1,4 @@ ### 请求 /menu/list 接口 => 成功 -GET {{baseUrl}}/system/dict-data/list-all-simple +GET {{baseUrl}}/system/menu/list Authorization: Bearer {{token}} tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java new file mode 100644 index 0000000..824fe41 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuRespVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSimpleRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import cn.iocoder.yudao.module.system.service.permission.MenuService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 菜单") +@RestController +@RequestMapping("/system/menu") +@Validated +public class MenuController { + + @Resource + private MenuService menuService; + + @PostMapping("/create") + @Operation(summary = "创建菜单") + @PreAuthorize("@ss.hasPermission('system:menu:create')") + public CommonResult createMenu(@Valid @RequestBody MenuSaveVO createReqVO) { + Long menuId = menuService.createMenu(createReqVO); + return success(menuId); + } + + @PutMapping("/update") + @Operation(summary = "修改菜单") + @PreAuthorize("@ss.hasPermission('system:menu:update')") + public CommonResult updateMenu(@Valid @RequestBody MenuSaveVO updateReqVO) { + menuService.updateMenu(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除菜单") + @Parameter(name = "id", description = "菜单编号", required= true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:menu:delete')") + public CommonResult deleteMenu(@RequestParam("id") Long id) { + menuService.deleteMenu(id); + return success(true); + } + + @GetMapping("/list") + @Operation(summary = "获取菜单列表", description = "用于【菜单管理】界面") + @PreAuthorize("@ss.hasPermission('system:menu:query')") + public CommonResult> getMenuList(MenuListReqVO reqVO) { + List list = menuService.getMenuList(reqVO); + list.sort(Comparator.comparing(MenuDO::getSort)); + return success(BeanUtils.toBean(list, MenuRespVO.class)); + } + + @GetMapping({"/list-all-simple", "simple-list"}) + @Operation(summary = "获取菜单精简信息列表", + description = "只包含被开启的菜单,用于【角色分配菜单】功能的选项。在多租户的场景下,会只返回租户所在套餐有的菜单") + public CommonResult> getSimpleMenuList() { + List list = menuService.getMenuListByTenant( + new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus())); + list = menuService.filterDisableMenus(list); + list.sort(Comparator.comparing(MenuDO::getSort)); + return success(BeanUtils.toBean(list, MenuSimpleRespVO.class)); + } + + @GetMapping("/get") + @Operation(summary = "获取菜单信息") + @PreAuthorize("@ss.hasPermission('system:menu:query')") + public CommonResult getMenu(Long id) { + MenuDO menu = menuService.getMenu(id); + return success(BeanUtils.toBean(menu, MenuRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java new file mode 100644 index 0000000..a7b7325 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleDataScopeReqVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleMenuReqVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import cn.iocoder.yudao.module.system.service.tenant.TenantService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +/** + * 权限 Controller,提供赋予用户、角色的权限的 API 接口 + * + * @author 芋道源码 + */ +@Tag(name = "管理后台 - 权限") +@RestController +@RequestMapping("/system/permission") +public class PermissionController { + + @Resource + private PermissionService permissionService; + @Resource + private TenantService tenantService; + + @Operation(summary = "获得角色拥有的菜单编号") + @Parameter(name = "roleId", description = "角色编号", required = true) + @GetMapping("/list-role-menus") + @PreAuthorize("@ss.hasPermission('system:permission:assign-role-menu')") + public CommonResult> getRoleMenuList(Long roleId) { + return success(permissionService.getRoleMenuListByRoleId(roleId)); + } + + @PostMapping("/assign-role-menu") + @Operation(summary = "赋予角色菜单") + @PreAuthorize("@ss.hasPermission('system:permission:assign-role-menu')") + public CommonResult assignRoleMenu(@Validated @RequestBody PermissionAssignRoleMenuReqVO reqVO) { + // 开启多租户的情况下,需要过滤掉未开通的菜单 + tenantService.handleTenantMenu(menuIds -> reqVO.getMenuIds().removeIf(menuId -> !CollUtil.contains(menuIds, menuId))); + + // 执行菜单的分配 + permissionService.assignRoleMenu(reqVO.getRoleId(), reqVO.getMenuIds()); + return success(true); + } + + @PostMapping("/assign-role-data-scope") + @Operation(summary = "赋予角色数据权限") + @PreAuthorize("@ss.hasPermission('system:permission:assign-role-data-scope')") + public CommonResult assignRoleDataScope(@Valid @RequestBody PermissionAssignRoleDataScopeReqVO reqVO) { + permissionService.assignRoleDataScope(reqVO.getRoleId(), reqVO.getDataScope(), reqVO.getDataScopeDeptIds()); + return success(true); + } + + @Operation(summary = "获得管理员拥有的角色编号列表") + @Parameter(name = "userId", description = "用户编号", required = true) + @GetMapping("/list-user-roles") + @PreAuthorize("@ss.hasPermission('system:permission:assign-user-role')") + public CommonResult> listAdminRoles(@RequestParam("userId") Long userId) { + return success(permissionService.getUserRoleIdListByUserId(userId)); + } + + @Operation(summary = "赋予用户角色") + @PostMapping("/assign-user-role") + @PreAuthorize("@ss.hasPermission('system:permission:assign-user-role')") + public CommonResult assignUserRole(@Validated @RequestBody PermissionAssignUserRoleReqVO reqVO) { + permissionService.assignUserRole(reqVO.getUserId(), reqVO.getRoleIds()); + return success(true); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http new file mode 100644 index 0000000..375180a --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http @@ -0,0 +1,42 @@ +### /role/create 成功 +POST {{baseUrl}}/system/role/create +Authorization: Bearer {{token}} +Content-Type: application/json +tenant-id: {{adminTenantId}} + +{ + "name": "测试角色", + "code": "test", + "sort": 0 +} + +### /role/update 成功 +POST {{baseUrl}}/system/role/update +Authorization: Bearer {{token}} +Content-Type: application/json +tenant-id: {{adminTenantId}} + +{ + "id": 100, + "name": "测试角色", + "code": "test", + "sort": 10 +} +### /resource/delete 成功 +POST {{baseUrl}}/system/role/delete +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +roleId=14 + +### /role/get 成功 +GET {{baseUrl}}/system/role/get?id=100 +Content-Type: application/x-www-form-urlencoded +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +### /role/page 成功 +GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10 +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java new file mode 100644 index 0000000..cf22687 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.*; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.service.permission.RoleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static java.util.Collections.singleton; + +@Tag(name = "管理后台 - 角色") +@RestController +@RequestMapping("/system/role") +@Validated +public class RoleController { + + @Resource + private RoleService roleService; + + @PostMapping("/create") + @Operation(summary = "创建角色") + @PreAuthorize("@ss.hasPermission('system:role:create')") + public CommonResult createRole(@Valid @RequestBody RoleSaveReqVO createReqVO) { + return success(roleService.createRole(createReqVO, null)); + } + + @PutMapping("/update") + @Operation(summary = "修改角色") + @PreAuthorize("@ss.hasPermission('system:role:update')") + public CommonResult updateRole(@Valid @RequestBody RoleSaveReqVO updateReqVO) { + roleService.updateRole(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除角色") + @Parameter(name = "id", description = "角色编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:role:delete')") + public CommonResult deleteRole(@RequestParam("id") Long id) { + roleService.deleteRole(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得角色信息") + @PreAuthorize("@ss.hasPermission('system:role:query')") + public CommonResult getRole(@RequestParam("id") Long id) { + RoleDO role = roleService.getRole(id); + return success(BeanUtils.toBean(role, RoleRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得角色分页") + @PreAuthorize("@ss.hasPermission('system:role:query')") + public CommonResult> getRolePage(RolePageReqVO pageReqVO) { + PageResult pageResult = roleService.getRolePage(pageReqVO); + return success(BeanUtils.toBean(pageResult, RoleRespVO.class)); + } + + @GetMapping({"/list-all-simple", "/simple-list"}) + @Operation(summary = "获取角色精简信息列表", description = "只包含被开启的角色,主要用于前端的下拉选项") + public CommonResult> getSimpleRoleList() { + List list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus())); + list.sort(Comparator.comparing(RoleDO::getSort)); + return success(BeanUtils.toBean(list, RoleRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出角色 Excel") + @ApiAccessLog(operateType = EXPORT) + @PreAuthorize("@ss.hasPermission('system:role:export')") + public void export(HttpServletResponse response, @Validated RolePageReqVO exportReqVO) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = roleService.getRolePage(exportReqVO).getList(); + // 输出 + ExcelUtils.write(response, "角色数据.xls", "数据", RoleRespVO.class, + BeanUtils.toBean(list, RoleRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuListReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuListReqVO.java new file mode 100644 index 0000000..b3b7f2d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuListReqVO.java @@ -0,0 +1,16 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 菜单列表 Request VO") +@Data +public class MenuListReqVO { + + @Schema(description = "菜单名称,模糊匹配", example = "芋道") + private String name; + + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuRespVO.java new file mode 100644 index 0000000..8dff186 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuRespVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 菜单信息 Response VO") +@Data +public class MenuRespVO { + + @Schema(description = "菜单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @NotBlank(message = "菜单名称不能为空") + @Size(max = 50, message = "菜单名称长度不能超过50个字符") + private String name; + + @Schema(description = "权限标识,仅菜单类型为按钮时,才需要传递", example = "sys:menu:add") + @Size(max = 100) + private String permission; + + @Schema(description = "类型,参见 MenuTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "菜单类型不能为空") + private Integer type; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "显示顺序不能为空") + private Integer sort; + + @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "父菜单 ID 不能为空") + private Long parentId; + + @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") + @Size(max = 200, message = "路由地址不能超过200个字符") + private String path; + + @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") + private String icon; + + @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") + @Size(max = 200, message = "组件路径不能超过255个字符") + private String component; + + @Schema(description = "组件名", example = "SystemUser") + private String componentName; + + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + + @Schema(description = "是否可见", example = "false") + private Boolean visible; + + @Schema(description = "是否缓存", example = "false") + private Boolean keepAlive; + + @Schema(description = "是否总是显示", example = "false") + private Boolean alwaysShow; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSaveVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSaveVO.java new file mode 100644 index 0000000..9f96f19 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSaveVO.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +@Schema(description = "管理后台 - 菜单创建/修改 Request VO") +@Data +public class MenuSaveVO { + + @Schema(description = "菜单编号", example = "1024") + private Long id; + + @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @NotBlank(message = "菜单名称不能为空") + @Size(max = 50, message = "菜单名称长度不能超过50个字符") + private String name; + + @Schema(description = "权限标识,仅菜单类型为按钮时,才需要传递", example = "sys:menu:add") + @Size(max = 100) + private String permission; + + @Schema(description = "类型,参见 MenuTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "菜单类型不能为空") + private Integer type; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "显示顺序不能为空") + private Integer sort; + + @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "父菜单 ID 不能为空") + private Long parentId; + + @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") + @Size(max = 200, message = "路由地址不能超过200个字符") + private String path; + + @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") + private String icon; + + @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") + @Size(max = 200, message = "组件路径不能超过255个字符") + private String component; + + @Schema(description = "组件名", example = "SystemUser") + private String componentName; + + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + + @Schema(description = "是否可见", example = "false") + private Boolean visible; + + @Schema(description = "是否缓存", example = "false") + private Boolean keepAlive; + + @Schema(description = "是否总是显示", example = "false") + private Boolean alwaysShow; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSimpleRespVO.java new file mode 100644 index 0000000..bb94601 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSimpleRespVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 菜单精简信息 Response VO") +@Data +public class MenuSimpleRespVO { + + @Schema(description = "菜单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long parentId; + + @Schema(description = "类型,参见 MenuTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java new file mode 100644 index 0000000..b889a04 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import java.util.Collections; +import java.util.Set; + +@Schema(description = "管理后台 - 赋予角色数据权限 Request VO") +@Data +public class PermissionAssignRoleDataScopeReqVO { + + @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "角色编号不能为空") + private Long roleId; + + @Schema(description = "数据范围,参见 DataScopeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "数据范围不能为空") + @InEnum(value = DataScopeEnum.class, message = "数据范围必须是 {value}") + private Integer dataScope; + + @Schema(description = "部门编号列表,只有范围类型为 DEPT_CUSTOM 时,该字段才需要", example = "1,3,5") + private Set dataScopeDeptIds = Collections.emptySet(); // 兜底 + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleMenuReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleMenuReqVO.java new file mode 100644 index 0000000..cd04c5b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleMenuReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import java.util.Collections; +import java.util.Set; + +@Schema(description = "管理后台 - 赋予角色菜单 Request VO") +@Data +public class PermissionAssignRoleMenuReqVO { + + @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "角色编号不能为空") + private Long roleId; + + @Schema(description = "菜单编号列表", example = "1,3,5") + private Set menuIds = Collections.emptySet(); // 兜底 + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignUserRoleReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignUserRoleReqVO.java new file mode 100644 index 0000000..bc97dcb --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignUserRoleReqVO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import java.util.Collections; +import java.util.Set; + +@Schema(description = "管理后台 - 赋予用户角色 Request VO") +@Data +public class PermissionAssignUserRoleReqVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "用户编号不能为空") + private Long userId; + + @Schema(description = "角色编号列表", example = "1,3,5") + private Set roleIds = Collections.emptySet(); // 兜底 + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RolePageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RolePageReqVO.java new file mode 100644 index 0000000..c871274 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RolePageReqVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 角色分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +public class RolePageReqVO extends PageParam { + + @Schema(description = "角色名称,模糊匹配", example = "芋道") + private String name; + + @Schema(description = "角色标识,模糊匹配", example = "yudao") + private String code; + + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") + private Integer status; + + @Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java new file mode 100644 index 0000000..89f80c6 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Set; + +@Schema(description = "管理后台 - 角色信息 Response VO") +@Data +@ExcelIgnoreUnannotated +public class RoleRespVO { + + @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("角色序号") + private Long id; + + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "管理员") + @ExcelProperty("角色名称") + private String name; + + @Schema(description = "角色标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "admin") + @NotBlank(message = "角色标志不能为空") + @ExcelProperty("角色标志") + private String code; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("角色排序") + private Integer sort; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "角色状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "角色类型,参见 RoleTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "备注", example = "我是一个角色") + private String remark; + + @Schema(description = "数据范围,参见 DataScopeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "数据范围", converter = DictConvert.class) + @DictFormat(DictTypeConstants.DATA_SCOPE) + private Integer dataScope; + + @Schema(description = "数据范围(指定部门数组)", example = "1") + private Set dataScopeDeptIds; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java new file mode 100644 index 0000000..2d273f3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; + +@Schema(description = "管理后台 - 角色创建/更新 Request VO") +@Data +public class RoleSaveReqVO { + + @Schema(description = "角色编号", example = "1") + private Long id; + + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "管理员") + @NotBlank(message = "角色名称不能为空") + @Size(max = 30, message = "角色名称长度不能超过 30 个字符") + @DiffLogField(name = "角色名称") + private String name; + + @NotBlank(message = "角色标志不能为空") + @Size(max = 100, message = "角色标志长度不能超过 100 个字符") + @Schema(description = "角色标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ADMIN") + @DiffLogField(name = "角色标志") + private String code; + + @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "显示顺序不能为空") + @DiffLogField(name = "显示顺序") + private Integer sort; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @DiffLogField(name = "状态") + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") + private Integer status; + + @Schema(description = "备注", example = "我是一个角色") + @Size(max = 500, message = "备注长度不能超过 500 个字符") + @DiffLogField(name = "备注") + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSimpleRespVO.java new file mode 100644 index 0000000..1e9a9df --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSimpleRespVO.java @@ -0,0 +1,18 @@ +package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - 角色精简信息 Response VO") +@Data +public class RoleSimpleRespVO { + + @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java new file mode 100644 index 0000000..7cfc3ea --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java @@ -0,0 +1,65 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; +import cn.iocoder.yudao.module.system.service.sms.SmsSendService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.servlet.http.HttpServletRequest; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 短信回调") +@RestController +@RequestMapping("/system/sms/callback") +public class SmsCallbackController { + + @Resource + private SmsSendService smsSendService; + + @PostMapping("/aliyun") + @PermitAll + @TenantIgnore + @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/document_detail/120998.html 文档") + public CommonResult receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable { + String text = ServletUtils.getBody(request); + smsSendService.receiveSmsStatus(SmsChannelEnum.ALIYUN.getCode(), text); + return success(true); + } + + @PostMapping("/tencent") + @PermitAll + @TenantIgnore + @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/52077 文档") + public CommonResult receiveTencentSmsStatus(HttpServletRequest request) throws Throwable { + String text = ServletUtils.getBody(request); + smsSendService.receiveSmsStatus(SmsChannelEnum.TENCENT.getCode(), text); + return success(true); + } + + + @PostMapping("/huawei") + @PermitAll + @TenantIgnore + @Operation(summary = "华为云短信的回调", description = "参见 https://support.huaweicloud.com/api-msgsms/sms_05_0003.html 文档") + public CommonResult receiveHuaweiSmsStatus(@RequestBody String requestBody) throws Throwable { + smsSendService.receiveSmsStatus(SmsChannelEnum.HUAWEI.getCode(), requestBody); + return success(true); + } + + @PostMapping("/qiniu") + @PermitAll + @TenantIgnore + @Operation(summary = "七牛云短信的回调", description = "参见 https://developer.qiniu.com/sms/5910/message-push 文档") + public CommonResult receiveQiniuSmsStatus(@RequestBody String requestBody) throws Throwable { + smsSendService.receiveSmsStatus(SmsChannelEnum.QINIU.getCode(), requestBody); + return success(true); + } + +} \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsChannelController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsChannelController.java new file mode 100644 index 0000000..cb26c7f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsChannelController.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelRespVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSimpleRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; +import cn.iocoder.yudao.module.system.service.sms.SmsChannelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.Comparator; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 短信渠道") +@RestController +@RequestMapping("system/sms-channel") +public class SmsChannelController { + + @Resource + private SmsChannelService smsChannelService; + + @PostMapping("/create") + @Operation(summary = "创建短信渠道") + @PreAuthorize("@ss.hasPermission('system:sms-channel:create')") + public CommonResult createSmsChannel(@Valid @RequestBody SmsChannelSaveReqVO createReqVO) { + return success(smsChannelService.createSmsChannel(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新短信渠道") + @PreAuthorize("@ss.hasPermission('system:sms-channel:update')") + public CommonResult updateSmsChannel(@Valid @RequestBody SmsChannelSaveReqVO updateReqVO) { + smsChannelService.updateSmsChannel(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除短信渠道") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('system:sms-channel:delete')") + public CommonResult deleteSmsChannel(@RequestParam("id") Long id) { + smsChannelService.deleteSmsChannel(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得短信渠道") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:sms-channel:query')") + public CommonResult getSmsChannel(@RequestParam("id") Long id) { + SmsChannelDO channel = smsChannelService.getSmsChannel(id); + return success(BeanUtils.toBean(channel, SmsChannelRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得短信渠道分页") + @PreAuthorize("@ss.hasPermission('system:sms-channel:query')") + public CommonResult> getSmsChannelPage(@Valid SmsChannelPageReqVO pageVO) { + PageResult pageResult = smsChannelService.getSmsChannelPage(pageVO); + return success(BeanUtils.toBean(pageResult, SmsChannelRespVO.class)); + } + + @GetMapping({"/list-all-simple", "/simple-list"}) + @Operation(summary = "获得短信渠道精简列表", description = "包含被禁用的短信渠道") + public CommonResult> getSimpleSmsChannelList() { + List list = smsChannelService.getSmsChannelList(); + list.sort(Comparator.comparing(SmsChannelDO::getId)); + return success(BeanUtils.toBean(list, SmsChannelSimpleRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java new file mode 100644 index 0000000..4646e78 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; +import cn.iocoder.yudao.module.system.service.sms.SmsLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 短信日志") +@RestController +@RequestMapping("/system/sms-log") +@Validated +public class SmsLogController { + + @Resource + private SmsLogService smsLogService; + + @GetMapping("/page") + @Operation(summary = "获得短信日志分页") + @PreAuthorize("@ss.hasPermission('system:sms-log:query')") + public CommonResult> getSmsLogPage(@Valid SmsLogPageReqVO pageReqVO) { + PageResult pageResult = smsLogService.getSmsLogPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, SmsLogRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出短信日志 Excel") + @PreAuthorize("@ss.hasPermission('system:sms-log:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportSmsLogExcel(@Valid SmsLogPageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = smsLogService.getSmsLogPage(exportReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "短信日志.xls", "数据", SmsLogRespVO.class, + BeanUtils.toBean(list, SmsLogRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http new file mode 100644 index 0000000..3b975c3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http @@ -0,0 +1,14 @@ +### 请求 /system/sms-template/send-sms 接口 => 成功 +POST {{baseUrl}}/system/sms-template/send-sms +Authorization: Bearer {{token}} +Content-Type: application/json +tenant-id: {{adminTenantId}} + +{ + "templateCode": "test_01", + "mobile": "15601691390", + "templateParams": { + "operation": "value01", + "code": "value02" + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java new file mode 100644 index 0000000..80c6082 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java @@ -0,0 +1,100 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.*; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; +import cn.iocoder.yudao.module.system.service.sms.SmsTemplateService; +import cn.iocoder.yudao.module.system.service.sms.SmsSendService; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 短信模板") +@RestController +@RequestMapping("/system/sms-template") +public class SmsTemplateController { + + @Resource + private SmsTemplateService smsTemplateService; + @Resource + private SmsSendService smsSendService; + + @PostMapping("/create") + @Operation(summary = "创建短信模板") + @PreAuthorize("@ss.hasPermission('system:sms-template:create')") + public CommonResult createSmsTemplate(@Valid @RequestBody SmsTemplateSaveReqVO createReqVO) { + return success(smsTemplateService.createSmsTemplate(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新短信模板") + @PreAuthorize("@ss.hasPermission('system:sms-template:update')") + public CommonResult updateSmsTemplate(@Valid @RequestBody SmsTemplateSaveReqVO updateReqVO) { + smsTemplateService.updateSmsTemplate(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除短信模板") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('system:sms-template:delete')") + public CommonResult deleteSmsTemplate(@RequestParam("id") Long id) { + smsTemplateService.deleteSmsTemplate(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得短信模板") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:sms-template:query')") + public CommonResult getSmsTemplate(@RequestParam("id") Long id) { + SmsTemplateDO template = smsTemplateService.getSmsTemplate(id); + return success(BeanUtils.toBean(template, SmsTemplateRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得短信模板分页") + @PreAuthorize("@ss.hasPermission('system:sms-template:query')") + public CommonResult> getSmsTemplatePage(@Valid SmsTemplatePageReqVO pageVO) { + PageResult pageResult = smsTemplateService.getSmsTemplatePage(pageVO); + return success(BeanUtils.toBean(pageResult, SmsTemplateRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出短信模板 Excel") + @PreAuthorize("@ss.hasPermission('system:sms-template:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportSmsTemplateExcel(@Valid SmsTemplatePageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = smsTemplateService.getSmsTemplatePage(exportReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "短信模板.xls", "数据", SmsTemplateRespVO.class, + BeanUtils.toBean(list, SmsTemplateRespVO.class)); + } + + @PostMapping("/send-sms") + @Operation(summary = "发送短信") + @PreAuthorize("@ss.hasPermission('system:sms-template:send-sms')") + public CommonResult sendSms(@Valid @RequestBody SmsTemplateSendReqVO sendReqVO) { + return success(smsSendService.sendSingleSmsToAdmin(sendReqVO.getMobile(), null, + sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java new file mode 100644 index 0000000..2ec9b2f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 短信渠道分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class SmsChannelPageReqVO extends PageParam { + + @Schema(description = "任务状态", example = "1") + private Integer status; + + @Schema(description = "短信签名,模糊匹配", example = "芋道源码") + private String signature; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java new file mode 100644 index 0000000..091dae8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 短信渠道 Response VO") +@Data +public class SmsChannelRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + @NotNull(message = "短信签名不能为空") + private String signature; + + @Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN") + private String code; + + @Schema(description = "启用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "启用状态不能为空") + private Integer status; + + @Schema(description = "备注", example = "好吃!") + private String remark; + + @Schema(description = "短信 API 的账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @NotNull(message = "短信 API 的账号不能为空") + private String apiKey; + + @Schema(description = "短信 API 的密钥", example = "yuanma") + private String apiSecret; + + @Schema(description = "短信发送回调 URL", example = "https://www.iocoder.cn") + @URL(message = "回调 URL 格式不正确") + private String callbackUrl; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java new file mode 100644 index 0000000..ade624e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 短信渠道创建/修改 Request VO") +@Data +public class SmsChannelSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + @NotNull(message = "短信签名不能为空") + private String signature; + + @Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN") + @NotNull(message = "渠道编码不能为空") + private String code; + + @Schema(description = "启用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "启用状态不能为空") + private Integer status; + + @Schema(description = "备注", example = "好吃!") + private String remark; + + @Schema(description = "短信 API 的账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @NotNull(message = "短信 API 的账号不能为空") + private String apiKey; + + @Schema(description = "短信 API 的密钥", example = "yuanma") + private String apiSecret; + + @Schema(description = "短信发送回调 URL", example = "http://www.iocoder.cn") + @URL(message = "回调 URL 格式不正确") + private String callbackUrl; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java new file mode 100644 index 0000000..350297c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 短信渠道精简 Response VO") +@Data +public class SmsChannelSimpleRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String signature; + + @Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN") + private String code; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogPageReqVO.java new file mode 100644 index 0000000..fbf9dcf --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogPageReqVO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.log; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 短信日志分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class SmsLogPageReqVO extends PageParam { + + @Schema(description = "短信渠道编号", example = "10") + private Long channelId; + + @Schema(description = "模板编号", example = "20") + private Long templateId; + + @Schema(description = "手机号", example = "15601691300") + private String mobile; + + @Schema(description = "发送状态,参见 SmsSendStatusEnum 枚举类", example = "1") + private Integer sendStatus; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "发送时间") + private LocalDateTime[] sendTime; + + @Schema(description = "接收状态,参见 SmsReceiveStatusEnum 枚举类", example = "0") + private Integer receiveStatus; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "接收时间") + private LocalDateTime[] receiveTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java new file mode 100644 index 0000000..77409ed --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java @@ -0,0 +1,116 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.log; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.framework.excel.core.convert.JsonConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Map; + +@Schema(description = "管理后台 - 短信日志 Response VO") +@Data +@ExcelIgnoreUnannotated +public class SmsLogRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "短信渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty("短信渠道编号") + private Long channelId; + + @Schema(description = "短信渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ALIYUN") + @ExcelProperty("短信渠道编码") + private String channelCode; + + @Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") + @ExcelProperty("模板编号") + private Long templateId; + + @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test-01") + @ExcelProperty("模板编码") + private String templateCode; + + @Schema(description = "短信类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "短信类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.SMS_TEMPLATE_TYPE) + private Integer templateType; + + @Schema(description = "短信内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你的验证码是 1024") + @ExcelProperty("短信内容") + private String templateContent; + + @Schema(description = "短信参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "name,code") + @ExcelProperty(value = "短信参数", converter = JsonConvert.class) + private Map templateParams; + + @Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "SMS_207945135") + @ExcelProperty("短信 API 的模板编号") + private String apiTemplateId; + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") + @ExcelProperty("手机号") + private String mobile; + + @Schema(description = "用户编号", example = "10") + @ExcelProperty("用户编号") + private Long userId; + + @Schema(description = "用户类型", example = "1") + @ExcelProperty(value = "用户类型", converter = DictConvert.class) + @DictFormat(DictTypeConstants.USER_TYPE) + private Integer userType; + + @Schema(description = "发送状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "发送状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.SMS_SEND_STATUS) + private Integer sendStatus; + + @Schema(description = "发送时间") + @ExcelProperty("发送时间") + private LocalDateTime sendTime; + + @Schema(description = "短信 API 发送结果的编码", example = "SUCCESS") + @ExcelProperty("短信 API 发送结果的编码") + private String apiSendCode; + + @Schema(description = "短信 API 发送失败的提示", example = "成功") + @ExcelProperty("短信 API 发送失败的提示") + private String apiSendMsg; + + @Schema(description = "短信 API 发送返回的唯一请求 ID", example = "3837C6D3-B96F-428C-BBB2-86135D4B5B99") + @ExcelProperty("短信 API 发送返回的唯一请求 ID") + private String apiRequestId; + + @Schema(description = "短信 API 发送返回的序号", example = "62923244790") + @ExcelProperty("短信 API 发送返回的序号") + private String apiSerialNo; + + @Schema(description = "接收状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + @ExcelProperty(value = "接收状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.SMS_RECEIVE_STATUS) + private Integer receiveStatus; + + @Schema(description = "接收时间") + @ExcelProperty("接收时间") + private LocalDateTime receiveTime; + + @Schema(description = "API 接收结果的编码", example = "DELIVRD") + @ExcelProperty("API 接收结果的编码") + private String apiReceiveCode; + + @Schema(description = "API 接收结果的说明", example = "用户接收成功") + @ExcelProperty("API 接收结果的说明") + private String apiReceiveMsg; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplatePageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplatePageReqVO.java new file mode 100644 index 0000000..acf99e3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplatePageReqVO.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.template; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 短信模板分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class SmsTemplatePageReqVO extends PageParam { + + @Schema(description = "短信签名", example = "1") + private Integer type; + + @Schema(description = "开启状态", example = "1") + private Integer status; + + @Schema(description = "模板编码,模糊匹配", example = "test_01") + private String code; + + @Schema(description = "模板内容,模糊匹配", example = "你好,{name}。你长的太{like}啦!") + private String content; + + @Schema(description = "短信 API 的模板编号,模糊匹配", example = "4383920") + private String apiTemplateId; + + @Schema(description = "短信渠道编号", example = "10") + private Long channelId; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java new file mode 100644 index 0000000..6b8aeca --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java @@ -0,0 +1,69 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.template; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 短信模板 Response VO") +@Data +@ExcelIgnoreUnannotated +public class SmsTemplateRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "短信类型,参见 SmsTemplateTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "短信签名", converter = DictConvert.class) + @DictFormat(DictTypeConstants.SMS_TEMPLATE_TYPE) + private Integer type; + + @Schema(description = "开启状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "开启状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") + @ExcelProperty("模板编码") + private String code; + + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @ExcelProperty("模板名称") + private String name; + + @Schema(description = "模板内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,{name}。你长的太{like}啦!") + @ExcelProperty("模板内容") + private String content; + + @Schema(description = "参数数组", example = "name,code") + private List params; + + @Schema(description = "备注", example = "哈哈哈") + @ExcelProperty("备注") + private String remark; + + @Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4383920") + @ExcelProperty("短信 API 的模板编号") + private String apiTemplateId; + + @Schema(description = "短信渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @ExcelProperty("短信渠道编号") + private Long channelId; + + @Schema(description = "短信渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ALIYUN") + @ExcelProperty(value = "短信渠道编码", converter = DictConvert.class) + @DictFormat(DictTypeConstants.SMS_CHANNEL_CODE) + private String channelCode; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java new file mode 100644 index 0000000..649061f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 短信模板创建/修改 Request VO") +@Data +public class SmsTemplateSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "短信类型,参见 SmsTemplateTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "短信类型不能为空") + private Integer type; + + @Schema(description = "开启状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "开启状态不能为空") + private Integer status; + + @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") + @NotNull(message = "模板编码不能为空") + private String code; + + @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @NotNull(message = "模板名称不能为空") + private String name; + + @Schema(description = "模板内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,{name}。你长的太{like}啦!") + @NotNull(message = "模板内容不能为空") + private String content; + + @Schema(description = "备注", example = "哈哈哈") + private String remark; + + @Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4383920") + @NotNull(message = "短信 API 的模板编号不能为空") + private String apiTemplateId; + + @Schema(description = "短信渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @NotNull(message = "短信渠道编号不能为空") + private Long channelId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSendReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSendReqVO.java new file mode 100644 index 0000000..12a36fd --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSendReqVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.system.controller.admin.sms.vo.template; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import java.util.Map; + +@Schema(description = "管理后台 - 短信模板的发送 Request VO") +@Data +public class SmsTemplateSendReqVO { + + @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") + @NotNull(message = "手机号不能为空") + private String mobile; + + @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") + @NotNull(message = "模板编码不能为空") + private String templateCode; + + @Schema(description = "模板参数") + private Map templateParams; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http new file mode 100644 index 0000000..9909b51 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http @@ -0,0 +1,20 @@ +### 请求 /system/social-client/send-subscribe-message 接口 => 发送测试订阅消息 +POST {{baseUrl}}/system/social-client/send-subscribe-message +Authorization: Bearer {{token}} +Content-Type: application/json +#Authorization: Bearer test100 +tenant-id: {{adminTenantId}} + +{ + "userId": 247, + "userType": 1, + "socialType": 34, + "templateTitle": "充值成功通知", + "page": "", + "messages": { + "character_string1":"5616122165165", + "amount2":"1000.00", + "time3":"2024-01-01 10:10:10", + "phrase4": "充值成功" + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.java new file mode 100644 index 0000000..e91a3d8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.java @@ -0,0 +1,83 @@ +package cn.iocoder.yudao.module.system.controller.admin.socail; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.social.SocialClientApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientRespVO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; +import cn.iocoder.yudao.module.system.service.social.SocialClientService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 社交客户端") +@RestController +@RequestMapping("/system/social-client") +@Validated +public class SocialClientController { + + @Resource + private SocialClientService socialClientService; + @Resource + private SocialClientApi socialClientApi; + + @PostMapping("/create") + @Operation(summary = "创建社交客户端") + @PreAuthorize("@ss.hasPermission('system:social-client:create')") + public CommonResult createSocialClient(@Valid @RequestBody SocialClientSaveReqVO createReqVO) { + return success(socialClientService.createSocialClient(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新社交客户端") + @PreAuthorize("@ss.hasPermission('system:social-client:update')") + public CommonResult updateSocialClient(@Valid @RequestBody SocialClientSaveReqVO updateReqVO) { + socialClientService.updateSocialClient(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除社交客户端") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('system:social-client:delete')") + public CommonResult deleteSocialClient(@RequestParam("id") Long id) { + socialClientService.deleteSocialClient(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得社交客户端") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:social-client:query')") + public CommonResult getSocialClient(@RequestParam("id") Long id) { + SocialClientDO client = socialClientService.getSocialClient(id); + return success(BeanUtils.toBean(client, SocialClientRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得社交客户端分页") + @PreAuthorize("@ss.hasPermission('system:social-client:query')") + public CommonResult> getSocialClientPage(@Valid SocialClientPageReqVO pageVO) { + PageResult pageResult = socialClientService.getSocialClientPage(pageVO); + return success(BeanUtils.toBean(pageResult, SocialClientRespVO.class)); + } + + @PostMapping("/send-subscribe-message") + @Operation(summary = "发送订阅消息") // 用于测试 + @PreAuthorize("@ss.hasPermission('system:social-client:query')") + public void sendSubscribeMessage(@RequestBody SocialWxaSubscribeMessageSendReqDTO reqDTO) { + socialClientApi.sendWxaSubscribeMessage(reqDTO); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialUserController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialUserController.java new file mode 100644 index 0000000..643e465 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialUserController.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.system.controller.admin.socail; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserRespVO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserUnbindReqVO; +import cn.iocoder.yudao.module.system.convert.social.SocialUserConvert; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; +import cn.iocoder.yudao.module.system.service.social.SocialUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 社交用户") +@RestController +@RequestMapping("/system/social-user") +@Validated +public class SocialUserController { + + @Resource + private SocialUserService socialUserService; + + @PostMapping("/bind") + @Operation(summary = "社交绑定,使用 code 授权码") + public CommonResult socialBind(@RequestBody @Valid SocialUserBindReqVO reqVO) { + socialUserService.bindSocialUser(SocialUserConvert.INSTANCE.convert( + getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO)); + return CommonResult.success(true); + } + + @DeleteMapping("/unbind") + @Operation(summary = "取消社交绑定") + public CommonResult socialUnbind(@RequestBody SocialUserUnbindReqVO reqVO) { + socialUserService.unbindSocialUser(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO.getType(), reqVO.getOpenid()); + return CommonResult.success(true); + } + + @GetMapping("/get-bind-list") + @Operation(summary = "获得绑定社交用户列表") + public CommonResult> getBindSocialUserList() { + List list = socialUserService.getSocialUserList(getLoginUserId(), UserTypeEnum.ADMIN.getValue()); + return success(convertList(list, socialUser -> new SocialUserRespVO() // 返回精简信息 + .setId(socialUser.getId()).setType(socialUser.getType()).setOpenid(socialUser.getOpenid()) + .setNickname(socialUser.getNickname()).setAvatar(socialUser.getNickname()))); + } + + // ==================== 社交用户 CRUD ==================== + + @GetMapping("/get") + @Operation(summary = "获得社交用户") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:social-user:query')") + public CommonResult getSocialUser(@RequestParam("id") Long id) { + SocialUserDO socialUser = socialUserService.getSocialUser(id); + return success(BeanUtils.toBean(socialUser, SocialUserRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得社交用户分页") + @PreAuthorize("@ss.hasPermission('system:social-user:query')") + public CommonResult> getSocialUserPage(@Valid SocialUserPageReqVO pageVO) { + PageResult pageResult = socialUserService.getSocialUserPage(pageVO); + return success(BeanUtils.toBean(pageResult, SocialUserRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java new file mode 100644 index 0000000..a225530 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.controller.admin.socail.vo.client; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - 社交客户端分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class SocialClientPageReqVO extends PageParam { + + @Schema(description = "应用名", example = "yudao商城") + private String name; + + @Schema(description = "社交平台的类型", example = "31") + private Integer socialType; + + @Schema(description = "用户类型", example = "2") + private Integer userType; + + @Schema(description = "客户端编号", example = "145442115") + private String clientId; + + @Schema(description = "状态", example = "1") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java new file mode 100644 index 0000000..900f363 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.controller.admin.socail.vo.client; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 社交客户端 Response VO") +@Data +public class SocialClientRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "27162") + private Long id; + + @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao商城") + private String name; + + @Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "31") + private Integer socialType; + + @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer userType; + + @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "wwd411c69a39ad2e54") + private String clientId; + + @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "peter") + private String clientSecret; + + @Schema(description = "授权方的网页应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000045") + private String agentId; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java new file mode 100644 index 0000000..9549b82 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.system.controller.admin.socail.vo.client; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; +import java.util.Objects; + +@Schema(description = "管理后台 - 社交客户端创建/修改 Request VO") +@Data +public class SocialClientSaveReqVO { + + @Schema(description = "编号", example = "27162") + private Long id; + + @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao商城") + @NotNull(message = "应用名不能为空") + private String name; + + @Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "31") + @NotNull(message = "社交平台的类型不能为空") + @InEnum(SocialTypeEnum.class) + private Integer socialType; + + @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + @NotNull(message = "用户类型不能为空") + @InEnum(UserTypeEnum.class) + private Integer userType; + + @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "wwd411c69a39ad2e54") + @NotNull(message = "客户端编号不能为空") + private String clientId; + + @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "peter") + @NotNull(message = "客户端密钥不能为空") + private String clientSecret; + + @Schema(description = "授权方的网页应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000045") + private String agentId; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @AssertTrue(message = "agentId 不能为空") + @JsonIgnore + public boolean isAgentIdValid() { + // 如果是企业微信,必须填写 agentId 属性 + return !Objects.equals(socialType, SocialTypeEnum.WECHAT_ENTERPRISE.getType()) + || !StrUtil.isEmpty(agentId); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java new file mode 100644 index 0000000..5ed0042 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user; + +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 社交绑定 Request VO,使用 code 授权码") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SocialUserBindReqVO { + + @Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(SocialTypeEnum.class) + @NotNull(message = "社交平台的类型不能为空") + private Integer type; + + @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "授权码不能为空") + private String code; + + @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") + @NotEmpty(message = "state 不能为空") + private String state; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java new file mode 100644 index 0000000..3297db4 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 社交用户分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class SocialUserPageReqVO extends PageParam { + + @Schema(description = "社交平台的类型", example = "30") + private Integer type; + + @Schema(description = "用户昵称", example = "李四") + private String nickname; + + @Schema(description = "社交 openid", example = "oz-Jdt0kd_jdhUxJHQdBJMlOFN7w") + private String openid; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java new file mode 100644 index 0000000..fd37703 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 社交用户 Response VO") +@Data +public class SocialUserRespVO { + + @Schema(description = "主键(自增策略)", requiredMode = Schema.RequiredMode.REQUIRED, example = "14569") + private Long id; + + @Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "30") + private Integer type; + + @Schema(description = "社交 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") + private String openid; + + @Schema(description = "社交 token", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") + private String token; + + @Schema(description = "原始 Token 数据,一般是 JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private String rawTokenInfo; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String nickname; + + @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") + private String avatar; + + @Schema(description = "原始用户数据,一般是 JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private String rawUserInfo; + + @Schema(description = "最后一次的认证 code", requiredMode = Schema.RequiredMode.REQUIRED, example = "666666") + private String code; + + @Schema(description = "最后一次的认证 state", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + private String state; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime updateTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java new file mode 100644 index 0000000..e5b1961 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 取消社交绑定 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class SocialUserUnbindReqVO { + + @Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(SocialTypeEnum.class) + @NotNull(message = "社交平台的类型不能为空") + private Integer type; + + @Schema(description = "社交用户的 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE") + @NotEmpty(message = "社交用户的 openid 不能为空") + private String openid; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http new file mode 100644 index 0000000..38aa594 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http @@ -0,0 +1,21 @@ +### 获取租户编号 /admin-api/system/get-id-by-name +GET {{baseUrl}}/system/tenant/get-id-by-name?name=芋道源码 + +### 创建租户 /admin-api/system/tenant/create +POST {{baseUrl}}/system/tenant/create +Content-Type: application/json +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} + +{ + "name": "芋道", + "contactName": "芋艿", + "contactMobile": "15601691300", + "status": 0, + "domain": "https://www.iocoder.cn", + "packageId": 110, + "expireTime": 1699545600000, + "accountCount": 20, + "username": "admin", + "password": "123321" +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java new file mode 100644 index 0000000..8b4f5c7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java @@ -0,0 +1,127 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenant; + +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; +import cn.iocoder.yudao.module.system.service.tenant.TenantService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.annotation.security.PermitAll; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - 租户") +@RestController +@RequestMapping("/system/tenant") +public class TenantController { + + @Resource + private TenantService tenantService; + + @GetMapping("/get-id-by-name") + @PermitAll + @TenantIgnore + @Operation(summary = "使用租户名,获得租户编号", description = "登录界面,根据用户的租户名,获得租户编号") + @Parameter(name = "name", description = "租户名", required = true, example = "1024") + public CommonResult getTenantIdByName(@RequestParam("name") String name) { + TenantDO tenant = tenantService.getTenantByName(name); + return success(tenant != null ? tenant.getId() : null); + } + + @GetMapping({ "simple-list" }) + @PermitAll + @TenantIgnore + @Operation(summary = "获取租户精简信息列表", description = "只包含被开启的租户,用于【首页】功能的选择租户选项") + public CommonResult> getTenantSimpleList() { + List list = tenantService.getTenantListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, tenantDO -> + new TenantRespVO().setId(tenantDO.getId()).setName(tenantDO.getName()))); + } + + @GetMapping("/get-by-website") + @PermitAll + @TenantIgnore + @Operation(summary = "使用域名,获得租户信息", description = "登录界面,根据用户的域名,获得租户信息") + @Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn") + public CommonResult getTenantByWebsite(@RequestParam("website") String website) { + TenantDO tenant = tenantService.getTenantByWebsite(website); + if (tenant == null || CommonStatusEnum.isDisable(tenant.getStatus())) { + return success(null); + } + return success(new TenantRespVO().setId(tenant.getId()).setName(tenant.getName())); + } + + @PostMapping("/create") + @Operation(summary = "创建租户") + @PreAuthorize("@ss.hasPermission('system:tenant:create')") + public CommonResult createTenant(@Valid @RequestBody TenantSaveReqVO createReqVO) { + return success(tenantService.createTenant(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新租户") + @PreAuthorize("@ss.hasPermission('system:tenant:update')") + public CommonResult updateTenant(@Valid @RequestBody TenantSaveReqVO updateReqVO) { + tenantService.updateTenant(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除租户") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:tenant:delete')") + public CommonResult deleteTenant(@RequestParam("id") Long id) { + tenantService.deleteTenant(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得租户") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:tenant:query')") + public CommonResult getTenant(@RequestParam("id") Long id) { + TenantDO tenant = tenantService.getTenant(id); + return success(BeanUtils.toBean(tenant, TenantRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得租户分页") + @PreAuthorize("@ss.hasPermission('system:tenant:query')") + public CommonResult> getTenantPage(@Valid TenantPageReqVO pageVO) { + PageResult pageResult = tenantService.getTenantPage(pageVO); + return success(BeanUtils.toBean(pageResult, TenantRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出租户 Excel") + @PreAuthorize("@ss.hasPermission('system:tenant:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportTenantExcel(@Valid TenantPageReqVO exportReqVO, HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = tenantService.getTenantPage(exportReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "租户.xls", "数据", TenantRespVO.class, + BeanUtils.toBean(list, TenantRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantPackageController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantPackageController.java new file mode 100644 index 0000000..ad9ab3d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantPackageController.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenant; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.*; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; +import cn.iocoder.yudao.module.system.service.tenant.TenantPackageService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - 租户套餐") +@RestController +@RequestMapping("/system/tenant-package") +@Validated +public class TenantPackageController { + + @Resource + private TenantPackageService tenantPackageService; + + @PostMapping("/create") + @Operation(summary = "创建租户套餐") + @PreAuthorize("@ss.hasPermission('system:tenant-package:create')") + public CommonResult createTenantPackage(@Valid @RequestBody TenantPackageSaveReqVO createReqVO) { + return success(tenantPackageService.createTenantPackage(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新租户套餐") + @PreAuthorize("@ss.hasPermission('system:tenant-package:update')") + public CommonResult updateTenantPackage(@Valid @RequestBody TenantPackageSaveReqVO updateReqVO) { + tenantPackageService.updateTenantPackage(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除租户套餐") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('system:tenant-package:delete')") + public CommonResult deleteTenantPackage(@RequestParam("id") Long id) { + tenantPackageService.deleteTenantPackage(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得租户套餐") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:tenant-package:query')") + public CommonResult getTenantPackage(@RequestParam("id") Long id) { + TenantPackageDO tenantPackage = tenantPackageService.getTenantPackage(id); + return success(BeanUtils.toBean(tenantPackage, TenantPackageRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得租户套餐分页") + @PreAuthorize("@ss.hasPermission('system:tenant-package:query')") + public CommonResult> getTenantPackagePage(@Valid TenantPackagePageReqVO pageVO) { + PageResult pageResult = tenantPackageService.getTenantPackagePage(pageVO); + return success(BeanUtils.toBean(pageResult, TenantPackageRespVO.class)); + } + + @GetMapping({"/get-simple-list", "simple-list"}) + @Operation(summary = "获取租户套餐精简信息列表", description = "只包含被开启的租户套餐,主要用于前端的下拉选项") + public CommonResult> getTenantPackageList() { + List list = tenantPackageService.getTenantPackageListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(BeanUtils.toBean(list, TenantPackageSimpleRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackagePageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackagePageReqVO.java new file mode 100755 index 0000000..525a5da --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackagePageReqVO.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 租户套餐分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantPackagePageReqVO extends PageParam { + + @Schema(description = "套餐名", example = "VIP") + private String name; + + @Schema(description = "状态", example = "1") + private Integer status; + + @Schema(description = "备注", example = "好") + private String remark; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageRespVO.java new file mode 100755 index 0000000..16ffd81 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageRespVO.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Set; + +@Schema(description = "管理后台 - 租户套餐 Response VO") +@Data +public class TenantPackageRespVO { + + @Schema(description = "套餐编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "套餐名", requiredMode = Schema.RequiredMode.REQUIRED, example = "VIP") + private String name; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "备注", example = "好") + private String remark; + + @Schema(description = "关联的菜单编号", requiredMode = Schema.RequiredMode.REQUIRED) + private Set menuIds; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSaveReqVO.java new file mode 100755 index 0000000..748283b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSaveReqVO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.util.Set; + +@Schema(description = "管理后台 - 租户套餐创建/修改 Request VO") +@Data +public class TenantPackageSaveReqVO { + + @Schema(description = "套餐编号", example = "1024") + private Long id; + + @Schema(description = "套餐名", requiredMode = Schema.RequiredMode.REQUIRED, example = "VIP") + @NotEmpty(message = "套餐名不能为空") + private String name; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") + private Integer status; + + @Schema(description = "备注", example = "好") + private String remark; + + @Schema(description = "关联的菜单编号", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "关联的菜单编号不能为空") + private Set menuIds; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSimpleRespVO.java new file mode 100644 index 0000000..5611731 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSimpleRespVO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 租户套餐精简 Response VO") +@Data +public class TenantPackageSimpleRespVO { + + @Schema(description = "套餐编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "套餐编号不能为空") + private Long id; + + @Schema(description = "套餐名", requiredMode = Schema.RequiredMode.REQUIRED, example = "VIP") + @NotNull(message = "套餐名不能为空") + private String name; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java new file mode 100755 index 0000000..512a4a7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 租户分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class TenantPageReqVO extends PageParam { + + @Schema(description = "租户名", example = "芋道") + private String name; + + @Schema(description = "联系人", example = "芋艿") + private String contactName; + + @Schema(description = "联系手机", example = "15601691300") + private String contactMobile; + + @Schema(description = "租户状态(0正常 1停用)", example = "1") + private Integer status; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java new file mode 100755 index 0000000..5a444b5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 租户 Response VO") +@Data +@ExcelIgnoreUnannotated +public class TenantRespVO { + + @Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @ExcelProperty("租户编号") + private Long id; + + @Schema(description = "租户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @ExcelProperty("租户名") + private String name; + + @Schema(description = "联系人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @ExcelProperty("联系人") + private String contactName; + + @Schema(description = "联系手机", example = "15601691300") + @ExcelProperty("联系手机") + private String contactMobile; + + @Schema(description = "租户状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "绑定域名", example = "https://www.iocoder.cn") + private String website; + + @Schema(description = "租户套餐编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long packageId; + + @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime expireTime; + + @Schema(description = "账号数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Integer accountCount; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java new file mode 100644 index 0000000..175afa3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; + +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 租户创建/修改 Request VO") +@Data +public class TenantSaveReqVO { + + @Schema(description = "租户编号", example = "1024") + private Long id; + + @Schema(description = "租户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @NotNull(message = "租户名不能为空") + private String name; + + @Schema(description = "联系人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @NotNull(message = "联系人不能为空") + private String contactName; + + @Schema(description = "联系手机", example = "15601691300") + private String contactMobile; + + @Schema(description = "租户状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "租户状态") + private Integer status; + + @Schema(description = "绑定域名", example = "https://www.iocoder.cn") + private String website; + + @Schema(description = "租户套餐编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "租户套餐编号不能为空") + private Long packageId; + + @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "过期时间不能为空") + private LocalDateTime expireTime; + + @Schema(description = "账号数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "账号数量不能为空") + private Integer accountCount; + + // ========== 仅【创建】时,需要传递的字段 ========== + + @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成") + @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符") + private String username; + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + + @AssertTrue(message = "用户账号、密码不能为空") + @JsonIgnore + public boolean isUsernameValid() { + return id != null // 修改时,不需要传递 + || (ObjectUtil.isAllNotEmpty(username, password)); // 新增时,必须都传递 username、password + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http new file mode 100644 index 0000000..0ca6915 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http @@ -0,0 +1,11 @@ +### 请求 /system/user/page 接口 => 没有权限 +GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10 +Authorization: Bearer {{token}} +#Authorization: Bearer test100 +tenant-id: {{adminTenantId}} + +### 请求 /system/user/page 接口(测试访问别的租户) +GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10 +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} +visit-tenant-id: 122 \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java new file mode 100644 index 0000000..1015bc0 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java @@ -0,0 +1,172 @@ +package cn.iocoder.yudao.module.system.controller.admin.user; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*; +import cn.iocoder.yudao.module.system.convert.user.UserConvert; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.enums.common.SexEnum; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +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.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - 用户") +@RestController +@RequestMapping("/system/user") +@Validated +public class UserController { + + @Resource + private AdminUserService userService; + @Resource + private DeptService deptService; + + @PostMapping("/create") + @Operation(summary = "新增用户") + @PreAuthorize("@ss.hasPermission('system:user:create')") + public CommonResult createUser(@Valid @RequestBody UserSaveReqVO reqVO) { + Long id = userService.createUser(reqVO); + return success(id); + } + + @PutMapping("update") + @Operation(summary = "修改用户") + @PreAuthorize("@ss.hasPermission('system:user:update')") + public CommonResult updateUser(@Valid @RequestBody UserSaveReqVO reqVO) { + userService.updateUser(reqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除用户") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:user:delete')") + public CommonResult deleteUser(@RequestParam("id") Long id) { + userService.deleteUser(id); + return success(true); + } + + @PutMapping("/update-password") + @Operation(summary = "重置用户密码") + @PreAuthorize("@ss.hasPermission('system:user:update-password')") + public CommonResult updateUserPassword(@Valid @RequestBody UserUpdatePasswordReqVO reqVO) { + userService.updateUserPassword(reqVO.getId(), reqVO.getPassword()); + return success(true); + } + + @PutMapping("/update-status") + @Operation(summary = "修改用户状态") + @PreAuthorize("@ss.hasPermission('system:user:update')") + public CommonResult updateUserStatus(@Valid @RequestBody UserUpdateStatusReqVO reqVO) { + userService.updateUserStatus(reqVO.getId(), reqVO.getStatus()); + return success(true); + } + + @GetMapping("/page") + @Operation(summary = "获得用户分页列表") + @PreAuthorize("@ss.hasPermission('system:user:query')") + public CommonResult> getUserPage(@Valid UserPageReqVO pageReqVO) { + // 获得用户分页列表 + PageResult pageResult = userService.getUserPage(pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(new PageResult<>(pageResult.getTotal())); + } + // 拼接数据 + Map deptMap = deptService.getDeptMap( + convertList(pageResult.getList(), AdminUserDO::getDeptId)); + return success(new PageResult<>(UserConvert.INSTANCE.convertList(pageResult.getList(), deptMap), + pageResult.getTotal())); + } + + @GetMapping({"/list-all-simple", "/simple-list"}) + @Operation(summary = "获取用户精简信息列表", description = "只包含被开启的用户,主要用于前端的下拉选项") + public CommonResult> getSimpleUserList() { + List list = userService.getUserListByStatus(CommonStatusEnum.ENABLE.getStatus()); + // 拼接数据 + Map deptMap = deptService.getDeptMap( + convertList(list, AdminUserDO::getDeptId)); + return success(UserConvert.INSTANCE.convertSimpleList(list, deptMap)); + } + + @GetMapping("/get") + @Operation(summary = "获得用户详情") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:user:query')") + public CommonResult getUser(@RequestParam("id") Long id) { + AdminUserDO user = userService.getUser(id); + if (user == null) { + return success(null); + } + // 拼接数据 + DeptDO dept = deptService.getDept(user.getDeptId()); + return success(UserConvert.INSTANCE.convert(user, dept)); + } + + @GetMapping("/export") + @Operation(summary = "导出用户") + @PreAuthorize("@ss.hasPermission('system:user:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportUserList(@Validated UserPageReqVO exportReqVO, + HttpServletResponse response) throws IOException { + exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = userService.getUserPage(exportReqVO).getList(); + // 输出 Excel + Map deptMap = deptService.getDeptMap( + convertList(list, AdminUserDO::getDeptId)); + ExcelUtils.write(response, "用户数据.xls", "数据", UserRespVO.class, + UserConvert.INSTANCE.convertList(list, deptMap)); + } + + @GetMapping("/get-import-template") + @Operation(summary = "获得导入用户模板") + public void importTemplate(HttpServletResponse response) throws IOException { + // 手动创建导出 demo + List list = Arrays.asList( + UserImportExcelVO.builder().username("yunai").deptId(1L).email("yunai@iocoder.cn").mobile("15601691300") + .nickname("芋道").status(CommonStatusEnum.ENABLE.getStatus()).sex(SexEnum.MALE.getSex()).build(), + UserImportExcelVO.builder().username("yuanma").deptId(2L).email("yuanma@iocoder.cn").mobile("15601701300") + .nickname("源码").status(CommonStatusEnum.DISABLE.getStatus()).sex(SexEnum.FEMALE.getSex()).build() + ); + // 输出 + ExcelUtils.write(response, "用户导入模板.xls", "用户列表", UserImportExcelVO.class, list); + } + + @PostMapping("/import") + @Operation(summary = "导入用户") + @Parameters({ + @Parameter(name = "file", description = "Excel 文件", required = true), + @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true") + }) + @PreAuthorize("@ss.hasPermission('system:user:import')") + public CommonResult importExcel(@RequestParam("file") MultipartFile file, + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { + List list = ExcelUtils.read(file, UserImportExcelVO.class); + return success(userService.importUserList(list, updateSupport)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http new file mode 100644 index 0000000..c94c2ad --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http @@ -0,0 +1,4 @@ +### 请求 /system/user/profile/get 接口 => 没有权限 +GET {{baseUrl}}/system/user/profile/get +Authorization: Bearer {{token}} +tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java new file mode 100644 index 0000000..d4391e5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.system.controller.admin.user; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; +import cn.iocoder.yudao.module.system.convert.user.UserConvert; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import cn.iocoder.yudao.module.system.service.dept.PostService; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import cn.iocoder.yudao.module.system.service.permission.RoleService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 用户个人中心") +@RestController +@RequestMapping("/system/user/profile") +@Validated +@Slf4j +public class UserProfileController { + + @Resource + private AdminUserService userService; + @Resource + private DeptService deptService; + @Resource + private PostService postService; + @Resource + private PermissionService permissionService; + @Resource + private RoleService roleService; + + @GetMapping("/get") + @Operation(summary = "获得登录用户信息") + @DataPermission(enable = false) // 关闭数据权限,避免只查看自己时,查询不到部门。 + public CommonResult getUserProfile() { + // 获得用户基本信息 + AdminUserDO user = userService.getUser(getLoginUserId()); + // 获得用户角色 + List userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(user.getId())); + // 获得部门信息 + DeptDO dept = user.getDeptId() != null ? deptService.getDept(user.getDeptId()) : null; + // 获得岗位信息 + List posts = CollUtil.isNotEmpty(user.getPostIds()) ? postService.getPostList(user.getPostIds()) : null; + return success(UserConvert.INSTANCE.convert(user, userRoles, dept, posts)); + } + + @PutMapping("/update") + @Operation(summary = "修改用户个人信息") + public CommonResult updateUserProfile(@Valid @RequestBody UserProfileUpdateReqVO reqVO) { + userService.updateUserProfile(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/update-password") + @Operation(summary = "修改用户个人密码") + public CommonResult updateUserProfilePassword(@Valid @RequestBody UserProfileUpdatePasswordReqVO reqVO) { + userService.updateUserPassword(getLoginUserId(), reqVO); + return success(true); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileRespVO.java new file mode 100644 index 0000000..dc9d72e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileRespVO.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.profile; + +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSimpleRespVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSimpleRespVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSimpleRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@Schema(description = "管理后台 - 用户个人中心信息 Response VO") +public class UserProfileRespVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + private String username; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String nickname; + + @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") + private String email; + + @Schema(description = "手机号码", example = "15601691300") + private String mobile; + + @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") + private Integer sex; + + @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") + private String avatar; + + @Schema(description = "最后登录 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "192.168.1.1") + private String loginIp; + + @Schema(description = "最后登录时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime loginDate; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime createTime; + + /** + * 所属角色 + */ + private List roles; + /** + * 所在部门 + */ + private DeptSimpleRespVO dept; + /** + * 所属岗位数组 + */ + private List posts; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java new file mode 100644 index 0000000..cfe37f6 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.profile; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.NotEmpty; + +@Schema(description = "管理后台 - 用户个人中心更新密码 Request VO") +@Data +public class UserProfileUpdatePasswordReqVO { + + @Schema(description = "旧密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @NotEmpty(message = "旧密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String oldPassword; + + @Schema(description = "新密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "654321") + @NotEmpty(message = "新密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String newPassword; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java new file mode 100644 index 0000000..150fbb9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.profile; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.Size; +import org.hibernate.validator.constraints.URL; + + +@Schema(description = "管理后台 - 用户个人信息更新 Request VO") +@Data +public class UserProfileUpdateReqVO { + + @Schema(description = "用户昵称", example = "芋艿") + @Size(max = 30, message = "用户昵称长度不能超过 30 个字符") + private String nickname; + + @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") + @Email(message = "邮箱格式不正确") + @Size(max = 50, message = "邮箱长度不能超过 50 个字符") + private String email; + + @Schema(description = "手机号码", example = "15601691300") + @Length(min = 11, max = 11, message = "手机号长度必须 11 位") + private String mobile; + + @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") + private Integer sex; + + @Schema(description = "角色头像", example = "https://www.iocoder.cn/1.png") + @URL(message = "头像地址格式不正确") + private String avatar; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java new file mode 100644 index 0000000..a360f1a --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 用户 Excel 导入 VO + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题 +public class UserImportExcelVO { + + @ExcelProperty("登录名称") + private String username; + + @ExcelProperty("用户名称") + private String nickname; + + @ExcelProperty("部门编号") + private Long deptId; + + @ExcelProperty("用户邮箱") + private String email; + + @ExcelProperty("手机号码") + private String mobile; + + @ExcelProperty(value = "用户性别", converter = DictConvert.class) + @DictFormat(DictTypeConstants.USER_SEX) + private Integer sex; + + @ExcelProperty(value = "账号状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportRespVO.java new file mode 100644 index 0000000..746c5af --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 用户导入 Response VO") +@Data +@Builder +public class UserImportRespVO { + + @Schema(description = "创建成功的用户名数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List createUsernames; + + @Schema(description = "更新成功的用户名数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List updateUsernames; + + @Schema(description = "导入失败的用户集合,key 为用户名,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) + private Map failureUsernames; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java new file mode 100644 index 0000000..7dadb51 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 用户分页 Request VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class UserPageReqVO extends PageParam { + + @Schema(description = "用户账号,模糊匹配", example = "yudao") + private String username; + + @Schema(description = "手机号码,模糊匹配", example = "yudao") + private String mobile; + + @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") + private Integer status; + + @Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "部门编号,同时筛选子部门", example = "1024") + private Long deptId; + + @Schema(description = "角色编号", example = "1024") + private Long roleId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java new file mode 100644 index 0000000..2837318 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; + +import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; +import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Set; + +@Schema(description = "管理后台 - 用户信息 Response VO") +@Data +@ExcelIgnoreUnannotated +public class UserRespVO{ + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty("用户编号") + private Long id; + + @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @ExcelProperty("用户名称") + private String username; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @ExcelProperty("用户昵称") + private String nickname; + + @Schema(description = "备注", example = "我是一个用户") + private String remark; + + @Schema(description = "部门ID", example = "我是一个用户") + private Long deptId; + @Schema(description = "部门名称", example = "IT 部") + @ExcelProperty("部门名称") + private String deptName; + + @Schema(description = "岗位编号数组", example = "1") + private Set postIds; + + @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") + @ExcelProperty("用户邮箱") + private String email; + + @Schema(description = "手机号码", example = "15601691300") + @ExcelProperty("手机号码") + private String mobile; + + @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") + @ExcelProperty(value = "用户性别", converter = DictConvert.class) + @DictFormat(DictTypeConstants.USER_SEX) + private Integer sex; + + @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") + private String avatar; + + @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @ExcelProperty(value = "帐号状态", converter = DictConvert.class) + @DictFormat(DictTypeConstants.COMMON_STATUS) + private Integer status; + + @Schema(description = "最后登录 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "192.168.1.1") + @ExcelProperty("最后登录IP") + private String loginIp; + + @Schema(description = "最后登录时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + @ExcelProperty("最后登录时间") + private LocalDateTime loginDate; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") + private LocalDateTime createTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java new file mode 100644 index 0000000..482d140 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.validation.Mobile; +import cn.iocoder.yudao.module.system.framework.operatelog.core.DeptParseFunction; +import cn.iocoder.yudao.module.system.framework.operatelog.core.PostParseFunction; +import cn.iocoder.yudao.module.system.framework.operatelog.core.SexParseFunction; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.mzt.logapi.starter.annotation.DiffLogField; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.*; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import java.util.Set; + +@Schema(description = "管理后台 - 用户创建/修改 Request VO") +@Data +public class UserSaveReqVO { + + @Schema(description = "用户编号", example = "1024") + private Long id; + + @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") + @NotBlank(message = "用户账号不能为空") + @Pattern(regexp = "^[a-zA-Z0-9]+$", message = "用户账号由 数字、字母 组成") + @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符") + @DiffLogField(name = "用户账号") + private String username; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @Size(max = 30, message = "用户昵称长度不能超过30个字符") + @DiffLogField(name = "用户昵称") + private String nickname; + + @Schema(description = "备注", example = "我是一个用户") + @DiffLogField(name = "备注") + private String remark; + + @Schema(description = "部门编号", example = "我是一个用户") + @DiffLogField(name = "部门", function = DeptParseFunction.NAME) + private Long deptId; + + @Schema(description = "岗位编号数组", example = "1") + @DiffLogField(name = "岗位", function = PostParseFunction.NAME) + private Set postIds; + + @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") + @Email(message = "邮箱格式不正确") + @Size(max = 50, message = "邮箱长度不能超过 50 个字符") + @DiffLogField(name = "用户邮箱") + private String email; + + @Schema(description = "手机号码", example = "15601691300") + @Mobile + @DiffLogField(name = "手机号码") + private String mobile; + + @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") + @DiffLogField(name = "用户性别", function = SexParseFunction.NAME) + private Integer sex; + + @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") + @DiffLogField(name = "用户头像") + private String avatar; + + // ========== 仅【创建】时,需要传递的字段 ========== + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + + @AssertTrue(message = "密码不能为空") + @JsonIgnore + public boolean isPasswordValid() { + return id != null // 修改时,不需要传递 + || (ObjectUtil.isAllNotEmpty(password)); // 新增时,必须都传递 password + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java new file mode 100644 index 0000000..6dac286 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "管理后台 - 用户精简信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserSimpleRespVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String nickname; + + @Schema(description = "部门ID", example = "我是一个用户") + private Long deptId; + @Schema(description = "部门名称", example = "IT 部") + private String deptName; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java new file mode 100644 index 0000000..065d0d9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 用户更新密码 Request VO") +@Data +public class UserUpdatePasswordReqVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "用户编号不能为空") + private Long id; + + @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") + @NotEmpty(message = "密码不能为空") + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") + private String password; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java new file mode 100644 index 0000000..ae03d18 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 用户更新状态 Request VO") +@Data +public class UserUpdateStatusReqVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "角色编号不能为空") + private Long id; + + @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/AppDictDataController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/AppDictDataController.java new file mode 100644 index 0000000..05e0003 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/AppDictDataController.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.system.controller.app.dict; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.app.dict.vo.AppDictDataRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; +import cn.iocoder.yudao.module.system.service.dict.DictDataService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "用户 App - 字典数据") +@RestController +@RequestMapping("/system/dict-data") +@Validated +public class AppDictDataController { + + @Resource + private DictDataService dictDataService; + + @GetMapping("/type") + @Operation(summary = "根据字典类型查询字典数据信息") + @Parameter(name = "type", description = "字典类型", required = true, example = "common_status") + @PermitAll + public CommonResult> getDictDataListByType(@RequestParam("type") String type) { + List list = dictDataService.getDictDataList( + CommonStatusEnum.ENABLE.getStatus(), type); + return success(BeanUtils.toBean(list, AppDictDataRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/vo/AppDictDataRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/vo/AppDictDataRespVO.java new file mode 100644 index 0000000..5e3bc1c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/vo/AppDictDataRespVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.controller.app.dict.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Schema(description = "用户 App - 字典数据信息 Response VO") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AppDictDataRespVO { + + @Schema(description = "字典数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String label; + + @Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder") + private String value; + + @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") + private String dictType; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/AppAreaController.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/AppAreaController.java new file mode 100644 index 0000000..b07add4 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/AppAreaController.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.system.controller.app.ip; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.ip.core.Area; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import cn.iocoder.yudao.module.system.controller.app.ip.vo.AppAreaNodeRespVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "用户 App - 地区") +@RestController +@RequestMapping("/system/area") +@Validated +public class AppAreaController { + + @GetMapping("/tree") + @Operation(summary = "获得地区树") + @PermitAll + public CommonResult> getAreaTree() { + Area area = AreaUtils.getArea(Area.ID_CHINA); + Assert.notNull(area, "获取不到中国"); + return success(BeanUtils.toBean(area.getChildren(), AppAreaNodeRespVO.class)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/vo/AppAreaNodeRespVO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/vo/AppAreaNodeRespVO.java new file mode 100644 index 0000000..f6ca4a8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/vo/AppAreaNodeRespVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.controller.app.ip.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Schema(description = "用户 App - 地区节点 Response VO") +@Data +public class AppAreaNodeRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "110000") + private Integer id; + + @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "北京") + private String name; + + /** + * 子节点 + */ + private List children; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/package-info.java new file mode 100644 index 0000000..659a909 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 RESTful API 给前端: + * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目 + * 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 + */ +package cn.iocoder.yudao.module.system.controller; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java new file mode 100644 index 0000000..18463e7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.system.convert.auth; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; +import org.slf4j.LoggerFactory; + +import java.util.*; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; +import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; + +@Mapper +public interface AuthConvert { + + AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); + + AuthLoginRespVO convert(OAuth2AccessTokenDO bean); + + default AuthPermissionInfoRespVO convert(AdminUserDO user, List roleList, List menuList) { + return AuthPermissionInfoRespVO.builder() + .user(BeanUtils.toBean(user, AuthPermissionInfoRespVO.UserVO.class)) + .roles(convertSet(roleList, RoleDO::getCode)) + // 权限标识信息 + .permissions(convertSet(menuList, MenuDO::getPermission)) + // 菜单树 + .menus(buildMenuTree(menuList)) + .build(); + } + + AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu); + + /** + * 将菜单列表,构建成菜单树 + * + * @param menuList 菜单列表 + * @return 菜单树 + */ + default List buildMenuTree(List menuList) { + if (CollUtil.isEmpty(menuList)) { + return Collections.emptyList(); + } + // 移除按钮 + menuList.removeIf(menu -> menu.getType().equals(MenuTypeEnum.BUTTON.getType())); + // 排序,保证菜单的有序性 + menuList.sort(Comparator.comparing(MenuDO::getSort)); + + // 构建菜单树 + // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。 + Map treeNodeMap = new LinkedHashMap<>(); + menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu))); + // 处理父子关系 + treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> { + // 获得父节点 + AuthPermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId()); + if (parentNode == null) { + LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]", + childNode.getId(), childNode.getParentId()); + return; + } + // 将自己添加到父节点中 + if (parentNode.getChildren() == null) { + parentNode.setChildren(new ArrayList<>()); + } + parentNode.getChildren().add(childNode); + }); + // 获得到所有的根节点 + return filterList(treeNodeMap.values(), node -> ID_ROOT.equals(node.getParentId())); + } + + SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialLoginReqVO reqVO); + + SmsCodeSendReqDTO convert(AuthSmsSendReqVO reqVO); + + SmsCodeUseReqDTO convert(AuthSmsLoginReqVO reqVO, Integer scene, String usedIp); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java new file mode 100644 index 0000000..24c9537 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.system.convert.oauth2; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Mapper +public interface OAuth2OpenConvert { + + OAuth2OpenConvert INSTANCE = Mappers.getMapper(OAuth2OpenConvert.class); + + default OAuth2OpenAccessTokenRespVO convert(OAuth2AccessTokenDO bean) { + OAuth2OpenAccessTokenRespVO respVO = BeanUtils.toBean(bean, OAuth2OpenAccessTokenRespVO.class); + respVO.setTokenType(SecurityFrameworkUtils.AUTHORIZATION_BEARER.toLowerCase()); + respVO.setExpiresIn(OAuth2Utils.getExpiresIn(bean.getExpiresTime())); + respVO.setScope(OAuth2Utils.buildScopeStr(bean.getScopes())); + return respVO; + } + + default OAuth2OpenCheckTokenRespVO convert2(OAuth2AccessTokenDO bean) { + OAuth2OpenCheckTokenRespVO respVO = BeanUtils.toBean(bean, OAuth2OpenCheckTokenRespVO.class); + respVO.setExp(LocalDateTimeUtil.toEpochMilli(bean.getExpiresTime()) / 1000L); + respVO.setUserType(UserTypeEnum.ADMIN.getValue()); + return respVO; + } + + default OAuth2OpenAuthorizeInfoRespVO convert(OAuth2ClientDO client, List approves) { + // 构建 scopes + List> scopes = new ArrayList<>(client.getScopes().size()); + Map approveMap = CollectionUtils.convertMap(approves, OAuth2ApproveDO::getScope); + client.getScopes().forEach(scope -> { + OAuth2ApproveDO approve = approveMap.get(scope); + scopes.add(new KeyValue<>(scope, approve != null ? approve.getApproved() : false)); + }); + // 拼接返回 + return new OAuth2OpenAuthorizeInfoRespVO( + new OAuth2OpenAuthorizeInfoRespVO.Client(client.getName(), client.getLogo()), scopes); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/package-info.java new file mode 100644 index 0000000..b1ce8e3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 POJO 类的实体转换 + * + * 目前使用 MapStruct 框架 + */ +package cn.iocoder.yudao.module.system.convert; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/social/SocialUserConvert.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/social/SocialUserConvert.java new file mode 100644 index 0000000..9e679a2 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/social/SocialUserConvert.java @@ -0,0 +1,17 @@ +package cn.iocoder.yudao.module.system.convert.social; + +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SocialUserConvert { + + SocialUserConvert INSTANCE = Mappers.getMapper(SocialUserConvert.class); + + @Mapping(source = "reqVO.type", target = "socialType") + SocialUserBindReqDTO convert(Long userId, Integer userType, SocialUserBindReqVO reqVO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/tenant/TenantConvert.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/tenant/TenantConvert.java new file mode 100755 index 0000000..669954d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/tenant/TenantConvert.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.convert.tenant; + +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * 租户 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface TenantConvert { + + TenantConvert INSTANCE = Mappers.getMapper(TenantConvert.class); + + default UserSaveReqVO convert02(TenantSaveReqVO bean) { + UserSaveReqVO reqVO = new UserSaveReqVO(); + reqVO.setUsername(bean.getUsername()); + reqVO.setPassword(bean.getPassword()); + reqVO.setNickname(bean.getContactName()).setMobile(bean.getContactMobile()); + return reqVO; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/user/UserConvert.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/user/UserConvert.java new file mode 100644 index 0000000..20e5fbb --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/user/UserConvert.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.system.convert.user; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSimpleRespVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSimpleRespVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSimpleRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSimpleRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +@Mapper +public interface UserConvert { + + UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); + + default List convertList(List list, Map deptMap) { + return CollectionUtils.convertList(list, user -> convert(user, deptMap.get(user.getDeptId()))); + } + + default UserRespVO convert(AdminUserDO user, DeptDO dept) { + UserRespVO userVO = BeanUtils.toBean(user, UserRespVO.class); + if (dept != null) { + userVO.setDeptName(dept.getName()); + } + return userVO; + } + + default List convertSimpleList(List list, Map deptMap) { + return CollectionUtils.convertList(list, user -> { + UserSimpleRespVO userVO = BeanUtils.toBean(user, UserSimpleRespVO.class); + MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> userVO.setDeptName(dept.getName())); + return userVO; + }); + } + + default UserProfileRespVO convert(AdminUserDO user, List userRoles, + DeptDO dept, List posts) { + UserProfileRespVO userVO = BeanUtils.toBean(user, UserProfileRespVO.class); + userVO.setRoles(BeanUtils.toBean(userRoles, RoleSimpleRespVO.class)); + userVO.setDept(BeanUtils.toBean(dept, DeptSimpleRespVO.class)); + userVO.setPosts(BeanUtils.toBean(posts, PostSimpleRespVO.class)); + return userVO; + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md index 8153487..8153487 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java new file mode 100644 index 0000000..a59fa8b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.dept; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 部门表 + * + * @author ruoyi + * @author 芋道源码 + */ +@TableName("system_dept") +@KeySequence("system_dept_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class DeptDO extends TenantBaseDO { + + public static final Long PARENT_ID_ROOT = 0L; + + /** + * 部门ID + */ + @TableId + private Long id; + /** + * 部门名称 + */ + private String name; + /** + * 父部门ID + * + * 关联 {@link #id} + */ + private Long parentId; + /** + * 显示顺序 + */ + private Integer sort; + /** + * 负责人 + * + * 关联 {@link AdminUserDO#getId()} + */ + private Long leaderUserId; + /** + * 联系电话 + */ + private String phone; + /** + * 邮箱 + */ + private String email; + /** + * 部门状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/PostDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/PostDO.java new file mode 100644 index 0000000..3c97a9c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/PostDO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.dept; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 岗位表 + * + * @author ruoyi + */ +@TableName("system_post") +@KeySequence("system_post_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class PostDO extends BaseDO { + + /** + * 岗位序号 + */ + @TableId + private Long id; + /** + * 岗位名称 + */ + private String name; + /** + * 岗位编码 + */ + private String code; + /** + * 岗位排序 + */ + private Integer sort; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/UserPostDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/UserPostDO.java new file mode 100644 index 0000000..b2a4f52 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/UserPostDO.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.dept; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 用户和岗位关联 + * + * @author ruoyi + */ +@TableName("system_user_post") +@KeySequence("system_user_post_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class UserPostDO extends BaseDO { + + /** + * 自增主键 + */ + @TableId + private Long id; + /** + * 用户 ID + * + * 关联 {@link AdminUserDO#getId()} + */ + private Long userId; + /** + * 角色 ID + * + * 关联 {@link PostDO#getId()} + */ + private Long postId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictDataDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictDataDO.java new file mode 100644 index 0000000..05aba92 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictDataDO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.dict; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 字典数据表 + * + * @author ruoyi + */ +@TableName("system_dict_data") +@KeySequence("system_dict_data_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@TenantIgnore +public class DictDataDO extends BaseDO { + + /** + * 字典数据编号 + */ + @TableId + private Long id; + /** + * 字典排序 + */ + private Integer sort; + /** + * 字典标签 + */ + private String label; + /** + * 字典值 + */ + private String value; + /** + * 字典类型 + * + * 冗余 {@link DictDataDO#getDictType()} + */ + private String dictType; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 颜色类型 + * + * 对应到 element-ui 为 default、primary、success、info、warning、danger + */ + private String colorType; + /** + * css 样式 + */ + @TableField(updateStrategy = FieldStrategy.ALWAYS) + private String cssClass; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictTypeDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictTypeDO.java new file mode 100644 index 0000000..39d78f0 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictTypeDO.java @@ -0,0 +1,58 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.dict; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 字典类型表 + * + * @author ruoyi + */ +@TableName("system_dict_type") +@KeySequence("system_dict_type_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TenantIgnore +public class DictTypeDO extends BaseDO { + + /** + * 字典主键 + */ + @TableId + private Long id; + /** + * 字典名称 + */ + private String name; + /** + * 字典类型 + */ + private String type; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + + /** + * 删除时间 + */ + private LocalDateTime deletedTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/LoginLogDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/LoginLogDO.java new file mode 100644 index 0000000..d2fbcb9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/LoginLogDO.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.logger; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; +import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * 登录日志表 + * + * 注意,包括登录和登出两种行为 + * + * @author 芋道源码 + */ +@TableName("system_login_log") +@KeySequence("system_login_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class LoginLogDO extends BaseDO { + + /** + * 日志主键 + */ + private Long id; + /** + * 日志类型 + * + * 枚举 {@link LoginLogTypeEnum} + */ + private Integer logType; + /** + * 链路追踪编号 + */ + private String traceId; + /** + * 用户编号 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 用户账号 + * + * 冗余,因为账号可以变更 + */ + private String username; + /** + * 登录结果 + * + * 枚举 {@link LoginResultEnum} + */ + private Integer result; + /** + * 用户 IP + */ + private String userIp; + /** + * 浏览器 UA + */ + private String userAgent; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java new file mode 100644 index 0000000..3c7d171 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.logger; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 操作日志表 + * + * @author 芋道源码 + */ +@TableName(value = "system_operate_log", autoResultMap = true) +@KeySequence("system_operate_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +public class OperateLogDO extends BaseDO { + + /** + * 日志主键 + */ + @TableId + private Long id; + /** + * 链路追踪编号 + * + * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。 + */ + private String traceId; + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性 + */ + private Long userId; + /** + * 用户类型 + * + * 关联 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 操作模块类型 + */ + private String type; + /** + * 操作名 + */ + private String subType; + /** + * 操作模块业务编号 + */ + private Long bizId; + /** + * 日志内容,记录整个操作的明细 + * + * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 + */ + private String action; + /** + * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) + * + * 例如说,记录订单编号,{ orderId: "1"} + */ + private String extra; + + /** + * 请求方法名 + */ + private String requestMethod; + /** + * 请求地址 + */ + private String requestUrl; + /** + * 用户 IP + */ + private String userIp; + /** + * 浏览器 UA + */ + private String userAgent; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailAccountDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailAccountDO.java new file mode 100644 index 0000000..3d9ec69 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailAccountDO.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.mail; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 邮箱账号 DO + * + * 用途:配置发送邮箱的账号 + * + * @author wangjingyi + * @since 2022-03-21 + */ +@TableName(value = "system_mail_account", autoResultMap = true) +@KeySequence("system_mail_account_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@TenantIgnore +public class MailAccountDO extends BaseDO { + + /** + * 主键 + */ + @TableId + private Long id; + /** + * 邮箱 + */ + private String mail; + + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * SMTP 服务器域名 + */ + private String host; + /** + * SMTP 服务器端口 + */ + private Integer port; + /** + * 是否开启 SSL + */ + private Boolean sslEnable; + /** + * 是否开启 STARTTLS + */ + private Boolean starttlsEnable; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java new file mode 100644 index 0000000..2a0da51 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java @@ -0,0 +1,125 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.mail; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Map; + +/** + * 邮箱日志 DO + * 记录每一次邮件的发送 + * + * @author wangjingyi + * @since 2022-03-21 + */ +@TableName(value = "system_mail_log", autoResultMap = true) +@KeySequence("system_mail_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TenantIgnore +public class MailLogDO extends BaseDO implements Serializable { + + /** + * 日志编号,自增 + */ + private Long id; + + /** + * 用户编码 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 接收邮箱地址 + */ + private String toMail; + + /** + * 邮箱账号编号 + * + * 关联 {@link MailAccountDO#getId()} + */ + private Long accountId; + /** + * 发送邮箱地址 + * + * 冗余 {@link MailAccountDO#getMail()} + */ + private String fromMail; + + // ========= 模板相关字段 ========= + /** + * 模版编号 + * + * 关联 {@link MailTemplateDO#getId()} + */ + private Long templateId; + /** + * 模版编码 + * + * 冗余 {@link MailTemplateDO#getCode()} + */ + private String templateCode; + /** + * 模版发送人名称 + * + * 冗余 {@link MailTemplateDO#getNickname()} + */ + private String templateNickname; + /** + * 模版标题 + */ + private String templateTitle; + /** + * 模版内容 + * + * 基于 {@link MailTemplateDO#getContent()} 格式化后的内容 + */ + private String templateContent; + /** + * 模版参数 + * + * 基于 {@link MailTemplateDO#getParams()} 输入后的参数 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map templateParams; + + // ========= 发送相关字段 ========= + /** + * 发送状态 + * + * 枚举 {@link MailSendStatusEnum} + */ + private Integer sendStatus; + /** + * 发送时间 + */ + private LocalDateTime sendTime; + /** + * 发送返回的消息 ID + */ + private String sendMessageId; + /** + * 发送异常 + */ + private String sendException; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java new file mode 100644 index 0000000..df026c1 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.mail; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 邮件模版 DO + * + * @author wangjingyi + * @since 2022-03-21 + */ +@TableName(value = "system_mail_template", autoResultMap = true) +@KeySequence("system_mail_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@TenantIgnore +public class MailTemplateDO extends BaseDO { + + /** + * 主键 + */ + private Long id; + /** + * 模版名称 + */ + private String name; + /** + * 模版编号 + */ + private String code; + /** + * 发送的邮箱账号编号 + * + * 关联 {@link MailAccountDO#getId()} + */ + private Long accountId; + + /** + * 发送人名称 + */ + private String nickname; + /** + * 标题 + */ + private String title; + /** + * 内容 + */ + private String content; + /** + * 参数数组(自动根据内容生成) + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List params; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notice/NoticeDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notice/NoticeDO.java new file mode 100644 index 0000000..e7149d1 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notice/NoticeDO.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.notice; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.system.enums.notice.NoticeTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 通知公告表 + * + * @author ruoyi + */ +@TableName("system_notice") +@KeySequence("system_notice_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class NoticeDO extends BaseDO { + + /** + * 公告ID + */ + private Long id; + /** + * 公告标题 + */ + private String title; + /** + * 公告类型 + * + * 枚举 {@link NoticeTypeEnum} + */ + private Integer type; + /** + * 公告内容 + */ + private String content; + /** + * 公告状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyMessageDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyMessageDO.java new file mode 100644 index 0000000..e73badf --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyMessageDO.java @@ -0,0 +1,101 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.notify; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.Date; +import java.util.Map; + +/** + * 站内信 DO + * + * @author xrcoder + */ +@TableName(value = "system_notify_message", autoResultMap = true) +@KeySequence("system_notify_message_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class NotifyMessageDO extends BaseDO { + + /** + * 站内信编号,自增 + */ + @TableId + private Long id; + /** + * 用户编号 + * + * 关联 MemberUserDO 的 id 字段、或者 AdminUserDO 的 id 字段 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + + // ========= 模板相关字段 ========= + + /** + * 模版编号 + * + * 关联 {@link NotifyTemplateDO#getId()} + */ + private Long templateId; + /** + * 模版编码 + * + * 关联 {@link NotifyTemplateDO#getCode()} + */ + private String templateCode; + /** + * 模版类型 + * + * 冗余 {@link NotifyTemplateDO#getType()} + */ + private Integer templateType; + /** + * 模版发送人名称 + * + * 冗余 {@link NotifyTemplateDO#getNickname()} + */ + private String templateNickname; + /** + * 模版内容 + * + * 基于 {@link NotifyTemplateDO#getContent()} 格式化后的内容 + */ + private String templateContent; + /** + * 模版参数 + * + * 基于 {@link NotifyTemplateDO#getParams()} 输入后的参数 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map templateParams; + + // ========= 读取相关字段 ========= + + /** + * 是否已读 + */ + private Boolean readStatus; + /** + * 阅读时间 + */ + private LocalDateTime readTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyTemplateDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyTemplateDO.java new file mode 100644 index 0000000..be4d185 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyTemplateDO.java @@ -0,0 +1,74 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.notify; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.util.List; + +/** + * 站内信模版 DO + * + * @author xrcoder + */ +@TableName(value = "system_notify_template", autoResultMap = true) +@KeySequence("system_notify_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TenantIgnore +public class NotifyTemplateDO extends BaseDO { + + /** + * ID + */ + @TableId + private Long id; + /** + * 模版名称 + */ + private String name; + /** + * 模版编码 + */ + private String code; + /** + * 模版类型 + * + * 对应 system_notify_template_type 字典 + */ + private Integer type; + /** + * 发送人名称 + */ + private String nickname; + /** + * 模版内容 + */ + private String content; + /** + * 参数数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List params; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2AccessTokenDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2AccessTokenDO.java new file mode 100644 index 0000000..ef235ed --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2AccessTokenDO.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * OAuth2 访问令牌 DO + * + * 如下字段,暂时未使用,暂时不支持: + * user_name、authentication(用户信息) + * + * @author 芋道源码 + */ +@TableName(value = "system_oauth2_access_token", autoResultMap = true) +@KeySequence("system_oauth2_access_token_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class OAuth2AccessTokenDO extends TenantBaseDO { + + /** + * 编号,数据库递增 + */ + @TableId + private Long id; + /** + * 访问令牌 + */ + private String accessToken; + /** + * 刷新令牌 + */ + private String refreshToken; + /** + * 用户编号 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 用户信息 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map userInfo; + /** + * 客户端编号 + * + * 关联 {@link OAuth2ClientDO#getId()} + */ + private String clientId; + /** + * 授权范围 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List scopes; + /** + * 过期时间 + */ + private LocalDateTime expiresTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java new file mode 100644 index 0000000..e07b0a0 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; + +/** + * OAuth2 批准 DO + * + * 用户在 sso.vue 界面时,记录接受的 scope 列表 + * + * @author 芋道源码 + */ +@TableName(value = "system_oauth2_approve", autoResultMap = true) +@KeySequence("system_oauth2_approve_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class OAuth2ApproveDO extends BaseDO { + + /** + * 编号,数据库自增 + */ + @TableId + private Long id; + /** + * 用户编号 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 客户端编号 + * + * 关联 {@link OAuth2ClientDO#getId()} + */ + private String clientId; + /** + * 授权范围 + */ + private String scope; + /** + * 是否接受 + * + * true - 接受 + * false - 拒绝 + */ + private Boolean approved; + /** + * 过期时间 + */ + private LocalDateTime expiresTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ClientDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ClientDO.java new file mode 100644 index 0000000..3851cc8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ClientDO.java @@ -0,0 +1,109 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2GrantTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * OAuth2 客户端 DO + * + * @author 芋道源码 + */ +@TableName(value = "system_oauth2_client", autoResultMap = true) +@KeySequence("system_oauth2_client_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@TenantIgnore +public class OAuth2ClientDO extends BaseDO { + + /** + * 编号,数据库自增 + * + * 由于 SQL Server 在存储 String 主键有点问题,所以暂时使用 Long 类型 + */ + @TableId + private Long id; + /** + * 客户端编号 + */ + private String clientId; + /** + * 客户端密钥 + */ + private String secret; + /** + * 应用名 + */ + private String name; + /** + * 应用图标 + */ + private String logo; + /** + * 应用描述 + */ + private String description; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 访问令牌的有效期 + */ + private Integer accessTokenValiditySeconds; + /** + * 刷新令牌的有效期 + */ + private Integer refreshTokenValiditySeconds; + /** + * 可重定向的 URI 地址 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List redirectUris; + /** + * 授权类型(模式) + * + * 枚举 {@link OAuth2GrantTypeEnum} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List authorizedGrantTypes; + /** + * 授权范围 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List scopes; + /** + * 自动授权的 Scope + * + * code 授权时,如果 scope 在这个范围内,则自动通过 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List autoApproveScopes; + /** + * 权限 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List authorities; + /** + * 资源 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List resourceIds; + /** + * 附加信息,JSON 格式 + */ + private String additionalInformation; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2CodeDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2CodeDO.java new file mode 100644 index 0000000..fad9c16 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2CodeDO.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * OAuth2 授权码 DO + * + * @author 芋道源码 + */ +@TableName(value = "system_oauth2_code", autoResultMap = true) +@KeySequence("system_oauth2_code_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class OAuth2CodeDO extends BaseDO { + + /** + * 编号,数据库递增 + */ + private Long id; + /** + * 授权码 + */ + private String code; + /** + * 用户编号 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 客户端编号 + * + * 关联 {@link OAuth2ClientDO#getClientId()} + */ + private String clientId; + /** + * 授权范围 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List scopes; + /** + * 重定向地址 + */ + private String redirectUri; + /** + * 状态 + */ + private String state; + /** + * 过期时间 + */ + private LocalDateTime expiresTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2RefreshTokenDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2RefreshTokenDO.java new file mode 100644 index 0000000..99d153e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2RefreshTokenDO.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * OAuth2 刷新令牌 + * + * @author 芋道源码 + */ +@TableName(value = "system_oauth2_refresh_token", autoResultMap = true) +// 由于 Oracle 的 SEQ 的名字长度有限制,所以就先用 system_oauth2_access_token_seq 吧,反正也没啥问题 +@KeySequence("system_oauth2_access_token_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@Accessors(chain = true) +public class OAuth2RefreshTokenDO extends TenantBaseDO { + + /** + * 编号,数据库字典 + */ + private Long id; + /** + * 刷新令牌 + */ + private String refreshToken; + /** + * 用户编号 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 客户端编号 + * + * 关联 {@link OAuth2ClientDO#getId()} + */ + private String clientId; + /** + * 授权范围 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List scopes; + /** + * 过期时间 + */ + private LocalDateTime expiresTime; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/MenuDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/MenuDO.java new file mode 100644 index 0000000..8922b61 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/MenuDO.java @@ -0,0 +1,109 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.permission; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 菜单 DO + * + * @author ruoyi + */ +@TableName("system_menu") +@KeySequence("system_menu_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@TenantIgnore +public class MenuDO extends BaseDO { + + /** + * 菜单编号 - 根节点 + */ + public static final Long ID_ROOT = 0L; + + /** + * 菜单编号 + */ + @TableId + private Long id; + /** + * 菜单名称 + */ + private String name; + /** + * 权限标识 + * + * 一般格式为:${系统}:${模块}:${操作} + * 例如说:system:admin:add,即 system 服务的添加管理员。 + * + * 当我们把该 MenuDO 赋予给角色后,意味着该角色有该资源: + * - 对于后端,配合 @PreAuthorize 注解,配置 API 接口需要该权限,从而对 API 接口进行权限控制。 + * - 对于前端,配合前端标签,配置按钮是否展示,避免用户没有该权限时,结果可以看到该操作。 + */ + private String permission; + /** + * 菜单类型 + * + * 枚举 {@link MenuTypeEnum} + */ + private Integer type; + /** + * 显示顺序 + */ + private Integer sort; + /** + * 父菜单ID + */ + private Long parentId; + /** + * 路由地址 + * + * 如果 path 为 http(s) 时,则它是外链 + */ + private String path; + /** + * 菜单图标 + */ + private String icon; + /** + * 组件路径 + */ + private String component; + /** + * 组件名 + */ + private String componentName; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 是否可见 + * + * 只有菜单、目录使用 + * 当设置为 true 时,该菜单不会展示在侧边栏,但是路由还是存在。例如说,一些独立的编辑页面 /edit/1024 等等 + */ + private Boolean visible; + /** + * 是否缓存 + * + * 只有菜单、目录使用,否使用 Vue 路由的 keep-alive 特性 + * 注意:如果开启缓存,则必须填写 {@link #componentName} 属性,否则无法缓存 + */ + private Boolean keepAlive; + /** + * 是否总是显示 + * + * 如果为 false 时,当该菜单只有一个子菜单时,不展示自己,直接展示子菜单 + */ + private Boolean alwaysShow; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java new file mode 100644 index 0000000..482a4b5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.permission; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; +import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Set; + +/** + * 角色 DO + * + * @author ruoyi + */ +@TableName(value = "system_role", autoResultMap = true) +@KeySequence("system_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class RoleDO extends TenantBaseDO { + + /** + * 角色ID + */ + @TableId + private Long id; + /** + * 角色名称 + */ + private String name; + /** + * 角色标识 + * + * 枚举 + */ + private String code; + /** + * 角色排序 + */ + private Integer sort; + /** + * 角色状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 角色类型 + * + * 枚举 {@link RoleTypeEnum} + */ + private Integer type; + /** + * 备注 + */ + private String remark; + + /** + * 数据范围 + * + * 枚举 {@link DataScopeEnum} + */ + private Integer dataScope; + /** + * 数据范围(指定部门数组) + * + * 适用于 {@link #dataScope} 的值为 {@link DataScopeEnum#DEPT_CUSTOM} 时 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Set dataScopeDeptIds; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleMenuDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleMenuDO.java new file mode 100644 index 0000000..4978b0e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleMenuDO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.permission; + +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 角色和菜单关联 + * + * @author ruoyi + */ +@TableName("system_role_menu") +@KeySequence("system_role_menu_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class RoleMenuDO extends TenantBaseDO { + + /** + * 自增主键 + */ + @TableId + private Long id; + /** + * 角色ID + */ + private Long roleId; + /** + * 菜单ID + */ + private Long menuId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java new file mode 100644 index 0000000..0101840 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.permission; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 用户和角色关联 + * + * @author ruoyi + */ +@TableName("system_user_role") +@KeySequence("system_user_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +public class UserRoleDO extends BaseDO { + + /** + * 自增主键 + */ + @TableId + private Long id; + /** + * 用户 ID + */ + private Long userId; + /** + * 角色 ID + */ + private Long roleId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsChannelDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsChannelDO.java new file mode 100644 index 0000000..c31aa0a --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsChannelDO.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.sms; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * 短信渠道 DO + * + * @author zzf + * @since 2021-01-25 + */ +@TableName(value = "system_sms_channel", autoResultMap = true) +@KeySequence("system_sms_channel_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@TenantIgnore +public class SmsChannelDO extends BaseDO { + + /** + * 渠道编号 + */ + private Long id; + /** + * 短信签名 + */ + private String signature; + /** + * 渠道编码 + * + * 枚举 {@link SmsChannelEnum} + */ + private String code; + /** + * 启用状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 短信 API 的账号 + */ + private String apiKey; + /** + * 短信 API 的密钥 + */ + private String apiSecret; + /** + * 短信发送回调 URL + */ + private String callbackUrl; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsCodeDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsCodeDO.java new file mode 100644 index 0000000..63c5481 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsCodeDO.java @@ -0,0 +1,67 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.sms; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 手机验证码 DO + * + * idx_mobile 索引:基于 {@link #mobile} 字段 + * + * @author 芋道源码 + */ +@TableName("system_sms_code") +@KeySequence("system_sms_code_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TenantIgnore +public class SmsCodeDO extends BaseDO { + + /** + * 编号 + */ + private Long id; + /** + * 手机号 + */ + private String mobile; + /** + * 验证码 + */ + private String code; + /** + * 发送场景 + * + * 枚举 {@link SmsCodeDO} + */ + private Integer scene; + /** + * 创建 IP + */ + private String createIp; + /** + * 今日发送的第几条 + */ + private Integer todayIndex; + /** + * 是否使用 + */ + private Boolean used; + /** + * 使用时间 + */ + private LocalDateTime usedTime; + /** + * 使用 IP + */ + private String usedIp; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java new file mode 100644 index 0000000..073ebff --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java @@ -0,0 +1,163 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.sms; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum; +import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.Map; + +/** + * 短信日志 DO + * + * @author zzf + * @since 2021-01-25 + */ +@TableName(value = "system_sms_log", autoResultMap = true) +@KeySequence("system_sms_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TenantIgnore +public class SmsLogDO extends BaseDO { + + /** + * 自增编号 + */ + private Long id; + + // ========= 渠道相关字段 ========= + + /** + * 短信渠道编号 + * + * 关联 {@link SmsChannelDO#getId()} + */ + private Long channelId; + /** + * 短信渠道编码 + * + * 冗余 {@link SmsChannelDO#getCode()} + */ + private String channelCode; + + // ========= 模板相关字段 ========= + + /** + * 模板编号 + * + * 关联 {@link SmsTemplateDO#getId()} + */ + private Long templateId; + /** + * 模板编码 + * + * 冗余 {@link SmsTemplateDO#getCode()} + */ + private String templateCode; + /** + * 短信类型 + * + * 冗余 {@link SmsTemplateDO#getType()} + */ + private Integer templateType; + /** + * 基于 {@link SmsTemplateDO#getContent()} 格式化后的内容 + */ + private String templateContent; + /** + * 基于 {@link SmsTemplateDO#getParams()} 输入后的参数 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Map templateParams; + /** + * 短信 API 的模板编号 + * + * 冗余 {@link SmsTemplateDO#getApiTemplateId()} + */ + private String apiTemplateId; + + // ========= 手机相关字段 ========= + + /** + * 手机号 + */ + private String mobile; + /** + * 用户编号 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + + // ========= 发送相关字段 ========= + + /** + * 发送状态 + * + * 枚举 {@link SmsSendStatusEnum} + */ + private Integer sendStatus; + /** + * 发送时间 + */ + private LocalDateTime sendTime; + /** + * 短信 API 发送结果的编码 + * + * 由于第三方的错误码可能是字符串,所以使用 String 类型 + */ + private String apiSendCode; + /** + * 短信 API 发送失败的提示 + */ + private String apiSendMsg; + /** + * 短信 API 发送返回的唯一请求 ID + * + * 用于和短信 API 进行定位于排错 + */ + private String apiRequestId; + /** + * 短信 API 发送返回的序号 + * + * 用于和短信 API 平台的发送记录关联 + */ + private String apiSerialNo; + + // ========= 接收相关字段 ========= + + /** + * 接收状态 + * + * 枚举 {@link SmsReceiveStatusEnum} + */ + private Integer receiveStatus; + /** + * 接收时间 + */ + private LocalDateTime receiveTime; + /** + * 短信 API 接收结果的编码 + */ + private String apiReceiveCode; + /** + * 短信 API 接收结果的提示 + */ + private String apiReceiveMsg; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsTemplateDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsTemplateDO.java new file mode 100644 index 0000000..450a0b9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsTemplateDO.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.sms; + +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; + +/** + * 短信模板 DO + * + * @author zzf + * @since 2021-01-25 + */ +@TableName(value = "system_sms_template", autoResultMap = true) +@KeySequence("system_sms_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@TenantIgnore +public class SmsTemplateDO extends BaseDO { + + /** + * 自增编号 + */ + private Long id; + + // ========= 模板相关字段 ========= + + /** + * 短信类型 + * + * 枚举 {@link SmsTemplateTypeEnum} + */ + private Integer type; + /** + * 启用状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 模板编码,保证唯一 + */ + private String code; + /** + * 模板名称 + */ + private String name; + /** + * 模板内容 + * + * 内容的参数,使用 {} 包括,例如说 {name} + */ + private String content; + /** + * 参数数组(自动根据内容生成) + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List params; + /** + * 备注 + */ + private String remark; + /** + * 短信 API 的模板编号 + */ + private String apiTemplateId; + + // ========= 渠道相关字段 ========= + + /** + * 短信渠道编号 + * + * 关联 {@link SmsChannelDO#getId()} + */ + private Long channelId; + /** + * 短信渠道编码 + * + * 冗余 {@link SmsChannelDO#getCode()} + */ + private String channelCode; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialClientDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialClientDO.java new file mode 100644 index 0000000..5449106 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialClientDO.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.social; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; +import me.zhyd.oauth.config.AuthConfig; + +/** + * 社交客户端 DO + * + * 对应 {@link AuthConfig} 配置,满足不同租户,有自己的客户端配置,实现社交(三方)登录 + * + * @author 芋道源码 + */ +@TableName(value = "system_social_client", autoResultMap = true) +@KeySequence("system_social_client_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SocialClientDO extends TenantBaseDO { + + /** + * 编号,自增 + */ + @TableId + private Long id; + /** + * 应用名 + */ + private String name; + /** + * 社交类型 + * + * 枚举 {@link SocialTypeEnum} + */ + private Integer socialType; + /** + * 用户类型 + * + * 目的:不同用户类型,对应不同的小程序,需要自己的配置 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + + /** + * 客户端 id + */ + private String clientId; + /** + * 客户端 Secret + */ + private String clientSecret; + + /** + * 代理编号 + * + * 目前只有部分“社交类型”在使用: + * 1. 企业微信:对应授权方的网页应用 ID + */ + private String agentId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java new file mode 100644 index 0000000..0f4e41f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.social; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 社交用户的绑定 + * 即 {@link SocialUserDO} 与 UserDO 的关联表 + * + * @author 芋道源码 + */ +@TableName(value = "system_social_user_bind", autoResultMap = true) +@KeySequence("system_social_user_bind_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SocialUserBindDO extends BaseDO { + + /** + * 编号 + */ + @TableId + private Long id; + /** + * 关联的用户编号 + * + * 关联 UserDO 的编号 + */ + private Long userId; + /** + * 用户类型 + * + * 枚举 {@link UserTypeEnum} + */ + private Integer userType; + + /** + * 社交平台的用户编号 + * + * 关联 {@link SocialUserDO#getId()} + */ + private Long socialUserId; + /** + * 社交平台的类型 + * + * 冗余 {@link SocialUserDO#getType()} + */ + private Integer socialType; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java new file mode 100644 index 0000000..f3adf03 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.social; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 社交(三方)用户 + * + * @author weir + */ +@TableName(value = "system_social_user", autoResultMap = true) +@KeySequence("system_social_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class SocialUserDO extends BaseDO { + + /** + * 自增主键 + */ + @TableId + private Long id; + /** + * 社交平台的类型 + * + * 枚举 {@link SocialTypeEnum} + */ + private Integer type; + + /** + * 社交 openid + */ + private String openid; + /** + * 社交 token + */ + private String token; + /** + * 原始 Token 数据,一般是 JSON 格式 + */ + private String rawTokenInfo; + + /** + * 用户昵称 + */ + private String nickname; + /** + * 用户头像 + */ + private String avatar; + /** + * 原始用户数据,一般是 JSON 格式 + */ + private String rawUserInfo; + + /** + * 最后一次的认证 code + */ + private String code; + /** + * 最后一次的认证 state + */ + private String state; + +} + + diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantDO.java new file mode 100644 index 0000000..6e60f61 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantDO.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.tenant; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +import java.time.LocalDateTime; + +/** + * 租户 DO + * + * @author 芋道源码 + */ +@TableName(value = "system_tenant", autoResultMap = true) +@KeySequence("system_tenant_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TenantIgnore +public class TenantDO extends BaseDO { + + /** + * 套餐编号 - 系统 + */ + public static final Long PACKAGE_ID_SYSTEM = 0L; + + /** + * 租户编号,自增 + */ + private Long id; + /** + * 租户名,唯一 + */ + private String name; + /** + * 联系人的用户编号 + * + * 关联 {@link AdminUserDO#getId()} + */ + private Long contactUserId; + /** + * 联系人 + */ + private String contactName; + /** + * 联系手机 + */ + private String contactMobile; + /** + * 租户状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 绑定域名 + */ + private String website; + /** + * 租户套餐编号 + * + * 关联 {@link TenantPackageDO#getId()} + * 特殊逻辑:系统内置租户,不使用套餐,暂时使用 {@link #PACKAGE_ID_SYSTEM} 标识 + */ + private Long packageId; + /** + * 过期时间 + */ + private LocalDateTime expireTime; + /** + * 账号数量 + */ + private Integer accountCount; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantPackageDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantPackageDO.java new file mode 100644 index 0000000..159a838 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantPackageDO.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.tenant; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; + +import java.util.Set; + +/** + * 租户套餐 DO + * + * @author 芋道源码 + */ +@TableName(value = "system_tenant_package", autoResultMap = true) +@KeySequence("system_tenant_package_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@AllArgsConstructor +@NoArgsConstructor +@TenantIgnore +public class TenantPackageDO extends BaseDO { + + /** + * 套餐编号,自增 + */ + private Long id; + /** + * 套餐名,唯一 + */ + private String name; + /** + * 租户套餐状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 关联的菜单编号 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Set menuIds; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java new file mode 100644 index 0000000..2f07a30 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java @@ -0,0 +1,96 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.user; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import cn.iocoder.yudao.module.system.enums.common.SexEnum; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.*; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +import java.time.LocalDateTime; +import java.util.Set; + +/** + * 管理后台的用户 DO + * + * @author 芋道源码 + */ +@TableName(value = "system_users", autoResultMap = true) // 由于 SQL Server 的 system_user 是关键字,所以使用 system_users +@KeySequence("system_users_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AdminUserDO extends TenantBaseDO { + + /** + * 用户ID + */ + @TableId + private Long id; + /** + * 用户账号 + */ + private String username; + /** + * 加密后的密码 + * + * 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐 + */ + private String password; + /** + * 用户昵称 + */ + private String nickname; + /** + * 备注 + */ + private String remark; + /** + * 部门 ID + */ + private Long deptId; + /** + * 岗位编号数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Set postIds; + /** + * 用户邮箱 + */ + private String email; + /** + * 手机号码 + */ + private String mobile; + /** + * 用户性别 + * + * 枚举类 {@link SexEnum} + */ + private Integer sex; + /** + * 用户头像 + */ + private String avatar; + /** + * 帐号状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 最后登录IP + */ + private String loginIp; + /** + * 最后登录时间 + */ + private LocalDateTime loginDate; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java new file mode 100644 index 0000000..a09fcf7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.system.dal.mysql.dept; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface DeptMapper extends BaseMapperX { + + default List selectList(DeptListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(DeptDO::getName, reqVO.getName()) + .eqIfPresent(DeptDO::getStatus, reqVO.getStatus())); + } + + default DeptDO selectByParentIdAndName(Long parentId, String name) { + return selectOne(DeptDO::getParentId, parentId, DeptDO::getName, name); + } + + default Long selectCountByParentId(Long parentId) { + return selectCount(DeptDO::getParentId, parentId); + } + + default List selectListByParentId(Collection parentIds) { + return selectList(DeptDO::getParentId, parentIds); + } + + default List selectListByLeaderUserId(Long id) { + return selectList(DeptDO::getLeaderUserId, id); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/PostMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/PostMapper.java new file mode 100644 index 0000000..e05c38c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/PostMapper.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.system.dal.mysql.dept; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface PostMapper extends BaseMapperX { + + default List selectList(Collection ids, Collection statuses) { + return selectList(new LambdaQueryWrapperX() + .inIfPresent(PostDO::getId, ids) + .inIfPresent(PostDO::getStatus, statuses)); + } + + default PageResult selectPage(PostPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(PostDO::getCode, reqVO.getCode()) + .likeIfPresent(PostDO::getName, reqVO.getName()) + .eqIfPresent(PostDO::getStatus, reqVO.getStatus()) + .orderByDesc(PostDO::getId)); + } + + default PostDO selectByName(String name) { + return selectOne(PostDO::getName, name); + } + + default PostDO selectByCode(String code) { + return selectOne(PostDO::getCode, code); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/UserPostMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/UserPostMapper.java new file mode 100644 index 0000000..addc67d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/UserPostMapper.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.system.dal.mysql.dept; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface UserPostMapper extends BaseMapperX { + + default List selectListByUserId(Long userId) { + return selectList(UserPostDO::getUserId, userId); + } + + default void deleteByUserIdAndPostId(Long userId, Collection postIds) { + delete(new LambdaQueryWrapperX() + .eq(UserPostDO::getUserId, userId) + .in(UserPostDO::getPostId, postIds)); + } + + default List selectListByPostIds(Collection postIds) { + return selectList(UserPostDO::getPostId, postIds); + } + + default void deleteByUserId(Long userId) { + delete(Wrappers.lambdaUpdate(UserPostDO.class).eq(UserPostDO::getUserId, userId)); + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java new file mode 100644 index 0000000..87a05c6 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.system.dal.mysql.dict; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +@Mapper +public interface DictDataMapper extends BaseMapperX { + + default DictDataDO selectByDictTypeAndValue(String dictType, String value) { + return selectOne(DictDataDO::getDictType, dictType, DictDataDO::getValue, value); + } + + default DictDataDO selectByDictTypeAndLabel(String dictType, String label) { + return selectOne(DictDataDO::getDictType, dictType, DictDataDO::getLabel, label); + } + + default List selectByDictTypeAndValues(String dictType, Collection values) { + return selectList(new LambdaQueryWrapper().eq(DictDataDO::getDictType, dictType) + .in(DictDataDO::getValue, values)); + } + + default long selectCountByDictType(String dictType) { + return selectCount(DictDataDO::getDictType, dictType); + } + + default PageResult selectPage(DictDataPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(DictDataDO::getLabel, reqVO.getLabel()) + .eqIfPresent(DictDataDO::getDictType, reqVO.getDictType()) + .eqIfPresent(DictDataDO::getStatus, reqVO.getStatus()) + .orderByDesc(Arrays.asList(DictDataDO::getDictType, DictDataDO::getSort))); + } + + default List selectListByStatusAndDictType(Integer status, String dictType) { + return selectList(new LambdaQueryWrapperX() + .eqIfPresent(DictDataDO::getStatus, status) + .eqIfPresent(DictDataDO::getDictType, dictType)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictTypeMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictTypeMapper.java new file mode 100644 index 0000000..52393a9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictTypeMapper.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.system.dal.mysql.dict; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Update; + +import java.time.LocalDateTime; + +@Mapper +public interface DictTypeMapper extends BaseMapperX { + + default PageResult selectPage(DictTypePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(DictTypeDO::getName, reqVO.getName()) + .likeIfPresent(DictTypeDO::getType, reqVO.getType()) + .eqIfPresent(DictTypeDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(DictTypeDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(DictTypeDO::getId)); + } + + default DictTypeDO selectByType(String type) { + return selectOne(DictTypeDO::getType, type); + } + + default DictTypeDO selectByName(String name) { + return selectOne(DictTypeDO::getName, name); + } + + @Update("UPDATE system_dict_type SET deleted = 1, deleted_time = #{deletedTime} WHERE id = #{id}") + void updateToDelete(@Param("id") Long id, @Param("deletedTime") LocalDateTime deletedTime); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/LoginLogMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/LoginLogMapper.java new file mode 100644 index 0000000..0cb47eb --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/LoginLogMapper.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.system.dal.mysql.logger; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; +import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface LoginLogMapper extends BaseMapperX { + + default PageResult selectPage(LoginLogPageReqVO reqVO) { + LambdaQueryWrapperX query = new LambdaQueryWrapperX() + .likeIfPresent(LoginLogDO::getUserIp, reqVO.getUserIp()) + .likeIfPresent(LoginLogDO::getUsername, reqVO.getUsername()) + .betweenIfPresent(LoginLogDO::getCreateTime, reqVO.getCreateTime()); + if (Boolean.TRUE.equals(reqVO.getStatus())) { + query.eq(LoginLogDO::getResult, LoginResultEnum.SUCCESS.getResult()); + } else if (Boolean.FALSE.equals(reqVO.getStatus())) { + query.gt(LoginLogDO::getResult, LoginResultEnum.SUCCESS.getResult()); + } + query.orderByDesc(LoginLogDO::getId); // 降序 + return selectPage(reqVO, query); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java new file mode 100644 index 0000000..14e308b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.dal.mysql.logger; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface OperateLogMapper extends BaseMapperX { + + default PageResult selectPage(OperateLogPageReqVO pageReqDTO) { + return selectPage(pageReqDTO, new LambdaQueryWrapperX() + .eqIfPresent(OperateLogDO::getUserId, pageReqDTO.getUserId()) + .eqIfPresent(OperateLogDO::getBizId, pageReqDTO.getBizId()) + .likeIfPresent(OperateLogDO::getType, pageReqDTO.getType()) + .likeIfPresent(OperateLogDO::getSubType, pageReqDTO.getSubType()) + .likeIfPresent(OperateLogDO::getAction, pageReqDTO.getAction()) + .betweenIfPresent(OperateLogDO::getCreateTime, pageReqDTO.getCreateTime()) + .orderByDesc(OperateLogDO::getId)); + } + + default PageResult selectPage(OperateLogPageReqDTO pageReqDTO) { + return selectPage(pageReqDTO, new LambdaQueryWrapperX() + .eqIfPresent(OperateLogDO::getType, pageReqDTO.getType()) + .eqIfPresent(OperateLogDO::getBizId, pageReqDTO.getBizId()) + .eqIfPresent(OperateLogDO::getUserId, pageReqDTO.getUserId()) + .orderByDesc(OperateLogDO::getId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java new file mode 100644 index 0000000..f9a9d7d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.system.dal.mysql.mail; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MailAccountMapper extends BaseMapperX { + + default PageResult selectPage(MailAccountPageReqVO pageReqVO) { + return selectPage(pageReqVO, new LambdaQueryWrapperX() + .likeIfPresent(MailAccountDO::getMail, pageReqVO.getMail()) + .likeIfPresent(MailAccountDO::getUsername , pageReqVO.getUsername()) + .orderByDesc(MailAccountDO::getId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java new file mode 100644 index 0000000..6b147cf --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.system.dal.mysql.mail; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MailLogMapper extends BaseMapperX { + + default PageResult selectPage(MailLogPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(MailLogDO::getUserId, reqVO.getUserId()) + .eqIfPresent(MailLogDO::getUserType, reqVO.getUserType()) + .likeIfPresent(MailLogDO::getToMail, reqVO.getToMail()) + .eqIfPresent(MailLogDO::getAccountId, reqVO.getAccountId()) + .eqIfPresent(MailLogDO::getTemplateId, reqVO.getTemplateId()) + .eqIfPresent(MailLogDO::getSendStatus, reqVO.getSendStatus()) + .betweenIfPresent(MailLogDO::getSendTime, reqVO.getSendTime()) + .orderByDesc(MailLogDO::getId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java new file mode 100644 index 0000000..1730e58 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.system.dal.mysql.mail; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MailTemplateMapper extends BaseMapperX { + + default PageResult selectPage(MailTemplatePageReqVO pageReqVO){ + return selectPage(pageReqVO , new LambdaQueryWrapperX() + .eqIfPresent(MailTemplateDO::getStatus, pageReqVO.getStatus()) + .likeIfPresent(MailTemplateDO::getCode, pageReqVO.getCode()) + .likeIfPresent(MailTemplateDO::getName, pageReqVO.getName()) + .eqIfPresent(MailTemplateDO::getAccountId, pageReqVO.getAccountId()) + .betweenIfPresent(MailTemplateDO::getCreateTime, pageReqVO.getCreateTime()) + .orderByDesc(MailTemplateDO::getId)); + } + + default Long selectCountByAccountId(Long accountId) { + return selectCount(MailTemplateDO::getAccountId, accountId); + } + + default MailTemplateDO selectByCode(String code) { + return selectOne(MailTemplateDO::getCode, code); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notice/NoticeMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notice/NoticeMapper.java new file mode 100644 index 0000000..67ff8db --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notice/NoticeMapper.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.system.dal.mysql.notice; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface NoticeMapper extends BaseMapperX { + + default PageResult selectPage(NoticePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(NoticeDO::getTitle, reqVO.getTitle()) + .eqIfPresent(NoticeDO::getStatus, reqVO.getStatus()) + .orderByDesc(NoticeDO::getId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java new file mode 100644 index 0000000..e9ce6d7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.system.dal.mysql.notify; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; + +@Mapper +public interface NotifyMessageMapper extends BaseMapperX { + + default PageResult selectPage(NotifyMessagePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(NotifyMessageDO::getUserId, reqVO.getUserId()) + .eqIfPresent(NotifyMessageDO::getUserType, reqVO.getUserType()) + .likeIfPresent(NotifyMessageDO::getTemplateCode, reqVO.getTemplateCode()) + .eqIfPresent(NotifyMessageDO::getTemplateType, reqVO.getTemplateType()) + .betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(NotifyMessageDO::getId)); + } + + default PageResult selectPage(NotifyMessageMyPageReqVO reqVO, Long userId, Integer userType) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(NotifyMessageDO::getReadStatus, reqVO.getReadStatus()) + .betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime()) + .eq(NotifyMessageDO::getUserId, userId) + .eq(NotifyMessageDO::getUserType, userType) + .orderByDesc(NotifyMessageDO::getId)); + } + + default int updateListRead(Collection ids, Long userId, Integer userType) { + return update(new NotifyMessageDO().setReadStatus(true).setReadTime(LocalDateTime.now()), + new LambdaQueryWrapperX() + .in(NotifyMessageDO::getId, ids) + .eq(NotifyMessageDO::getUserId, userId) + .eq(NotifyMessageDO::getUserType, userType) + .eq(NotifyMessageDO::getReadStatus, false)); + } + + default int updateListRead(Long userId, Integer userType) { + return update(new NotifyMessageDO().setReadStatus(true).setReadTime(LocalDateTime.now()), + new LambdaQueryWrapperX() + .eq(NotifyMessageDO::getUserId, userId) + .eq(NotifyMessageDO::getUserType, userType) + .eq(NotifyMessageDO::getReadStatus, false)); + } + + default List selectUnreadListByUserIdAndUserType(Long userId, Integer userType, Integer size) { + return selectList(new QueryWrapperX() // 由于要使用 limitN 语句,所以只能用 QueryWrapperX + .eq("user_id", userId) + .eq("user_type", userType) + .eq("read_status", false) + .orderByDesc("id").limitN(size)); + } + + default Long selectUnreadCountByUserIdAndUserType(Long userId, Integer userType) { + return selectCount(new LambdaQueryWrapperX() + .eq(NotifyMessageDO::getReadStatus, false) + .eq(NotifyMessageDO::getUserId, userId) + .eq(NotifyMessageDO::getUserType, userType)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java new file mode 100644 index 0000000..1fcb8ee --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.dal.mysql.notify; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface NotifyTemplateMapper extends BaseMapperX { + + default NotifyTemplateDO selectByCode(String code) { + return selectOne(NotifyTemplateDO::getCode, code); + } + + default PageResult selectPage(NotifyTemplatePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(NotifyTemplateDO::getCode, reqVO.getCode()) + .likeIfPresent(NotifyTemplateDO::getName, reqVO.getName()) + .eqIfPresent(NotifyTemplateDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(NotifyTemplateDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(NotifyTemplateDO::getId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java new file mode 100644 index 0000000..81ca13f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import org.apache.ibatis.annotations.Mapper; + +import java.time.LocalDateTime; +import java.util.List; + +@Mapper +public interface OAuth2AccessTokenMapper extends BaseMapperX { + + @TenantIgnore // 获取 token 的时候,需要忽略租户编号。原因是:一些场景下,可能不会传递 tenant-id 请求头,例如说文件上传、积木报表等等 + default OAuth2AccessTokenDO selectByAccessToken(String accessToken) { + return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken); + } + + default List selectListByRefreshToken(String refreshToken) { + return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken); + } + + default PageResult selectPage(OAuth2AccessTokenPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(OAuth2AccessTokenDO::getUserId, reqVO.getUserId()) + .eqIfPresent(OAuth2AccessTokenDO::getUserType, reqVO.getUserType()) + .likeIfPresent(OAuth2AccessTokenDO::getClientId, reqVO.getClientId()) + .gt(OAuth2AccessTokenDO::getExpiresTime, LocalDateTime.now()) + .orderByDesc(OAuth2AccessTokenDO::getId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java new file mode 100644 index 0000000..61a976f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface OAuth2ApproveMapper extends BaseMapperX { + + default int update(OAuth2ApproveDO updateObj) { + return update(updateObj, new LambdaQueryWrapperX() + .eq(OAuth2ApproveDO::getUserId, updateObj.getUserId()) + .eq(OAuth2ApproveDO::getUserType, updateObj.getUserType()) + .eq(OAuth2ApproveDO::getClientId, updateObj.getClientId()) + .eq(OAuth2ApproveDO::getScope, updateObj.getScope())); + } + + default List selectListByUserIdAndUserTypeAndClientId(Long userId, Integer userType, String clientId) { + return selectList(new LambdaQueryWrapperX() + .eq(OAuth2ApproveDO::getUserId, userId) + .eq(OAuth2ApproveDO::getUserType, userType) + .eq(OAuth2ApproveDO::getClientId, clientId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java new file mode 100755 index 0000000..e6012b0 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import org.apache.ibatis.annotations.Mapper; + + +/** + * OAuth2 客户端 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface OAuth2ClientMapper extends BaseMapperX { + + default PageResult selectPage(OAuth2ClientPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(OAuth2ClientDO::getName, reqVO.getName()) + .eqIfPresent(OAuth2ClientDO::getStatus, reqVO.getStatus()) + .orderByDesc(OAuth2ClientDO::getId)); + } + + default OAuth2ClientDO selectByClientId(String clientId) { + return selectOne(OAuth2ClientDO::getClientId, clientId); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java new file mode 100644 index 0000000..c85581c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java @@ -0,0 +1,14 @@ +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface OAuth2CodeMapper extends BaseMapperX { + + default OAuth2CodeDO selectByCode(String code) { + return selectOne(OAuth2CodeDO::getCode, code); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java new file mode 100644 index 0000000..bf91457 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.system.dal.mysql.oauth2; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface OAuth2RefreshTokenMapper extends BaseMapperX { + + default int deleteByRefreshToken(String refreshToken) { + return delete(new LambdaQueryWrapperX() + .eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken)); + } + + @TenantIgnore // 获取 token 的时候,需要忽略租户编号。原因是:一些场景下,可能不会传递 tenant-id 请求头,例如说文件上传、积木报表等等 + default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) { + return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/package-info.java new file mode 100644 index 0000000..6ad304f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/package-info.java @@ -0,0 +1,9 @@ +/** + * DAL = Data Access Layer 数据访问层 + * 1. data object:数据对象 + * 2. redis:Redis 的 CRUD 操作 + * 3. mysql:MySQL 的 CRUD 操作 + * + * 其中,MySQL 的表以 system_ 作为前缀 + */ +package cn.iocoder.yudao.module.system.dal.mysql; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java new file mode 100644 index 0000000..31eb117 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.system.dal.mysql.permission; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface MenuMapper extends BaseMapperX { + + default MenuDO selectByParentIdAndName(Long parentId, String name) { + return selectOne(MenuDO::getParentId, parentId, MenuDO::getName, name); + } + + default Long selectCountByParentId(Long parentId) { + return selectCount(MenuDO::getParentId, parentId); + } + + default List selectList(MenuListReqVO reqVO) { + return selectList(new LambdaQueryWrapperX() + .likeIfPresent(MenuDO::getName, reqVO.getName()) + .eqIfPresent(MenuDO::getStatus, reqVO.getStatus())); + } + + default List selectListByPermission(String permission) { + return selectList(MenuDO::getPermission, permission); + } + + default MenuDO selectByComponentName(String componentName) { + return selectOne(MenuDO::getComponentName, componentName); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java new file mode 100644 index 0000000..2e4da2b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.dal.mysql.permission; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.lang.Nullable; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface RoleMapper extends BaseMapperX { + + default PageResult selectPage(RolePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(RoleDO::getName, reqVO.getName()) + .likeIfPresent(RoleDO::getCode, reqVO.getCode()) + .eqIfPresent(RoleDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BaseDO::getCreateTime, reqVO.getCreateTime()) + .orderByAsc(RoleDO::getSort)); + } + + default RoleDO selectByName(String name) { + return selectOne(RoleDO::getName, name); + } + + default RoleDO selectByCode(String code) { + return selectOne(RoleDO::getCode, code); + } + + default List selectListByStatus(@Nullable Collection statuses) { + return selectList(RoleDO::getStatus, statuses); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java new file mode 100644 index 0000000..02cb6b8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.system.dal.mysql.permission; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface RoleMenuMapper extends BaseMapperX { + + default List selectListByRoleId(Long roleId) { + return selectList(RoleMenuDO::getRoleId, roleId); + } + + default List selectListByRoleId(Collection roleIds) { + return selectList(RoleMenuDO::getRoleId, roleIds); + } + + default List selectListByMenuId(Long menuId) { + return selectList(RoleMenuDO::getMenuId, menuId); + } + + default void deleteListByRoleIdAndMenuIds(Long roleId, Collection menuIds) { + delete(new LambdaQueryWrapper() + .eq(RoleMenuDO::getRoleId, roleId) + .in(RoleMenuDO::getMenuId, menuIds)); + } + + default void deleteListByMenuId(Long menuId) { + delete(new LambdaQueryWrapper().eq(RoleMenuDO::getMenuId, menuId)); + } + + default void deleteListByRoleId(Long roleId) { + delete(new LambdaQueryWrapper().eq(RoleMenuDO::getRoleId, roleId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/UserRoleMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/UserRoleMapper.java new file mode 100644 index 0000000..12ff88a --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/UserRoleMapper.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.system.dal.mysql.permission; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface UserRoleMapper extends BaseMapperX { + + default List selectListByUserId(Long userId) { + return selectList(UserRoleDO::getUserId, userId); + } + + default void deleteListByUserIdAndRoleIdIds(Long userId, Collection roleIds) { + delete(new LambdaQueryWrapper() + .eq(UserRoleDO::getUserId, userId) + .in(UserRoleDO::getRoleId, roleIds)); + } + + default void deleteListByUserId(Long userId) { + delete(new LambdaQueryWrapper().eq(UserRoleDO::getUserId, userId)); + } + + default void deleteListByRoleId(Long roleId) { + delete(new LambdaQueryWrapper().eq(UserRoleDO::getRoleId, roleId)); + } + + default List selectListByRoleIds(Collection roleIds) { + return selectList(UserRoleDO::getRoleId, roleIds); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java new file mode 100644 index 0000000..1f356ed --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.system.dal.mysql.sms; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SmsChannelMapper extends BaseMapperX { + + default PageResult selectPage(SmsChannelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(SmsChannelDO::getSignature, reqVO.getSignature()) + .eqIfPresent(SmsChannelDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(SmsChannelDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(SmsChannelDO::getId)); + } + + default SmsChannelDO selectByCode(String code) { + return selectOne(SmsChannelDO::getCode, code); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java new file mode 100644 index 0000000..599c1ab --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.system.dal.mysql.sms; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SmsCodeMapper extends BaseMapperX { + + /** + * 获得手机号的最后一个手机验证码 + * + * @param mobile 手机号 + * @param scene 发送场景,选填 + * @param code 验证码 选填 + * @return 手机验证码 + */ + default SmsCodeDO selectLastByMobile(String mobile, String code, Integer scene) { + return selectOne(new QueryWrapperX() + .eq("mobile", mobile) + .eqIfPresent("scene", scene) + .eqIfPresent("code", code) + .orderByDesc("id") + .limitN(1)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsLogMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsLogMapper.java new file mode 100644 index 0000000..f238871 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsLogMapper.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.system.dal.mysql.sms; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SmsLogMapper extends BaseMapperX { + + default PageResult selectPage(SmsLogPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(SmsLogDO::getChannelId, reqVO.getChannelId()) + .eqIfPresent(SmsLogDO::getTemplateId, reqVO.getTemplateId()) + .likeIfPresent(SmsLogDO::getMobile, reqVO.getMobile()) + .eqIfPresent(SmsLogDO::getSendStatus, reqVO.getSendStatus()) + .betweenIfPresent(SmsLogDO::getSendTime, reqVO.getSendTime()) + .eqIfPresent(SmsLogDO::getReceiveStatus, reqVO.getReceiveStatus()) + .betweenIfPresent(SmsLogDO::getReceiveTime, reqVO.getReceiveTime()) + .orderByDesc(SmsLogDO::getId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsTemplateMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsTemplateMapper.java new file mode 100644 index 0000000..a9a1ebb --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsTemplateMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.dal.mysql.sms; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SmsTemplateMapper extends BaseMapperX { + + default SmsTemplateDO selectByCode(String code) { + return selectOne(SmsTemplateDO::getCode, code); + } + + default PageResult selectPage(SmsTemplatePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(SmsTemplateDO::getType, reqVO.getType()) + .eqIfPresent(SmsTemplateDO::getStatus, reqVO.getStatus()) + .likeIfPresent(SmsTemplateDO::getCode, reqVO.getCode()) + .likeIfPresent(SmsTemplateDO::getContent, reqVO.getContent()) + .likeIfPresent(SmsTemplateDO::getApiTemplateId, reqVO.getApiTemplateId()) + .eqIfPresent(SmsTemplateDO::getChannelId, reqVO.getChannelId()) + .betweenIfPresent(SmsTemplateDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(SmsTemplateDO::getId)); + } + + default Long selectCountByChannelId(Long channelId) { + return selectCount(SmsTemplateDO::getChannelId, channelId); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialClientMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialClientMapper.java new file mode 100644 index 0000000..4ebe226 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialClientMapper.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.system.dal.mysql.social; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SocialClientMapper extends BaseMapperX { + + default SocialClientDO selectBySocialTypeAndUserType(Integer socialType, Integer userType) { + return selectOne(SocialClientDO::getSocialType, socialType, + SocialClientDO::getUserType, userType); + } + + default PageResult selectPage(SocialClientPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(SocialClientDO::getName, reqVO.getName()) + .eqIfPresent(SocialClientDO::getSocialType, reqVO.getSocialType()) + .eqIfPresent(SocialClientDO::getUserType, reqVO.getUserType()) + .likeIfPresent(SocialClientDO::getClientId, reqVO.getClientId()) + .eqIfPresent(SocialClientDO::getStatus, reqVO.getStatus()) + .orderByDesc(SocialClientDO::getId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java new file mode 100644 index 0000000..d9957f7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.system.dal.mysql.social; + +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface SocialUserBindMapper extends BaseMapperX { + + default void deleteByUserTypeAndUserIdAndSocialType(Integer userType, Long userId, Integer socialType) { + delete(new LambdaQueryWrapperX() + .eq(SocialUserBindDO::getUserType, userType) + .eq(SocialUserBindDO::getUserId, userId) + .eq(SocialUserBindDO::getSocialType, socialType)); + } + + default void deleteByUserTypeAndSocialUserId(Integer userType, Long socialUserId) { + delete(new LambdaQueryWrapperX() + .eq(SocialUserBindDO::getUserType, userType) + .eq(SocialUserBindDO::getSocialUserId, socialUserId)); + } + + default SocialUserBindDO selectByUserTypeAndSocialUserId(Integer userType, Long socialUserId) { + return selectOne(SocialUserBindDO::getUserType, userType, + SocialUserBindDO::getSocialUserId, socialUserId); + } + + default List selectListByUserIdAndUserType(Long userId, Integer userType) { + return selectList(new LambdaQueryWrapperX() + .eq(SocialUserBindDO::getUserId, userId) + .eq(SocialUserBindDO::getUserType, userType)); + } + + default SocialUserBindDO selectByUserIdAndUserTypeAndSocialType(Long userId, Integer userType, Integer socialType) { + return selectOne(new LambdaQueryWrapperX() + .eq(SocialUserBindDO::getUserId, userId) + .eq(SocialUserBindDO::getUserType, userType) + .eq(SocialUserBindDO::getSocialType, socialType)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java new file mode 100644 index 0000000..a90e6ac --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.dal.mysql.social; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface SocialUserMapper extends BaseMapperX { + + default SocialUserDO selectByTypeAndCodeAnState(Integer type, String code, String state) { + return selectOne(SocialUserDO::getType, type, + SocialUserDO::getCode, code, + SocialUserDO::getState, state); + } + + default SocialUserDO selectByTypeAndOpenid(Integer type, String openid) { + return selectFirstOne(SocialUserDO::getType, type, + SocialUserDO::getOpenid, openid); + } + + default PageResult selectPage(SocialUserPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(SocialUserDO::getType, reqVO.getType()) + .likeIfPresent(SocialUserDO::getNickname, reqVO.getNickname()) + .likeIfPresent(SocialUserDO::getOpenid, reqVO.getOpenid()) + .betweenIfPresent(SocialUserDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(SocialUserDO::getId)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java new file mode 100755 index 0000000..8ddf060 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.system.dal.mysql.tenant; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 租户 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface TenantMapper extends BaseMapperX { + + default PageResult selectPage(TenantPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(TenantDO::getName, reqVO.getName()) + .likeIfPresent(TenantDO::getContactName, reqVO.getContactName()) + .likeIfPresent(TenantDO::getContactMobile, reqVO.getContactMobile()) + .eqIfPresent(TenantDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(TenantDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(TenantDO::getId)); + } + + default TenantDO selectByName(String name) { + return selectOne(TenantDO::getName, name); + } + + default TenantDO selectByWebsite(String website) { + return selectOne(TenantDO::getWebsite, website); + } + + default Long selectCountByPackageId(Long packageId) { + return selectCount(TenantDO::getPackageId, packageId); + } + + default List selectListByPackageId(Long packageId) { + return selectList(TenantDO::getPackageId, packageId); + } + + default List selectListByStatus(Integer status) { + return selectList(TenantDO::getStatus, status); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantPackageMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantPackageMapper.java new file mode 100755 index 0000000..2918617 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantPackageMapper.java @@ -0,0 +1,36 @@ +package cn.iocoder.yudao.module.system.dal.mysql.tenant; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 租户套餐 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface TenantPackageMapper extends BaseMapperX { + + default PageResult selectPage(TenantPackagePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(TenantPackageDO::getName, reqVO.getName()) + .eqIfPresent(TenantPackageDO::getStatus, reqVO.getStatus()) + .likeIfPresent(TenantPackageDO::getRemark, reqVO.getRemark()) + .betweenIfPresent(TenantPackageDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(TenantPackageDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(TenantPackageDO::getStatus, status); + } + + default TenantPackageDO selectByName(String name) { + return selectOne(TenantPackageDO::getName, name); + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java new file mode 100644 index 0000000..c0c9be8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.system.dal.mysql.user; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface AdminUserMapper extends BaseMapperX { + + default AdminUserDO selectByUsername(String username) { + return selectOne(AdminUserDO::getUsername, username); + } + + default AdminUserDO selectByEmail(String email) { + return selectOne(AdminUserDO::getEmail, email); + } + + default AdminUserDO selectByMobile(String mobile) { + return selectOne(AdminUserDO::getMobile, mobile); + } + + default PageResult selectPage(UserPageReqVO reqVO, Collection deptIds, Collection userIds) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AdminUserDO::getUsername, reqVO.getUsername()) + .likeIfPresent(AdminUserDO::getMobile, reqVO.getMobile()) + .eqIfPresent(AdminUserDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(AdminUserDO::getCreateTime, reqVO.getCreateTime()) + .inIfPresent(AdminUserDO::getDeptId, deptIds) + .inIfPresent(AdminUserDO::getId, userIds) + .orderByDesc(AdminUserDO::getId)); + } + + default List selectListByNickname(String nickname) { + return selectList(new LambdaQueryWrapperX().like(AdminUserDO::getNickname, nickname)); + } + + default List selectListByStatus(Integer status) { + return selectList(AdminUserDO::getStatus, status); + } + + default List selectListByDeptIds(Collection deptIds) { + return selectList(AdminUserDO::getDeptId, deptIds); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java new file mode 100644 index 0000000..1cd5830 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.system.dal.redis; + +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; + +/** + * System Redis Key 枚举类 + * + * @author 芋道源码 + */ +public interface RedisKeyConstants { + + /** + * 指定部门的所有子部门编号数组的缓存 + *

+ * KEY 格式:dept_children_ids:{id} + * VALUE 数据类型:String 子部门编号集合 + */ + String DEPT_CHILDREN_ID_LIST = "dept_children_ids"; + + /** + * 角色的缓存 + *

+ * KEY 格式:role:{id} + * VALUE 数据类型:String 角色信息 + */ + String ROLE = "role"; + + /** + * 用户拥有的角色编号的缓存 + *

+ * KEY 格式:user_role_ids:{userId} + * VALUE 数据类型:String 角色编号集合 + */ + String USER_ROLE_ID_LIST = "user_role_ids"; + + /** + * 拥有指定菜单的角色编号的缓存 + *

+ * KEY 格式:user_role_ids:{menuId} + * VALUE 数据类型:String 角色编号集合 + */ + String MENU_ROLE_ID_LIST = "menu_role_ids"; + + /** + * 拥有权限对应的菜单编号数组的缓存 + *

+ * KEY 格式:permission_menu_ids:{permission} + * VALUE 数据类型:String 菜单编号数组 + */ + String PERMISSION_MENU_ID_LIST = "permission_menu_ids"; + + /** + * OAuth2 客户端的缓存 + *

+ * KEY 格式:oauth_client:{id} + * VALUE 数据类型:String 客户端信息 + */ + String OAUTH_CLIENT = "oauth_client"; + + /** + * 访问令牌的缓存 + *

+ * KEY 格式:oauth2_access_token:{token} + * VALUE 数据类型:String 访问令牌信息 {@link OAuth2AccessTokenDO} + *

+ * 由于动态过期时间,使用 RedisTemplate 操作 + */ + String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s"; + + /** + * 站内信模版的缓存 + *

+ * KEY 格式:notify_template:{code} + * VALUE 数据格式:String 模版信息 + */ + String NOTIFY_TEMPLATE = "notify_template"; + + /** + * 邮件账号的缓存 + *

+ * KEY 格式:mail_account:{id} + * VALUE 数据格式:String 账号信息 + */ + String MAIL_ACCOUNT = "mail_account"; + + /** + * 邮件模版的缓存 + *

+ * KEY 格式:mail_template:{code} + * VALUE 数据格式:String 模版信息 + */ + String MAIL_TEMPLATE = "mail_template"; + + /** + * 短信模版的缓存 + *

+ * KEY 格式:sms_template:{id} + * VALUE 数据格式:String 模版信息 + */ + String SMS_TEMPLATE = "sms_template"; + + /** + * 小程序订阅模版的缓存 + * + * KEY 格式:wxa_subscribe_template:{userType} + * VALUE 数据格式 String, 模版信息 + */ + String WXA_SUBSCRIBE_TEMPLATE = "wxa_subscribe_template"; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java new file mode 100644 index 0000000..81501b4 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java @@ -0,0 +1,59 @@ +package cn.iocoder.yudao.module.system.dal.redis.oauth2; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN; + +/** + * {@link OAuth2AccessTokenDO} 的 RedisDAO + * + * @author 芋道源码 + */ +@Repository +public class OAuth2AccessTokenRedisDAO { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + public OAuth2AccessTokenDO get(String accessToken) { + String redisKey = formatKey(accessToken); + return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class); + } + + public void set(OAuth2AccessTokenDO accessTokenDO) { + String redisKey = formatKey(accessTokenDO.getAccessToken()); + // 清理多余字段,避免缓存 + accessTokenDO.setUpdater(null).setUpdateTime(null).setCreateTime(null).setCreator(null).setDeleted(null); + long time = LocalDateTimeUtil.between(LocalDateTime.now(), accessTokenDO.getExpiresTime(), ChronoUnit.SECONDS); + if (time > 0) { + stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(accessTokenDO), time, TimeUnit.SECONDS); + } + } + + public void delete(String accessToken) { + String redisKey = formatKey(accessToken); + stringRedisTemplate.delete(redisKey); + } + + public void deleteList(Collection accessTokens) { + List redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey); + stringRedisTemplate.delete(redisKeys); + } + + private static String formatKey(String accessToken) { + return String.format(OAUTH2_ACCESS_TOKEN, accessToken); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java new file mode 100644 index 0000000..d7592c3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.enums; + +/** + * System 字典类型的枚举类 + * + * @author 芋道源码 + */ +public interface DictTypeConstants { + + String USER_TYPE = "user_type"; // 用户类型 + String COMMON_STATUS = "common_status"; // 系统状态 + + // ========== SYSTEM 模块 ========== + + String USER_SEX = "system_user_sex"; // 用户性别 + String DATA_SCOPE = "system_data_scope"; // 数据范围 + + String LOGIN_TYPE = "system_login_type"; // 登录日志的类型 + String LOGIN_RESULT = "system_login_result"; // 登录结果 + + String SMS_CHANNEL_CODE = "system_sms_channel_code"; // 短信渠道编码 + String SMS_TEMPLATE_TYPE = "system_sms_template_type"; // 短信模板类型 + String SMS_SEND_STATUS = "system_sms_send_status"; // 短信发送状态 + String SMS_RECEIVE_STATUS = "system_sms_receive_status"; // 短信接收状态 + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java new file mode 100644 index 0000000..c89a5dc --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -0,0 +1,171 @@ +package cn.iocoder.yudao.module.system.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * System 错误码枚举类 + * + * system 系统,使用 1-002-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== AUTH 模块 1-002-000-000 ========== + ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_002_000_000, "登录失败,账号密码不正确"); + ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_002_000_001, "登录失败,账号被禁用"); + ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_004, "验证码不正确,原因:{}"); + ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定"); + ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在"); + ErrorCode AUTH_REGISTER_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_008, "验证码不正确,原因:{}"); + + // ========== 菜单模块 1-002-001-000 ========== + ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1_002_001_000, "已经存在该名字的菜单"); + ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1_002_001_001, "父菜单不存在"); + ErrorCode MENU_PARENT_ERROR = new ErrorCode(1_002_001_002, "不能设置自己为父菜单"); + ErrorCode MENU_NOT_EXISTS = new ErrorCode(1_002_001_003, "菜单不存在"); + ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1_002_001_004, "存在子菜单,无法删除"); + ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1_002_001_005, "父菜单的类型必须是目录或者菜单"); + ErrorCode MENU_COMPONENT_NAME_DUPLICATE = new ErrorCode(1_002_001_006, "已经存在该组件名的菜单"); + + // ========== 角色模块 1-002-002-000 ========== + ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1_002_002_000, "角色不存在"); + ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1_002_002_001, "已经存在名为【{}】的角色"); + ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1_002_002_002, "已经存在标识为【{}】的角色"); + ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色"); + ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用"); + ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "标识【{}】不能使用"); + + // ========== 用户模块 1-002-003-000 ========== + ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1_002_003_000, "用户账号已经存在"); + ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1_002_003_001, "手机号已经存在"); + ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1_002_003_002, "邮箱已经存在"); + ErrorCode USER_NOT_EXISTS = new ErrorCode(1_002_003_003, "用户不存在"); + ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_002_003_004, "导入用户数据不能为空!"); + ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1_002_003_005, "用户密码校验失败"); + ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用"); + ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!"); + ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); + ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册"); + ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭"); + + // ========== 部门模块 1-002-004-000 ========== + ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); + ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1_002_004_001,"父级部门不存在"); + ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在"); + ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除"); + ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门"); + ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择"); + ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门"); + + // ========== 岗位模块 1-002-005-000 ========== + ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在"); + ErrorCode POST_NOT_ENABLE = new ErrorCode(1_002_005_001, "岗位({}) 不处于开启状态,不允许选择"); + ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1_002_005_002, "已经存在该名字的岗位"); + ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1_002_005_003, "已经存在该标识的岗位"); + + // ========== 字典类型 1-002-006-000 ========== + ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1_002_006_001, "当前字典类型不存在"); + ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1_002_006_002, "字典类型不处于开启状态,不允许选择"); + ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1_002_006_003, "已经存在该名字的字典类型"); + ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1_002_006_004, "已经存在该类型的字典类型"); + ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1_002_006_005, "无法删除,该字典类型还有字典数据"); + + // ========== 字典数据 1-002-007-000 ========== + ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1_002_007_001, "当前字典数据不存在"); + ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1_002_007_002, "字典数据({})不处于开启状态,不允许选择"); + ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1_002_007_003, "已经存在该值的字典数据"); + + // ========== 通知公告 1-002-008-000 ========== + ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1_002_008_001, "当前通知公告不存在"); + + // ========== 短信渠道 1-002-011-000 ========== + ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1_002_011_000, "短信渠道不存在"); + ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1_002_011_001, "短信渠道不处于开启状态,不允许选择"); + ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1_002_011_002, "无法删除,该短信渠道还有短信模板"); + + // ========== 短信模板 1-002-012-000 ========== + ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在"); + ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板"); + ErrorCode SMS_TEMPLATE_API_ERROR = new ErrorCode(1_002_012_002, "短信 API 模板调用失败,原因是:{}"); + ErrorCode SMS_TEMPLATE_API_AUDIT_CHECKING = new ErrorCode(1_002_012_003, "短信 API 模版无法使用,原因:审批中"); + ErrorCode SMS_TEMPLATE_API_AUDIT_FAIL = new ErrorCode(1_002_012_004, "短信 API 模版无法使用,原因:审批不通过,{}"); + ErrorCode SMS_TEMPLATE_API_NOT_FOUND = new ErrorCode(1_002_012_005, "短信 API 模版无法使用,原因:模版不存在"); + + // ========== 短信发送 1-002-013-000 ========== + ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在"); + ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_013_001, "模板参数({})缺失"); + ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_013_002, "短信模板不存在"); + + // ========== 短信验证码 1-002-014-000 ========== + ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在"); + ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1_002_014_001, "验证码已过期"); + ErrorCode SMS_CODE_USED = new ErrorCode(1_002_014_002, "验证码已使用"); + ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量"); + ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁"); + + // ========== 租户信息 1-002-015-000 ========== + ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在"); + ErrorCode TENANT_DISABLE = new ErrorCode(1_002_015_001, "名字为【{}】的租户已被禁用"); + ErrorCode TENANT_EXPIRE = new ErrorCode(1_002_015_002, "名字为【{}】的租户已过期"); + ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1_002_015_003, "系统租户不能进行修改、删除等操作!"); + ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1_002_015_004, "名字为【{}】的租户已存在"); + ErrorCode TENANT_WEBSITE_DUPLICATE = new ErrorCode(1_002_015_005, "域名为【{}】的租户已存在"); + + // ========== 租户套餐 1-002-016-000 ========== + ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1_002_016_000, "租户套餐不存在"); + ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1_002_016_001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除"); + ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1_002_016_002, "名字为【{}】的租户套餐已被禁用"); + ErrorCode TENANT_PACKAGE_NAME_DUPLICATE = new ErrorCode(1_002_016_003, "已经存在该名字的租户套餐"); + + // ========== 社交用户 1-002-018-000 ========== + ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1_002_018_000, "社交授权失败,原因是:{}"); + ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1_002_018_001, "社交授权失败,找不到对应的用户"); + + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_002_018_200, "获得手机号失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR = new ErrorCode(1_002_018_201, "获得小程序码失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR = new ErrorCode(1_002_018_202, "获得小程序订阅消息模版失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR = new ErrorCode(1_002_018_203, "发送小程序订阅消息失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR = new ErrorCode(1_002_018_204, "上传微信小程序发货信息失败"); + ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR = new ErrorCode(1_002_018_205, "上传微信小程序订单收货信息失败"); + ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_210, "社交客户端不存在"); + ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置"); + + // ========== OAuth2 客户端 1-002-020-000 ========= + ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在"); + ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在"); + ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1_002_020_002, "OAuth2 客户端已禁用"); + ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1_002_020_003, "不支持该授权类型"); + ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1_002_020_004, "授权范围过大"); + ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1_002_020_005, "无效 redirect_uri: {}"); + ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1_002_020_006, "无效 client_secret: {}"); + + // ========== OAuth2 授权 1-002-021-000 ========= + ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1_002_021_000, "client_id 不匹配"); + ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1_002_021_001, "redirect_uri 不匹配"); + ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1_002_021_002, "state 不匹配"); + + // ========== OAuth2 授权 1-002-022-000 ========= + ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在"); + ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1_002_022_001, "code 已过期"); + + // ========== 邮箱账号 1-002-023-000 ========== + ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1_002_023_000, "邮箱账号不存在"); + ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1_002_023_001, "无法删除,该邮箱账号还有邮件模板"); + + // ========== 邮件模版 1-002-024-000 ========== + ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_024_000, "邮件模版不存在"); + ErrorCode MAIL_TEMPLATE_CODE_EXISTS = new ErrorCode(1_002_024_001, "邮件模版 code({}) 已存在"); + + // ========== 邮件发送 1-002-025-000 ========== + ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_025_000, "模板参数({})缺失"); + ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1_002_025_001, "邮箱不存在"); + + // ========== 站内信模版 1-002-026-000 ========== + ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_026_000, "站内信模版不存在"); + ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_026_001, "已经存在编码为【{}】的站内信模板"); + + // ========== 站内信模版 1-002-027-000 ========== + + // ========== 站内信发送 1-002-028-000 ========== + ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失"); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java new file mode 100644 index 0000000..4ee3840 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.enums; + +/** + * System 操作日志枚举 + * 目的:统一管理,也减少 Service 里各种“复杂”字符串 + * + * @author 芋道源码 + */ +public interface LogRecordConstants { + + // ======================= SYSTEM_USER 用户 ======================= + + String SYSTEM_USER_TYPE = "SYSTEM 用户"; + String SYSTEM_USER_CREATE_SUB_TYPE = "创建用户"; + String SYSTEM_USER_CREATE_SUCCESS = "创建了用户【{{#user.nickname}}】"; + String SYSTEM_USER_UPDATE_SUB_TYPE = "更新用户"; + String SYSTEM_USER_UPDATE_SUCCESS = "更新了用户【{{#user.nickname}}】: {_DIFF{#updateReqVO}}"; + String SYSTEM_USER_DELETE_SUB_TYPE = "删除用户"; + String SYSTEM_USER_DELETE_SUCCESS = "删除了用户【{{#user.nickname}}】"; + String SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE = "重置用户密码"; + String SYSTEM_USER_UPDATE_PASSWORD_SUCCESS = "将用户【{{#user.nickname}}】的密码从【{{#user.password}}】重置为【{{#newPassword}}】"; + + // ======================= SYSTEM_ROLE 角色 ======================= + + String SYSTEM_ROLE_TYPE = "SYSTEM 角色"; + String SYSTEM_ROLE_CREATE_SUB_TYPE = "创建角色"; + String SYSTEM_ROLE_CREATE_SUCCESS = "创建了角色【{{#role.name}}】"; + String SYSTEM_ROLE_UPDATE_SUB_TYPE = "更新角色"; + String SYSTEM_ROLE_UPDATE_SUCCESS = "更新了角色【{{#role.name}}】: {_DIFF{#updateReqVO}}"; + String SYSTEM_ROLE_DELETE_SUB_TYPE = "删除角色"; + String SYSTEM_ROLE_DELETE_SUCCESS = "删除了角色【{{#role.name}}】"; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java new file mode 100644 index 0000000..c1e222b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.enums.common; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 性别的枚举值 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum SexEnum { + + /** 男 */ + MALE(1), + /** 女 */ + FEMALE(2), + /* 未知 */ + UNKNOWN(0); + + /** + * 性别 + */ + private final Integer sex; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java new file mode 100644 index 0000000..4c51f91 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.enums.logger; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 登录日志的类型枚举 + */ +@Getter +@AllArgsConstructor +public enum LoginLogTypeEnum { + + LOGIN_USERNAME(100), // 使用账号登录 + LOGIN_SOCIAL(101), // 使用社交登录 + LOGIN_MOBILE(103), // 使用手机登陆 + LOGIN_SMS(104), // 使用短信登陆 + + LOGOUT_SELF(200), // 自己主动登出 + LOGOUT_DELETE(202), // 强制退出 + ; + + /** + * 日志类型 + */ + private final Integer type; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginResultEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginResultEnum.java new file mode 100644 index 0000000..afb92e4 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginResultEnum.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.enums.logger; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 登录结果的枚举类 + */ +@Getter +@AllArgsConstructor +public enum LoginResultEnum { + + SUCCESS(0), // 成功 + BAD_CREDENTIALS(10), // 账号或密码不正确 + USER_DISABLED(20), // 用户被禁用 + CAPTCHA_NOT_FOUND(30), // 图片验证码不存在 + CAPTCHA_CODE_ERROR(31), // 图片验证码不正确 + + ; + + /** + * 结果 + */ + private final Integer result; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java new file mode 100644 index 0000000..7cb16ed --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.system.enums.mail; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 邮件的发送状态枚举 + * + * @author wangjingyi + * @since 2022/4/10 13:39 + */ +@Getter +@AllArgsConstructor +public enum MailSendStatusEnum { + + INIT(0), // 初始化 + SUCCESS(10), // 发送成功 + FAILURE(20), // 发送失败 + IGNORE(30), // 忽略,即不发送 + ; + + private final int status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/notice/NoticeTypeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/notice/NoticeTypeEnum.java new file mode 100644 index 0000000..9a2a3c4 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/notice/NoticeTypeEnum.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.enums.notice; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 通知类型 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum NoticeTypeEnum { + + NOTICE(1), + ANNOUNCEMENT(2); + + /** + * 类型 + */ + private final Integer type; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/notify/NotifyTemplateTypeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/notify/NotifyTemplateTypeEnum.java new file mode 100644 index 0000000..dccfb19 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/notify/NotifyTemplateTypeEnum.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.enums.notify; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 通知模板类型枚举 + * + * @author HUIHUI + */ +@Getter +@AllArgsConstructor +public enum NotifyTemplateTypeEnum { + + /** + * 系统消息 + */ + SYSTEM_MESSAGE(2), + /** + * 通知消息 + */ + NOTIFICATION_MESSAGE(1); + + private final Integer type; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2ClientConstants.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2ClientConstants.java new file mode 100644 index 0000000..a123d57 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2ClientConstants.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.system.enums.oauth2; + +/** + * OAuth2.0 客户端的通用枚举 + * + * @author 芋道源码 + */ +public interface OAuth2ClientConstants { + + String CLIENT_ID_DEFAULT = "default"; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2GrantTypeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2GrantTypeEnum.java new file mode 100644 index 0000000..0b09fe5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2GrantTypeEnum.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.system.enums.oauth2; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * OAuth2 授权类型(模式)的枚举 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum OAuth2GrantTypeEnum { + + PASSWORD("password"), // 密码模式 + AUTHORIZATION_CODE("authorization_code"), // 授权码模式 + IMPLICIT("implicit"), // 简化模式 + CLIENT_CREDENTIALS("client_credentials"), // 客户端模式 + REFRESH_TOKEN("refresh_token"), // 刷新模式 + ; + + private final String grantType; + + public static OAuth2GrantTypeEnum getByGrantType(String grantType) { + return ArrayUtil.firstMatch(o -> o.getGrantType().equals(grantType), values()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java new file mode 100644 index 0000000..3440a17 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.system.enums.permission; + +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 数据范围枚举类 + * + * 用于实现数据级别的权限 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum DataScopeEnum implements ArrayValuable { + + ALL(1), // 全部数据权限 + + DEPT_CUSTOM(2), // 指定部门数据权限 + DEPT_ONLY(3), // 部门数据权限 + DEPT_AND_CHILD(4), // 部门及以下数据权限 + + SELF(5); // 仅本人数据权限 + + /** + * 范围 + */ + private final Integer scope; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java new file mode 100644 index 0000000..575a33c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.system.enums.permission; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 菜单类型枚举类 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum MenuTypeEnum { + + DIR(1), // 目录 + MENU(2), // 菜单 + BUTTON(3) // 按钮 + ; + + /** + * 类型 + */ + private final Integer type; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleCodeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleCodeEnum.java new file mode 100644 index 0000000..cdf9a00 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleCodeEnum.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.system.enums.permission; + +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 角色标识枚举 + */ +@Getter +@AllArgsConstructor +public enum RoleCodeEnum { + + SUPER_ADMIN("super_admin", "超级管理员"), + TENANT_ADMIN("tenant_admin", "租户管理员"), + CRM_ADMIN("crm_admin", "CRM 管理员"); // CRM 系统专用 + ; + + /** + * 角色编码 + */ + private final String code; + /** + * 名字 + */ + private final String name; + + public static boolean isSuperAdmin(String code) { + return ObjectUtils.equalsAny(code, SUPER_ADMIN.getCode()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleTypeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleTypeEnum.java new file mode 100644 index 0000000..1607b20 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleTypeEnum.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.enums.permission; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum RoleTypeEnum { + + /** + * 内置角色 + */ + SYSTEM(1), + /** + * 自定义角色 + */ + CUSTOM(2); + + private final Integer type; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsReceiveStatusEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsReceiveStatusEnum.java new file mode 100644 index 0000000..581ee6f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsReceiveStatusEnum.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.enums.sms; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 短信的接收状态枚举 + * + * @author 芋道源码 + * @date 2021/2/1 13:39 + */ +@Getter +@AllArgsConstructor +public enum SmsReceiveStatusEnum { + + INIT(0), // 初始化 + SUCCESS(10), // 接收成功 + FAILURE(20), // 接收失败 + ; + + private final int status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java new file mode 100644 index 0000000..b8114ef --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java @@ -0,0 +1,53 @@ +package cn.iocoder.yudao.module.system.enums.sms; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 用户短信验证码发送场景的枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum SmsSceneEnum implements ArrayValuable { + + MEMBER_LOGIN(1, "user-sms-login", "会员用户 - 手机号登陆"), + MEMBER_UPDATE_MOBILE(2, "user-update-mobile", "会员用户 - 修改手机"), + MEMBER_UPDATE_PASSWORD(3, "user-update-password", "会员用户 - 修改密码"), + MEMBER_RESET_PASSWORD(4, "user-reset-password", "会员用户 - 忘记密码"), + + ADMIN_MEMBER_LOGIN(21, "admin-sms-login", "后台用户 - 手机号登录"), + ADMIN_MEMBER_REGISTER(22, "admin-sms-register", "后台用户 - 手机号注册"), + ADMIN_MEMBER_RESET_PASSWORD(23, "admin-reset-password", "后台用户 - 忘记密码"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(SmsSceneEnum::getScene).toArray(Integer[]::new); + + /** + * 验证场景的编号 + */ + private final Integer scene; + /** + * 模版编码 + */ + private final String templateCode; + /** + * 描述 + */ + private final String description; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static SmsSceneEnum getCodeByScene(Integer scene) { + return ArrayUtil.firstMatch(sceneEnum -> sceneEnum.getScene().equals(scene), + values()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSendStatusEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSendStatusEnum.java new file mode 100644 index 0000000..307c9f6 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSendStatusEnum.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.system.enums.sms; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 短信的发送状态枚举 + * + * @author zzf + * @date 2021/2/1 13:39 + */ +@Getter +@AllArgsConstructor +public enum SmsSendStatusEnum { + + INIT(0), // 初始化 + SUCCESS(10), // 发送成功 + FAILURE(20), // 发送失败 + IGNORE(30), // 忽略,即不发送 + ; + + private final int status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsTemplateTypeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsTemplateTypeEnum.java new file mode 100644 index 0000000..d24b07a --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsTemplateTypeEnum.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.system.enums.sms; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 短信的模板类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum SmsTemplateTypeEnum { + + VERIFICATION_CODE(1), // 验证码 + NOTICE(2), // 通知 + PROMOTION(3), // 营销 + ; + + /** + * 类型 + */ + private final int type; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java new file mode 100644 index 0000000..e6c4dc3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.system.enums.social; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 社交平台的类型枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum SocialTypeEnum implements ArrayValuable { + + /** + * Gitee + * + * @see 接入文档 + */ + GITEE(10, "GITEE"), + /** + * 钉钉 + * + * @see 接入文档 + */ + DINGTALK(20, "DINGTALK"), + + /** + * 企业微信 + * + * @see 接入文档 + */ + WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"), + /** + * 微信公众平台 - 移动端 H5 + * + * @see 接入文档 + */ + WECHAT_MP(31, "WECHAT_MP"), + /** + * 微信开放平台 - 网站应用 PC 端扫码授权登录 + * + * @see 接入文档 + */ + WECHAT_OPEN(32, "WECHAT_OPEN"), + /** + * 微信小程序 + * + * @see 接入文档 + */ + WECHAT_MINI_PROGRAM(34, "WECHAT_MINI_PROGRAM"), + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(SocialTypeEnum::getType).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer type; + /** + * 类型的标识 + */ + private final String source; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static SocialTypeEnum valueOfType(Integer type) { + return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/YudaoCaptchaConfiguration.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/YudaoCaptchaConfiguration.java new file mode 100644 index 0000000..5db497e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/YudaoCaptchaConfiguration.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.framework.captcha.config; + +import cn.iocoder.yudao.module.system.framework.captcha.core.RedisCaptchaServiceImpl; +import com.anji.captcha.config.AjCaptchaAutoConfiguration; +import com.anji.captcha.properties.AjCaptchaProperties; +import com.anji.captcha.service.CaptchaCacheService; +import com.anji.captcha.service.impl.CaptchaServiceFactory; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.core.StringRedisTemplate; + +/** + * 验证码的配置类 + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +@ImportAutoConfiguration(AjCaptchaAutoConfiguration.class) // 目的:解决 aj-captcha 针对 SpringBoot 3.X 自动配置不生效的问题 +public class YudaoCaptchaConfiguration { + + @Bean(name = "AjCaptchaCacheService") + @Primary + public CaptchaCacheService captchaCacheService(AjCaptchaProperties config, + StringRedisTemplate stringRedisTemplate) { + CaptchaCacheService captchaCacheService = CaptchaServiceFactory.getCache(config.getCacheType().name()); + if (captchaCacheService instanceof RedisCaptchaServiceImpl) { + ((RedisCaptchaServiceImpl) captchaCacheService).setStringRedisTemplate(stringRedisTemplate); + } + return captchaCacheService; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/RedisCaptchaServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/RedisCaptchaServiceImpl.java new file mode 100644 index 0000000..2e66a67 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/RedisCaptchaServiceImpl.java @@ -0,0 +1,49 @@ +package cn.iocoder.yudao.module.system.framework.captcha.core; + +import com.anji.captcha.service.CaptchaCacheService; +import lombok.Setter; +import org.springframework.data.redis.core.StringRedisTemplate; + +import java.util.concurrent.TimeUnit; + +/** + * 基于 Redis 实现验证码的存储 + * + * @author 星语 + */ +@Setter +public class RedisCaptchaServiceImpl implements CaptchaCacheService { + + private StringRedisTemplate stringRedisTemplate; + + @Override + public String type() { + return "redis"; + } + + @Override + public void set(String key, String value, long expiresInSeconds) { + stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS); + } + + @Override + public boolean exists(String key) { + return stringRedisTemplate.hasKey(key); + } + + @Override + public void delete(String key) { + stringRedisTemplate.delete(key); + } + + @Override + public String get(String key) { + return stringRedisTemplate.opsForValue().get(key); + } + + @Override + public Long increment(String key, long val) { + return stringRedisTemplate.opsForValue().increment(key,val); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/package-info.java new file mode 100644 index 0000000..a94be21 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/package-info.java @@ -0,0 +1,8 @@ +/** + * 验证码拓展 + * + * 基于 aj-captcha 实现滑块验证码,文档:https://ajcaptcha.beliefteam.cn/captcha-doc/ + * + * @author 星语 + */ +package cn.iocoder.yudao.module.system.framework.captcha; \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java new file mode 100644 index 0000000..136866c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.system.framework.datapermission.config; + +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRuleCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * system 模块的数据权限 Configuration + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +public class DataPermissionConfiguration { + + @Bean + public DeptDataPermissionRuleCustomizer sysDeptDataPermissionRuleCustomizer() { + return rule -> { + // dept + rule.addDeptColumn(AdminUserDO.class); + rule.addDeptColumn(DeptDO.class, "id"); + // user + rule.addUserColumn(AdminUserDO.class, "id"); + }; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/package-info.java new file mode 100644 index 0000000..a666845 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/package-info.java @@ -0,0 +1,4 @@ +/** + * system 模块的数据权限配置 + */ +package cn.iocoder.yudao.module.system.framework.datapermission; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/config/YudaoJustAuthConfiguration.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/config/YudaoJustAuthConfiguration.java new file mode 100644 index 0000000..56a24ee --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/config/YudaoJustAuthConfiguration.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.framework.justauth.config; + +import cn.iocoder.yudao.module.system.framework.justauth.core.AuthRequestFactory; +import com.xkcoding.justauth.autoconfigure.JustAuthProperties; +import com.xkcoding.justauth.support.cache.RedisStateCache; +import me.zhyd.oauth.cache.AuthStateCache; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.core.RedisTemplate; + +/** + * JustAuth 配置类 TODO 芋艿:等 justauth 1.4.1 版本发布!!! + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties({JustAuthProperties.class}) +public class YudaoJustAuthConfiguration { + + @Bean + @ConditionalOnProperty( + prefix = "justauth", + value = {"enabled"}, + havingValue = "true", + matchIfMissing = true + ) + public AuthRequestFactory authRequestFactory(JustAuthProperties properties, AuthStateCache authStateCache) { + return new AuthRequestFactory(properties, authStateCache); + } + + @Bean + public AuthStateCache authStateCache(RedisTemplate justAuthRedisCacheTemplate, + JustAuthProperties justAuthProperties) { + return new RedisStateCache(justAuthRedisCacheTemplate, justAuthProperties.getCache()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java new file mode 100644 index 0000000..4ae8b78 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2019-2029, xkcoding & Yangkai.Shen & 沈扬凯 (237497819@qq.com & xkcoding.com). + *

+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package cn.iocoder.yudao.module.system.framework.justauth.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.EnumUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import com.xkcoding.http.config.HttpConfig; +import com.xkcoding.justauth.autoconfigure.ExtendProperties; +import com.xkcoding.justauth.autoconfigure.JustAuthProperties; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.cache.AuthStateCache; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.config.AuthDefaultSource; +import me.zhyd.oauth.config.AuthSource; +import me.zhyd.oauth.enums.AuthResponseStatus; +import me.zhyd.oauth.exception.AuthException; +import me.zhyd.oauth.request.*; +import org.springframework.util.CollectionUtils; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +// TODO @芋艿:等官方发布 1.4.1!!! +/** + *

+ * AuthRequest工厂类 + *

+ * + * @author yangkai.shen + * @date Created in 2019-07-22 14:21 + */ +@Slf4j +@RequiredArgsConstructor +public class AuthRequestFactory { + private final JustAuthProperties properties; + private final AuthStateCache authStateCache; + + /** + * 返回当前Oauth列表 + * + * @return Oauth列表 + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + public List oauthList() { + // 默认列表 + List defaultList = new ArrayList<>(properties.getType().keySet()); + // 扩展列表 + List extendList = new ArrayList<>(); + ExtendProperties extend = properties.getExtend(); + if (null != extend) { + Class enumClass = extend.getEnumClass(); + List names = EnumUtil.getNames(enumClass); + // 扩展列表 + extendList = extend.getConfig() + .keySet() + .stream() + .filter(x -> names.contains(x.toUpperCase())) + .map(String::toUpperCase) + .collect(Collectors.toList()); + } + + // 合并 + return (List) CollUtil.addAll(defaultList, extendList); + } + + /** + * 返回AuthRequest对象 + * + * @param source {@link AuthSource} + * @return {@link AuthRequest} + */ + public AuthRequest get(String source) { + if (StrUtil.isBlank(source)) { + throw new AuthException(AuthResponseStatus.NO_AUTH_SOURCE); + } + + // 获取 JustAuth 中已存在的 + AuthRequest authRequest = getDefaultRequest(source); + + // 如果获取不到则尝试取自定义的 + if (authRequest == null) { + authRequest = getExtendRequest(properties.getExtend().getEnumClass(), source); + } + + if (authRequest == null) { + throw new AuthException(AuthResponseStatus.UNSUPPORTED); + } + + return authRequest; + } + + /** + * 获取自定义的 request + * + * @param clazz 枚举类 {@link AuthSource} + * @param source {@link AuthSource} + * @return {@link AuthRequest} + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + private AuthRequest getExtendRequest(Class clazz, String source) { + String upperSource = source.toUpperCase(); + try { + EnumUtil.fromString(clazz, upperSource); + } catch (IllegalArgumentException e) { + // 无自定义匹配 + return null; + } + + Map extendConfig = properties.getExtend().getConfig(); + + // key 转大写 + Map upperConfig = new HashMap<>(6); + extendConfig.forEach((k, v) -> upperConfig.put(k.toUpperCase(), v)); + + ExtendProperties.ExtendRequestConfig extendRequestConfig = upperConfig.get(upperSource); + if (extendRequestConfig != null) { + + // 配置 http config + configureHttpConfig(upperSource, extendRequestConfig, properties.getHttpConfig()); + + Class requestClass = extendRequestConfig.getRequestClass(); + + if (requestClass != null) { + // 反射获取 Request 对象,所以必须实现 2 个参数的构造方法 + return ReflectUtil.newInstance(requestClass, (AuthConfig) extendRequestConfig, authStateCache); + } + } + + return null; + } + + + /** + * 获取默认的 Request + * + * @param source {@link AuthSource} + * @return {@link AuthRequest} + */ + private AuthRequest getDefaultRequest(String source) { + AuthDefaultSource authDefaultSource; + + try { + authDefaultSource = EnumUtil.fromString(AuthDefaultSource.class, source.toUpperCase()); + } catch (IllegalArgumentException e) { + // 无自定义匹配 + return null; + } + + AuthConfig config = properties.getType().get(authDefaultSource.name()); + // 找不到对应关系,直接返回空 + if (config == null) { + return null; + } + + // 配置 http config + configureHttpConfig(authDefaultSource.name(), config, properties.getHttpConfig()); + + switch (authDefaultSource) { + case GITHUB: + return new AuthGithubRequest(config, authStateCache); + case WEIBO: + return new AuthWeiboRequest(config, authStateCache); + case GITEE: + return new AuthGiteeRequest(config, authStateCache); + case DINGTALK: + return new AuthDingTalkRequest(config, authStateCache); + case DINGTALK_V2: + return new AuthDingTalkV2Request(config, authStateCache); + case DINGTALK_ACCOUNT: + return new AuthDingTalkAccountRequest(config, authStateCache); + case BAIDU: + return new AuthBaiduRequest(config, authStateCache); + case CSDN: + return new AuthCsdnRequest(config, authStateCache); + case CODING: + return new AuthCodingRequest(config, authStateCache); + case OSCHINA: + return new AuthOschinaRequest(config, authStateCache); + case ALIPAY: + return new AuthAlipayRequest(config, authStateCache); + case QQ: + return new AuthQqRequest(config, authStateCache); + case WECHAT_OPEN: + return new AuthWeChatOpenRequest(config, authStateCache); + case WECHAT_MP: + return new AuthWeChatMpRequest(config, authStateCache); + case TAOBAO: + return new AuthTaobaoRequest(config, authStateCache); + case GOOGLE: + return new AuthGoogleRequest(config, authStateCache); + case FACEBOOK: + return new AuthFacebookRequest(config, authStateCache); + case DOUYIN: + return new AuthDouyinRequest(config, authStateCache); + case LINKEDIN: + return new AuthLinkedinRequest(config, authStateCache); + case MICROSOFT: + return new AuthMicrosoftRequest(config, authStateCache); + case MICROSOFT_CN: + return new AuthMicrosoftCnRequest(config, authStateCache); + + case MI: + return new AuthMiRequest(config, authStateCache); + case TOUTIAO: + return new AuthToutiaoRequest(config, authStateCache); + case TEAMBITION: + return new AuthTeambitionRequest(config, authStateCache); + case RENREN: + return new AuthRenrenRequest(config, authStateCache); + case PINTEREST: + return new AuthPinterestRequest(config, authStateCache); + case STACK_OVERFLOW: + return new AuthStackOverflowRequest(config, authStateCache); + case HUAWEI: + return new AuthHuaweiRequest(config, authStateCache); + case HUAWEI_V3: + return new AuthHuaweiV3Request(config, authStateCache); + case WECHAT_ENTERPRISE: + return new AuthWeChatEnterpriseQrcodeRequest(config, authStateCache); + case WECHAT_ENTERPRISE_V2: + return new AuthWeChatEnterpriseQrcodeV2Request(config, authStateCache); + case WECHAT_ENTERPRISE_QRCODE_THIRD: + return new AuthWeChatEnterpriseThirdQrcodeRequest(config, authStateCache); + case WECHAT_ENTERPRISE_WEB: + return new AuthWeChatEnterpriseWebRequest(config, authStateCache); + case KUJIALE: + return new AuthKujialeRequest(config, authStateCache); + case GITLAB: + return new AuthGitlabRequest(config, authStateCache); + case MEITUAN: + return new AuthMeituanRequest(config, authStateCache); + case ELEME: + return new AuthElemeRequest(config, authStateCache); + case TWITTER: + return new AuthTwitterRequest(config, authStateCache); + case FEISHU: + return new AuthFeishuRequest(config, authStateCache); + case JD: + return new AuthJdRequest(config, authStateCache); + case ALIYUN: + return new AuthAliyunRequest(config, authStateCache); + case XMLY: + return new AuthXmlyRequest(config, authStateCache); + case AMAZON: + return new AuthAmazonRequest(config, authStateCache); + case SLACK: + return new AuthSlackRequest(config, authStateCache); + case LINE: + return new AuthLineRequest(config, authStateCache); + case OKTA: + return new AuthOktaRequest(config, authStateCache); + case PROGINN: + return new AuthProginnRequest(config,authStateCache); + case AFDIAN: + return new AuthAfDianRequest(config,authStateCache); + case APPLE: + return new AuthAppleRequest(config,authStateCache); + case FIGMA: + return new AuthFigmaRequest(config,authStateCache); + case WECHAT_MINI_PROGRAM: + config.setIgnoreCheckRedirectUri(true); + config.setIgnoreCheckState(true); + return new AuthWechatMiniProgramRequest(config, authStateCache); + case QQ_MINI_PROGRAM: + config.setIgnoreCheckRedirectUri(true); + config.setIgnoreCheckState(true); + return new AuthQQMiniProgramRequest(config, authStateCache); + default: + return null; + } + } + + /** + * 配置 http 相关的配置 + * + * @param authSource {@link AuthSource} + * @param authConfig {@link AuthConfig} + */ + private void configureHttpConfig(String authSource, AuthConfig authConfig, JustAuthProperties.JustAuthHttpConfig httpConfig) { + if (null == httpConfig) { + return; + } + Map proxyConfigMap = httpConfig.getProxy(); + if (CollectionUtils.isEmpty(proxyConfigMap)) { + return; + } + JustAuthProperties.JustAuthProxyConfig proxyConfig = proxyConfigMap.get(authSource); + + if (null == proxyConfig) { + return; + } + + authConfig.setHttpConfig(HttpConfig.builder() + .timeout(httpConfig.getTimeout()) + .proxy(new Proxy(Proxy.Type.valueOf(proxyConfig.getType()), new InetSocketAddress(proxyConfig.getHostname(), proxyConfig.getPort()))) + .build()); + } +} \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/package-info.java new file mode 100644 index 0000000..e9af3ab --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/package-info.java @@ -0,0 +1,6 @@ +/** + * justauth 三方登录的拓展 + * + * @author 芋道源码 + */ +package cn.iocoder.yudao.module.system.framework.justauth; \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java new file mode 100644 index 0000000..f071251 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.system.framework.operatelog.core; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 管理员名字的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Slf4j +@Component +public class AdminUserParseFunction implements IParseFunction { + + public static final String NAME = "getAdminUserById"; + + @Resource + private AdminUserService adminUserService; + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + + // 获取用户信息 + AdminUserDO user = adminUserService.getUser(Convert.toLong(value)); + if (user == null) { + log.warn("[apply][获取用户{{}}为空", value); + return ""; + } + // 返回格式 芋道源码(13888888888) + String nickname = user.getNickname(); + if (StrUtil.isEmpty(user.getMobile())) { + return nickname; + } + return StrUtil.format("{}({})", nickname, user.getMobile()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java new file mode 100644 index 0000000..f2b9089 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.framework.operatelog.core; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 地名的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Slf4j +@Component +public class AreaParseFunction implements IParseFunction { + + public static final String NAME = "getArea"; + + @Override + public boolean executeBefore() { + return true; // 先转换值后对比 + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return AreaUtils.format(Convert.toInt(value)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/BooleanParseFunction.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/BooleanParseFunction.java new file mode 100644 index 0000000..dacb1cd --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/BooleanParseFunction.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; +import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 是否类型的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class BooleanParseFunction implements IParseFunction { + + public static final String NAME = "getBoolean"; + + @Override + public boolean executeBefore() { + return true; // 先转换值后对比 + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.BOOLEAN_STRING, value.toString()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java new file mode 100644 index 0000000..125c324 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.framework.operatelog.core; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 部门名字的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Slf4j +@Component +public class DeptParseFunction implements IParseFunction { + + public static final String NAME = "getDeptById"; + + @Resource + private DeptService deptService; + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + + // 获取部门信息 + DeptDO dept = deptService.getDept(Convert.toLong(value)); + if (dept == null) { + log.warn("[apply][获取部门{{}}为空", value); + return ""; + } + return dept.getName(); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java new file mode 100644 index 0000000..e246d12 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java @@ -0,0 +1,46 @@ +package cn.iocoder.yudao.module.system.framework.operatelog.core; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.service.dept.PostService; +import com.mzt.logapi.service.IParseFunction; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 岗位名字的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Slf4j +@Component +public class PostParseFunction implements IParseFunction { + + public static final String NAME = "getPostById"; + + @Resource + private PostService postService; + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + + // 获取岗位信息 + PostDO post = postService.getPost(Convert.toLong(value)); + if (post == null) { + log.warn("[apply][获取岗位{{}}为空", value); + return ""; + } + return post.getName(); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/SexParseFunction.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/SexParseFunction.java new file mode 100644 index 0000000..684b388 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/SexParseFunction.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.framework.operatelog.core; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; +import cn.iocoder.yudao.module.system.enums.DictTypeConstants; +import com.mzt.logapi.service.IParseFunction; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 行业的 {@link IParseFunction} 实现类 + * + * @author HUIHUI + */ +@Component +@Slf4j +public class SexParseFunction implements IParseFunction { + + public static final String NAME = "getSex"; + + @Override + public boolean executeBefore() { + return true; // 先转换值后对比 + } + + @Override + public String functionName() { + return NAME; + } + + @Override + public String apply(Object value) { + if (StrUtil.isEmptyIfStr(value)) { + return ""; + } + return DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.USER_SEX, value.toString()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java new file mode 100644 index 0000000..c7f1248 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位文件,避免文件夹缩进 + */ +package cn.iocoder.yudao.module.system.framework.operatelog; \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2ClientConstants.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/package-info.java index a123d57..4b84406 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2ClientConstants.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/package-info.java @@ -1,12 +1,6 @@ -package cn.iocoder.yudao.module.system.enums.oauth2; - /** - * OAuth2.0 客户端的通用枚举 + * 属于 system 模块的 framework 封装 * * @author 芋道源码 */ -public interface OAuth2ClientConstants { - - String CLIENT_ID_DEFAULT = "default"; - -} +package cn.iocoder.yudao.module.system.framework; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsCodeProperties.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsCodeProperties.java new file mode 100644 index 0000000..16f15a5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsCodeProperties.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.system.framework.sms.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.constraints.NotNull; +import java.time.Duration; + +@ConfigurationProperties(prefix = "yudao.sms-code") +@Validated +@Data +public class SmsCodeProperties { + + /** + * 过期时间 + */ + @NotNull(message = "过期时间不能为空") + private Duration expireTimes; + /** + * 短信发送频率 + */ + @NotNull(message = "短信发送频率不能为空") + private Duration sendFrequency; + /** + * 每日发送最大数量 + */ + @NotNull(message = "每日发送最大数量不能为空") + private Integer sendMaximumQuantityPerDay; + /** + * 验证码最小值 + */ + @NotNull(message = "验证码最小值不能为空") + private Integer beginCode; + /** + * 验证码最大值 + */ + @NotNull(message = "验证码最大值不能为空") + private Integer endCode; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsConfiguration.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsConfiguration.java new file mode 100644 index 0000000..eb34775 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsConfiguration.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.framework.sms.config; + +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClientFactory; +import cn.iocoder.yudao.module.system.framework.sms.core.client.impl.SmsClientFactoryImpl; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 短信配置类,包括短信客户端、短信验证码两部分 + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(SmsCodeProperties.class) +public class SmsConfiguration { + + @Bean + public SmsClientFactory smsClientFactory() { + return new SmsClientFactoryImpl(); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClient.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClient.java new file mode 100644 index 0000000..5d7b809 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClient.java @@ -0,0 +1,56 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client; + +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; + +import java.util.List; + +/** + * 短信客户端,用于对接各短信平台的 SDK,实现短信发送等功能 + * + * @author zzf + * @since 2021/1/25 14:14 + */ +public interface SmsClient { + + /** + * 获得渠道编号 + * + * @return 渠道编号 + */ + Long getId(); + + /** + * 发送消息 + * + * @param logId 日志编号 + * @param mobile 手机号 + * @param apiTemplateId 短信 API 的模板编号 + * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序 + * @return 短信发送结果 + */ + SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId, + List> templateParams) throws Throwable; + + /** + * 解析接收短信的接收结果 + * + * @param text 结果 + * @return 结果内容 + * @throws Throwable 当解析 text 发生异常时,则会抛出异常 + */ + List parseSmsReceiveStatus(String text) throws Throwable; + + /** + * 查询指定的短信模板 + * + * 如果查询失败,则返回 null 空 + * + * @param apiTemplateId 短信 API 的模板编号 + * @return 短信模板 + */ + SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClientFactory.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClientFactory.java new file mode 100644 index 0000000..ad878b7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClientFactory.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client; + +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; + +/** + * 短信客户端的工厂接口 + * + * @author zzf + * @since 2021/1/28 14:01 + */ +public interface SmsClientFactory { + + /** + * 获得短信 Client + * + * @param channelId 渠道编号 + * @return 短信 Client + */ + SmsClient getSmsClient(Long channelId); + + /** + * 获得短信 Client + * + * @param channelCode 渠道编码 + * @return 短信 Client + */ + SmsClient getSmsClient(String channelCode); + + /** + * 创建短信 Client + * + * @param properties 配置对象 + * @return 短信 Client + */ + SmsClient createOrUpdateSmsClient(SmsChannelProperties properties); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsReceiveRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsReceiveRespDTO.java new file mode 100644 index 0000000..c90a33e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsReceiveRespDTO.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.dto; + +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 消息接收 Response DTO + * + * @author 芋道源码 + */ +@Data +public class SmsReceiveRespDTO { + + /** + * 是否接收成功 + */ + private Boolean success; + /** + * API 接收结果的编码 + */ + private String errorCode; + /** + * API 接收结果的说明 + */ + private String errorMsg; + + /** + * 手机号 + */ + private String mobile; + /** + * 用户接收时间 + */ + private LocalDateTime receiveTime; + + /** + * 短信 API 发送返回的序号 + */ + private String serialNo; + /** + * 短信日志编号 + * + * 对应 SysSmsLogDO 的编号 + */ + private Long logId; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsSendRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsSendRespDTO.java new file mode 100644 index 0000000..9f3094b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsSendRespDTO.java @@ -0,0 +1,43 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.dto; + +import lombok.Data; + +/** + * 短信发送 Response DTO + * + * @author 芋道源码 + */ +@Data +public class SmsSendRespDTO { + + /** + * 是否成功 + */ + private Boolean success; + + /** + * API 请求编号 + */ + private String apiRequestId; + + // ==================== 成功时字段 ==================== + + /** + * 短信 API 发送返回的序号 + */ + private String serialNo; + + // ==================== 失败时字段 ==================== + + /** + * API 返回错误码 + * + * 由于第三方的错误码可能是字符串,所以使用 String 类型 + */ + private String apiCode; + /** + * API 返回提示 + */ + private String apiMsg; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsTemplateRespDTO.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsTemplateRespDTO.java new file mode 100644 index 0000000..38a43aa --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsTemplateRespDTO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.dto; + +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import lombok.Data; + +/** + * 短信模板 Response DTO + * + * @author 芋道源码 + */ +@Data +public class SmsTemplateRespDTO { + + /** + * 模板编号 + */ + private String id; + /** + * 短信内容 + */ + private String content; + /** + * 审核状态 + * + * 枚举 {@link SmsTemplateAuditStatusEnum} + */ + private Integer auditStatus; + /** + * 审核未通过的理由 + */ + private String auditReason; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AbstractSmsClient.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AbstractSmsClient.java new file mode 100644 index 0000000..a1883bf --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AbstractSmsClient.java @@ -0,0 +1,48 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import lombok.extern.slf4j.Slf4j; + +/** + * 短信客户端的抽象类,提供模板方法,减少子类的冗余代码 + * + * @author zzf + * @since 2021/2/1 9:28 + */ +@Slf4j +public abstract class AbstractSmsClient implements SmsClient { + + /** + * 短信渠道配置 + */ + protected volatile SmsChannelProperties properties; + + public AbstractSmsClient(SmsChannelProperties properties) { + this.properties = properties; + } + + /** + * 初始化 + */ + public final void init() { + log.debug("[init][配置({}) 初始化完成]", properties); + } + + public final void refresh(SmsChannelProperties properties) { + // 判断是否更新 + if (properties.equals(this.properties)) { + return; + } + log.info("[refresh][配置({})发生变化,重新初始化]", properties); + this.properties = properties; + // 初始化 + this.init(); + } + + @Override + public Long getId() { + return properties.getId(); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java new file mode 100644 index 0000000..3dd1249 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java @@ -0,0 +1,193 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.hutool.core.date.format.FastDateFormat; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import com.google.common.annotations.VisibleForTesting; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 阿里短信客户端的实现类 + * + * @author zzf + * @since 2021/1/25 14:17 + */ +@Slf4j +public class AliyunSmsClient extends AbstractSmsClient { + + private static final String URL = "https://dysmsapi.aliyuncs.com"; + private static final String HOST = "dysmsapi.aliyuncs.com"; + private static final String VERSION = "2017-05-25"; + + private static final String RESPONSE_CODE_SUCCESS = "OK"; + + public AliyunSmsClient(SmsChannelProperties properties) { + super(properties); + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); + Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); + } + + @Override + public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, + List> templateParams) throws Throwable { + Assert.notBlank(properties.getSignature(), "短信签名不能为空"); + // 1. 执行请求 + // 参考链接 https://api.aliyun.com/document/Dysmsapi/2017-05-25/SendSms + TreeMap queryParam = new TreeMap<>(); + queryParam.put("PhoneNumbers", mobile); + queryParam.put("SignName", properties.getSignature()); + queryParam.put("TemplateCode", apiTemplateId); + queryParam.put("TemplateParam", JsonUtils.toJsonString(MapUtils.convertMap(templateParams))); + queryParam.put("OutId", sendLogId); + JSONObject response = request("SendSms", queryParam); + + // 2. 解析请求 + return new SmsSendRespDTO() + .setSuccess(Objects.equals(response.getStr("Code"), RESPONSE_CODE_SUCCESS)) + .setSerialNo(response.getStr("BizId")) + .setApiRequestId(response.getStr("RequestId")) + .setApiCode(response.getStr("Code")) + .setApiMsg(response.getStr("Message")); + } + + @Override + public List parseSmsReceiveStatus(String text) { + JSONArray statuses = JSONUtil.parseArray(text); + // 字段参考 https://help.aliyun.com/zh/sms/developer-reference/smsreport-2 + return convertList(statuses, status -> { + JSONObject statusObj = (JSONObject) status; + return new SmsReceiveRespDTO() + .setSuccess(statusObj.getBool("success")) // 是否接收成功 + .setErrorCode(statusObj.getStr("err_code")) // 状态报告编码 + .setErrorMsg(statusObj.getStr("err_msg")) // 状态报告说明 + .setMobile(statusObj.getStr("phone_number")) // 手机号 + .setReceiveTime(statusObj.getLocalDateTime("report_time", null)) // 状态报告时间 + .setSerialNo(statusObj.getStr("biz_id")) // 发送序列号 + .setLogId(statusObj.getLong("out_id")); // 用户序列号 + }); + } + + @Override + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { + // 1. 执行请求 + // 参考链接 https://api.aliyun.com/document/Dysmsapi/2017-05-25/QuerySmsTemplate + TreeMap queryParam = new TreeMap<>(); + queryParam.put("TemplateCode", apiTemplateId); + JSONObject response = request("QuerySmsTemplate", queryParam); + + // 2.1 请求失败 + String code = response.getStr("Code"); + if (ObjectUtil.notEqual(code, RESPONSE_CODE_SUCCESS)) { + log.error("[getSmsTemplate][模版编号({}) 响应不正确({})]", apiTemplateId, response); + return null; + } + // 2.2 请求成功 + return new SmsTemplateRespDTO() + .setId(response.getStr("TemplateCode")) + .setContent(response.getStr("TemplateContent")) + .setAuditStatus(convertSmsTemplateAuditStatus(response.getInt("TemplateStatus"))) + .setAuditReason(response.getStr("Reason")); + } + + @VisibleForTesting + Integer convertSmsTemplateAuditStatus(Integer templateStatus) { + switch (templateStatus) { + case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); + case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); + case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); + default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); + } + } + + /** + * 请求阿里云短信 + * + * @see V3 版本请求体&签名机制 + * @param apiName 请求的 API 名称 + * @param queryParams 请求参数 + * @return 请求结果 + */ + private JSONObject request(String apiName, TreeMap queryParams) { + // 1. 请求参数 + String queryString = queryParams.entrySet().stream() + .map(entry -> percentCode(entry.getKey()) + "=" + percentCode(String.valueOf(entry.getValue()))) + .collect(Collectors.joining("&")); + + // 2.1 请求 Header + TreeMap headers = new TreeMap<>(); + headers.put("host", HOST); + headers.put("x-acs-version", VERSION); + headers.put("x-acs-action", apiName); + headers.put("x-acs-date", FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("GMT")).format(new Date())); + headers.put("x-acs-signature-nonce", IdUtil.randomUUID()); + + // 2.2 构建签名 Header + StringBuilder canonicalHeaders = new StringBuilder(); // 构造请求头,多个规范化消息头,按照消息头名称(小写)的字符代码顺序以升序排列后拼接在一起 + StringBuilder signedHeadersBuilder = new StringBuilder(); // 已签名消息头列表,多个请求头名称(小写)按首字母升序排列并以英文分号(;)分隔 + headers.entrySet().stream().filter(entry -> entry.getKey().toLowerCase().startsWith("x-acs-") + || entry.getKey().equalsIgnoreCase("host") + || entry.getKey().equalsIgnoreCase("content-type")) + .sorted(Map.Entry.comparingByKey()).forEach(entry -> { + String lowerKey = entry.getKey().toLowerCase(); + canonicalHeaders.append(lowerKey).append(":").append(String.valueOf(entry.getValue()).trim()).append("\n"); + signedHeadersBuilder.append(lowerKey).append(";"); + }); + String signedHeaders = signedHeadersBuilder.substring(0, signedHeadersBuilder.length() - 1); + + // 3. 请求 Body + String requestBody = ""; // 短信 API 为 RPC 接口,query parameters 在 uri 中拼接,因此 request body 如果没有特殊要求,设置为空。 + String hashedRequestBody = DigestUtil.sha256Hex(requestBody); + + // 4. 构建 Authorization 签名 + String canonicalRequest = "POST" + "\n" + "/" + "\n" + queryString + "\n" + + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestBody; + String hashedCanonicalRequest = DigestUtil.sha256Hex(canonicalRequest); + String stringToSign = "ACS3-HMAC-SHA256" + "\n" + hashedCanonicalRequest; + String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign); // 计算签名 + headers.put("Authorization", "ACS3-HMAC-SHA256" + " " + "Credential=" + properties.getApiKey() + + ", " + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature); + + // 5. 发起请求 + String responseBody = HttpUtils.post(URL + "?" + queryString, headers, requestBody); + return JSONUtil.parseObj(responseBody); + } + + /** + * 对指定的字符串进行 URL 编码,并对特定的字符进行替换,以符合URL编码规范 + * + * @param str 需要进行 URL 编码的字符串 + * @return 编码后的字符串 + */ + @SneakyThrows + private static String percentCode(String str) { + Assert.notNull(str, "str 不能为空"); + return HttpUtils.encodeUtf8(str) + .replace("+", "%20") // 加号 "+" 被替换为 "%20" + .replace("*", "%2A") // 星号 "*" 被替换为 "%2A" + .replace("%7E", "~"); // 波浪号 "%7E" 被替换为 "~" + } + +} \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/DebugDingTalkSmsClient.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/DebugDingTalkSmsClient.java new file mode 100644 index 0000000..6d2f2d0 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/DebugDingTalkSmsClient.java @@ -0,0 +1,91 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.crypto.digest.HmacAlgorithm; +import cn.hutool.http.HttpUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 基于钉钉 WebHook 实现的调试的短信客户端实现类 + * + * 考虑到省钱,我们使用钉钉 WebHook 模拟发送短信,方便调试。 + * + * @author 芋道源码 + */ +public class DebugDingTalkSmsClient extends AbstractSmsClient { + + public DebugDingTalkSmsClient(SmsChannelProperties properties) { + super(properties); + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); + Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); + } + + @Override + public SmsSendRespDTO sendSms(Long sendLogId, String mobile, + String apiTemplateId, List> templateParams) throws Throwable { + // 构建请求 + String url = buildUrl("robot/send"); + Map params = new HashMap<>(); + params.put("msgtype", "text"); + String content = String.format("【模拟短信】\n手机号:%s\n短信日志编号:%d\n模板参数:%s", + mobile, sendLogId, MapUtils.convertMap(templateParams)); + params.put("text", MapUtil.builder().put("content", content).build()); + // 执行请求 + String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params)); + // 解析结果 + Map responseObj = JsonUtils.parseObject(responseText, Map.class); + String errorCode = MapUtil.getStr(responseObj, "errcode"); + return new SmsSendRespDTO().setSuccess(Objects.equals(errorCode, "0")).setSerialNo(StrUtil.uuid()) + .setApiCode(errorCode).setApiMsg(MapUtil.getStr(responseObj, "errorMsg")); + } + + /** + * 构建请求地址 + * + * 参见 文档 + * + * @param path 请求路径 + * @return 请求地址 + */ + @SuppressWarnings("SameParameterValue") + private String buildUrl(String path) { + // 生成 timestamp + long timestamp = System.currentTimeMillis(); + // 生成 sign + String secret = properties.getApiSecret(); + String stringToSign = timestamp + "\n" + secret; + byte[] signData = DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.bytes(secret)).digest(stringToSign); + String sign = Base64.encode(signData); + // 构建最终 URL + return String.format("https://oapi.dingtalk.com/%s?access_token=%s×tamp=%d&sign=%s", + path, properties.getApiKey(), timestamp, sign); + } + + @Override + public List parseSmsReceiveStatus(String text) { + throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调"); + } + + @Override + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) { + return new SmsTemplateRespDTO().setId(apiTemplateId).setContent("") + .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(""); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java new file mode 100644 index 0000000..e3e997d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java @@ -0,0 +1,162 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.date.format.FastDateFormat; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; + +import static cn.hutool.crypto.digest.DigestUtil.sha256Hex; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 华为短信客户端的实现类 + * + * @author scholar + * @since 2024/6/02 11:55 + */ +@Slf4j +public class HuaweiSmsClient extends AbstractSmsClient { + + private static final String URL = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1";//APP接入地址+接口访问URI + private static final String HOST = "smsapi.cn-north-4.myhuaweicloud.com:443"; + private static final String SIGNEDHEADERS = "content-type;host;x-sdk-date"; + + private static final String RESPONSE_CODE_SUCCESS = "000000"; + + public HuaweiSmsClient(SmsChannelProperties properties) { + super(properties); + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); + Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); + validateSender(properties); + } + + /** + * 参数校验华为云的 sender 通道号 + * + * 原因是:验华为云发放短信的时候,需要额外的参数 sender + * + * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。 + * + * @param properties 配置 + */ + private static void validateSender(SmsChannelProperties properties) { + String combineKey = properties.getApiKey(); + Assert.notEmpty(combineKey, "apiKey 不能为空"); + String[] keys = combineKey.trim().split(" "); + Assert.isTrue(keys.length == 2, "华为云短信 apiKey 配置格式错误,请配置 为[accessKeyId sender]"); + } + + private String getAccessKey() { + return StrUtil.subBefore(properties.getApiKey(), " ", true); + } + + private String getSender() { + return StrUtil.subAfter(properties.getApiKey(), " ", true); + } + + @Override + public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, + List> templateParams) throws Throwable { + StringBuilder requestBody = new StringBuilder(); + appendToBody(requestBody, "from=", getSender()); + appendToBody(requestBody, "&to=", mobile); + appendToBody(requestBody, "&templateId=", apiTemplateId); + appendToBody(requestBody, "&templateParas=", JsonUtils.toJsonString( + convertList(templateParams, kv -> String.valueOf(kv.getValue())))); + appendToBody(requestBody, "&statusCallback=", properties.getCallbackUrl()); + appendToBody(requestBody, "&extend=", String.valueOf(sendLogId)); + JSONObject response = request("/sms/batchSendSms/v1/", "POST", requestBody.toString()); + + // 2. 解析请求 + if (!response.containsKey("result")) { // 例如说:密钥不正确 + return new SmsSendRespDTO().setSuccess(false) + .setApiCode(response.getStr("code")) + .setApiMsg(response.getStr("description")); + } + JSONObject sendResult = response.getJSONArray("result").getJSONObject(0); + return new SmsSendRespDTO().setSuccess(RESPONSE_CODE_SUCCESS.equals(response.getStr("code"))) + .setSerialNo(sendResult.getStr("smsMsgId")).setApiCode(sendResult.getStr("status")); + } + + /** + * 请求华为云短信 + * + * @see https://support.huaweicloud.com/api-msgsms/sms_05_0046.html + * @param uri 请求 URI + * @param method 请求 Method + * @param requestBody 请求 Body + * @return 请求结果 + */ + private JSONObject request(String uri, String method, String requestBody) { + // 1.1 请求 Header + TreeMap headers = new TreeMap<>(); + headers.put("Content-Type", "application/x-www-form-urlencoded"); + String sdkDate = FastDateFormat.getInstance("yyyyMMdd'T'HHmmss'Z'", TimeZone.getTimeZone("UTC")).format(new Date()); + headers.put("X-Sdk-Date", sdkDate); + headers.put("host", HOST); + + // 1.2 构建签名 Header + String canonicalQueryString = ""; // 查询参数为空 + String canonicalHeaders = "content-type:application/x-www-form-urlencoded\n" + + "host:"+ HOST +"\n" + "x-sdk-date:" + sdkDate + "\n"; + String canonicalRequest = method + "\n" + uri + "\n" + canonicalQueryString + "\n" + + canonicalHeaders + "\n" + SIGNEDHEADERS + "\n" + sha256Hex(requestBody); + String stringToSign = "SDK-HMAC-SHA256" + "\n" + sdkDate + "\n" + sha256Hex(canonicalRequest); + String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign); // 计算签名 + headers.put("Authorization", "SDK-HMAC-SHA256" + " " + "Access=" + getAccessKey() + + ", " + "SignedHeaders=" + SIGNEDHEADERS + ", " + "Signature=" + signature); + + // 2. 发起请求 + String responseBody = HttpUtils.post(URL, headers, requestBody); + return JSONUtil.parseObj(responseBody); + } + + @Override + public List parseSmsReceiveStatus(String requestBody) { + Map params = HttpUtil.decodeParamMap(requestBody, StandardCharsets.UTF_8); + // 字段参考 https://support.huaweicloud.com/api-msgsms/sms_05_0003.html + return ListUtil.of(new SmsReceiveRespDTO() + .setSuccess("DELIVRD".equals(params.get("status"))) // 是否接收成功 + .setErrorCode(params.get("status")) // 状态报告编码 + .setErrorMsg(params.get("statusDesc")) + .setMobile(params.get("to")) // 手机号 + .setReceiveTime(LocalDateTime.ofInstant(Instant.parse(params.get("updateTime")), ZoneId.of("UTC"))) // 状态报告时间 + .setSerialNo(params.get("smsMsgId")) // 发送序列号 + .setLogId(Long.valueOf(params.get("extend")))); // 用户序列号 + } + + @Override + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { + // 华为短信模板查询和发送短信,是不同的两套 key 和 secret,与阿里、腾讯的区别较大,这里模板查询校验暂不实现 + String[] strs = apiTemplateId.split(" "); + Assert.isTrue(strs.length == 2, "格式不正确,需要满足:apiTemplateId sender"); + return new SmsTemplateRespDTO().setId(apiTemplateId).setContent(null) + .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(null); + } + + private static void appendToBody(StringBuilder body, String key, String value) { + if (StrUtil.isNotEmpty(value)) { + body.append(key).append(HttpUtils.encodeUtf8(value)); + } + } + +} \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java new file mode 100644 index 0000000..41ba95b --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java @@ -0,0 +1,155 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.hutool.core.collection.CollStreamUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.digest.HmacAlgorithm; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; +import java.util.function.Function; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 七牛云短信客户端的实现类 + * + * @author scholar + * @since 2024/08/26 15:35 + */ +@Slf4j +public class QiniuSmsClient extends AbstractSmsClient { + + private static final String HOST = "sms.qiniuapi.com"; + + public QiniuSmsClient(SmsChannelProperties properties) { + super(properties); + Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); + Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); + } + + public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, + List> templateParams) throws Throwable { + // 1. 执行请求 + // 参考链接 https://developer.qiniu.com/sms/5824/through-the-api-send-text-messages + LinkedHashMap body = new LinkedHashMap<>(); + body.put("template_id", apiTemplateId); + body.put("mobile", mobile); + body.put("parameters", CollStreamUtil.toMap(templateParams, KeyValue::getKey, KeyValue::getValue)); + body.put("seq", Long.toString(sendLogId)); + JSONObject response = request("POST", body, "/v1/message/single"); + + // 2. 解析请求 + if (ObjectUtil.isNotEmpty(response.getStr("error"))) { + // 短信请求失败 + return new SmsSendRespDTO().setSuccess(false) + .setApiCode(response.getStr("error")) + .setApiRequestId(response.getStr("request_id")) + .setApiMsg(response.getStr("message")); + } + return new SmsSendRespDTO().setSuccess(response.containsKey("message_id")) + .setSerialNo(response.getStr("message_id")); + } + + /** + * 请求七牛云短信 + * + * @see + * @param httpMethod http请求方法 + * @param body http请求消息体 + * @param path URL path + * @return 请求结果 + */ + private JSONObject request(String httpMethod, LinkedHashMap body, String path) { + String signDate = DateUtil.date().setTimeZone(TimeZone.getTimeZone("UTC")).toString("yyyyMMdd'T'HHmmss'Z'"); + // 1. 请求头 + Map header = new HashMap<>(4); + header.put("HOST", HOST); + header.put("Authorization", getSignature(httpMethod, path, body != null ? JSONUtil.toJsonStr(body) : "", signDate)); + header.put("Content-Type", "application/json"); + header.put("X-Qiniu-Date", signDate); + + // 2. 发起请求 + String responseBody; + if (Objects.equals(httpMethod, "POST")){ + responseBody = HttpUtils.post("https://" + HOST + path, header, JSONUtil.toJsonStr(body)); + } else { + responseBody = HttpUtils.get("https://" + HOST + path, header); + } + return JSONUtil.parseObj(responseBody); + } + + private String getSignature(String method, String path, String body, String signDate) { + StringBuilder dataToSign = new StringBuilder(); + dataToSign.append(method.toUpperCase()).append(" ").append(path) + .append("\nHost: ").append(HOST) + .append("\n").append("Content-Type").append(": ").append("application/json") + .append("\n").append("X-Qiniu-Date").append(": ").append(signDate) + .append("\n\n"); + if (ObjectUtil.isNotEmpty(body)) { + dataToSign.append(body); + } + String signature = SecureUtil.hmac(HmacAlgorithm.HmacSHA1, properties.getApiSecret()) + .digestBase64(dataToSign.toString(), true); + return "Qiniu " + properties.getApiKey() + ":" + signature; + } + + @Override + public List parseSmsReceiveStatus(String text) { + JSONObject status = JSONUtil.parseObj(text); + // 字段参考 https://developer.qiniu.com/sms/5910/message-push + return convertList(status.getJSONArray("items"), new Function() { + + @Override + public SmsReceiveRespDTO apply(Object item) { + JSONObject statusObj = (JSONObject) item; + return new SmsReceiveRespDTO() + .setSuccess("DELIVRD".equals(statusObj.getStr("status"))) // 是否接收成功 + .setErrorMsg(statusObj.getStr("status")) // 状态报告编码 + .setMobile(statusObj.getStr("mobile")) // 手机号 + .setReceiveTime(LocalDateTimeUtil.of(statusObj.getLong("delivrd_at") * 1000L)) // 状态报告时间 + .setSerialNo(statusObj.getStr("message_id")) // 发送序列号 + .setLogId(statusObj.getLong("seq")); // 用户序列号 + } + + }); + } + + @Override + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { + // 1. 执行请求 + // 参考链接 https://developer.qiniu.com/sms/5969/query-a-single-template + JSONObject response = request("GET", null, "/v1/template/" + apiTemplateId); + + // 2.2 解析请求 + return new SmsTemplateRespDTO() + .setId(response.getStr("id")) + .setContent(response.getStr("template")) + .setAuditStatus(convertSmsTemplateAuditStatus(response.getStr("audit_status"))) + .setAuditReason(response.getStr("reject_reason")); + } + + @VisibleForTesting + Integer convertSmsTemplateAuditStatus(String templateStatus) { + switch (templateStatus) { + case "passed": return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); + case "reviewing": return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); + case "rejected": return SmsTemplateAuditStatusEnum.FAIL.getStatus(); + default: + throw new IllegalArgumentException(String.format("未知审核状态(%str)", templateStatus)); + } + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java new file mode 100644 index 0000000..da78318 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClientFactory; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.Assert; +import org.springframework.validation.annotation.Validated; + +import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * 短信客户端工厂接口 + * + * @author zzf + */ +@Validated +@Slf4j +public class SmsClientFactoryImpl implements SmsClientFactory { + + /** + * 短信客户端 Map + * key:渠道编号,使用 {@link SmsChannelProperties#getId()} + */ + private final ConcurrentMap channelIdClients = new ConcurrentHashMap<>(); + + /** + * 短信客户端 Map + * key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()} + * + * 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。 + * 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients} + */ + private final ConcurrentMap channelCodeClients = new ConcurrentHashMap<>(); + + public SmsClientFactoryImpl() { + // 初始化 channelCodeClients 集合 + Arrays.stream(SmsChannelEnum.values()).forEach(channel -> { + // 创建一个空的 SmsChannelProperties 对象 + SmsChannelProperties properties = new SmsChannelProperties().setCode(channel.getCode()) + .setApiKey("default default").setApiSecret("default"); + // 创建 Sms 客户端 + AbstractSmsClient smsClient = createSmsClient(properties); + channelCodeClients.put(channel.getCode(), smsClient); + }); + } + + @Override + public SmsClient getSmsClient(Long channelId) { + return channelIdClients.get(channelId); + } + + @Override + public SmsClient getSmsClient(String channelCode) { + return channelCodeClients.get(channelCode); + } + + @Override + public SmsClient createOrUpdateSmsClient(SmsChannelProperties properties) { + AbstractSmsClient client = channelIdClients.get(properties.getId()); + if (client == null) { + client = this.createSmsClient(properties); + client.init(); + channelIdClients.put(client.getId(), client); + } else { + client.refresh(properties); + } + return client; + } + + private AbstractSmsClient createSmsClient(SmsChannelProperties properties) { + SmsChannelEnum channelEnum = SmsChannelEnum.getByCode(properties.getCode()); + Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum)); + // 创建客户端 + switch (channelEnum) { + case ALIYUN: return new AliyunSmsClient(properties); + case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(properties); + case TENCENT: return new TencentSmsClient(properties); + case HUAWEI: return new HuaweiSmsClient(properties); + case QINIU: return new QiniuSmsClient(properties); + } + // 创建失败,错误日志 + 抛出异常 + log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties); + throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", properties)); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java new file mode 100644 index 0000000..19cde8c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java @@ -0,0 +1,201 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.hutool.core.date.format.FastDateFormat; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.HexUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.crypto.digest.HmacAlgorithm; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import com.google.common.annotations.VisibleForTesting; + +import java.nio.charset.StandardCharsets; +import java.util.*; + +import static cn.hutool.crypto.digest.DigestUtil.sha256Hex; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 腾讯云短信功能实现 + * + * 参见 文档 + * + * @author shiwp + */ +public class TencentSmsClient extends AbstractSmsClient { + + private static final String HOST = "sms.tencentcloudapi.com"; + private static final String VERSION = "2021-01-11"; + private static final String REGION = "ap-guangzhou"; + + /** + * 调用成功 code + */ + public static final String API_CODE_SUCCESS = "Ok"; + + /** + * 是否国际/港澳台短信: + * + * 0:表示国内短信。 + * 1:表示国际/港澳台短信。 + */ + private static final long INTERNATIONAL_CHINA = 0L; + + public TencentSmsClient(SmsChannelProperties properties) { + super(properties); + Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); + validateSdkAppId(properties); + } + + /** + * 参数校验腾讯云的 SDK AppId + * + * 原因是:腾讯云发放短信的时候,需要额外的参数 sdkAppId + * + * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。 + * + * @param properties 配置 + */ + private static void validateSdkAppId(SmsChannelProperties properties) { + String combineKey = properties.getApiKey(); + Assert.notEmpty(combineKey, "apiKey 不能为空"); + String[] keys = combineKey.trim().split(" "); + Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]"); + } + + private String getSdkAppId() { + return StrUtil.subAfter(properties.getApiKey(), " ", true); + } + + private String getApiKey() { + return StrUtil.subBefore(properties.getApiKey(), " ", true); + } + + @Override + public SmsSendRespDTO sendSms(Long sendLogId, String mobile, + String apiTemplateId, List> templateParams) throws Throwable { + // 1. 执行请求 + // 参考链接 https://cloud.tencent.com/document/product/382/55981 + TreeMap body = new TreeMap<>(); + body.put("PhoneNumberSet", new String[]{mobile}); + body.put("SmsSdkAppId", getSdkAppId()); + body.put("SignName", properties.getSignature()); + body.put("TemplateId", apiTemplateId); + body.put("TemplateParamSet", ArrayUtils.toArray(templateParams, param -> String.valueOf(param.getValue()))); + JSONObject response = request("SendSms", body); + + // 2. 解析请求 + JSONObject responseResult = response.getJSONObject("Response"); + JSONObject error = responseResult.getJSONObject("Error"); + if (error != null) { + return new SmsSendRespDTO().setSuccess(false) + .setApiRequestId(responseResult.getStr("RequestId")) + .setApiCode(error.getStr("Code")) + .setApiMsg(error.getStr("Message")); + } + JSONObject sendResult = responseResult.getJSONArray("SendStatusSet").getJSONObject(0); + return new SmsSendRespDTO().setSuccess(Objects.equals(API_CODE_SUCCESS, sendResult.getStr("Code"))) + .setApiRequestId(responseResult.getStr("RequestId")) + .setSerialNo(sendResult.getStr("SerialNo")) + .setApiMsg(sendResult.getStr("Message")); + } + + @Override + public List parseSmsReceiveStatus(String text) { + JSONArray statuses = JSONUtil.parseArray(text); + // 字段参考 + return convertList(statuses, status -> { + JSONObject statusObj = (JSONObject) status; + return new SmsReceiveRespDTO() + .setSuccess("SUCCESS".equals(statusObj.getStr("report_status"))) // 是否接收成功 + .setErrorCode(statusObj.getStr("errmsg")) // 状态报告编码 + .setMobile(statusObj.getStr("mobile")) // 手机号 + .setReceiveTime(statusObj.getLocalDateTime("user_receive_time", null)) // 状态报告时间 + .setSerialNo(statusObj.getStr("sid")); // 发送序列号 + }); + } + + @Override + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { + // 1. 构建请求 + // 参考链接 https://cloud.tencent.com/document/product/382/52067 + TreeMap body = new TreeMap<>(); + body.put("International", INTERNATIONAL_CHINA); + body.put("TemplateIdSet", new Integer[]{Integer.valueOf(apiTemplateId)}); + JSONObject response = request("DescribeSmsTemplateList", body); + + // 2. 解析请求 + JSONObject statusResult = response.getJSONObject("Response") + .getJSONArray("DescribeTemplateStatusSet").getJSONObject(0); + return new SmsTemplateRespDTO().setId(apiTemplateId) + .setContent(statusResult.get("TemplateContent").toString()) + .setAuditStatus(convertSmsTemplateAuditStatus(statusResult.getInt("StatusCode"))) + .setAuditReason(statusResult.get("ReviewReply").toString()); + } + + @VisibleForTesting + Integer convertSmsTemplateAuditStatus(int templateStatus) { + switch (templateStatus) { + case 1: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); + case 0: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); + case -1: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); + default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); + } + } + + /** + * 请求腾讯云短信 + * + * @see 签名方法 v3 + * + * @param action 请求的 API 名称 + * @param body 请求参数 + * @return 请求结果 + */ + private JSONObject request(String action, TreeMap body) { + // 1.1 请求 Header + Map headers = new HashMap<>(); + headers.put("Content-Type", "application/json; charset=utf-8"); + headers.put("Host", HOST); + headers.put("X-TC-Action", action); + Date now = new Date(); + String nowStr = FastDateFormat.getInstance("yyyy-MM-dd", TimeZone.getTimeZone("UTC")).format(now); + headers.put("X-TC-Timestamp", String.valueOf(now.getTime() / 1000)); + headers.put("X-TC-Version", VERSION); + headers.put("X-TC-Region", REGION); + + // 1.2 构建签名 Header + String canonicalQueryString = ""; + String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + + "host:" + HOST + "\n" + "x-tc-action:" + action.toLowerCase() + "\n"; + String signedHeaders = "content-type;host;x-tc-action"; + String canonicalRequest = "POST" + "\n" + "/" + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + + signedHeaders + "\n" + sha256Hex(JSONUtil.toJsonStr(body)); + String credentialScope = nowStr + "/" + "sms" + "/" + "tc3_request"; + String stringToSign = "TC3-HMAC-SHA256" + "\n" + now.getTime() / 1000 + "\n" + credentialScope + "\n" + + sha256Hex(canonicalRequest); + byte[] secretService = hmac256(hmac256(("TC3" + properties.getApiSecret()).getBytes(StandardCharsets.UTF_8), nowStr), "sms"); + String signature = HexUtil.encodeHexStr(hmac256(hmac256(secretService, "tc3_request"), stringToSign)); + headers.put("Authorization", "TC3-HMAC-SHA256" + " " + "Credential=" + getApiKey() + "/" + credentialScope + ", " + + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature); + + // 2. 发起请求 + String responseBody = HttpUtils.post("https://" + HOST, headers, JSONUtil.toJsonStr(body)); + return JSONUtil.parseObj(responseBody); + } + + private static byte[] hmac256(byte[] key, String msg) { + return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, key).digest(msg); + } + +} \ No newline at end of file diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsChannelEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsChannelEnum.java new file mode 100644 index 0000000..cbbde69 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsChannelEnum.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.enums; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 短信渠道枚举 + * + * @author zzf + * @since 2021/1/25 10:56 + */ +@Getter +@AllArgsConstructor +public enum SmsChannelEnum { + + DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"), + ALIYUN("ALIYUN", "阿里云"), + TENCENT("TENCENT", "腾讯云"), + HUAWEI("HUAWEI", "华为云"), + QINIU("QINIU", "七牛云"), + ; + + /** + * 编码 + */ + private final String code; + /** + * 名字 + */ + private final String name; + + public static SmsChannelEnum getByCode(String code) { + return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values()); + } + +} + diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java new file mode 100644 index 0000000..489f69e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 短信模板的审核状态枚举 + * + * @author 芋道源码 + */ +@AllArgsConstructor +@Getter +public enum SmsTemplateAuditStatusEnum { + + CHECKING(1), + SUCCESS(2), + FAIL(3); + + private final Integer status; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/property/SmsChannelProperties.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/property/SmsChannelProperties.java new file mode 100644 index 0000000..0362b92 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/property/SmsChannelProperties.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.property; + +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; +import lombok.Data; +import org.springframework.validation.annotation.Validated; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * 短信渠道配置类 + * + * @author zzf + * @since 2021/1/25 17:01 + */ +@Data +@Validated +public class SmsChannelProperties { + + /** + * 渠道编号 + */ + @NotNull(message = "短信渠道 ID 不能为空") + private Long id; + /** + * 短信签名 + */ + @NotEmpty(message = "短信签名不能为空") + private String signature; + /** + * 渠道编码 + * + * 枚举 {@link SmsChannelEnum} + */ + @NotEmpty(message = "渠道编码不能为空") + private String code; + /** + * 短信 API 的账号 + */ + @NotEmpty(message = "短信 API 的账号不能为空") + private String apiKey; + /** + * 短信 API 的密钥 + */ + @NotEmpty(message = "短信 API 的密钥不能为空") + private String apiSecret; + /** + * 短信发送回调 URL + */ + private String callbackUrl; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/web/config/SystemWebConfiguration.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/web/config/SystemWebConfiguration.java new file mode 100644 index 0000000..9e6d662 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/web/config/SystemWebConfiguration.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.system.framework.web.config; + +import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration; +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * system 模块的 web 组件的 Configuration + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +public class SystemWebConfiguration { + + /** + * system 模块的 API 分组 + */ + @Bean + public GroupedOpenApi systemGroupedOpenApi() { + return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("system"); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/web/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/web/package-info.java new file mode 100644 index 0000000..1a17a3d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/web/package-info.java @@ -0,0 +1,4 @@ +/** + * system 模块的 web 配置 + */ +package cn.iocoder.yudao.module.system.framework.web; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/job/DemoJob.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/job/DemoJob.java new file mode 100644 index 0000000..62b8862 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/job/DemoJob.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.job; + +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +@Component +public class DemoJob implements JobHandler { + + @Resource + private AdminUserMapper adminUserMapper; + + @Override + @TenantJob // 标记多租户 + public String execute(String param) { + System.out.println("当前租户:" + TenantContextHolder.getTenantId()); + List users = adminUserMapper.selectList(); + return "用户数量:" + users.size(); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/job/package-info.java index a7f5954..a7f5954 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/package-info.java +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/job/package-info.java diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java new file mode 100644 index 0000000..2d55e44 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.system.mq.consumer.mail; + +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; +import cn.iocoder.yudao.module.system.service.mail.MailSendService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +/** + * 针对 {@link MailSendMessage} 的消费者 + * + * @author 芋道源码 + */ +@Component +@Slf4j +public class MailSendConsumer { + + @Resource + private MailSendService mailSendService; + + @EventListener + @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步 + public void onMessage(MailSendMessage message) { + log.info("[onMessage][消息内容({})]", message); + mailSendService.doSendMail(message); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsSendConsumer.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsSendConsumer.java new file mode 100644 index 0000000..f1d9602 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsSendConsumer.java @@ -0,0 +1,31 @@ +package cn.iocoder.yudao.module.system.mq.consumer.sms; + +import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; +import cn.iocoder.yudao.module.system.service.sms.SmsSendService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +/** + * 针对 {@link SmsSendMessage} 的消费者 + * + * @author zzf + */ +@Component +@Slf4j +public class SmsSendConsumer { + + @Resource + private SmsSendService smsSendService; + + @EventListener + @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步 + public void onMessage(SmsSendMessage message) { + log.info("[onMessage][消息内容({})]", message); + smsSendService.doSendSms(message); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java new file mode 100644 index 0000000..8d5af7c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java @@ -0,0 +1,47 @@ +package cn.iocoder.yudao.module.system.mq.message.mail; + +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +/** + * 邮箱发送消息 + * + * @author 芋道源码 + */ +@Data +public class MailSendMessage { + + /** + * 邮件日志编号 + */ + @NotNull(message = "邮件日志编号不能为空") + private Long logId; + /** + * 接收邮件地址 + */ + @NotNull(message = "接收邮件地址不能为空") + private String mail; + /** + * 邮件账号编号 + */ + @NotNull(message = "邮件账号编号不能为空") + private Long accountId; + + /** + * 邮件发件人 + */ + private String nickname; + /** + * 邮件标题 + */ + @NotEmpty(message = "邮件标题不能为空") + private String title; + /** + * 邮件内容 + */ + @NotEmpty(message = "邮件内容不能为空") + private String content; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsSendMessage.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsSendMessage.java new file mode 100644 index 0000000..21c0c13 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsSendMessage.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.module.system.mq.message.sms; + +import cn.iocoder.yudao.framework.common.core.KeyValue; +import lombok.Data; + +import jakarta.validation.constraints.NotNull; +import java.util.List; + +/** + * 短信发送消息 + * + * @author 芋道源码 + */ +@Data +public class SmsSendMessage { + + /** + * 短信日志编号 + */ + @NotNull(message = "短信日志编号不能为空") + private Long logId; + /** + * 手机号 + */ + @NotNull(message = "手机号不能为空") + private String mobile; + /** + * 短信渠道编号 + */ + @NotNull(message = "短信渠道编号不能为空") + private Long channelId; + /** + * 短信 API 的模板编号 + */ + @NotNull(message = "短信 API 的模板编号不能为空") + private String apiTemplateId; + /** + * 短信模板参数 + */ + private List> templateParams; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java new file mode 100644 index 0000000..5a44218 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.system.mq.producer.mail; + +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; + +/** + * Mail 邮件相关消息的 Producer + * + * @author wangjingyi + * @since 2021/4/19 13:33 + */ +@Slf4j +@Component +public class MailProducer { + + @Resource + private ApplicationContext applicationContext; + + /** + * 发送 {@link MailSendMessage} 消息 + * + * @param sendLogId 发送日志编码 + * @param mail 接收邮件地址 + * @param accountId 邮件账号编号 + * @param nickname 邮件发件人 + * @param title 邮件标题 + * @param content 邮件内容 + */ + public void sendMailSendMessage(Long sendLogId, String mail, Long accountId, + String nickname, String title, String content) { + MailSendMessage message = new MailSendMessage() + .setLogId(sendLogId).setMail(mail).setAccountId(accountId) + .setNickname(nickname).setTitle(title).setContent(content); + applicationContext.publishEvent(message); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java new file mode 100644 index 0000000..cb3d546 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java @@ -0,0 +1,41 @@ +package cn.iocoder.yudao.module.system.mq.producer.sms; + +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +import jakarta.annotation.Resource; +import java.util.List; + +/** + * Sms 短信相关消息的 Producer + * + * @author zzf + * @since 2021/3/9 16:35 + */ +@Slf4j +@Component +public class SmsProducer { + + @Resource + private ApplicationContext applicationContext; + + /** + * 发送 {@link SmsSendMessage} 消息 + * + * @param logId 短信日志编号 + * @param mobile 手机号 + * @param channelId 渠道编号 + * @param apiTemplateId 短信模板编号 + * @param templateParams 短信模板参数 + */ + public void sendSmsSendMessage(Long logId, String mobile, + Long channelId, String apiTemplateId, List> templateParams) { + SmsSendMessage message = new SmsSendMessage().setLogId(logId).setMobile(mobile); + message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams); + applicationContext.publishEvent(message); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/package-info.java new file mode 100644 index 0000000..df82ac7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/package-info.java @@ -0,0 +1,8 @@ +/** + * system 模块下,我们放通用业务,支撑上层的核心业务。 + * 例如说:用户、部门、权限、数据字典等等 + * + * 1. Controller URL:以 /system/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 system_ 开头,方便在数据库中区分 + */ +package cn.iocoder.yudao.module.system; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java new file mode 100644 index 0000000..3901e08 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java @@ -0,0 +1,88 @@ +package cn.iocoder.yudao.module.system.service.auth; + +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; + +import jakarta.validation.Valid; + +/** + * 管理后台的认证 Service 接口 + * + * 提供用户的登录、登出的能力 + * + * @author 芋道源码 + */ +public interface AdminAuthService { + + /** + * 验证账号 + 密码。如果通过,则返回用户 + * + * @param username 账号 + * @param password 密码 + * @return 用户 + */ + AdminUserDO authenticate(String username, String password); + + /** + * 账号登录 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AuthLoginRespVO login(@Valid AuthLoginReqVO reqVO); + + /** + * 基于 token 退出登录 + * + * @param token token + * @param logType 登出类型 + */ + void logout(String token, Integer logType); + + /** + * 短信验证码发送 + * + * @param reqVO 发送请求 + */ + void sendSmsCode(AuthSmsSendReqVO reqVO); + + /** + * 短信登录 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO); + + /** + * 社交快捷登录,使用 code 授权码 + * + * @param reqVO 登录信息 + * @return 登录结果 + */ + AuthLoginRespVO socialLogin(@Valid AuthSocialLoginReqVO reqVO); + + /** + * 刷新访问令牌 + * + * @param refreshToken 刷新令牌 + * @return 登录结果 + */ + AuthLoginRespVO refreshToken(String refreshToken); + + /** + * 用户注册 + * + * @param createReqVO 注册用户 + * @return 注册结果 + */ + AuthLoginRespVO register(AuthRegisterReqVO createReqVO); + + /** + * 重置密码 + * + * @param reqVO 验证码信息 + */ + void resetPassword(AuthResetPasswordReqVO reqVO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java new file mode 100644 index 0000000..94e589d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java @@ -0,0 +1,303 @@ +package cn.iocoder.yudao.module.system.service.auth; + +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; +import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; +import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; +import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; +import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import cn.iocoder.yudao.module.system.service.logger.LoginLogService; +import cn.iocoder.yudao.module.system.service.member.MemberService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; +import cn.iocoder.yudao.module.system.service.social.SocialUserService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.anji.captcha.model.common.ResponseModel; +import com.anji.captcha.model.vo.CaptchaVO; +import com.anji.captcha.service.CaptchaService; +import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Resource; +import jakarta.validation.Validator; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * Auth Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class AdminAuthServiceImpl implements AdminAuthService { + + @Resource + private AdminUserService userService; + @Resource + private LoginLogService loginLogService; + @Resource + private OAuth2TokenService oauth2TokenService; + @Resource + private SocialUserService socialUserService; + @Resource + private MemberService memberService; + @Resource + private Validator validator; + @Resource + private CaptchaService captchaService; + @Resource + private SmsCodeApi smsCodeApi; + + /** + * 验证码的开关,默认为 true + */ + @Value("${yudao.captcha.enable:true}") + @Setter // 为了单测:开启或者关闭验证码 + private Boolean captchaEnable; + + @Override + public AdminUserDO authenticate(String username, String password) { + final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; + // 校验账号是否存在 + AdminUserDO user = userService.getUserByUsername(username); + if (user == null) { + createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); + } + if (!userService.isPasswordMatch(password, user.getPassword())) { + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); + } + // 校验是否禁用 + if (CommonStatusEnum.isDisable(user.getStatus())) { + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED); + throw exception(AUTH_LOGIN_USER_DISABLED); + } + return user; + } + + @Override + public AuthLoginRespVO login(AuthLoginReqVO reqVO) { + // 校验验证码 + validateCaptcha(reqVO); + + // 使用账号密码,进行登录 + AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword()); + + // 如果 socialType 非空,说明需要绑定社交用户 + if (reqVO.getSocialType() != null) { + socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), + reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); + } + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); + } + + @Override + public void sendSmsCode(AuthSmsSendReqVO reqVO) { + // 如果是重置密码场景,需要校验图形验证码是否正确 + if (Objects.equals(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene(), reqVO.getScene())) { + ResponseModel response = doValidateCaptcha(reqVO); + if (!response.isSuccess()) { + throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); + } + } + + // 登录场景,验证是否存在 + if (userService.getUserByMobile(reqVO.getMobile()) == null) { + throw exception(AUTH_MOBILE_NOT_EXISTS); + } + // 发送验证码 + smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); + } + + @Override + public AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) { + // 校验验证码 + smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP())); + + // 获得用户信息 + AdminUserDO user = userService.getUserByMobile(reqVO.getMobile()); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); + } + + private void createLoginLog(Long userId, String username, + LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { + // 插入登录日志 + LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); + reqDTO.setLogType(logTypeEnum.getType()); + reqDTO.setTraceId(TracerUtils.getTraceId()); + reqDTO.setUserId(userId); + reqDTO.setUserType(getUserType().getValue()); + reqDTO.setUsername(username); + reqDTO.setUserAgent(ServletUtils.getUserAgent()); + reqDTO.setUserIp(ServletUtils.getClientIP()); + reqDTO.setResult(loginResult.getResult()); + loginLogService.createLoginLog(reqDTO); + // 更新最后登录时间 + if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { + userService.updateUserLogin(userId, ServletUtils.getClientIP()); + } + } + + @Override + public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) { + // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 + SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), + reqVO.getCode(), reqVO.getState()); + if (socialUser == null || socialUser.getUserId() == null) { + throw exception(AUTH_THIRD_LOGIN_NOT_BIND); + } + + // 获得用户 + AdminUserDO user = userService.getUser(socialUser.getUserId()); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + + // 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); + } + + @VisibleForTesting + void validateCaptcha(AuthLoginReqVO reqVO) { + ResponseModel response = doValidateCaptcha(reqVO); + // 校验验证码 + if (!response.isSuccess()) { + // 创建登录失败日志(验证码不正确) + createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR); + throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg()); + } + } + + private ResponseModel doValidateCaptcha(CaptchaVerificationReqVO reqVO) { + // 如果验证码关闭,则不进行校验 + if (!captchaEnable) { + return ResponseModel.success(); + } + ValidationUtils.validate(validator, reqVO, CaptchaVerificationReqVO.CodeEnableGroup.class); + CaptchaVO captchaVO = new CaptchaVO(); + captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification()); + return captchaService.verification(captchaVO); + } + + private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) { + // 插入登陆日志 + createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS); + // 创建访问令牌 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(), + OAuth2ClientConstants.CLIENT_ID_DEFAULT, null); + // 构建返回结果 + return AuthConvert.INSTANCE.convert(accessTokenDO); + } + + @Override + public AuthLoginRespVO refreshToken(String refreshToken) { + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT); + return AuthConvert.INSTANCE.convert(accessTokenDO); + } + + @Override + public void logout(String token, Integer logType) { + // 删除访问令牌 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token); + if (accessTokenDO == null) { + return; + } + // 删除成功,则记录登出日志 + createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType); + } + + private void createLogoutLog(Long userId, Integer userType, Integer logType) { + LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); + reqDTO.setLogType(logType); + reqDTO.setTraceId(TracerUtils.getTraceId()); + reqDTO.setUserId(userId); + reqDTO.setUserType(userType); + if (ObjectUtil.equal(getUserType().getValue(), userType)) { + reqDTO.setUsername(getUsername(userId)); + } else { + reqDTO.setUsername(memberService.getMemberUserMobile(userId)); + } + reqDTO.setUserAgent(ServletUtils.getUserAgent()); + reqDTO.setUserIp(ServletUtils.getClientIP()); + reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); + loginLogService.createLoginLog(reqDTO); + } + + private String getUsername(Long userId) { + if (userId == null) { + return null; + } + AdminUserDO user = userService.getUser(userId); + return user != null ? user.getUsername() : null; + } + + private UserTypeEnum getUserType() { + return UserTypeEnum.ADMIN; + } + + @Override + public AuthLoginRespVO register(AuthRegisterReqVO registerReqVO) { + // 1. 校验验证码 + validateCaptcha(registerReqVO); + + // 2. 校验用户名是否已存在 + Long userId = userService.registerUser(registerReqVO); + + // 3. 创建 Token 令牌,记录登录日志 + return createTokenAfterLoginSuccess(userId, registerReqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); + } + + @VisibleForTesting + void validateCaptcha(AuthRegisterReqVO reqVO) { + ResponseModel response = doValidateCaptcha(reqVO); + // 验证不通过 + if (!response.isSuccess()) { + throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resetPassword(AuthResetPasswordReqVO reqVO) { + AdminUserDO userByMobile = userService.getUserByMobile(reqVO.getMobile()); + if (userByMobile == null) { + throw exception(USER_MOBILE_NOT_EXISTS); + } + + smsCodeApi.useSmsCode(new SmsCodeUseReqDTO() + .setCode(reqVO.getCode()) + .setMobile(reqVO.getMobile()) + .setScene(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene()) + .setUsedIp(getClientIP()) + ); + + userService.updateUserPassword(userByMobile.getId(), reqVO.getPassword()); + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java new file mode 100644 index 0000000..a0b765e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java @@ -0,0 +1,117 @@ +package cn.iocoder.yudao.module.system.service.dept; + +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; + +import java.util.*; + +/** + * 部门 Service 接口 + * + * @author 芋道源码 + */ +public interface DeptService { + + /** + * 创建部门 + * + * @param createReqVO 部门信息 + * @return 部门编号 + */ + Long createDept(DeptSaveReqVO createReqVO); + + /** + * 更新部门 + * + * @param updateReqVO 部门信息 + */ + void updateDept(DeptSaveReqVO updateReqVO); + + /** + * 删除部门 + * + * @param id 部门编号 + */ + void deleteDept(Long id); + + /** + * 获得部门信息 + * + * @param id 部门编号 + * @return 部门信息 + */ + DeptDO getDept(Long id); + + /** + * 获得部门信息数组 + * + * @param ids 部门编号数组 + * @return 部门信息数组 + */ + List getDeptList(Collection ids); + + /** + * 筛选部门列表 + * + * @param reqVO 筛选条件请求 VO + * @return 部门列表 + */ + List getDeptList(DeptListReqVO reqVO); + + /** + * 获得指定编号的部门 Map + * + * @param ids 部门编号数组 + * @return 部门 Map + */ + default Map getDeptMap(Collection ids) { + List list = getDeptList(ids); + return CollectionUtils.convertMap(list, DeptDO::getId); + } + + /** + * 获得指定部门的所有子部门 + * + * @param id 部门编号 + * @return 子部门列表 + */ + default List getChildDeptList(Long id) { + return getChildDeptList(Collections.singleton(id)); + } + + /** + * 获得指定部门的所有子部门 + * + * @param ids 部门编号数组 + * @return 子部门列表 + */ + List getChildDeptList(Collection ids); + + /** + * 获得指定领导者的部门列表 + * + * @param id 领导者编号 + * @return 部门列表 + */ + List getDeptListByLeaderUserId(Long id); + + /** + * 获得所有子部门,从缓存中 + * + * @param id 父部门编号 + * @return 子部门列表 + */ + Set getChildDeptIdListFromCache(Long id); + + /** + * 校验部门们是否有效。如下情况,视为无效: + * 1. 部门编号不存在 + * 2. 部门被禁用 + * + * @param ids 角色编号数组 + */ + void validateDeptList(Collection ids); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java new file mode 100644 index 0000000..c0d7b0e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java @@ -0,0 +1,223 @@ +package cn.iocoder.yudao.module.system.service.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 部门 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class DeptServiceImpl implements DeptService { + + @Resource + private DeptMapper deptMapper; + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 + public Long createDept(DeptSaveReqVO createReqVO) { + if (createReqVO.getParentId() == null) { + createReqVO.setParentId(DeptDO.PARENT_ID_ROOT); + } + // 校验父部门的有效性 + validateParentDept(null, createReqVO.getParentId()); + // 校验部门名的唯一性 + validateDeptNameUnique(null, createReqVO.getParentId(), createReqVO.getName()); + + // 插入部门 + DeptDO dept = BeanUtils.toBean(createReqVO, DeptDO.class); + deptMapper.insert(dept); + return dept.getId(); + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 + public void updateDept(DeptSaveReqVO updateReqVO) { + if (updateReqVO.getParentId() == null) { + updateReqVO.setParentId(DeptDO.PARENT_ID_ROOT); + } + // 校验自己存在 + validateDeptExists(updateReqVO.getId()); + // 校验父部门的有效性 + validateParentDept(updateReqVO.getId(), updateReqVO.getParentId()); + // 校验部门名的唯一性 + validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); + + // 更新部门 + DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class); + deptMapper.updateById(updateObj); + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 + public void deleteDept(Long id) { + // 校验是否存在 + validateDeptExists(id); + // 校验是否有子部门 + if (deptMapper.selectCountByParentId(id) > 0) { + throw exception(DEPT_EXITS_CHILDREN); + } + // 删除部门 + deptMapper.deleteById(id); + } + + @VisibleForTesting + void validateDeptExists(Long id) { + if (id == null) { + return; + } + DeptDO dept = deptMapper.selectById(id); + if (dept == null) { + throw exception(DEPT_NOT_FOUND); + } + } + + @VisibleForTesting + void validateParentDept(Long id, Long parentId) { + if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) { + return; + } + // 1. 不能设置自己为父部门 + if (Objects.equals(id, parentId)) { + throw exception(DEPT_PARENT_ERROR); + } + // 2. 父部门不存在 + DeptDO parentDept = deptMapper.selectById(parentId); + if (parentDept == null) { + throw exception(DEPT_PARENT_NOT_EXITS); + } + // 3. 递归校验父部门,如果父部门是自己的子部门,则报错,避免形成环路 + if (id == null) { // id 为空,说明新增,不需要考虑环路 + return; + } + for (int i = 0; i < Short.MAX_VALUE; i++) { + // 3.1 校验环路 + parentId = parentDept.getParentId(); + if (Objects.equals(id, parentId)) { + throw exception(DEPT_PARENT_IS_CHILD); + } + // 3.2 继续递归下一级父部门 + if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) { + break; + } + parentDept = deptMapper.selectById(parentId); + if (parentDept == null) { + break; + } + } + } + + @VisibleForTesting + void validateDeptNameUnique(Long id, Long parentId, String name) { + DeptDO dept = deptMapper.selectByParentIdAndName(parentId, name); + if (dept == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的部门 + if (id == null) { + throw exception(DEPT_NAME_DUPLICATE); + } + if (ObjectUtil.notEqual(dept.getId(), id)) { + throw exception(DEPT_NAME_DUPLICATE); + } + } + + @Override + public DeptDO getDept(Long id) { + return deptMapper.selectById(id); + } + + @Override + public List getDeptList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return deptMapper.selectBatchIds(ids); + } + + @Override + public List getDeptList(DeptListReqVO reqVO) { + List list = deptMapper.selectList(reqVO); + list.sort(Comparator.comparing(DeptDO::getSort)); + return list; + } + + @Override + public List getChildDeptList(Collection ids) { + List children = new LinkedList<>(); + // 遍历每一层 + Collection parentIds = ids; + for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下,存在死循环 + // 查询当前层,所有的子部门 + List depts = deptMapper.selectListByParentId(parentIds); + // 1. 如果没有子部门,则结束遍历 + if (CollUtil.isEmpty(depts)) { + break; + } + // 2. 如果有子部门,继续遍历 + children.addAll(depts); + parentIds = convertSet(depts, DeptDO::getId); + } + return children; + } + + @Override + public List getDeptListByLeaderUserId(Long id) { + return deptMapper.selectListByLeaderUserId(id); + } + + @Override + @DataPermission(enable = false) // 禁用数据权限,避免建立不正确的缓存 + @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id") + public Set getChildDeptIdListFromCache(Long id) { + List children = getChildDeptList(id); + return convertSet(children, DeptDO::getId); + } + + @Override + public void validateDeptList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得科室信息 + Map deptMap = getDeptMap(ids); + // 校验 + ids.forEach(id -> { + DeptDO dept = deptMap.get(id); + if (dept == null) { + throw exception(DEPT_NOT_FOUND); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) { + throw exception(DEPT_NOT_ENABLE, dept.getName()); + } + }); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostService.java new file mode 100644 index 0000000..25604ce --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostService.java @@ -0,0 +1,84 @@ +package cn.iocoder.yudao.module.system.service.dept; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import org.springframework.lang.Nullable; + +import java.util.Collection; +import java.util.List; + +/** + * 岗位 Service 接口 + * + * @author 芋道源码 + */ +public interface PostService { + + /** + * 创建岗位 + * + * @param createReqVO 岗位信息 + * @return 岗位编号 + */ + Long createPost(PostSaveReqVO createReqVO); + + /** + * 更新岗位 + * + * @param updateReqVO 岗位信息 + */ + void updatePost(PostSaveReqVO updateReqVO); + + /** + * 删除岗位信息 + * + * @param id 岗位编号 + */ + void deletePost(Long id); + + /** + * 获得岗位列表 + * + * @param ids 岗位编号数组 + * @return 部门列表 + */ + List getPostList(@Nullable Collection ids); + + /** + * 获得符合条件的岗位列表 + * + * @param ids 岗位编号数组。如果为空,不进行筛选 + * @param statuses 状态数组。如果为空,不进行筛选 + * @return 部门列表 + */ + List getPostList(@Nullable Collection ids, + @Nullable Collection statuses); + + /** + * 获得岗位分页列表 + * + * @param reqVO 分页条件 + * @return 部门分页列表 + */ + PageResult getPostPage(PostPageReqVO reqVO); + + /** + * 获得岗位信息 + * + * @param id 岗位编号 + * @return 岗位信息 + */ + PostDO getPost(Long id); + + /** + * 校验岗位们是否有效。如下情况,视为无效: + * 1. 岗位编号不存在 + * 2. 岗位被禁用 + * + * @param ids 岗位编号数组 + */ + void validatePostList(Collection ids); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImpl.java new file mode 100644 index 0000000..5e568b0 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImpl.java @@ -0,0 +1,153 @@ +package cn.iocoder.yudao.module.system.service.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 岗位 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class PostServiceImpl implements PostService { + + @Resource + private PostMapper postMapper; + + @Override + public Long createPost(PostSaveReqVO createReqVO) { + // 校验正确性 + validatePostForCreateOrUpdate(null, createReqVO.getName(), createReqVO.getCode()); + + // 插入岗位 + PostDO post = BeanUtils.toBean(createReqVO, PostDO.class); + postMapper.insert(post); + return post.getId(); + } + + @Override + public void updatePost(PostSaveReqVO updateReqVO) { + // 校验正确性 + validatePostForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getName(), updateReqVO.getCode()); + + // 更新岗位 + PostDO updateObj = BeanUtils.toBean(updateReqVO, PostDO.class); + postMapper.updateById(updateObj); + } + + @Override + public void deletePost(Long id) { + // 校验是否存在 + validatePostExists(id); + // 删除部门 + postMapper.deleteById(id); + } + + private void validatePostForCreateOrUpdate(Long id, String name, String code) { + // 校验自己存在 + validatePostExists(id); + // 校验岗位名的唯一性 + validatePostNameUnique(id, name); + // 校验岗位编码的唯一性 + validatePostCodeUnique(id, code); + } + + private void validatePostNameUnique(Long id, String name) { + PostDO post = postMapper.selectByName(name); + if (post == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的岗位 + if (id == null) { + throw exception(POST_NAME_DUPLICATE); + } + if (!post.getId().equals(id)) { + throw exception(POST_NAME_DUPLICATE); + } + } + + private void validatePostCodeUnique(Long id, String code) { + PostDO post = postMapper.selectByCode(code); + if (post == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的岗位 + if (id == null) { + throw exception(POST_CODE_DUPLICATE); + } + if (!post.getId().equals(id)) { + throw exception(POST_CODE_DUPLICATE); + } + } + + private void validatePostExists(Long id) { + if (id == null) { + return; + } + if (postMapper.selectById(id) == null) { + throw exception(POST_NOT_FOUND); + } + } + + @Override + public List getPostList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return postMapper.selectBatchIds(ids); + } + + @Override + public List getPostList(Collection ids, Collection statuses) { + return postMapper.selectList(ids, statuses); + } + + @Override + public PageResult getPostPage(PostPageReqVO reqVO) { + return postMapper.selectPage(reqVO); + } + + @Override + public PostDO getPost(Long id) { + return postMapper.selectById(id); + } + + @Override + public void validatePostList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得岗位信息 + List posts = postMapper.selectBatchIds(ids); + Map postMap = convertMap(posts, PostDO::getId); + // 校验 + ids.forEach(id -> { + PostDO post = postMap.get(id); + if (post == null) { + throw exception(POST_NOT_FOUND); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(post.getStatus())) { + throw exception(POST_NOT_ENABLE, post.getName()); + } + }); + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java new file mode 100644 index 0000000..fc2498f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java @@ -0,0 +1,110 @@ +package cn.iocoder.yudao.module.system.service.dict; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; +import org.springframework.lang.Nullable; + +import java.util.Collection; +import java.util.List; + +/** + * 字典数据 Service 接口 + * + * @author ruoyi + */ +public interface DictDataService { + + /** + * 创建字典数据 + * + * @param createReqVO 字典数据信息 + * @return 字典数据编号 + */ + Long createDictData(DictDataSaveReqVO createReqVO); + + /** + * 更新字典数据 + * + * @param updateReqVO 字典数据信息 + */ + void updateDictData(DictDataSaveReqVO updateReqVO); + + /** + * 删除字典数据 + * + * @param id 字典数据编号 + */ + void deleteDictData(Long id); + + /** + * 获得字典数据列表 + * + * @param status 状态 + * @param dictType 字典类型 + * @return 字典数据全列表 + */ + List getDictDataList(@Nullable Integer status, @Nullable String dictType); + + /** + * 获得字典数据分页列表 + * + * @param pageReqVO 分页请求 + * @return 字典数据分页列表 + */ + PageResult getDictDataPage(DictDataPageReqVO pageReqVO); + + /** + * 获得字典数据详情 + * + * @param id 字典数据编号 + * @return 字典数据 + */ + DictDataDO getDictData(Long id); + + /** + * 获得指定字典类型的数据数量 + * + * @param dictType 字典类型 + * @return 数据数量 + */ + long getDictDataCountByDictType(String dictType); + + /** + * 校验字典数据们是否有效。如下情况,视为无效: + * 1. 字典数据不存在 + * 2. 字典数据被禁用 + * + * @param dictType 字典类型 + * @param values 字典数据值的数组 + */ + void validateDictDataList(String dictType, Collection values); + + /** + * 获得指定的字典数据 + * + * @param dictType 字典类型 + * @param value 字典数据值 + * @return 字典数据 + */ + DictDataDO getDictData(String dictType, String value); + + /** + * 解析获得指定的字典数据,从缓存中 + * + * @param dictType 字典类型 + * @param label 字典数据标签 + * @return 字典数据 + */ + DictDataDO parseDictData(String dictType, String label); + + /** + * 获得指定数据类型的字典数据列表 + * + * @param dictType 字典类型 + * @return 字典数据列表 + */ + List getDictDataListByDictType(String dictType); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java new file mode 100644 index 0000000..b15fd50 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java @@ -0,0 +1,179 @@ +package cn.iocoder.yudao.module.system.service.dict; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; +import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper; +import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 字典数据 Service 实现类 + * + * @author ruoyi + */ +@Service +@Slf4j +public class DictDataServiceImpl implements DictDataService { + + /** + * 排序 dictType > sort + */ + private static final Comparator COMPARATOR_TYPE_AND_SORT = Comparator + .comparing(DictDataDO::getDictType) + .thenComparingInt(DictDataDO::getSort); + + @Resource + private DictTypeService dictTypeService; + + @Resource + private DictDataMapper dictDataMapper; + + @Override + public List getDictDataList(Integer status, String dictType) { + List list = dictDataMapper.selectListByStatusAndDictType(status, dictType); + list.sort(COMPARATOR_TYPE_AND_SORT); + return list; + } + + @Override + public PageResult getDictDataPage(DictDataPageReqVO pageReqVO) { + return dictDataMapper.selectPage(pageReqVO); + } + + @Override + public DictDataDO getDictData(Long id) { + return dictDataMapper.selectById(id); + } + + @Override + public Long createDictData(DictDataSaveReqVO createReqVO) { + // 校验字典类型有效 + validateDictTypeExists(createReqVO.getDictType()); + // 校验字典数据的值的唯一性 + validateDictDataValueUnique(null, createReqVO.getDictType(), createReqVO.getValue()); + + // 插入字典类型 + DictDataDO dictData = BeanUtils.toBean(createReqVO, DictDataDO.class); + dictDataMapper.insert(dictData); + return dictData.getId(); + } + + @Override + public void updateDictData(DictDataSaveReqVO updateReqVO) { + // 校验自己存在 + validateDictDataExists(updateReqVO.getId()); + // 校验字典类型有效 + validateDictTypeExists(updateReqVO.getDictType()); + // 校验字典数据的值的唯一性 + validateDictDataValueUnique(updateReqVO.getId(), updateReqVO.getDictType(), updateReqVO.getValue()); + + // 更新字典类型 + DictDataDO updateObj = BeanUtils.toBean(updateReqVO, DictDataDO.class); + dictDataMapper.updateById(updateObj); + } + + @Override + public void deleteDictData(Long id) { + // 校验是否存在 + validateDictDataExists(id); + + // 删除字典数据 + dictDataMapper.deleteById(id); + } + + @Override + public long getDictDataCountByDictType(String dictType) { + return dictDataMapper.selectCountByDictType(dictType); + } + + @VisibleForTesting + public void validateDictDataValueUnique(Long id, String dictType, String value) { + DictDataDO dictData = dictDataMapper.selectByDictTypeAndValue(dictType, value); + if (dictData == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的字典数据 + if (id == null) { + throw exception(DICT_DATA_VALUE_DUPLICATE); + } + if (!dictData.getId().equals(id)) { + throw exception(DICT_DATA_VALUE_DUPLICATE); + } + } + + @VisibleForTesting + public void validateDictDataExists(Long id) { + if (id == null) { + return; + } + DictDataDO dictData = dictDataMapper.selectById(id); + if (dictData == null) { + throw exception(DICT_DATA_NOT_EXISTS); + } + } + + @VisibleForTesting + public void validateDictTypeExists(String type) { + DictTypeDO dictType = dictTypeService.getDictType(type); + if (dictType == null) { + throw exception(DICT_TYPE_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(dictType.getStatus())) { + throw exception(DICT_TYPE_NOT_ENABLE); + } + } + + @Override + public void validateDictDataList(String dictType, Collection values) { + if (CollUtil.isEmpty(values)) { + return; + } + Map dictDataMap = CollectionUtils.convertMap( + dictDataMapper.selectByDictTypeAndValues(dictType, values), DictDataDO::getValue); + // 校验 + values.forEach(value -> { + DictDataDO dictData = dictDataMap.get(value); + if (dictData == null) { + throw exception(DICT_DATA_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(dictData.getStatus())) { + throw exception(DICT_DATA_NOT_ENABLE, dictData.getLabel()); + } + }); + } + + @Override + public DictDataDO getDictData(String dictType, String value) { + return dictDataMapper.selectByDictTypeAndValue(dictType, value); + } + + @Override + public DictDataDO parseDictData(String dictType, String label) { + return dictDataMapper.selectByDictTypeAndLabel(dictType, label); + } + + @Override + public List getDictDataListByDictType(String dictType) { + List list = dictDataMapper.selectList(DictDataDO::getDictType, dictType); + list.sort(Comparator.comparing(DictDataDO::getSort)); + return list; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeService.java new file mode 100644 index 0000000..4091189 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeService.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.system.service.dict; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; + +import java.util.List; + +/** + * 字典类型 Service 接口 + * + * @author 芋道源码 + */ +public interface DictTypeService { + + /** + * 创建字典类型 + * + * @param createReqVO 字典类型信息 + * @return 字典类型编号 + */ + Long createDictType(DictTypeSaveReqVO createReqVO); + + /** + * 更新字典类型 + * + * @param updateReqVO 字典类型信息 + */ + void updateDictType(DictTypeSaveReqVO updateReqVO); + + /** + * 删除字典类型 + * + * @param id 字典类型编号 + */ + void deleteDictType(Long id); + + /** + * 获得字典类型分页列表 + * + * @param pageReqVO 分页请求 + * @return 字典类型分页列表 + */ + PageResult getDictTypePage(DictTypePageReqVO pageReqVO); + + /** + * 获得字典类型详情 + * + * @param id 字典类型编号 + * @return 字典类型 + */ + DictTypeDO getDictType(Long id); + + /** + * 获得字典类型详情 + * + * @param type 字典类型 + * @return 字典类型详情 + */ + DictTypeDO getDictType(String type); + + /** + * 获得全部字典类型列表 + * + * @return 字典类型列表 + */ + List getDictTypeList(); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImpl.java new file mode 100644 index 0000000..21d2b4f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImpl.java @@ -0,0 +1,140 @@ +package cn.iocoder.yudao.module.system.service.dict; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; +import cn.iocoder.yudao.module.system.dal.mysql.dict.DictTypeMapper; +import com.google.common.annotations.VisibleForTesting; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 字典类型 Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class DictTypeServiceImpl implements DictTypeService { + + @Resource + private DictDataService dictDataService; + + @Resource + private DictTypeMapper dictTypeMapper; + + @Override + public PageResult getDictTypePage(DictTypePageReqVO pageReqVO) { + return dictTypeMapper.selectPage(pageReqVO); + } + + @Override + public DictTypeDO getDictType(Long id) { + return dictTypeMapper.selectById(id); + } + + @Override + public DictTypeDO getDictType(String type) { + return dictTypeMapper.selectByType(type); + } + + @Override + public Long createDictType(DictTypeSaveReqVO createReqVO) { + // 校验字典类型的名字的唯一性 + validateDictTypeNameUnique(null, createReqVO.getName()); + // 校验字典类型的类型的唯一性 + validateDictTypeUnique(null, createReqVO.getType()); + + // 插入字典类型 + DictTypeDO dictType = BeanUtils.toBean(createReqVO, DictTypeDO.class); + dictType.setDeletedTime(LocalDateTimeUtils.EMPTY); // 唯一索引,避免 null 值 + dictTypeMapper.insert(dictType); + return dictType.getId(); + } + + @Override + public void updateDictType(DictTypeSaveReqVO updateReqVO) { + // 校验自己存在 + validateDictTypeExists(updateReqVO.getId()); + // 校验字典类型的名字的唯一性 + validateDictTypeNameUnique(updateReqVO.getId(), updateReqVO.getName()); + // 校验字典类型的类型的唯一性 + validateDictTypeUnique(updateReqVO.getId(), updateReqVO.getType()); + + // 更新字典类型 + DictTypeDO updateObj = BeanUtils.toBean(updateReqVO, DictTypeDO.class); + dictTypeMapper.updateById(updateObj); + } + + @Override + public void deleteDictType(Long id) { + // 校验是否存在 + DictTypeDO dictType = validateDictTypeExists(id); + // 校验是否有字典数据 + if (dictDataService.getDictDataCountByDictType(dictType.getType()) > 0) { + throw exception(DICT_TYPE_HAS_CHILDREN); + } + // 删除字典类型 + dictTypeMapper.updateToDelete(id, LocalDateTime.now()); + } + + @Override + public List getDictTypeList() { + return dictTypeMapper.selectList(); + } + + @VisibleForTesting + void validateDictTypeNameUnique(Long id, String name) { + DictTypeDO dictType = dictTypeMapper.selectByName(name); + if (dictType == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 + if (id == null) { + throw exception(DICT_TYPE_NAME_DUPLICATE); + } + if (!dictType.getId().equals(id)) { + throw exception(DICT_TYPE_NAME_DUPLICATE); + } + } + + @VisibleForTesting + void validateDictTypeUnique(Long id, String type) { + if (StrUtil.isEmpty(type)) { + return; + } + DictTypeDO dictType = dictTypeMapper.selectByType(type); + if (dictType == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 + if (id == null) { + throw exception(DICT_TYPE_TYPE_DUPLICATE); + } + if (!dictType.getId().equals(id)) { + throw exception(DICT_TYPE_TYPE_DUPLICATE); + } + } + + @VisibleForTesting + DictTypeDO validateDictTypeExists(Long id) { + if (id == null) { + return null; + } + DictTypeDO dictType = dictTypeMapper.selectById(id); + if (dictType == null) { + throw exception(DICT_TYPE_NOT_EXISTS); + } + return dictType; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogService.java new file mode 100644 index 0000000..ba9722e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogService.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.system.service.logger; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; + +import jakarta.validation.Valid; + +/** + * 登录日志 Service 接口 + */ +public interface LoginLogService { + + /** + * 获得登录日志分页 + * + * @param pageReqVO 分页条件 + * @return 登录日志分页 + */ + PageResult getLoginLogPage(LoginLogPageReqVO pageReqVO); + + /** + * 创建登录日志 + * + * @param reqDTO 日志信息 + */ + void createLoginLog(@Valid LoginLogCreateReqDTO reqDTO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImpl.java new file mode 100644 index 0000000..421b91d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImpl.java @@ -0,0 +1,35 @@ +package cn.iocoder.yudao.module.system.service.logger; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; +import cn.iocoder.yudao.module.system.dal.mysql.logger.LoginLogMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; + +/** + * 登录日志 Service 实现 + */ +@Service +@Validated +public class LoginLogServiceImpl implements LoginLogService { + + @Resource + private LoginLogMapper loginLogMapper; + + @Override + public PageResult getLoginLogPage(LoginLogPageReqVO pageReqVO) { + return loginLogMapper.selectPage(pageReqVO); + } + + @Override + public void createLoginLog(LoginLogCreateReqDTO reqDTO) { + LoginLogDO loginLog = BeanUtils.toBean(reqDTO, LoginLogDO.class); + loginLogMapper.insert(loginLog); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java new file mode 100644 index 0000000..c647e82 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.service.logger; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; + +/** + * 操作日志 Service 接口 + * + * @author 芋道源码 + */ +public interface OperateLogService { + + /** + * 记录操作日志 + * + * @param createReqDTO 创建请求 + */ + void createOperateLog(OperateLogCreateReqDTO createReqDTO); + + /** + * 获得操作日志分页列表 + * + * @param pageReqVO 分页条件 + * @return 操作日志分页列表 + */ + PageResult getOperateLogPage(OperateLogPageReqVO pageReqVO); + + /** + * 获得操作日志分页列表 + * + * @param pageReqVO 分页条件 + * @return 操作日志分页列表 + */ + PageResult getOperateLogPage(OperateLogPageReqDTO pageReqVO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java new file mode 100644 index 0000000..167be55 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.module.system.service.logger; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; +import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +/** + * 操作日志 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class OperateLogServiceImpl implements OperateLogService { + + @Resource + private OperateLogMapper operateLogMapper; + + @Override + public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { + OperateLogDO log = BeanUtils.toBean(createReqDTO, OperateLogDO.class); + operateLogMapper.insert(log); + } + + @Override + public PageResult getOperateLogPage(OperateLogPageReqVO pageReqVO) { + return operateLogMapper.selectPage(pageReqVO); + } + + @Override + public PageResult getOperateLogPage(OperateLogPageReqDTO pageReqDTO) { + return operateLogMapper.selectPage(pageReqDTO); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java new file mode 100644 index 0000000..eda9ff2 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; + +import jakarta.validation.Valid; +import java.util.List; + +/** + * 邮箱账号 Service 接口 + * + * @author wangjingyi + * @since 2022-03-21 + */ +public interface MailAccountService { + + /** + * 创建邮箱账号 + * + * @param createReqVO 邮箱账号信息 + * @return 编号 + */ + Long createMailAccount(@Valid MailAccountSaveReqVO createReqVO); + + /** + * 修改邮箱账号 + * + * @param updateReqVO 邮箱账号信息 + */ + void updateMailAccount(@Valid MailAccountSaveReqVO updateReqVO); + + /** + * 删除邮箱账号 + * + * @param id 编号 + */ + void deleteMailAccount(Long id); + + /** + * 获取邮箱账号信息 + * + * @param id 编号 + * @return 邮箱账号信息 + */ + MailAccountDO getMailAccount(Long id); + + /** + * 从缓存中获取邮箱账号 + * + * @param id 编号 + * @return 邮箱账号 + */ + MailAccountDO getMailAccountFromCache(Long id); + + /** + * 获取邮箱账号分页信息 + * + * @param pageReqVO 邮箱账号分页参数 + * @return 邮箱账号分页信息 + */ + PageResult getMailAccountPage(MailAccountPageReqVO pageReqVO); + + /** + * 获取邮箱数组信息 + * + * @return 邮箱账号信息数组 + */ + List getMailAccountList(); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java new file mode 100644 index 0000000..3a5879a --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java @@ -0,0 +1,99 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS; + +/** + * 邮箱账号 Service 实现类 + * + * @author wangjingyi + * @since 2022-03-21 + */ +@Service +@Validated +@Slf4j +public class MailAccountServiceImpl implements MailAccountService { + + @Resource + private MailAccountMapper mailAccountMapper; + + @Resource + private MailTemplateService mailTemplateService; + + @Override + public Long createMailAccount(MailAccountSaveReqVO createReqVO) { + MailAccountDO account = BeanUtils.toBean(createReqVO, MailAccountDO.class); + mailAccountMapper.insert(account); + return account.getId(); + } + + @Override + @CacheEvict(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#updateReqVO.id") + public void updateMailAccount(MailAccountSaveReqVO updateReqVO) { + // 校验是否存在 + validateMailAccountExists(updateReqVO.getId()); + + // 更新 + MailAccountDO updateObj = BeanUtils.toBean(updateReqVO, MailAccountDO.class); + mailAccountMapper.updateById(updateObj); + } + + @Override + @CacheEvict(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id") + public void deleteMailAccount(Long id) { + // 校验是否存在账号 + validateMailAccountExists(id); + // 校验是否存在关联模版 + if (mailTemplateService.getMailTemplateCountByAccountId(id) > 0) { + throw exception(MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS); + } + + // 删除 + mailAccountMapper.deleteById(id); + } + + private void validateMailAccountExists(Long id) { + if (mailAccountMapper.selectById(id) == null) { + throw exception(MAIL_ACCOUNT_NOT_EXISTS); + } + } + + @Override + public MailAccountDO getMailAccount(Long id) { + return mailAccountMapper.selectById(id); + } + + @Override + @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id", unless = "#result == null") + public MailAccountDO getMailAccountFromCache(Long id) { + return getMailAccount(id); + } + + @Override + public PageResult getMailAccountPage(MailAccountPageReqVO pageReqVO) { + return mailAccountMapper.selectPage(pageReqVO); + } + + @Override + public List getMailAccountList() { + return mailAccountMapper.selectList(); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java new file mode 100644 index 0000000..4a0b204 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java @@ -0,0 +1,61 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; + +import java.util.Map; + +/** + * 邮件日志 Service 接口 + * + * @author wangjingyi + * @since 2022-03-21 + */ +public interface MailLogService { + + /** + * 邮件日志分页 + * + * @param pageVO 分页参数 + * @return 分页结果 + */ + PageResult getMailLogPage(MailLogPageReqVO pageVO); + + /** + * 获得指定编号的邮件日志 + * + * @param id 日志编号 + * @return 邮件日志 + */ + MailLogDO getMailLog(Long id); + + /** + * 创建邮件日志 + * + * @param userId 用户编码 + * @param userType 用户类型 + * @param toMail 收件人邮件 + * @param account 邮件账号信息 + * @param template 模版信息 + * @param templateContent 模版内容 + * @param templateParams 模版参数 + * @param isSend 是否发送成功 + * @return 日志编号 + */ + Long createMailLog(Long userId, Integer userType, String toMail, + MailAccountDO account, MailTemplateDO template , + String templateContent, Map templateParams, Boolean isSend); + + /** + * 更新邮件发送结果 + * + * @param logId 日志编号 + * @param messageId 发送后的消息编号 + * @param exception 发送异常 + */ + void updateMailSendResult(Long logId, String messageId, Exception exception); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImpl.java new file mode 100644 index 0000000..827d0c5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImpl.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper; +import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.Objects; + +import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage; + +/** + * 邮件日志 Service 实现类 + * + * @author wangjingyi + * @since 2022-03-21 + */ +@Service +@Validated +public class MailLogServiceImpl implements MailLogService { + + @Resource + private MailLogMapper mailLogMapper; + + @Override + public PageResult getMailLogPage(MailLogPageReqVO pageVO) { + return mailLogMapper.selectPage(pageVO); + } + + @Override + public MailLogDO getMailLog(Long id) { + return mailLogMapper.selectById(id); + } + + @Override + public Long createMailLog(Long userId, Integer userType, String toMail, + MailAccountDO account, MailTemplateDO template, + String templateContent, Map templateParams, Boolean isSend) { + MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder(); + // 根据是否要发送,设置状态 + logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus() + : MailSendStatusEnum.IGNORE.getStatus()) + // 用户信息 + .userId(userId).userType(userType).toMail(toMail) + .accountId(account.getId()).fromMail(account.getMail()) + // 模板相关字段 + .templateId(template.getId()).templateCode(template.getCode()).templateNickname(template.getNickname()) + .templateTitle(template.getTitle()).templateContent(templateContent).templateParams(templateParams); + + // 插入数据库 + MailLogDO logDO = logDOBuilder.build(); + mailLogMapper.insert(logDO); + return logDO.getId(); + } + + @Override + public void updateMailSendResult(Long logId, String messageId, Exception exception) { + // 1. 成功 + if (exception == null) { + mailLogMapper.updateById(new MailLogDO().setId(logId).setSendTime(LocalDateTime.now()) + .setSendStatus(MailSendStatusEnum.SUCCESS.getStatus()).setSendMessageId(messageId)); + return; + } + // 2. 失败 + mailLogMapper.updateById(new MailLogDO().setId(logId).setSendTime(LocalDateTime.now()) + .setSendStatus(MailSendStatusEnum.FAILURE.getStatus()).setSendException(getRootCauseMessage(exception))); + + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java new file mode 100644 index 0000000..8988168 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java @@ -0,0 +1,60 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; + +import java.util.Map; + +/** + * 邮件发送 Service 接口 + * + * @author wangjingyi + * @since 2022-03-21 + */ +public interface MailSendService { + + /** + * 发送单条邮件给管理后台的用户 + * + * @param mail 邮箱 + * @param userId 用户编码 + * @param templateCode 邮件模版编码 + * @param templateParams 邮件模版参数 + * @return 发送日志编号 + */ + Long sendSingleMailToAdmin(String mail, Long userId, + String templateCode, Map templateParams); + + /** + * 发送单条邮件给用户 APP 的用户 + * + * @param mail 邮箱 + * @param userId 用户编码 + * @param templateCode 邮件模版编码 + * @param templateParams 邮件模版参数 + * @return 发送日志编号 + */ + Long sendSingleMailToMember(String mail, Long userId, + String templateCode, Map templateParams); + + /** + * 发送单条邮件给用户 + * + * @param mail 邮箱 + * @param userId 用户编码 + * @param userType 用户类型 + * @param templateCode 邮件模版编码 + * @param templateParams 邮件模版参数 + * @return 发送日志编号 + */ + Long sendSingleMail(String mail, Long userId, Integer userType, + String templateCode, Map templateParams); + + /** + * 执行真正的邮件发送 + * 注意,该方法仅仅提供给 MQ Consumer 使用 + * + * @param message 邮件 + */ + void doSendMail(MailSendMessage message); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java new file mode 100644 index 0000000..306b05c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java @@ -0,0 +1,173 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; +import cn.iocoder.yudao.module.system.service.member.MemberService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.dromara.hutool.extra.mail.*; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 邮箱发送 Service 实现类 + * + * @author wangjingyi + * @since 2022-03-21 + */ +@Service +@Validated +@Slf4j +public class MailSendServiceImpl implements MailSendService { + + @Resource + private AdminUserService adminUserService; + @Resource + private MemberService memberService; + + @Resource + private MailAccountService mailAccountService; + @Resource + private MailTemplateService mailTemplateService; + + @Resource + private MailLogService mailLogService; + @Resource + private MailProducer mailProducer; + + @Override + public Long sendSingleMailToAdmin(String mail, Long userId, + String templateCode, Map templateParams) { + // 如果 mail 为空,则加载用户编号对应的邮箱 + if (StrUtil.isEmpty(mail)) { + AdminUserDO user = adminUserService.getUser(userId); + if (user != null) { + mail = user.getEmail(); + } + } + // 执行发送 + return sendSingleMail(mail, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams); + } + + @Override + public Long sendSingleMailToMember(String mail, Long userId, + String templateCode, Map templateParams) { + // 如果 mail 为空,则加载用户编号对应的邮箱 + if (StrUtil.isEmpty(mail)) { + mail = memberService.getMemberUserEmail(userId); + } + // 执行发送 + return sendSingleMail(mail, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); + } + + @Override + public Long sendSingleMail(String mail, Long userId, Integer userType, + String templateCode, Map templateParams) { + // 校验邮箱模版是否合法 + MailTemplateDO template = validateMailTemplate(templateCode); + // 校验邮箱账号是否合法 + MailAccountDO account = validateMailAccount(template.getAccountId()); + + // 校验邮箱是否存在 + mail = validateMail(mail); + validateTemplateParams(template, templateParams); + + // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 + Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()); + String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams); + String content = mailTemplateService.formatMailTemplateContent(template.getContent(), templateParams); + Long sendLogId = mailLogService.createMailLog(userId, userType, mail, + account, template, content, templateParams, isSend); + // 发送 MQ 消息,异步执行发送短信 + if (isSend) { + mailProducer.sendMailSendMessage(sendLogId, mail, account.getId(), + template.getNickname(), title, content); + } + return sendLogId; + } + + @Override + public void doSendMail(MailSendMessage message) { + // 1. 创建发送账号 + MailAccountDO account = validateMailAccount(message.getAccountId()); + MailAccount mailAccount = buildMailAccount(account, message.getNickname()); + // 2. 发送邮件 + try { + String messageId = MailUtil.send(mailAccount, message.getMail(), + message.getTitle(), message.getContent(), true); + // 3. 更新结果(成功) + mailLogService.updateMailSendResult(message.getLogId(), messageId, null); + } catch (Exception e) { + // 3. 更新结果(异常) + mailLogService.updateMailSendResult(message.getLogId(), null, e); + } + } + + private MailAccount buildMailAccount(MailAccountDO account, String nickname) { + String from = StrUtil.isNotEmpty(nickname) ? nickname + " <" + account.getMail() + ">" : account.getMail(); + return new MailAccount().setFrom(from).setAuth(true) + .setUser(account.getUsername()).setPass(account.getPassword().toCharArray()) + .setHost(account.getHost()).setPort(account.getPort()) + .setSslEnable(account.getSslEnable()).setStarttlsEnable(account.getStarttlsEnable()); + } + + @VisibleForTesting + MailTemplateDO validateMailTemplate(String templateCode) { + // 获得邮件模板。考虑到效率,从缓存中获取 + MailTemplateDO template = mailTemplateService.getMailTemplateByCodeFromCache(templateCode); + // 邮件模板不存在 + if (template == null) { + throw exception(MAIL_TEMPLATE_NOT_EXISTS); + } + return template; + } + + @VisibleForTesting + MailAccountDO validateMailAccount(Long accountId) { + // 获得邮箱账号。考虑到效率,从缓存中获取 + MailAccountDO account = mailAccountService.getMailAccountFromCache(accountId); + // 邮箱账号不存在 + if (account == null) { + throw exception(MAIL_ACCOUNT_NOT_EXISTS); + } + return account; + } + + @VisibleForTesting + String validateMail(String mail) { + if (StrUtil.isEmpty(mail)) { + throw exception(MAIL_SEND_MAIL_NOT_EXISTS); + } + return mail; + } + + /** + * 校验邮件参数是否确实 + * + * @param template 邮箱模板 + * @param templateParams 参数列表 + */ + @VisibleForTesting + void validateTemplateParams(MailTemplateDO template, Map templateParams) { + template.getParams().forEach(key -> { + Object value = templateParams.get(key); + if (value == null) { + throw exception(MAIL_SEND_TEMPLATE_PARAM_MISS, key); + } + }); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java new file mode 100644 index 0000000..006972c --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; + +import jakarta.validation.Valid; +import java.util.List; +import java.util.Map; + +/** + * 邮件模版 Service 接口 + * + * @author wangjingyi + * @since 2022-03-21 + */ +public interface MailTemplateService { + + /** + * 邮件模版创建 + * + * @param createReqVO 邮件信息 + * @return 编号 + */ + Long createMailTemplate(@Valid MailTemplateSaveReqVO createReqVO); + + /** + * 邮件模版修改 + * + * @param updateReqVO 邮件信息 + */ + void updateMailTemplate(@Valid MailTemplateSaveReqVO updateReqVO); + + /** + * 邮件模版删除 + * + * @param id 编号 + */ + void deleteMailTemplate(Long id); + + /** + * 获取邮件模版 + * + * @param id 编号 + * @return 邮件模版 + */ + MailTemplateDO getMailTemplate(Long id); + + /** + * 获取邮件模版分页 + * + * @param pageReqVO 模版信息 + * @return 邮件模版分页信息 + */ + PageResult getMailTemplatePage(MailTemplatePageReqVO pageReqVO); + + /** + * 获取邮件模板数组 + * + * @return 模版数组 + */ + List getMailTemplateList(); + + /** + * 从缓存中获取邮件模版 + * + * @param code 模板编码 + * @return 邮件模板 + */ + MailTemplateDO getMailTemplateByCodeFromCache(String code); + + /** + * 邮件模版内容合成 + * + * @param content 邮件模版 + * @param params 合成参数 + * @return 格式化后的内容 + */ + String formatMailTemplateContent(String content, Map params); + + /** + * 获得指定邮件账号下的邮件模板数量 + * + * @param accountId 账号编号 + * @return 数量 + */ + long getMailTemplateCountByAccountId(Long accountId); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java new file mode 100644 index 0000000..6757750 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java @@ -0,0 +1,138 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_CODE_EXISTS; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; + +/** + * 邮箱模版 Service 实现类 + * + * @author wangjingyi + * @since 2022-03-21 + */ +@Service +@Validated +@Slf4j +public class MailTemplateServiceImpl implements MailTemplateService { + + /** + * 正则表达式,匹配 {} 中的变量 + */ + private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}"); + + @Resource + private MailTemplateMapper mailTemplateMapper; + + @Override + public Long createMailTemplate(MailTemplateSaveReqVO createReqVO) { + // 校验 code 是否唯一 + validateCodeUnique(null, createReqVO.getCode()); + + // 插入 + MailTemplateDO template = BeanUtils.toBean(createReqVO, MailTemplateDO.class) + .setParams(parseTemplateContentParams(createReqVO.getContent())); + mailTemplateMapper.insert(template); + return template.getId(); + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.MAIL_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 + public void updateMailTemplate(@Valid MailTemplateSaveReqVO updateReqVO) { + // 校验是否存在 + validateMailTemplateExists(updateReqVO.getId()); + // 校验 code 是否唯一 + validateCodeUnique(updateReqVO.getId(),updateReqVO.getCode()); + + // 更新 + MailTemplateDO updateObj = BeanUtils.toBean(updateReqVO, MailTemplateDO.class) + .setParams(parseTemplateContentParams(updateReqVO.getContent())); + mailTemplateMapper.updateById(updateObj); + } + + @VisibleForTesting + void validateCodeUnique(Long id, String code) { + MailTemplateDO template = mailTemplateMapper.selectByCode(code); + if (template == null) { + return; + } + // 存在 template 记录的情况下 + if (id == null // 新增时,说明重复 + || ObjUtil.notEqual(id, template.getId())) { // 更新时,如果 id 不一致,说明重复 + throw exception(MAIL_TEMPLATE_CODE_EXISTS); + } + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.MAIL_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 + public void deleteMailTemplate(Long id) { + // 校验是否存在 + validateMailTemplateExists(id); + + // 删除 + mailTemplateMapper.deleteById(id); + } + + private void validateMailTemplateExists(Long id) { + if (mailTemplateMapper.selectById(id) == null) { + throw exception(MAIL_TEMPLATE_NOT_EXISTS); + } + } + + @Override + public MailTemplateDO getMailTemplate(Long id) {return mailTemplateMapper.selectById(id);} + + @Override + @Cacheable(value = RedisKeyConstants.MAIL_TEMPLATE, key = "#code", unless = "#result == null") + public MailTemplateDO getMailTemplateByCodeFromCache(String code) { + return mailTemplateMapper.selectByCode(code); + } + + @Override + public PageResult getMailTemplatePage(MailTemplatePageReqVO pageReqVO) { + return mailTemplateMapper.selectPage(pageReqVO); + } + + @Override + public List getMailTemplateList() {return mailTemplateMapper.selectList();} + + @Override + public String formatMailTemplateContent(String content, Map params) { + return StrUtil.format(content, params); + } + + @VisibleForTesting + public List parseTemplateContentParams(String content) { + return ReUtil.findAllGroup1(PATTERN_PARAMS, content); + } + + @Override + public long getMailTemplateCountByAccountId(Long accountId) { + return mailTemplateMapper.selectCountByAccountId(accountId); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberService.java new file mode 100644 index 0000000..933e5bf --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberService.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.service.member; + +/** + * Member Service 接口 + * + * @author 芋道源码 + */ +public interface MemberService { + + /** + * 获得会员用户的手机号码 + * + * @param id 会员用户编号 + * @return 手机号码 + */ + String getMemberUserMobile(Long id); + + /** + * 获得会员用户的邮箱 + * + * @param id 会员用户编号 + * @return 邮箱 + */ + String getMemberUserEmail(Long id); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberServiceImpl.java new file mode 100644 index 0000000..7b31e04 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberServiceImpl.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.system.service.member; + +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +/** + * Member Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class MemberServiceImpl implements MemberService { + + @Value("${yudao.info.base-package}") + private String basePackage; + + private volatile Object memberUserApi; + + @Override + public String getMemberUserMobile(Long id) { + Object user = getMemberUser(id); + if (user == null) { + return null; + } + return ReflectUtil.invoke(user, "getMobile"); + } + + @Override + public String getMemberUserEmail(Long id) { + Object user = getMemberUser(id); + if (user == null) { + return null; + } + return ReflectUtil.invoke(user, "getEmail"); + } + + private Object getMemberUser(Long id) { + if (id == null) { + return null; + } + return ReflectUtil.invoke(getMemberUserApi(), "getUser", id); + } + + private Object getMemberUserApi() { + if (memberUserApi == null) { + memberUserApi = SpringUtil.getBean(ClassUtil.loadClass(String.format("%s.module.member.api.user.MemberUserApi", basePackage))); + } + return memberUserApi; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/member/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/member/package-info.java new file mode 100644 index 0000000..ce080b4 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/member/package-info.java @@ -0,0 +1,4 @@ +/** + * yudao-module-member 模块的适配,解除 yudao-module-system 对它们的依赖 + */ +package cn.iocoder.yudao.module.system.service.member; diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeService.java new file mode 100644 index 0000000..c935c49 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeService.java @@ -0,0 +1,51 @@ +package cn.iocoder.yudao.module.system.service.notice; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; + +/** + * 通知公告 Service 接口 + */ +public interface NoticeService { + + /** + * 创建通知公告 + * + * @param createReqVO 通知公告 + * @return 编号 + */ + Long createNotice(NoticeSaveReqVO createReqVO); + + /** + * 更新通知公告 + * + * @param reqVO 通知公告 + */ + void updateNotice(NoticeSaveReqVO reqVO); + + /** + * 删除通知公告 + * + * @param id 编号 + */ + void deleteNotice(Long id); + + /** + * 获得通知公告分页列表 + * + * @param reqVO 分页条件 + * @return 部门分页列表 + */ + PageResult getNoticePage(NoticePageReqVO reqVO); + + /** + * 获得通知公告 + * + * @param id 编号 + * @return 通知公告 + */ + NoticeDO getNotice(Long id); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImpl.java new file mode 100644 index 0000000..e6648ff --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImpl.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.system.service.notice; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; +import cn.iocoder.yudao.module.system.dal.mysql.notice.NoticeMapper; +import com.google.common.annotations.VisibleForTesting; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND; + +/** + * 通知公告 Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class NoticeServiceImpl implements NoticeService { + + @Resource + private NoticeMapper noticeMapper; + + @Override + public Long createNotice(NoticeSaveReqVO createReqVO) { + NoticeDO notice = BeanUtils.toBean(createReqVO, NoticeDO.class); + noticeMapper.insert(notice); + return notice.getId(); + } + + @Override + public void updateNotice(NoticeSaveReqVO updateReqVO) { + // 校验是否存在 + validateNoticeExists(updateReqVO.getId()); + // 更新通知公告 + NoticeDO updateObj = BeanUtils.toBean(updateReqVO, NoticeDO.class); + noticeMapper.updateById(updateObj); + } + + @Override + public void deleteNotice(Long id) { + // 校验是否存在 + validateNoticeExists(id); + // 删除通知公告 + noticeMapper.deleteById(id); + } + + @Override + public PageResult getNoticePage(NoticePageReqVO reqVO) { + return noticeMapper.selectPage(reqVO); + } + + @Override + public NoticeDO getNotice(Long id) { + return noticeMapper.selectById(id); + } + + @VisibleForTesting + public void validateNoticeExists(Long id) { + if (id == null) { + return; + } + NoticeDO notice = noticeMapper.selectById(id); + if (notice == null) { + throw exception(NOTICE_NOT_FOUND); + } + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageService.java new file mode 100644 index 0000000..b06aef3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageService.java @@ -0,0 +1,97 @@ +package cn.iocoder.yudao.module.system.service.notify; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 站内信 Service 接口 + * + * @author xrcoder + */ +public interface NotifyMessageService { + + /** + * 创建站内信 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param template 模版信息 + * @param templateContent 模版内容 + * @param templateParams 模版参数 + * @return 站内信编号 + */ + Long createNotifyMessage(Long userId, Integer userType, + NotifyTemplateDO template, String templateContent, Map templateParams); + + /** + * 获得站内信分页 + * + * @param pageReqVO 分页查询 + * @return 站内信分页 + */ + PageResult getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO); + + /** + * 获得【我的】站内信分页 + * + * @param pageReqVO 分页查询 + * @param userId 用户编号 + * @param userType 用户类型 + * @return 站内信分页 + */ + PageResult getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType); + + /** + * 获得站内信 + * + * @param id 编号 + * @return 站内信 + */ + NotifyMessageDO getNotifyMessage(Long id); + + /** + * 获得【我的】未读站内信列表 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param size 数量 + * @return 站内信列表 + */ + List getUnreadNotifyMessageList(Long userId, Integer userType, Integer size); + + /** + * 统计用户未读站内信条数 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @return 返回未读站内信条数 + */ + Long getUnreadNotifyMessageCount(Long userId, Integer userType); + + /** + * 标记站内信为已读 + * + * @param ids 站内信编号集合 + * @param userId 用户编号 + * @param userType 用户类型 + * @return 更新到的条数 + */ + int updateNotifyMessageRead(Collection ids, Long userId, Integer userType); + + /** + * 标记所有站内信为已读 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @return 更新到的条数 + */ + int updateAllNotifyMessageRead(Long userId, Integer userType); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImpl.java new file mode 100644 index 0000000..9593473 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImpl.java @@ -0,0 +1,75 @@ +package cn.iocoder.yudao.module.system.service.notify; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 站内信 Service 实现类 + * + * @author xrcoder + */ +@Service +@Validated +public class NotifyMessageServiceImpl implements NotifyMessageService { + + @Resource + private NotifyMessageMapper notifyMessageMapper; + + @Override + public Long createNotifyMessage(Long userId, Integer userType, + NotifyTemplateDO template, String templateContent, Map templateParams) { + NotifyMessageDO message = new NotifyMessageDO().setUserId(userId).setUserType(userType) + .setTemplateId(template.getId()).setTemplateCode(template.getCode()) + .setTemplateType(template.getType()).setTemplateNickname(template.getNickname()) + .setTemplateContent(templateContent).setTemplateParams(templateParams).setReadStatus(false); + notifyMessageMapper.insert(message); + return message.getId(); + } + + @Override + public PageResult getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO) { + return notifyMessageMapper.selectPage(pageReqVO); + } + + @Override + public PageResult getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType) { + return notifyMessageMapper.selectPage(pageReqVO, userId, userType); + } + + @Override + public NotifyMessageDO getNotifyMessage(Long id) { + return notifyMessageMapper.selectById(id); + } + + @Override + public List getUnreadNotifyMessageList(Long userId, Integer userType, Integer size) { + return notifyMessageMapper.selectUnreadListByUserIdAndUserType(userId, userType, size); + } + + @Override + public Long getUnreadNotifyMessageCount(Long userId, Integer userType) { + return notifyMessageMapper.selectUnreadCountByUserIdAndUserType(userId, userType); + } + + @Override + public int updateNotifyMessageRead(Collection ids, Long userId, Integer userType) { + return notifyMessageMapper.updateListRead(ids, userId, userType); + } + + @Override + public int updateAllNotifyMessageRead(Long userId, Integer userType) { + return notifyMessageMapper.updateListRead(userId, userType); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java new file mode 100644 index 0000000..d848bcb --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java @@ -0,0 +1,55 @@ +package cn.iocoder.yudao.module.system.service.notify; + +import java.util.List; +import java.util.Map; + +/** + * 站内信发送 Service 接口 + * + * @author xrcoder + */ +public interface NotifySendService { + + /** + * 发送单条站内信给管理后台的用户 + * + * 在 mobile 为空时,使用 userId 加载对应管理员的手机号 + * + * @param userId 用户编号 + * @param templateCode 短信模板编号 + * @param templateParams 短信模板参数 + * @return 发送日志编号 + */ + Long sendSingleNotifyToAdmin(Long userId, + String templateCode, Map templateParams); + /** + * 发送单条站内信给用户 APP 的用户 + * + * 在 mobile 为空时,使用 userId 加载对应会员的手机号 + * + * @param userId 用户编号 + * @param templateCode 站内信模板编号 + * @param templateParams 站内信模板参数 + * @return 发送日志编号 + */ + Long sendSingleNotifyToMember(Long userId, + String templateCode, Map templateParams); + + /** + * 发送单条站内信给用户 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param templateCode 站内信模板编号 + * @param templateParams 站内信模板参数 + * @return 发送日志编号 + */ + Long sendSingleNotify( Long userId, Integer userType, + String templateCode, Map templateParams); + + default void sendBatchNotify(List mobiles, List userIds, Integer userType, + String templateCode, Map templateParams) { + throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!"); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java new file mode 100644 index 0000000..a47d7fe --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java @@ -0,0 +1,86 @@ +package cn.iocoder.yudao.module.system.service.notify; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.Map; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 站内信发送 Service 实现类 + * + * @author xrcoder + */ +@Service +@Validated +@Slf4j +public class NotifySendServiceImpl implements NotifySendService { + + @Resource + private NotifyTemplateService notifyTemplateService; + + @Resource + private NotifyMessageService notifyMessageService; + + @Override + public Long sendSingleNotifyToAdmin(Long userId, String templateCode, Map templateParams) { + return sendSingleNotify(userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams); + } + + @Override + public Long sendSingleNotifyToMember(Long userId, String templateCode, Map templateParams) { + return sendSingleNotify(userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); + } + + @Override + public Long sendSingleNotify(Long userId, Integer userType, String templateCode, Map templateParams) { + // 校验模版 + NotifyTemplateDO template = validateNotifyTemplate(templateCode); + if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { + log.info("[sendSingleNotify][模版({})已经关闭,无法给用户({}/{})发送]", templateCode, userId, userType); + return null; + } + // 校验参数 + validateTemplateParams(template, templateParams); + + // 发送站内信 + String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), templateParams); + return notifyMessageService.createNotifyMessage(userId, userType, template, content, templateParams); + } + + @VisibleForTesting + public NotifyTemplateDO validateNotifyTemplate(String templateCode) { + // 获得站内信模板。考虑到效率,从缓存中获取 + NotifyTemplateDO template = notifyTemplateService.getNotifyTemplateByCodeFromCache(templateCode); + // 站内信模板不存在 + if (template == null) { + throw exception(NOTICE_NOT_FOUND); + } + return template; + } + + /** + * 校验站内信模版参数是否确实 + * + * @param template 邮箱模板 + * @param templateParams 参数列表 + */ + @VisibleForTesting + public void validateTemplateParams(NotifyTemplateDO template, Map templateParams) { + template.getParams().forEach(key -> { + Object value = templateParams.get(key); + if (value == null) { + throw exception(NOTIFY_SEND_TEMPLATE_PARAM_MISS, key); + } + }); + } +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java new file mode 100644 index 0000000..bac122d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java @@ -0,0 +1,73 @@ +package cn.iocoder.yudao.module.system.service.notify; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; + +import jakarta.validation.Valid; +import java.util.Map; + +/** + * 站内信模版 Service 接口 + * + * @author xrcoder + */ +public interface NotifyTemplateService { + + /** + * 创建站内信模版 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createNotifyTemplate(@Valid NotifyTemplateSaveReqVO createReqVO); + + /** + * 更新站内信模版 + * + * @param updateReqVO 更新信息 + */ + void updateNotifyTemplate(@Valid NotifyTemplateSaveReqVO updateReqVO); + + /** + * 删除站内信模版 + * + * @param id 编号 + */ + void deleteNotifyTemplate(Long id); + + /** + * 获得站内信模版 + * + * @param id 编号 + * @return 站内信模版 + */ + NotifyTemplateDO getNotifyTemplate(Long id); + + /** + * 获得站内信模板,从缓存中 + * + * @param code 模板编码 + * @return 站内信模板 + */ + NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code); + + /** + * 获得站内信模版分页 + * + * @param pageReqVO 分页查询 + * @return 站内信模版分页 + */ + PageResult getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO); + + /** + * 格式化站内信内容 + * + * @param content 站内信模板的内容 + * @param params 站内信内容的参数 + * @return 格式化后的内容 + */ + String formatNotifyTemplateContent(String content, Map params); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java new file mode 100644 index 0000000..d52b2b7 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java @@ -0,0 +1,138 @@ +package cn.iocoder.yudao.module.system.service.notify; + +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_CODE_DUPLICATE; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS; + +/** + * 站内信模版 Service 实现类 + * + * @author xrcoder + */ +@Service +@Validated +@Slf4j +public class NotifyTemplateServiceImpl implements NotifyTemplateService { + + /** + * 正则表达式,匹配 {} 中的变量 + */ + private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}"); + + @Resource + private NotifyTemplateMapper notifyTemplateMapper; + + @Override + public Long createNotifyTemplate(NotifyTemplateSaveReqVO createReqVO) { + // 校验站内信编码是否重复 + validateNotifyTemplateCodeDuplicate(null, createReqVO.getCode()); + + // 插入 + NotifyTemplateDO notifyTemplate = BeanUtils.toBean(createReqVO, NotifyTemplateDO.class); + notifyTemplate.setParams(parseTemplateContentParams(notifyTemplate.getContent())); + notifyTemplateMapper.insert(notifyTemplate); + return notifyTemplate.getId(); + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 + public void updateNotifyTemplate(NotifyTemplateSaveReqVO updateReqVO) { + // 校验存在 + validateNotifyTemplateExists(updateReqVO.getId()); + // 校验站内信编码是否重复 + validateNotifyTemplateCodeDuplicate(updateReqVO.getId(), updateReqVO.getCode()); + + // 更新 + NotifyTemplateDO updateObj = BeanUtils.toBean(updateReqVO, NotifyTemplateDO.class); + updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); + notifyTemplateMapper.updateById(updateObj); + } + + @VisibleForTesting + public List parseTemplateContentParams(String content) { + return ReUtil.findAllGroup1(PATTERN_PARAMS, content); + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 + public void deleteNotifyTemplate(Long id) { + // 校验存在 + validateNotifyTemplateExists(id); + // 删除 + notifyTemplateMapper.deleteById(id); + } + + private void validateNotifyTemplateExists(Long id) { + if (notifyTemplateMapper.selectById(id) == null) { + throw exception(NOTIFY_TEMPLATE_NOT_EXISTS); + } + } + + @Override + public NotifyTemplateDO getNotifyTemplate(Long id) { + return notifyTemplateMapper.selectById(id); + } + + @Override + @Cacheable(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, key = "#code", + unless = "#result == null") + public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) { + return notifyTemplateMapper.selectByCode(code); + } + + @Override + public PageResult getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) { + return notifyTemplateMapper.selectPage(pageReqVO); + } + + @VisibleForTesting + void validateNotifyTemplateCodeDuplicate(Long id, String code) { + NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code); + if (template == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 + if (id == null) { + throw exception(NOTIFY_TEMPLATE_CODE_DUPLICATE, code); + } + if (!template.getId().equals(id)) { + throw exception(NOTIFY_TEMPLATE_CODE_DUPLICATE, code); + } + } + + /** + * 格式化站内信内容 + * + * @param content 站内信模板的内容 + * @param params 站内信内容的参数 + * @return 格式化后的内容 + */ + @Override + public String formatNotifyTemplateContent(String content, Map params) { + return StrUtil.format(content, params); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java new file mode 100644 index 0000000..def3e5e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * OAuth2 批准 Service 接口 + * + * 从功能上,和 Spring Security OAuth 的 ApprovalStoreUserApprovalHandler 的功能,记录用户针对指定客户端的授权,减少手动确定。 + * + * @author 芋道源码 + */ +public interface OAuth2ApproveService { + + /** + * 获得指定用户,针对指定客户端的指定授权,是否通过 + * + * 参考 ApprovalStoreUserApprovalHandler 的 checkForPreApproval 方法 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param requestedScopes 授权范围 + * @return 是否授权通过 + */ + boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection requestedScopes); + + /** + * 在用户发起批准时,基于 scopes 的选项,计算最终是否通过 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param requestedScopes 授权范围 + * @return 是否授权通过 + */ + boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map requestedScopes); + + /** + * 获得用户的批准列表,排除已过期的 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @return 是否授权通过 + */ + List getApproveList(Long userId, Integer userType, String clientId); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java new file mode 100644 index 0000000..7cd5271 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ApproveMapper; +import com.google.common.annotations.VisibleForTesting; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * OAuth2 批准 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { + + /** + * 批准的过期时间,默认 30 天 + */ + private static final Integer TIMEOUT = 30 * 24 * 60 * 60; // 单位:秒 + + @Resource + private OAuth2ClientService oauth2ClientService; + + @Resource + private OAuth2ApproveMapper oauth2ApproveMapper; + + @Override + @Transactional + public boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection requestedScopes) { + // 第一步,基于 Client 的自动授权计算,如果 scopes 都在自动授权中,则返回 true 通过 + OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); + Assert.notNull(clientDO, "客户端不能为空"); // 防御性编程 + if (CollUtil.containsAll(clientDO.getAutoApproveScopes(), requestedScopes)) { + // gh-877 - if all scopes are auto approved, approvals still need to be added to the approval store. + LocalDateTime expireTime = LocalDateTime.now().plusSeconds(TIMEOUT); + for (String scope : requestedScopes) { + saveApprove(userId, userType, clientId, scope, true, expireTime); + } + return true; + } + + // 第二步,算上用户已经批准的授权。如果 scopes 都包含,则返回 true + List approveDOs = getApproveList(userId, userType, clientId); + Set scopes = convertSet(approveDOs, OAuth2ApproveDO::getScope, + OAuth2ApproveDO::getApproved); // 只保留未过期的 + 同意的 + return CollUtil.containsAll(scopes, requestedScopes); + } + + @Override + @Transactional + public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map requestedScopes) { + // 如果 requestedScopes 为空,说明没有要求,则返回 true 通过 + if (CollUtil.isEmpty(requestedScopes)) { + return true; + } + + // 更新批准的信息 + boolean success = false; // 需要至少有一个同意 + LocalDateTime expireTime = LocalDateTime.now().plusSeconds(TIMEOUT); + for (Map.Entry entry : requestedScopes.entrySet()) { + if (entry.getValue()) { + success = true; + } + saveApprove(userId, userType, clientId, entry.getKey(), entry.getValue(), expireTime); + } + return success; + } + + @Override + public List getApproveList(Long userId, Integer userType, String clientId) { + List approveDOs = oauth2ApproveMapper.selectListByUserIdAndUserTypeAndClientId( + userId, userType, clientId); + approveDOs.removeIf(o -> DateUtils.isExpired(o.getExpiresTime())); + return approveDOs; + } + + @VisibleForTesting + void saveApprove(Long userId, Integer userType, String clientId, + String scope, Boolean approved, LocalDateTime expireTime) { + // 先更新 + OAuth2ApproveDO approveDO = new OAuth2ApproveDO().setUserId(userId).setUserType(userType) + .setClientId(clientId).setScope(scope).setApproved(approved).setExpiresTime(expireTime); + if (oauth2ApproveMapper.update(approveDO) == 1) { + return; + } + // 失败,则说明不存在,进行更新 + oauth2ApproveMapper.insert(approveDO); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java new file mode 100644 index 0000000..1d6074e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java @@ -0,0 +1,90 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; + +import jakarta.validation.Valid; +import java.util.Collection; + +/** + * OAuth2.0 Client Service 接口 + * + * 从功能上,和 JdbcClientDetailsService 的功能,提供客户端的操作 + * + * @author 芋道源码 + */ +public interface OAuth2ClientService { + + /** + * 创建 OAuth2 客户端 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createOAuth2Client(@Valid OAuth2ClientSaveReqVO createReqVO); + + /** + * 更新 OAuth2 客户端 + * + * @param updateReqVO 更新信息 + */ + void updateOAuth2Client(@Valid OAuth2ClientSaveReqVO updateReqVO); + + /** + * 删除 OAuth2 客户端 + * + * @param id 编号 + */ + void deleteOAuth2Client(Long id); + + /** + * 获得 OAuth2 客户端 + * + * @param id 编号 + * @return OAuth2 客户端 + */ + OAuth2ClientDO getOAuth2Client(Long id); + + /** + * 获得 OAuth2 客户端,从缓存中 + * + * @param clientId 客户端编号 + * @return OAuth2 客户端 + */ + OAuth2ClientDO getOAuth2ClientFromCache(String clientId); + + /** + * 获得 OAuth2 客户端分页 + * + * @param pageReqVO 分页查询 + * @return OAuth2 客户端分页 + */ + PageResult getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO); + + /** + * 从缓存中,校验客户端是否合法 + * + * @return 客户端 + */ + default OAuth2ClientDO validOAuthClientFromCache(String clientId) { + return validOAuthClientFromCache(clientId, null, null, null, null); + } + + /** + * 从缓存中,校验客户端是否合法 + * + * 非空时,进行校验 + * + * @param clientId 客户端编号 + * @param clientSecret 客户端密钥 + * @param authorizedGrantType 授权方式 + * @param scopes 授权范围 + * @param redirectUri 重定向地址 + * @return 客户端 + */ + OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType, + Collection scopes, String redirectUri); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java new file mode 100644 index 0000000..3c22954 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java @@ -0,0 +1,153 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.string.StrUtils; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.Collection; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * OAuth2.0 Client Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class OAuth2ClientServiceImpl implements OAuth2ClientService { + + @Resource + private OAuth2ClientMapper oauth2ClientMapper; + + @Override + public Long createOAuth2Client(OAuth2ClientSaveReqVO createReqVO) { + validateClientIdExists(null, createReqVO.getClientId()); + // 插入 + OAuth2ClientDO client = BeanUtils.toBean(createReqVO, OAuth2ClientDO.class); + oauth2ClientMapper.insert(client); + return client.getId(); + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 clientId 字段,不好清理 + public void updateOAuth2Client(OAuth2ClientSaveReqVO updateReqVO) { + // 校验存在 + validateOAuth2ClientExists(updateReqVO.getId()); + // 校验 Client 未被占用 + validateClientIdExists(updateReqVO.getId(), updateReqVO.getClientId()); + + // 更新 + OAuth2ClientDO updateObj = BeanUtils.toBean(updateReqVO, OAuth2ClientDO.class); + oauth2ClientMapper.updateById(updateObj); + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 key,不好清理 + public void deleteOAuth2Client(Long id) { + // 校验存在 + validateOAuth2ClientExists(id); + // 删除 + oauth2ClientMapper.deleteById(id); + } + + private void validateOAuth2ClientExists(Long id) { + if (oauth2ClientMapper.selectById(id) == null) { + throw exception(OAUTH2_CLIENT_NOT_EXISTS); + } + } + + @VisibleForTesting + void validateClientIdExists(Long id, String clientId) { + OAuth2ClientDO client = oauth2ClientMapper.selectByClientId(clientId); + if (client == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的客户端 + if (id == null) { + throw exception(OAUTH2_CLIENT_EXISTS); + } + if (!client.getId().equals(id)) { + throw exception(OAUTH2_CLIENT_EXISTS); + } + } + + @Override + public OAuth2ClientDO getOAuth2Client(Long id) { + return oauth2ClientMapper.selectById(id); + } + + @Override + @Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId", + unless = "#result == null") + public OAuth2ClientDO getOAuth2ClientFromCache(String clientId) { + return oauth2ClientMapper.selectByClientId(clientId); + } + + @Override + public PageResult getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO) { + return oauth2ClientMapper.selectPage(pageReqVO); + } + + @Override + public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType, + Collection scopes, String redirectUri) { + // 校验客户端存在、且开启 + OAuth2ClientDO client = getSelf().getOAuth2ClientFromCache(clientId); + if (client == null) { + throw exception(OAUTH2_CLIENT_NOT_EXISTS); + } + if (CommonStatusEnum.isDisable(client.getStatus())) { + throw exception(OAUTH2_CLIENT_DISABLE); + } + + // 校验客户端密钥 + if (StrUtil.isNotEmpty(clientSecret) && ObjectUtil.notEqual(client.getSecret(), clientSecret)) { + throw exception(OAUTH2_CLIENT_CLIENT_SECRET_ERROR); + } + // 校验授权方式 + if (StrUtil.isNotEmpty(authorizedGrantType) && !CollUtil.contains(client.getAuthorizedGrantTypes(), authorizedGrantType)) { + throw exception(OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); + } + // 校验授权范围 + if (CollUtil.isNotEmpty(scopes) && !CollUtil.containsAll(client.getScopes(), scopes)) { + throw exception(OAUTH2_CLIENT_SCOPE_OVER); + } + // 校验回调地址 + if (StrUtil.isNotEmpty(redirectUri) && !StrUtils.startWithAny(redirectUri, client.getRedirectUris())) { + throw exception(OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, redirectUri); + } + return client; + } + + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private OAuth2ClientServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java new file mode 100644 index 0000000..5ace87e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; + +import java.util.List; + +/** + * OAuth2.0 授权码 Service 接口 + * + * 从功能上,和 Spring Security OAuth 的 JdbcAuthorizationCodeServices 的功能,提供授权码的操作 + * + * @author 芋道源码 + */ +public interface OAuth2CodeService { + + /** + * 创建授权码 + * + * 参考 JdbcAuthorizationCodeServices 的 createAuthorizationCode 方法 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @param redirectUri 重定向 URI + * @param state 状态 + * @return 授权码的信息 + */ + OAuth2CodeDO createAuthorizationCode(Long userId, Integer userType, String clientId, + List scopes, String redirectUri, String state); + + /** + * 使用授权码 + * + * @param code 授权码 + */ + OAuth2CodeDO consumeAuthorizationCode(String code); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java new file mode 100644 index 0000000..11150fa --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java @@ -0,0 +1,64 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.core.util.IdUtil; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2CodeMapper; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_EXPIRE; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_NOT_EXISTS; + +/** + * OAuth2.0 授权码 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class OAuth2CodeServiceImpl implements OAuth2CodeService { + + /** + * 授权码的过期时间,默认 5 分钟 + */ + private static final Integer TIMEOUT = 5 * 60; + + @Resource + private OAuth2CodeMapper oauth2CodeMapper; + + @Override + public OAuth2CodeDO createAuthorizationCode(Long userId, Integer userType, String clientId, + List scopes, String redirectUri, String state) { + OAuth2CodeDO codeDO = new OAuth2CodeDO().setCode(generateCode()) + .setUserId(userId).setUserType(userType) + .setClientId(clientId).setScopes(scopes) + .setExpiresTime(LocalDateTime.now().plusSeconds(TIMEOUT)) + .setRedirectUri(redirectUri).setState(state); + oauth2CodeMapper.insert(codeDO); + return codeDO; + } + + @Override + public OAuth2CodeDO consumeAuthorizationCode(String code) { + OAuth2CodeDO codeDO = oauth2CodeMapper.selectByCode(code); + if (codeDO == null) { + throw exception(OAUTH2_CODE_NOT_EXISTS); + } + if (DateUtils.isExpired(codeDO.getExpiresTime())) { + throw exception(OAUTH2_CODE_EXPIRE); + } + oauth2CodeMapper.deleteById(codeDO.getId()); + return codeDO; + } + + private static String generateCode() { + return IdUtil.fastSimpleUUID(); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java new file mode 100644 index 0000000..fef1d5e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java @@ -0,0 +1,113 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; + +import java.util.List; + +/** + * OAuth2 授予 Service 接口 + * + * 从功能上,和 Spring Security OAuth 的 TokenGranter 的功能,提供访问令牌、刷新令牌的操作 + * + * 将自身的 AdminUser 用户,授权给第三方应用,采用 OAuth2.0 的协议。 + * + * 问题:为什么自身也作为一个第三方应用,也走这套流程呢? + * 回复:当然可以这么做,采用 password 模式。考虑到大多数开发者使用不到这个特性,OAuth2.0 毕竟有一定学习成本,所以暂时没有采取这种方式。 + * + * @author 芋道源码 + */ +public interface OAuth2GrantService { + + /** + * 简化模式 + * + * 对应 Spring Security OAuth2 的 ImplicitTokenGranter 功能 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @return 访问令牌 + */ + OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, + String clientId, List scopes); + + /** + * 授权码模式,第一阶段,获得 code 授权码 + * + * 对应 Spring Security OAuth2 的 AuthorizationEndpoint 的 generateCode 方法 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @param redirectUri 重定向 URI + * @param state 状态 + * @return 授权码 + */ + String grantAuthorizationCodeForCode(Long userId, Integer userType, + String clientId, List scopes, + String redirectUri, String state); + + /** + * 授权码模式,第二阶段,获得 accessToken 访问令牌 + * + * 对应 Spring Security OAuth2 的 AuthorizationCodeTokenGranter 功能 + * + * @param clientId 客户端编号 + * @param code 授权码 + * @param redirectUri 重定向 URI + * @param state 状态 + * @return 访问令牌 + */ + OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code, + String redirectUri, String state); + + /** + * 密码模式 + * + * 对应 Spring Security OAuth2 的 ResourceOwnerPasswordTokenGranter 功能 + * + * @param username 账号 + * @param password 密码 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @return 访问令牌 + */ + OAuth2AccessTokenDO grantPassword(String username, String password, + String clientId, List scopes); + + /** + * 刷新模式 + * + * 对应 Spring Security OAuth2 的 ResourceOwnerPasswordTokenGranter 功能 + * + * @param refreshToken 刷新令牌 + * @param clientId 客户端编号 + * @return 访问令牌 + */ + OAuth2AccessTokenDO grantRefreshToken(String refreshToken, String clientId); + + /** + * 客户端模式 + * + * 对应 Spring Security OAuth2 的 ClientCredentialsTokenGranter 功能 + * + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @return 访问令牌 + */ + OAuth2AccessTokenDO grantClientCredentials(String clientId, List scopes); + + /** + * 移除访问令牌 + * + * 对应 Spring Security OAuth2 的 ConsumerTokenServices 的 revokeToken 方法 + * + * @param accessToken 访问令牌 + * @param clientId 客户端编号 + * @return 是否移除到 + */ + boolean revokeToken(String clientId, String accessToken); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java new file mode 100644 index 0000000..e95fecc --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java @@ -0,0 +1,104 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants; +import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; + +/** + * OAuth2 授予 Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class OAuth2GrantServiceImpl implements OAuth2GrantService { + + @Resource + private OAuth2TokenService oauth2TokenService; + @Resource + private OAuth2CodeService oauth2CodeService; + @Resource + private AdminAuthService adminAuthService; + + @Override + public OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, + String clientId, List scopes) { + return oauth2TokenService.createAccessToken(userId, userType, clientId, scopes); + } + + @Override + public String grantAuthorizationCodeForCode(Long userId, Integer userType, + String clientId, List scopes, + String redirectUri, String state) { + return oauth2CodeService.createAuthorizationCode(userId, userType, clientId, scopes, + redirectUri, state).getCode(); + } + + @Override + public OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code, + String redirectUri, String state) { + OAuth2CodeDO codeDO = oauth2CodeService.consumeAuthorizationCode(code); + Assert.notNull(codeDO, "授权码不能为空"); // 防御性编程 + // 校验 clientId 是否匹配 + if (!StrUtil.equals(clientId, codeDO.getClientId())) { + throw exception(ErrorCodeConstants.OAUTH2_GRANT_CLIENT_ID_MISMATCH); + } + // 校验 redirectUri 是否匹配 + if (!StrUtil.equals(redirectUri, codeDO.getRedirectUri())) { + throw exception(ErrorCodeConstants.OAUTH2_GRANT_REDIRECT_URI_MISMATCH); + } + // 校验 state 是否匹配 + state = StrUtil.nullToDefault(state, ""); // 数据库 state 为 null 时,会设置为 "" 空串 + if (!StrUtil.equals(state, codeDO.getState())) { + throw exception(ErrorCodeConstants.OAUTH2_GRANT_STATE_MISMATCH); + } + + // 创建访问令牌 + return oauth2TokenService.createAccessToken(codeDO.getUserId(), codeDO.getUserType(), + codeDO.getClientId(), codeDO.getScopes()); + } + + @Override + public OAuth2AccessTokenDO grantPassword(String username, String password, String clientId, List scopes) { + // 使用账号 + 密码进行登录 + AdminUserDO user = adminAuthService.authenticate(username, password); + Assert.notNull(user, "用户不能为空!"); // 防御性编程 + + // 创建访问令牌 + return oauth2TokenService.createAccessToken(user.getId(), UserTypeEnum.ADMIN.getValue(), clientId, scopes); + } + + @Override + public OAuth2AccessTokenDO grantRefreshToken(String refreshToken, String clientId) { + return oauth2TokenService.refreshAccessToken(refreshToken, clientId); + } + + @Override + public OAuth2AccessTokenDO grantClientCredentials(String clientId, List scopes) { + // TODO 芋艿:项目中使用 OAuth2 解决的是三方应用的授权,内部的 SSO 等问题,所以暂时不考虑 client_credentials 这个场景 + throw new UnsupportedOperationException("暂时不支持 client_credentials 授权模式"); + } + + @Override + public boolean revokeToken(String clientId, String accessToken) { + // 先查询,保证 clientId 时匹配的 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.getAccessToken(accessToken); + if (accessTokenDO == null || ObjectUtil.notEqual(clientId, accessTokenDO.getClientId())) { + return false; + } + // 再删除 + return oauth2TokenService.removeAccessToken(accessToken) != null; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java new file mode 100644 index 0000000..977d935 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; + +import java.util.List; + +/** + * OAuth2.0 Token Service 接口 + * + * 从功能上,和 Spring Security OAuth 的 DefaultTokenServices + JdbcTokenStore 的功能,提供访问令牌、刷新令牌的操作 + * + * @author 芋道源码 + */ +public interface OAuth2TokenService { + + /** + * 创建访问令牌 + * 注意:该流程中,会包含创建刷新令牌的创建 + * + * 参考 DefaultTokenServices 的 createAccessToken 方法 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param clientId 客户端编号 + * @param scopes 授权范围 + * @return 访问令牌的信息 + */ + OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List scopes); + + /** + * 刷新访问令牌 + * + * 参考 DefaultTokenServices 的 refreshAccessToken 方法 + * + * @param refreshToken 刷新令牌 + * @param clientId 客户端编号 + * @return 访问令牌的信息 + */ + OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId); + + /** + * 获得访问令牌 + * + * 参考 DefaultTokenServices 的 getAccessToken 方法 + * + * @param accessToken 访问令牌 + * @return 访问令牌的信息 + */ + OAuth2AccessTokenDO getAccessToken(String accessToken); + + /** + * 校验访问令牌 + * + * @param accessToken 访问令牌 + * @return 访问令牌的信息 + */ + OAuth2AccessTokenDO checkAccessToken(String accessToken); + + /** + * 移除访问令牌 + * 注意:该流程中,会移除相关的刷新令牌 + * + * 参考 DefaultTokenServices 的 revokeToken 方法 + * + * @param accessToken 刷新令牌 + * @return 访问令牌的信息 + */ + OAuth2AccessTokenDO removeAccessToken(String accessToken); + + /** + * 获得访问令牌分页 + * + * @param reqVO 请求 + * @return 访问令牌分页 + */ + PageResult getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java new file mode 100644 index 0000000..fb0e756 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java @@ -0,0 +1,219 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper; +import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * OAuth2.0 Token Service 实现类 + * + * @author 芋道源码 + */ +@Service +public class OAuth2TokenServiceImpl implements OAuth2TokenService { + + @Resource + private OAuth2AccessTokenMapper oauth2AccessTokenMapper; + @Resource + private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper; + + @Resource + private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO; + + @Resource + private OAuth2ClientService oauth2ClientService; + @Resource + @Lazy // 懒加载,避免循环依赖 + private AdminUserService adminUserService; + + @Override + @Transactional(rollbackFor = Exception.class) + public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List scopes) { + OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); + // 创建刷新令牌 + OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO, scopes); + // 创建访问令牌 + return createOAuth2AccessToken(refreshTokenDO, clientDO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId) { + // 查询访问令牌 + OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken); + if (refreshTokenDO == null) { + throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "无效的刷新令牌"); + } + + // 校验 Client 匹配 + OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); + if (ObjectUtil.notEqual(clientId, refreshTokenDO.getClientId())) { + throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "刷新令牌的客户端编号不正确"); + } + + // 移除相关的访问令牌 + List accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshToken); + if (CollUtil.isNotEmpty(accessTokenDOs)) { + oauth2AccessTokenMapper.deleteByIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getId)); + oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken)); + } + + // 已过期的情况下,删除刷新令牌 + if (DateUtils.isExpired(refreshTokenDO.getExpiresTime())) { + oauth2RefreshTokenMapper.deleteById(refreshTokenDO.getId()); + throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "刷新令牌已过期"); + } + + // 创建访问令牌 + return createOAuth2AccessToken(refreshTokenDO, clientDO); + } + + @Override + public OAuth2AccessTokenDO getAccessToken(String accessToken) { + // 优先从 Redis 中获取 + OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken); + if (accessTokenDO != null) { + return accessTokenDO; + } + + // 获取不到,从 MySQL 中获取访问令牌 + accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken); + if (accessTokenDO == null) { + // 特殊:从 MySQL 中获取刷新令牌。原因:解决部分场景不方便刷新访问令牌场景 + // 例如说,积木报表只允许传递 token,不允许传递 refresh_token,导致无法刷新访问令牌 + // 再例如说,前端 WebSocket 的 token 直接跟在 url 上,无法传递 refresh_token + OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(accessToken); + if (refreshTokenDO != null && !DateUtils.isExpired(refreshTokenDO.getExpiresTime())) { + accessTokenDO = convertToAccessToken(refreshTokenDO); + } + } + + // 如果在 MySQL 存在,则往 Redis 中写入 + if (accessTokenDO != null && !DateUtils.isExpired(accessTokenDO.getExpiresTime())) { + oauth2AccessTokenRedisDAO.set(accessTokenDO); + } + return accessTokenDO; + } + + @Override + public OAuth2AccessTokenDO checkAccessToken(String accessToken) { + OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken); + if (accessTokenDO == null) { + throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌不存在"); + } + if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) { + throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌已过期"); + } + return accessTokenDO; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public OAuth2AccessTokenDO removeAccessToken(String accessToken) { + // 删除访问令牌 + OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken); + if (accessTokenDO == null) { + return null; + } + oauth2AccessTokenMapper.deleteById(accessTokenDO.getId()); + oauth2AccessTokenRedisDAO.delete(accessToken); + // 删除刷新令牌 + oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken()); + return accessTokenDO; + } + + @Override + public PageResult getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) { + return oauth2AccessTokenMapper.selectPage(reqVO); + } + + private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) { + OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken()) + .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()) + .setUserInfo(buildUserInfo(refreshTokenDO.getUserId(), refreshTokenDO.getUserType())) + .setClientId(clientDO.getClientId()).setScopes(refreshTokenDO.getScopes()) + .setRefreshToken(refreshTokenDO.getRefreshToken()) + .setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getAccessTokenValiditySeconds())); + accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号 + oauth2AccessTokenMapper.insert(accessTokenDO); + // 记录到 Redis 中 + oauth2AccessTokenRedisDAO.set(accessTokenDO); + return accessTokenDO; + } + + private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO, List scopes) { + OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken()) + .setUserId(userId).setUserType(userType) + .setClientId(clientDO.getClientId()).setScopes(scopes) + .setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getRefreshTokenValiditySeconds())); + oauth2RefreshTokenMapper.insert(refreshToken); + return refreshToken; + } + + private OAuth2AccessTokenDO convertToAccessToken(OAuth2RefreshTokenDO refreshTokenDO) { + OAuth2AccessTokenDO accessTokenDO = BeanUtils.toBean(refreshTokenDO, OAuth2AccessTokenDO.class) + .setAccessToken(refreshTokenDO.getRefreshToken()); + TenantUtils.execute(refreshTokenDO.getTenantId(), + () -> accessTokenDO.setUserInfo(buildUserInfo(refreshTokenDO.getUserId(), refreshTokenDO.getUserType()))); + return accessTokenDO; + } + + /** + * 加载用户信息,方便 {@link cn.iocoder.yudao.framework.security.core.LoginUser} 获取到昵称、部门等信息 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @return 用户信息 + */ + private Map buildUserInfo(Long userId, Integer userType) { + if (userType.equals(UserTypeEnum.ADMIN.getValue())) { + AdminUserDO user = adminUserService.getUser(userId); + return MapUtil.builder(LoginUser.INFO_KEY_NICKNAME, user.getNickname()) + .put(LoginUser.INFO_KEY_DEPT_ID, StrUtil.toStringOrNull(user.getDeptId())).build(); + } else if (userType.equals(UserTypeEnum.MEMBER.getValue())) { + // 注意:目前 Member 暂时不读取,可以按需实现 + return Collections.emptyMap(); + } + return null; + } + + private static String generateAccessToken() { + return IdUtil.fastSimpleUUID(); + } + + private static String generateRefreshToken() { + return IdUtil.fastSimpleUUID(); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java new file mode 100644 index 0000000..d74dc61 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java @@ -0,0 +1,95 @@ +package cn.iocoder.yudao.module.system.service.permission; + +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; + +import java.util.Collection; +import java.util.List; + +/** + * 菜单 Service 接口 + * + * @author 芋道源码 + */ +public interface MenuService { + + /** + * 创建菜单 + * + * @param createReqVO 菜单信息 + * @return 创建出来的菜单编号 + */ + Long createMenu(MenuSaveVO createReqVO); + + /** + * 更新菜单 + * + * @param updateReqVO 菜单信息 + */ + void updateMenu(MenuSaveVO updateReqVO); + + /** + * 删除菜单 + * + * @param id 菜单编号 + */ + void deleteMenu(Long id); + + /** + * 获得所有菜单列表 + * + * @return 菜单列表 + */ + List getMenuList(); + + /** + * 基于租户,筛选菜单列表 + * 注意,如果是系统租户,返回的还是全菜单 + * + * @param reqVO 筛选条件请求 VO + * @return 菜单列表 + */ + List getMenuListByTenant(MenuListReqVO reqVO); + + /** + * 过滤掉关闭的菜单及其子菜单 + * + * @param list 菜单列表 + * @return 过滤后的菜单列表 + */ + List filterDisableMenus(List list); + + /** + * 筛选菜单列表 + * + * @param reqVO 筛选条件请求 VO + * @return 菜单列表 + */ + List getMenuList(MenuListReqVO reqVO); + + /** + * 获得权限对应的菜单编号数组 + * + * @param permission 权限标识 + * @return 数组 + */ + List getMenuIdListByPermissionFromCache(String permission); + + /** + * 获得菜单 + * + * @param id 菜单编号 + * @return 菜单 + */ + MenuDO getMenu(Long id); + + /** + * 获得菜单数组 + * + * @param ids 菜单编号数组 + * @return 菜单数组 + */ + List getMenuList(Collection ids); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java new file mode 100644 index 0000000..5378108 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java @@ -0,0 +1,289 @@ +package cn.iocoder.yudao.module.system.service.permission; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; +import cn.iocoder.yudao.module.system.service.tenant.TenantService; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 菜单 Service 实现 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class MenuServiceImpl implements MenuService { + + @Resource + private MenuMapper menuMapper; + @Resource + private PermissionService permissionService; + @Resource + @Lazy // 延迟,避免循环依赖报错 + private TenantService tenantService; + + @Override + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#createReqVO.permission", + condition = "#createReqVO.permission != null") + public Long createMenu(MenuSaveVO createReqVO) { + // 校验父菜单存在 + validateParentMenu(createReqVO.getParentId(), null); + // 校验菜单(自己) + validateMenuName(createReqVO.getParentId(), createReqVO.getName(), null); + validateMenuComponentName(createReqVO.getComponentName(), null); + + // 插入数据库 + MenuDO menu = BeanUtils.toBean(createReqVO, MenuDO.class); + initMenuProperty(menu); + menuMapper.insert(menu); + // 返回 + return menu.getId(); + } + + @Override + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为 permission 如果变更,涉及到新老两个 permission。直接清理,简单有效 + public void updateMenu(MenuSaveVO updateReqVO) { + // 校验更新的菜单是否存在 + if (menuMapper.selectById(updateReqVO.getId()) == null) { + throw exception(MENU_NOT_EXISTS); + } + // 校验父菜单存在 + validateParentMenu(updateReqVO.getParentId(), updateReqVO.getId()); + // 校验菜单(自己) + validateMenuName(updateReqVO.getParentId(), updateReqVO.getName(), updateReqVO.getId()); + validateMenuComponentName(updateReqVO.getComponentName(), updateReqVO.getId()); + + // 更新到数据库 + MenuDO updateObj = BeanUtils.toBean(updateReqVO, MenuDO.class); + initMenuProperty(updateObj); + menuMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,因为此时不知道 id 对应的 permission 是多少。直接清理,简单有效 + public void deleteMenu(Long id) { + // 校验是否还有子菜单 + if (menuMapper.selectCountByParentId(id) > 0) { + throw exception(MENU_EXISTS_CHILDREN); + } + // 校验删除的菜单是否存在 + if (menuMapper.selectById(id) == null) { + throw exception(MENU_NOT_EXISTS); + } + // 标记删除 + menuMapper.deleteById(id); + // 删除授予给角色的权限 + permissionService.processMenuDeleted(id); + } + + @Override + public List getMenuList() { + return menuMapper.selectList(); + } + + @Override + public List getMenuListByTenant(MenuListReqVO reqVO) { + // 查询所有菜单,并过滤掉关闭的节点 + List menus = getMenuList(reqVO); + // 开启多租户的情况下,需要过滤掉未开通的菜单 + tenantService.handleTenantMenu(menuIds -> menus.removeIf(menu -> !CollUtil.contains(menuIds, menu.getId()))); + return menus; + } + + @Override + public List filterDisableMenus(List menuList) { + if (CollUtil.isEmpty(menuList)){ + return Collections.emptyList(); + } + Map menuMap = convertMap(menuList, MenuDO::getId); + + // 遍历 menu 菜单,查找不是禁用的菜单,添加到 enabledMenus 结果 + List enabledMenus = new ArrayList<>(); + Set disabledMenuCache = new HashSet<>(); // 存下递归搜索过被禁用的菜单,防止重复的搜索 + for (MenuDO menu : menuList) { + if (isMenuDisabled(menu, menuMap, disabledMenuCache)) { + continue; + } + enabledMenus.add(menu); + } + return enabledMenus; + } + + private boolean isMenuDisabled(MenuDO node, Map menuMap, Set disabledMenuCache) { + // 如果已经判定是禁用的节点,直接结束 + if (disabledMenuCache.contains(node.getId())) { + return true; + } + + // 1. 先判断自身是否禁用 + if (CommonStatusEnum.isDisable(node.getStatus())) { + disabledMenuCache.add(node.getId()); + return true; + } + + // 2. 遍历到 parentId 为根节点,则无需判断 + Long parentId = node.getParentId(); + if (ObjUtil.equal(parentId, ID_ROOT)) { + return false; + } + + // 3. 继续遍历 parent 节点 + MenuDO parent = menuMap.get(parentId); + if (parent == null || isMenuDisabled(parent, menuMap, disabledMenuCache)) { + disabledMenuCache.add(node.getId()); + return true; + } + return false; + } + + @Override + public List getMenuList(MenuListReqVO reqVO) { + return menuMapper.selectList(reqVO); + } + + @Override + @Cacheable(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#permission") + public List getMenuIdListByPermissionFromCache(String permission) { + List menus = menuMapper.selectListByPermission(permission); + return convertList(menus, MenuDO::getId); + } + + @Override + public MenuDO getMenu(Long id) { + return menuMapper.selectById(id); + } + + @Override + public List getMenuList(Collection ids) { + // 当 ids 为空时,返回一个空的实例对象 + if (CollUtil.isEmpty(ids)) { + return Lists.newArrayList(); + } + return menuMapper.selectBatchIds(ids); + } + + /** + * 校验父菜单是否合法 + *

+ * 1. 不能设置自己为父菜单 + * 2. 父菜单不存在 + * 3. 父菜单必须是 {@link MenuTypeEnum#MENU} 菜单类型 + * + * @param parentId 父菜单编号 + * @param childId 当前菜单编号 + */ + @VisibleForTesting + void validateParentMenu(Long parentId, Long childId) { + if (parentId == null || ID_ROOT.equals(parentId)) { + return; + } + // 不能设置自己为父菜单 + if (parentId.equals(childId)) { + throw exception(MENU_PARENT_ERROR); + } + MenuDO menu = menuMapper.selectById(parentId); + // 父菜单不存在 + if (menu == null) { + throw exception(MENU_PARENT_NOT_EXISTS); + } + // 父菜单必须是目录或者菜单类型 + if (!MenuTypeEnum.DIR.getType().equals(menu.getType()) + && !MenuTypeEnum.MENU.getType().equals(menu.getType())) { + throw exception(MENU_PARENT_NOT_DIR_OR_MENU); + } + } + + /** + * 校验菜单是否合法 + *

+ * 1. 校验相同父菜单编号下,是否存在相同的菜单名 + * + * @param name 菜单名字 + * @param parentId 父菜单编号 + * @param id 菜单编号 + */ + @VisibleForTesting + void validateMenuName(Long parentId, String name, Long id) { + MenuDO menu = menuMapper.selectByParentIdAndName(parentId, name); + if (menu == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的菜单 + if (id == null) { + throw exception(MENU_NAME_DUPLICATE); + } + if (!menu.getId().equals(id)) { + throw exception(MENU_NAME_DUPLICATE); + } + } + + /** + * 校验菜单组件名是否合法 + * + * @param componentName 组件名 + * @param id 菜单编号 + */ + @VisibleForTesting + void validateMenuComponentName(String componentName, Long id) { + if (StrUtil.isBlank(componentName)) { + return; + } + MenuDO menu = menuMapper.selectByComponentName(componentName); + if (menu == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的菜单 + if (id == null) { + return; + } + if (!menu.getId().equals(id)) { + throw exception(MENU_COMPONENT_NAME_DUPLICATE); + } + } + + /** + * 初始化菜单的通用属性。 + *

+ * 例如说,只有目录或者菜单类型的菜单,才设置 icon + * + * @param menu 菜单 + */ + private void initMenuProperty(MenuDO menu) { + // 菜单为按钮类型时,无需 component、icon、path 属性,进行置空 + if (MenuTypeEnum.BUTTON.getType().equals(menu.getType())) { + menu.setComponent(""); + menu.setComponentName(""); + menu.setIcon(""); + menu.setPath(""); + } + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java new file mode 100644 index 0000000..f7f8122 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java @@ -0,0 +1,146 @@ +package cn.iocoder.yudao.module.system.service.permission; + +import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; + +import java.util.Collection; +import java.util.Set; + +import static java.util.Collections.singleton; + +/** + * 权限 Service 接口 + *

+ * 提供用户-角色、角色-菜单、角色-部门的关联权限处理 + * + * @author 芋道源码 + */ +public interface PermissionService { + + /** + * 判断是否有权限,任一一个即可 + * + * @param userId 用户编号 + * @param permissions 权限 + * @return 是否 + */ + boolean hasAnyPermissions(Long userId, String... permissions); + + /** + * 判断是否有角色,任一一个即可 + * + * @param roles 角色数组 + * @return 是否 + */ + boolean hasAnyRoles(Long userId, String... roles); + + // ========== 角色-菜单的相关方法 ========== + + /** + * 设置角色菜单 + * + * @param roleId 角色编号 + * @param menuIds 菜单编号集合 + */ + void assignRoleMenu(Long roleId, Set menuIds); + + /** + * 处理角色删除时,删除关联授权数据 + * + * @param roleId 角色编号 + */ + void processRoleDeleted(Long roleId); + + /** + * 处理菜单删除时,删除关联授权数据 + * + * @param menuId 菜单编号 + */ + void processMenuDeleted(Long menuId); + + /** + * 获得角色拥有的菜单编号集合 + * + * @param roleId 角色编号 + * @return 菜单编号集合 + */ + default Set getRoleMenuListByRoleId(Long roleId) { + return getRoleMenuListByRoleId(singleton(roleId)); + } + + /** + * 获得角色们拥有的菜单编号集合 + * + * @param roleIds 角色编号数组 + * @return 菜单编号集合 + */ + Set getRoleMenuListByRoleId(Collection roleIds); + + /** + * 获得拥有指定菜单的角色编号数组,从缓存中获取 + * + * @param menuId 菜单编号 + * @return 角色编号数组 + */ + Set getMenuRoleIdListByMenuIdFromCache(Long menuId); + + // ========== 用户-角色的相关方法 ========== + + /** + * 设置用户角色 + * + * @param userId 角色编号 + * @param roleIds 角色编号集合 + */ + void assignUserRole(Long userId, Set roleIds); + + /** + * 处理用户删除时,删除关联授权数据 + * + * @param userId 用户编号 + */ + void processUserDeleted(Long userId); + + /** + * 获得拥有多个角色的用户编号集合 + * + * @param roleIds 角色编号集合 + * @return 用户编号集合 + */ + Set getUserRoleIdListByRoleId(Collection roleIds); + + /** + * 获得用户拥有的角色编号集合 + * + * @param userId 用户编号 + * @return 角色编号集合 + */ + Set getUserRoleIdListByUserId(Long userId); + + /** + * 获得用户拥有的角色编号集合,从缓存中获取 + * + * @param userId 用户编号 + * @return 角色编号集合 + */ + Set getUserRoleIdListByUserIdFromCache(Long userId); + + // ========== 用户-部门的相关方法 ========== + + /** + * 设置角色的数据权限 + * + * @param roleId 角色编号 + * @param dataScope 数据范围 + * @param dataScopeDeptIds 部门编号数组 + */ + void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds); + + /** + * 获得登陆用户的部门数据权限 + * + * @param userId 用户编号 + * @return 部门数据权限 + */ + DeptDataPermissionRespDTO getDeptDataPermission(Long userId); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java new file mode 100644 index 0000000..8eb5b09 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java @@ -0,0 +1,341 @@ +package cn.iocoder.yudao.module.system.service.permission; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; +import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; +import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; +import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Suppliers; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.Caching; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import jakarta.annotation.Resource; +import java.util.*; +import java.util.function.Supplier; + +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; + +/** + * 权限 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class PermissionServiceImpl implements PermissionService { + + @Resource + private RoleMenuMapper roleMenuMapper; + @Resource + private UserRoleMapper userRoleMapper; + + @Resource + private RoleService roleService; + @Resource + private MenuService menuService; + @Resource + private DeptService deptService; + @Resource + private AdminUserService userService; + + @Override + public boolean hasAnyPermissions(Long userId, String... permissions) { + // 如果为空,说明已经有权限 + if (ArrayUtil.isEmpty(permissions)) { + return true; + } + + // 获得当前登录的角色。如果为空,说明没有权限 + List roles = getEnableUserRoleListByUserIdFromCache(userId); + if (CollUtil.isEmpty(roles)) { + return false; + } + + // 情况一:遍历判断每个权限,如果有一满足,说明有权限 + for (String permission : permissions) { + if (hasAnyPermission(roles, permission)) { + return true; + } + } + + // 情况二:如果是超管,也说明有权限 + return roleService.hasAnySuperAdmin(convertSet(roles, RoleDO::getId)); + } + + /** + * 判断指定角色,是否拥有该 permission 权限 + * + * @param roles 指定角色数组 + * @param permission 权限标识 + * @return 是否拥有 + */ + private boolean hasAnyPermission(List roles, String permission) { + List menuIds = menuService.getMenuIdListByPermissionFromCache(permission); + // 采用严格模式,如果权限找不到对应的 Menu 的话,也认为没有权限 + if (CollUtil.isEmpty(menuIds)) { + return false; + } + + // 判断是否有权限 + Set roleIds = convertSet(roles, RoleDO::getId); + for (Long menuId : menuIds) { + // 获得拥有该菜单的角色编号集合 + Set menuRoleIds = getSelf().getMenuRoleIdListByMenuIdFromCache(menuId); + // 如果有交集,说明有权限 + if (CollUtil.containsAny(menuRoleIds, roleIds)) { + return true; + } + } + return false; + } + + @Override + public boolean hasAnyRoles(Long userId, String... roles) { + // 如果为空,说明已经有权限 + if (ArrayUtil.isEmpty(roles)) { + return true; + } + + // 获得当前登录的角色。如果为空,说明没有权限 + List roleList = getEnableUserRoleListByUserIdFromCache(userId); + if (CollUtil.isEmpty(roleList)) { + return false; + } + + // 判断是否有角色 + Set userRoles = convertSet(roleList, RoleDO::getCode); + return CollUtil.containsAny(userRoles, Sets.newHashSet(roles)); + } + + // ========== 角色-菜单的相关方法 ========== + + @Override + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 + @Caching(evict = { + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, + allEntries = true), + @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,主要一次更新涉及到的 menuIds 较多,反倒批量会更快 + }) + public void assignRoleMenu(Long roleId, Set menuIds) { + // 获得角色拥有菜单编号 + Set dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId); + // 计算新增和删除的菜单编号 + Set menuIdList = CollUtil.emptyIfNull(menuIds); + Collection createMenuIds = CollUtil.subtract(menuIdList, dbMenuIds); + Collection deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIdList); + // 执行新增和删除。对于已经授权的菜单,不用做任何处理 + if (CollUtil.isNotEmpty(createMenuIds)) { + roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> { + RoleMenuDO entity = new RoleMenuDO(); + entity.setRoleId(roleId); + entity.setMenuId(menuId); + return entity; + })); + } + if (CollUtil.isNotEmpty(deleteMenuIds)) { + roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @Caching(evict = { + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, + allEntries = true), // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 menu 缓存们 + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, + allEntries = true) // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 user 缓存们 + }) + public void processRoleDeleted(Long roleId) { + // 标记删除 UserRole + userRoleMapper.deleteListByRoleId(roleId); + // 标记删除 RoleMenu + roleMenuMapper.deleteListByRoleId(roleId); + } + + @Override + @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId") + public void processMenuDeleted(Long menuId) { + roleMenuMapper.deleteListByMenuId(menuId); + } + + @Override + public Set getRoleMenuListByRoleId(Collection roleIds) { + if (CollUtil.isEmpty(roleIds)) { + return Collections.emptySet(); + } + + // 如果是管理员的情况下,获取全部菜单编号 + if (roleService.hasAnySuperAdmin(roleIds)) { + return convertSet(menuService.getMenuList(), MenuDO::getId); + } + // 如果是非管理员的情况下,获得拥有的菜单编号 + return convertSet(roleMenuMapper.selectListByRoleId(roleIds), RoleMenuDO::getMenuId); + } + + @Override + @Cacheable(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId") + public Set getMenuRoleIdListByMenuIdFromCache(Long menuId) { + return convertSet(roleMenuMapper.selectListByMenuId(menuId), RoleMenuDO::getRoleId); + } + + // ========== 用户-角色的相关方法 ========== + + @Override + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") + public void assignUserRole(Long userId, Set roleIds) { + // 获得角色拥有角色编号 + Set dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId), + UserRoleDO::getRoleId); + // 计算新增和删除的角色编号 + Set roleIdList = CollUtil.emptyIfNull(roleIds); + Collection createRoleIds = CollUtil.subtract(roleIdList, dbRoleIds); + Collection deleteMenuIds = CollUtil.subtract(dbRoleIds, roleIdList); + // 执行新增和删除。对于已经授权的角色,不用做任何处理 + if (!CollectionUtil.isEmpty(createRoleIds)) { + userRoleMapper.insertBatch(CollectionUtils.convertList(createRoleIds, roleId -> { + UserRoleDO entity = new UserRoleDO(); + entity.setUserId(userId); + entity.setRoleId(roleId); + return entity; + })); + } + if (!CollectionUtil.isEmpty(deleteMenuIds)) { + userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds); + } + } + + @Override + @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") + public void processUserDeleted(Long userId) { + userRoleMapper.deleteListByUserId(userId); + } + + @Override + public Set getUserRoleIdListByUserId(Long userId) { + return convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId); + } + + @Override + @Cacheable(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") + public Set getUserRoleIdListByUserIdFromCache(Long userId) { + return getUserRoleIdListByUserId(userId); + } + + @Override + public Set getUserRoleIdListByRoleId(Collection roleIds) { + return convertSet(userRoleMapper.selectListByRoleIds(roleIds), UserRoleDO::getUserId); + } + + /** + * 获得用户拥有的角色,并且这些角色是开启状态的 + * + * @param userId 用户编号 + * @return 用户拥有的角色 + */ + @VisibleForTesting + List getEnableUserRoleListByUserIdFromCache(Long userId) { + // 获得用户拥有的角色编号 + Set roleIds = getSelf().getUserRoleIdListByUserIdFromCache(userId); + // 获得角色数组,并移除被禁用的 + List roles = roleService.getRoleListFromCache(roleIds); + roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); + return roles; + } + + // ========== 用户-部门的相关方法 ========== + + @Override + public void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds) { + roleService.updateRoleDataScope(roleId, dataScope, dataScopeDeptIds); + } + + @Override + @DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题 + public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) { + // 获得用户的角色 + List roles = getEnableUserRoleListByUserIdFromCache(userId); + + // 如果角色为空,则只能查看自己 + DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO(); + if (CollUtil.isEmpty(roles)) { + result.setSelf(true); + return result; + } + + // 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询 + Supplier userDeptId = Suppliers.memoize(() -> userService.getUser(userId).getDeptId()); + // 遍历每个角色,计算 + for (RoleDO role : roles) { + // 为空时,跳过 + if (role.getDataScope() == null) { + continue; + } + // 情况一,ALL + if (Objects.equals(role.getDataScope(), DataScopeEnum.ALL.getScope())) { + result.setAll(true); + continue; + } + // 情况二,DEPT_CUSTOM + if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_CUSTOM.getScope())) { + CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds()); + // 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。 + // 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉 + CollUtil.addAll(result.getDeptIds(), userDeptId.get()); + continue; + } + // 情况三,DEPT_ONLY + if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) { + CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptId.get()); + continue; + } + // 情况四,DEPT_DEPT_AND_CHILD + if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) { + CollUtil.addAll(result.getDeptIds(), deptService.getChildDeptIdListFromCache(userDeptId.get())); + // 添加本身部门编号 + CollUtil.addAll(result.getDeptIds(), userDeptId.get()); + continue; + } + // 情况五,SELF + if (Objects.equals(role.getDataScope(), DataScopeEnum.SELF.getScope())) { + result.setSelf(true); + continue; + } + // 未知情况,error log 即可 + log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, toJsonString(result)); + } + return result; + } + + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private PermissionServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java new file mode 100644 index 0000000..6de8b51 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java @@ -0,0 +1,124 @@ +package cn.iocoder.yudao.module.system.service.permission; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; + +import jakarta.validation.Valid; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * 角色 Service 接口 + * + * @author 芋道源码 + */ +public interface RoleService { + + /** + * 创建角色 + * + * @param createReqVO 创建角色信息 + * @param type 角色类型 + * @return 角色编号 + */ + Long createRole(@Valid RoleSaveReqVO createReqVO, Integer type); + + /** + * 更新角色 + * + * @param updateReqVO 更新角色信息 + */ + void updateRole(@Valid RoleSaveReqVO updateReqVO); + + /** + * 删除角色 + * + * @param id 角色编号 + */ + void deleteRole(Long id); + + /** + * 设置角色的数据权限 + * + * @param id 角色编号 + * @param dataScope 数据范围 + * @param dataScopeDeptIds 部门编号数组 + */ + void updateRoleDataScope(Long id, Integer dataScope, Set dataScopeDeptIds); + + /** + * 获得角色 + * + * @param id 角色编号 + * @return 角色 + */ + RoleDO getRole(Long id); + + /** + * 获得角色,从缓存中 + * + * @param id 角色编号 + * @return 角色 + */ + RoleDO getRoleFromCache(Long id); + + /** + * 获得角色列表 + * + * @param ids 角色编号数组 + * @return 角色列表 + */ + List getRoleList(Collection ids); + + /** + * 获得角色数组,从缓存中 + * + * @param ids 角色编号数组 + * @return 角色数组 + */ + List getRoleListFromCache(Collection ids); + + /** + * 获得角色列表 + * + * @param statuses 筛选的状态 + * @return 角色列表 + */ + List getRoleListByStatus(Collection statuses); + + /** + * 获得所有角色列表 + * + * @return 角色列表 + */ + List getRoleList(); + + /** + * 获得角色分页 + * + * @param reqVO 角色分页查询 + * @return 角色分页结果 + */ + PageResult getRolePage(RolePageReqVO reqVO); + + /** + * 判断角色编号数组中,是否有管理员 + * + * @param ids 角色编号数组 + * @return 是否有管理员 + */ + boolean hasAnySuperAdmin(Collection ids); + + /** + * 校验角色们是否有效。如下情况,视为无效: + * 1. 角色编号不存在 + * 2. 角色被禁用 + * + * @param ids 角色编号数组 + */ + void validateRoleList(Collection ids); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java new file mode 100644 index 0000000..c18ef31 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java @@ -0,0 +1,262 @@ +package cn.iocoder.yudao.module.system.service.permission; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; +import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; +import com.google.common.annotations.VisibleForTesting; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.StringUtils; + +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*; + +/** + * 角色 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class RoleServiceImpl implements RoleService { + + @Resource + private PermissionService permissionService; + + @Resource + private RoleMapper roleMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_CREATE_SUB_TYPE, bizNo = "{{#role.id}}", + success = SYSTEM_ROLE_CREATE_SUCCESS) + public Long createRole(RoleSaveReqVO createReqVO, Integer type) { + // 1. 校验角色 + validateRoleDuplicate(createReqVO.getName(), createReqVO.getCode(), null); + + // 2. 插入到数据库 + RoleDO role = BeanUtils.toBean(createReqVO, RoleDO.class) + .setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType())) + .setStatus(ObjUtil.defaultIfNull(createReqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) + .setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限 + roleMapper.insert(role); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("role", role); + return role.getId(); + } + + @Override + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#updateReqVO.id") + @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = SYSTEM_ROLE_UPDATE_SUCCESS) + public void updateRole(RoleSaveReqVO updateReqVO) { + // 1.1 校验是否可以更新 + RoleDO role = validateRoleForUpdate(updateReqVO.getId()); + // 1.2 校验角色的唯一字段是否重复 + validateRoleDuplicate(updateReqVO.getName(), updateReqVO.getCode(), updateReqVO.getId()); + + // 2. 更新到数据库 + RoleDO updateObj = BeanUtils.toBean(updateReqVO, RoleDO.class); + roleMapper.updateById(updateObj); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(role, RoleSaveReqVO.class)); + LogRecordContext.putVariable("role", role); + } + + @Override + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") + public void updateRoleDataScope(Long id, Integer dataScope, Set dataScopeDeptIds) { + // 校验是否可以更新 + validateRoleForUpdate(id); + + // 更新数据范围 + RoleDO updateObject = new RoleDO(); + updateObject.setId(id); + updateObject.setDataScope(dataScope); + updateObject.setDataScopeDeptIds(dataScopeDeptIds); + roleMapper.updateById(updateObject); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") + @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = SYSTEM_ROLE_DELETE_SUCCESS) + public void deleteRole(Long id) { + // 1. 校验是否可以更新 + RoleDO role = validateRoleForUpdate(id); + + // 2.1 标记删除 + roleMapper.deleteById(id); + // 2.2 删除相关数据 + permissionService.processRoleDeleted(id); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("role", role); + } + + /** + * 校验角色的唯一字段是否重复 + * + * 1. 是否存在相同名字的角色 + * 2. 是否存在相同编码的角色 + * + * @param name 角色名字 + * @param code 角色额编码 + * @param id 角色编号 + */ + @VisibleForTesting + void validateRoleDuplicate(String name, String code, Long id) { + // 0. 超级管理员,不允许创建 + if (RoleCodeEnum.isSuperAdmin(code)) { + throw exception(ROLE_ADMIN_CODE_ERROR, code); + } + // 1. 该 name 名字被其它角色所使用 + RoleDO role = roleMapper.selectByName(name); + if (role != null && !role.getId().equals(id)) { + throw exception(ROLE_NAME_DUPLICATE, name); + } + // 2. 是否存在相同编码的角色 + if (!StringUtils.hasText(code)) { + return; + } + // 该 code 编码被其它角色所使用 + role = roleMapper.selectByCode(code); + if (role != null && !role.getId().equals(id)) { + throw exception(ROLE_CODE_DUPLICATE, code); + } + } + + /** + * 校验角色是否可以被更新 + * + * @param id 角色编号 + */ + @VisibleForTesting + RoleDO validateRoleForUpdate(Long id) { + RoleDO role = roleMapper.selectById(id); + if (role == null) { + throw exception(ROLE_NOT_EXISTS); + } + // 内置角色,不允许删除 + if (RoleTypeEnum.SYSTEM.getType().equals(role.getType())) { + throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); + } + return role; + } + + @Override + public RoleDO getRole(Long id) { + return roleMapper.selectById(id); + } + + @Override + @Cacheable(value = RedisKeyConstants.ROLE, key = "#id", + unless = "#result == null") + public RoleDO getRoleFromCache(Long id) { + return roleMapper.selectById(id); + } + + + @Override + public List getRoleListByStatus(Collection statuses) { + return roleMapper.selectListByStatus(statuses); + } + + @Override + public List getRoleList() { + return roleMapper.selectList(); + } + + @Override + public List getRoleList(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return roleMapper.selectBatchIds(ids); + } + + @Override + public List getRoleListFromCache(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + // 这里采用 for 循环从缓存中获取,主要考虑 Spring CacheManager 无法批量操作的问题 + RoleServiceImpl self = getSelf(); + return CollectionUtils.convertList(ids, self::getRoleFromCache); + } + + @Override + public PageResult getRolePage(RolePageReqVO reqVO) { + return roleMapper.selectPage(reqVO); + } + + @Override + public boolean hasAnySuperAdmin(Collection ids) { + if (CollectionUtil.isEmpty(ids)) { + return false; + } + RoleServiceImpl self = getSelf(); + return ids.stream().anyMatch(id -> { + RoleDO role = self.getRoleFromCache(id); + return role != null && RoleCodeEnum.isSuperAdmin(role.getCode()); + }); + } + + @Override + public void validateRoleList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得角色信息 + List roles = roleMapper.selectBatchIds(ids); + Map roleMap = convertMap(roles, RoleDO::getId); + // 校验 + ids.forEach(id -> { + RoleDO role = roleMap.get(id); + if (role == null) { + throw exception(ROLE_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())) { + throw exception(ROLE_IS_DISABLE, role.getName()); + } + }); + } + + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private RoleServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java new file mode 100644 index 0000000..c33c775 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java @@ -0,0 +1,81 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; + +import jakarta.validation.Valid; +import java.util.List; + +/** + * 短信渠道 Service 接口 + * + * @author zzf + * @since 2021/1/25 9:24 + */ +public interface SmsChannelService { + + /** + * 创建短信渠道 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSmsChannel(@Valid SmsChannelSaveReqVO createReqVO); + + /** + * 更新短信渠道 + * + * @param updateReqVO 更新信息 + */ + void updateSmsChannel(@Valid SmsChannelSaveReqVO updateReqVO); + + /** + * 删除短信渠道 + * + * @param id 编号 + */ + void deleteSmsChannel(Long id); + + /** + * 获得短信渠道 + * + * @param id 编号 + * @return 短信渠道 + */ + SmsChannelDO getSmsChannel(Long id); + + /** + * 获得所有短信渠道列表 + * + * @return 短信渠道列表 + */ + List getSmsChannelList(); + + /** + * 获得短信渠道分页 + * + * @param pageReqVO 分页查询 + * @return 短信渠道分页 + */ + PageResult getSmsChannelPage(SmsChannelPageReqVO pageReqVO); + + /** + * 获得短信客户端 + * + * @param id 编号 + * @return 短信客户端 + */ + SmsClient getSmsClient(Long id); + + /** + * 获得短信客户端 + * + * @param code 编码 + * @return 短信客户端 + */ + SmsClient getSmsClient(String code); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java new file mode 100644 index 0000000..5c6f36d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; +import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClientFactory; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; + +/** + * 短信渠道 Service 实现类 + * + * @author zzf + */ +@Service +@Slf4j +public class SmsChannelServiceImpl implements SmsChannelService { + + @Resource + private SmsClientFactory smsClientFactory; + + @Resource + private SmsChannelMapper smsChannelMapper; + + @Resource + private SmsTemplateService smsTemplateService; + + @Override + public Long createSmsChannel(SmsChannelSaveReqVO createReqVO) { + SmsChannelDO channel = BeanUtils.toBean(createReqVO, SmsChannelDO.class); + smsChannelMapper.insert(channel); + return channel.getId(); + } + + @Override + public void updateSmsChannel(SmsChannelSaveReqVO updateReqVO) { + // 校验存在 + validateSmsChannelExists(updateReqVO.getId()); + // 更新 + SmsChannelDO updateObj = BeanUtils.toBean(updateReqVO, SmsChannelDO.class); + smsChannelMapper.updateById(updateObj); + } + + @Override + public void deleteSmsChannel(Long id) { + // 校验存在 + validateSmsChannelExists(id); + // 校验是否有在使用该账号的模版 + if (smsTemplateService.getSmsTemplateCountByChannelId(id) > 0) { + throw exception(SMS_CHANNEL_HAS_CHILDREN); + } + // 删除 + smsChannelMapper.deleteById(id); + } + + private SmsChannelDO validateSmsChannelExists(Long id) { + SmsChannelDO channel = smsChannelMapper.selectById(id); + if (channel == null) { + throw exception(SMS_CHANNEL_NOT_EXISTS); + } + return channel; + } + + @Override + public SmsChannelDO getSmsChannel(Long id) { + return smsChannelMapper.selectById(id); + } + + @Override + public List getSmsChannelList() { + return smsChannelMapper.selectList(); + } + + @Override + public PageResult getSmsChannelPage(SmsChannelPageReqVO pageReqVO) { + return smsChannelMapper.selectPage(pageReqVO); + } + + @Override + public SmsClient getSmsClient(Long id) { + SmsChannelDO channel = smsChannelMapper.selectById(id); + SmsChannelProperties properties = BeanUtils.toBean(channel, SmsChannelProperties.class); + return smsClientFactory.createOrUpdateSmsClient(properties); + } + + @Override + public SmsClient getSmsClient(String code) { + return smsClientFactory.getSmsClient(code); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeService.java new file mode 100644 index 0000000..86242f9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeService.java @@ -0,0 +1,40 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; + +import jakarta.validation.Valid; + +/** + * 短信验证码 Service 接口 + * + * @author 芋道源码 + */ +public interface SmsCodeService { + + /** + * 创建短信验证码,并进行发送 + * + * @param reqDTO 发送请求 + */ + void sendSmsCode(@Valid SmsCodeSendReqDTO reqDTO); + + /** + * 验证短信验证码,并进行使用 + * 如果正确,则将验证码标记成已使用 + * 如果错误,则抛出 {@link ServiceException} 异常 + * + * @param reqDTO 使用请求 + */ + void useSmsCode(@Valid SmsCodeUseReqDTO reqDTO); + + /** + * 检查验证码是否有效 + * + * @param reqDTO 校验请求 + */ + void validateSmsCode(@Valid SmsCodeValidateReqDTO reqDTO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java new file mode 100644 index 0000000..6b77277 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java @@ -0,0 +1,112 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO; +import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsCodeMapper; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import cn.iocoder.yudao.module.system.framework.sms.config.SmsCodeProperties; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.randomInt; +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.isToday; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 短信验证码 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class SmsCodeServiceImpl implements SmsCodeService { + + @Resource + private SmsCodeProperties smsCodeProperties; + + @Resource + private SmsCodeMapper smsCodeMapper; + + @Resource + private SmsSendService smsSendService; + + @Override + public void sendSmsCode(SmsCodeSendReqDTO reqDTO) { + SmsSceneEnum sceneEnum = SmsSceneEnum.getCodeByScene(reqDTO.getScene()); + Assert.notNull(sceneEnum, "验证码场景({}) 查找不到配置", reqDTO.getScene()); + // 创建验证码 + String code = createSmsCode(reqDTO.getMobile(), reqDTO.getScene(), reqDTO.getCreateIp()); + // 发送验证码 + smsSendService.sendSingleSms(reqDTO.getMobile(), null, null, + sceneEnum.getTemplateCode(), MapUtil.of("code", code)); + } + + private String createSmsCode(String mobile, Integer scene, String ip) { + // 校验是否可以发送验证码,不用筛选场景 + SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null, null); + if (lastSmsCode != null) { + if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis() + < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁 + throw exception(SMS_CODE_SEND_TOO_FAST); + } + if (isToday(lastSmsCode.getCreateTime()) && // 必须是今天,才能计算超过当天的上限 + lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。 + throw exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); + } + // TODO 芋艿:提升,每个 IP 每天可发送数量 + // TODO 芋艿:提升,每个 IP 每小时可发送数量 + } + + // 创建验证码记录 + String code = String.format("%0" + smsCodeProperties.getEndCode().toString().length() + "d", + randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1)); + SmsCodeDO newSmsCode = SmsCodeDO.builder().mobile(mobile).code(code).scene(scene) + .todayIndex(lastSmsCode != null && isToday(lastSmsCode.getCreateTime()) ? lastSmsCode.getTodayIndex() + 1 : 1) + .createIp(ip).used(false).build(); + smsCodeMapper.insert(newSmsCode); + return code; + } + + @Override + public void useSmsCode(SmsCodeUseReqDTO reqDTO) { + // 检测验证码是否有效 + SmsCodeDO lastSmsCode = validateSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); + // 使用验证码 + smsCodeMapper.updateById(SmsCodeDO.builder().id(lastSmsCode.getId()) + .used(true).usedTime(LocalDateTime.now()).usedIp(reqDTO.getUsedIp()).build()); + } + + @Override + public void validateSmsCode(SmsCodeValidateReqDTO reqDTO) { + validateSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); + } + + private SmsCodeDO validateSmsCode0(String mobile, String code, Integer scene) { + // 校验验证码 + SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, code, scene); + // 若验证码不存在,抛出异常 + if (lastSmsCode == null) { + throw exception(SMS_CODE_NOT_FOUND); + } + // 超过时间 + if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis() + >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期 + throw exception(SMS_CODE_EXPIRED); + } + // 判断验证码是否已被使用 + if (Boolean.TRUE.equals(lastSmsCode.getUsed())) { + throw exception(SMS_CODE_USED); + } + return lastSmsCode; + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java new file mode 100644 index 0000000..0c86c0f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java @@ -0,0 +1,68 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; + +import java.time.LocalDateTime; +import java.util.Map; + +/** + * 短信日志 Service 接口 + * + * @author zzf + * @date 13:48 2021/3/2 + */ +public interface SmsLogService { + + /** + * 创建短信日志 + * + * @param mobile 手机号 + * @param userId 用户编号 + * @param userType 用户类型 + * @param isSend 是否发送 + * @param template 短信模板 + * @param templateContent 短信内容 + * @param templateParams 短信参数 + * @return 发送日志编号 + */ + Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend, + SmsTemplateDO template, String templateContent, Map templateParams); + + /** + * 更新日志的发送结果 + * + * @param id 日志编号 + * @param success 发送是否成功 + * @param apiSendCode 短信 API 发送结果的编码 + * @param apiSendMsg 短信 API 发送失败的提示 + * @param apiRequestId 短信 API 发送返回的唯一请求 ID + * @param apiSerialNo 短信 API 发送返回的序号 + */ + void updateSmsSendResult(Long id, Boolean success, + String apiSendCode, String apiSendMsg, + String apiRequestId, String apiSerialNo); + + /** + * 更新日志的接收结果 + * + * @param id 日志编号 + * @param success 是否接收成功 + * @param receiveTime 用户接收时间 + * @param apiReceiveCode API 接收结果的编码 + * @param apiReceiveMsg API 接收结果的说明 + */ + void updateSmsReceiveResult(Long id, Boolean success, + LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg); + + /** + * 获得短信日志分页 + * + * @param pageReqVO 分页查询 + * @return 短信日志分页 + */ + PageResult getSmsLogPage(SmsLogPageReqVO pageReqVO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java new file mode 100644 index 0000000..4f969ce --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java @@ -0,0 +1,79 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper; +import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum; +import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.Objects; + +/** + * 短信日志 Service 实现类 + * + * @author zzf + */ +@Slf4j +@Service +public class SmsLogServiceImpl implements SmsLogService { + + @Resource + private SmsLogMapper smsLogMapper; + + @Override + public Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend, + SmsTemplateDO template, String templateContent, Map templateParams) { + SmsLogDO.SmsLogDOBuilder logBuilder = SmsLogDO.builder(); + // 根据是否要发送,设置状态 + logBuilder.sendStatus(Objects.equals(isSend, true) ? SmsSendStatusEnum.INIT.getStatus() + : SmsSendStatusEnum.IGNORE.getStatus()); + // 设置手机相关字段 + logBuilder.mobile(mobile).userId(userId).userType(userType); + // 设置模板相关字段 + logBuilder.templateId(template.getId()).templateCode(template.getCode()).templateType(template.getType()); + logBuilder.templateContent(templateContent).templateParams(templateParams) + .apiTemplateId(template.getApiTemplateId()); + // 设置渠道相关字段 + logBuilder.channelId(template.getChannelId()).channelCode(template.getChannelCode()); + // 设置接收相关字段 + logBuilder.receiveStatus(SmsReceiveStatusEnum.INIT.getStatus()); + + // 插入数据库 + SmsLogDO logDO = logBuilder.build(); + smsLogMapper.insert(logDO); + return logDO.getId(); + } + + @Override + public void updateSmsSendResult(Long id, Boolean success, + String apiSendCode, String apiSendMsg, + String apiRequestId, String apiSerialNo) { + SmsSendStatusEnum sendStatus = success ? SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE; + smsLogMapper.updateById(SmsLogDO.builder().id(id) + .sendStatus(sendStatus.getStatus()).sendTime(LocalDateTime.now()) + .apiSendCode(apiSendCode).apiSendMsg(apiSendMsg) + .apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build()); + } + + @Override + public void updateSmsReceiveResult(Long id, Boolean success, LocalDateTime receiveTime, + String apiReceiveCode, String apiReceiveMsg) { + SmsReceiveStatusEnum receiveStatus = Objects.equals(success, true) ? + SmsReceiveStatusEnum.SUCCESS : SmsReceiveStatusEnum.FAILURE; + smsLogMapper.updateById(SmsLogDO.builder().id(id).receiveStatus(receiveStatus.getStatus()) + .receiveTime(receiveTime).apiReceiveCode(apiReceiveCode).apiReceiveMsg(apiReceiveMsg).build()); + } + + @Override + public PageResult getSmsLogPage(SmsLogPageReqVO pageReqVO) { + return smsLogMapper.selectPage(pageReqVO); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendService.java new file mode 100644 index 0000000..3c42cf5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendService.java @@ -0,0 +1,78 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; + +import java.util.List; +import java.util.Map; + +/** + * 短信发送 Service 接口 + * + * @author 芋道源码 + */ +public interface SmsSendService { + + /** + * 发送单条短信给管理后台的用户 + * + * 在 mobile 为空时,使用 userId 加载对应管理员的手机号 + * + * @param mobile 手机号 + * @param userId 用户编号 + * @param templateCode 短信模板编号 + * @param templateParams 短信模板参数 + * @return 发送日志编号 + */ + Long sendSingleSmsToAdmin(String mobile, Long userId, + String templateCode, Map templateParams); + + /** + * 发送单条短信给用户 APP 的用户 + * + * 在 mobile 为空时,使用 userId 加载对应会员的手机号 + * + * @param mobile 手机号 + * @param userId 用户编号 + * @param templateCode 短信模板编号 + * @param templateParams 短信模板参数 + * @return 发送日志编号 + */ + Long sendSingleSmsToMember(String mobile, Long userId, + String templateCode, Map templateParams); + + /** + * 发送单条短信给用户 + * + * @param mobile 手机号 + * @param userId 用户编号 + * @param userType 用户类型 + * @param templateCode 短信模板编号 + * @param templateParams 短信模板参数 + * @return 发送日志编号 + */ + Long sendSingleSms(String mobile, Long userId, Integer userType, + String templateCode, Map templateParams); + + default void sendBatchSms(List mobiles, List userIds, Integer userType, + String templateCode, Map templateParams) { + throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!"); + } + + /** + * 执行真正的短信发送 + * 注意,该方法仅仅提供给 MQ Consumer 使用 + * + * @param message 短信 + */ + void doSendSms(SmsSendMessage message); + + /** + * 接收短信的接收结果 + * + * @param channelCode 渠道编码 + * @param text 结果内容 + * @throws Throwable 处理失败时,抛出异常 + */ + void receiveSmsStatus(String channelCode, String text) throws Throwable; + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java new file mode 100644 index 0000000..41f429e --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java @@ -0,0 +1,191 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; +import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; +import cn.iocoder.yudao.module.system.service.member.MemberService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 短信发送 Service 发送的实现 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class SmsSendServiceImpl implements SmsSendService { + + @Resource + private AdminUserService adminUserService; + @Resource + private MemberService memberService; + @Resource + private SmsChannelService smsChannelService; + @Resource + private SmsTemplateService smsTemplateService; + @Resource + private SmsLogService smsLogService; + + @Resource + private SmsProducer smsProducer; + + @Override + @DataPermission(enable = false) // 发送短信时,无需考虑数据权限 + public Long sendSingleSmsToAdmin(String mobile, Long userId, String templateCode, Map templateParams) { + // 如果 mobile 为空,则加载用户编号对应的手机号 + if (StrUtil.isEmpty(mobile)) { + AdminUserDO user = adminUserService.getUser(userId); + if (user != null) { + mobile = user.getMobile(); + } + } + // 执行发送 + return sendSingleSms(mobile, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams); + } + + @Override + public Long sendSingleSmsToMember(String mobile, Long userId, String templateCode, Map templateParams) { + // 如果 mobile 为空,则加载用户编号对应的手机号 + if (StrUtil.isEmpty(mobile)) { + mobile = memberService.getMemberUserMobile(userId); + } + // 执行发送 + return sendSingleSms(mobile, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); + } + + @Override + public Long sendSingleSms(String mobile, Long userId, Integer userType, + String templateCode, Map templateParams) { + // 校验短信模板是否合法 + SmsTemplateDO template = validateSmsTemplate(templateCode); + // 校验短信渠道是否合法 + SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId()); + + // 校验手机号码是否存在 + mobile = validateMobile(mobile); + // 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志 + List> newTemplateParams = buildTemplateParams(template, templateParams); + + // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 + Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()) + && CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus()); + String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams); + Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams); + + // 发送 MQ 消息,异步执行发送短信 + if (isSend) { + smsProducer.sendSmsSendMessage(sendLogId, mobile, template.getChannelId(), + template.getApiTemplateId(), newTemplateParams); + } + return sendLogId; + } + + @VisibleForTesting + SmsChannelDO validateSmsChannel(Long channelId) { + // 获得短信模板。考虑到效率,从缓存中获取 + SmsChannelDO channelDO = smsChannelService.getSmsChannel(channelId); + // 短信模板不存在 + if (channelDO == null) { + throw exception(SMS_CHANNEL_NOT_EXISTS); + } + return channelDO; + } + + @VisibleForTesting + SmsTemplateDO validateSmsTemplate(String templateCode) { + // 获得短信模板。考虑到效率,从缓存中获取 + SmsTemplateDO template = smsTemplateService.getSmsTemplateByCodeFromCache(templateCode); + // 短信模板不存在 + if (template == null) { + throw exception(SMS_SEND_TEMPLATE_NOT_EXISTS); + } + return template; + } + + /** + * 将参数模板,处理成有序的 KeyValue 数组 + *

+ * 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说 腾讯云 + * + * @param template 短信模板 + * @param templateParams 原始参数 + * @return 处理后的参数 + */ + @VisibleForTesting + List> buildTemplateParams(SmsTemplateDO template, Map templateParams) { + return template.getParams().stream().map(key -> { + Object value = templateParams.get(key); + if (value == null) { + throw exception(SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, key); + } + return new KeyValue<>(key, value); + }).collect(Collectors.toList()); + } + + @VisibleForTesting + public String validateMobile(String mobile) { + if (StrUtil.isEmpty(mobile)) { + throw exception(SMS_SEND_MOBILE_NOT_EXISTS); + } + return mobile; + } + + @Override + public void doSendSms(SmsSendMessage message) { + // 获得渠道对应的 SmsClient 客户端 + SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId()); + Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId()); + // 发送短信 + try { + SmsSendRespDTO sendResponse = smsClient.sendSms(message.getLogId(), message.getMobile(), + message.getApiTemplateId(), message.getTemplateParams()); + smsLogService.updateSmsSendResult(message.getLogId(), sendResponse.getSuccess(), + sendResponse.getApiCode(), sendResponse.getApiMsg(), + sendResponse.getApiRequestId(), sendResponse.getSerialNo()); + } catch (Throwable ex) { + log.error("[doSendSms][发送短信异常,日志编号({})]", message.getLogId(), ex); + smsLogService.updateSmsSendResult(message.getLogId(), false, + "EXCEPTION", ExceptionUtil.getRootCauseMessage(ex), null, null); + } + } + + @Override + public void receiveSmsStatus(String channelCode, String text) throws Throwable { + // 获得渠道对应的 SmsClient 客户端 + SmsClient smsClient = smsChannelService.getSmsClient(channelCode); + Assert.notNull(smsClient, "短信客户端({}) 不存在", channelCode); + // 解析内容 + List receiveResults = smsClient.parseSmsReceiveStatus(text); + if (CollUtil.isEmpty(receiveResults)) { + return; + } + // 更新短信日志的接收结果. 因为量一般不大,所以先使用 for 循环更新 + receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(result.getLogId(), + result.getSuccess(), result.getReceiveTime(), result.getErrorCode(), result.getErrorMsg())); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java new file mode 100644 index 0000000..65cdba5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java @@ -0,0 +1,82 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; + +import jakarta.validation.Valid; +import java.util.Map; + +/** + * 短信模板 Service 接口 + * + * @author zzf + * @since 2021/1/25 9:24 + */ +public interface SmsTemplateService { + + /** + * 创建短信模板 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSmsTemplate(@Valid SmsTemplateSaveReqVO createReqVO); + + /** + * 更新短信模板 + * + * @param updateReqVO 更新信息 + */ + void updateSmsTemplate(@Valid SmsTemplateSaveReqVO updateReqVO); + + /** + * 删除短信模板 + * + * @param id 编号 + */ + void deleteSmsTemplate(Long id); + + /** + * 获得短信模板 + * + * @param id 编号 + * @return 短信模板 + */ + SmsTemplateDO getSmsTemplate(Long id); + + /** + * 获得短信模板,从缓存中 + * + * @param code 模板编码 + * @return 短信模板 + */ + SmsTemplateDO getSmsTemplateByCodeFromCache(String code); + + /** + * 获得短信模板分页 + * + * @param pageReqVO 分页查询 + * @return 短信模板分页 + */ + PageResult getSmsTemplatePage(SmsTemplatePageReqVO pageReqVO); + + /** + * 获得指定短信渠道下的短信模板数量 + * + * @param channelId 短信渠道编号 + * @return 数量 + */ + Long getSmsTemplateCountByChannelId(Long channelId); + + /** + * 格式化短信内容 + * + * @param content 短信模板的内容 + * @param params 内容的参数 + * @return 格式化后的内容 + */ + String formatSmsTemplateContent(String content, Map params); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java new file mode 100644 index 0000000..a8762a9 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java @@ -0,0 +1,199 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Pattern; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 短信模板 Service 实现类 + * + * @author zzf + * @since 2021/1/25 9:25 + */ +@Service +@Slf4j +public class SmsTemplateServiceImpl implements SmsTemplateService { + + /** + * 正则表达式,匹配 {} 中的变量 + */ + private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}"); + + @Resource + private SmsTemplateMapper smsTemplateMapper; + + @Resource + private SmsChannelService smsChannelService; + + @Override + public Long createSmsTemplate(SmsTemplateSaveReqVO createReqVO) { + // 校验短信渠道 + SmsChannelDO channelDO = validateSmsChannel(createReqVO.getChannelId()); + // 校验短信编码是否重复 + validateSmsTemplateCodeDuplicate(null, createReqVO.getCode()); + // 校验短信模板 + validateApiTemplate(createReqVO.getChannelId(), createReqVO.getApiTemplateId()); + + // 插入 + SmsTemplateDO template = BeanUtils.toBean(createReqVO, SmsTemplateDO.class); + template.setParams(parseTemplateContentParams(template.getContent())); + template.setChannelCode(channelDO.getCode()); + smsTemplateMapper.insert(template); + // 返回 + return template.getId(); + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 + public void updateSmsTemplate(SmsTemplateSaveReqVO updateReqVO) { + // 校验存在 + validateSmsTemplateExists(updateReqVO.getId()); + // 校验短信渠道 + SmsChannelDO channelDO = validateSmsChannel(updateReqVO.getChannelId()); + // 校验短信编码是否重复 + validateSmsTemplateCodeDuplicate(updateReqVO.getId(), updateReqVO.getCode()); + // 校验短信模板 + validateApiTemplate(updateReqVO.getChannelId(), updateReqVO.getApiTemplateId()); + + // 更新 + SmsTemplateDO updateObj = BeanUtils.toBean(updateReqVO, SmsTemplateDO.class); + updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); + updateObj.setChannelCode(channelDO.getCode()); + smsTemplateMapper.updateById(updateObj); + } + + @Override + @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, + allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 + public void deleteSmsTemplate(Long id) { + // 校验存在 + validateSmsTemplateExists(id); + // 更新 + smsTemplateMapper.deleteById(id); + } + + private void validateSmsTemplateExists(Long id) { + if (smsTemplateMapper.selectById(id) == null) { + throw exception(SMS_TEMPLATE_NOT_EXISTS); + } + } + + @Override + public SmsTemplateDO getSmsTemplate(Long id) { + return smsTemplateMapper.selectById(id); + } + + @Override + @Cacheable(cacheNames = RedisKeyConstants.SMS_TEMPLATE, key = "#code", + unless = "#result == null") + public SmsTemplateDO getSmsTemplateByCodeFromCache(String code) { + return smsTemplateMapper.selectByCode(code); + } + + @Override + public PageResult getSmsTemplatePage(SmsTemplatePageReqVO pageReqVO) { + return smsTemplateMapper.selectPage(pageReqVO); + } + + @Override + public Long getSmsTemplateCountByChannelId(Long channelId) { + return smsTemplateMapper.selectCountByChannelId(channelId); + } + + @VisibleForTesting + public SmsChannelDO validateSmsChannel(Long channelId) { + SmsChannelDO channelDO = smsChannelService.getSmsChannel(channelId); + if (channelDO == null) { + throw exception(SMS_CHANNEL_NOT_EXISTS); + } + if (CommonStatusEnum.isDisable(channelDO.getStatus())) { + throw exception(SMS_CHANNEL_DISABLE); + } + return channelDO; + } + + @VisibleForTesting + public void validateSmsTemplateCodeDuplicate(Long id, String code) { + SmsTemplateDO template = smsTemplateMapper.selectByCode(code); + if (template == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 + if (id == null) { + throw exception(SMS_TEMPLATE_CODE_DUPLICATE, code); + } + if (!template.getId().equals(id)) { + throw exception(SMS_TEMPLATE_CODE_DUPLICATE, code); + } + } + + /** + * 校验 API 短信平台的模板是否有效 + * + * @param channelId 渠道编号 + * @param apiTemplateId API 模板编号 + */ + @VisibleForTesting + void validateApiTemplate(Long channelId, String apiTemplateId) { + // 获得短信模板 + SmsClient smsClient = smsChannelService.getSmsClient(channelId); + Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId)); + SmsTemplateRespDTO template; + try { + template = smsClient.getSmsTemplate(apiTemplateId); + } catch (Throwable ex) { + throw exception(SMS_TEMPLATE_API_ERROR, ExceptionUtil.getRootCauseMessage(ex)); + } + // 校验短信模版 + if (template == null) { + throw exception(SMS_TEMPLATE_API_NOT_FOUND); + } + if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.CHECKING.getStatus())) { + throw exception(SMS_TEMPLATE_API_AUDIT_CHECKING); + } + if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.FAIL.getStatus())) { + throw exception(SMS_TEMPLATE_API_AUDIT_FAIL, template.getAuditReason()); + } + Assert.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), + String.format("短信模板(%s) 审核状态(%d) 不正确", apiTemplateId, template.getAuditStatus())); + } + + @Override + public String formatSmsTemplateContent(String content, Map params) { + return StrUtil.format(content, params); + } + + @VisibleForTesting + List parseTemplateContentParams(String content) { + return ReUtil.findAllGroup1(PATTERN_PARAMS, content); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java new file mode 100644 index 0000000..91bb9c1 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java @@ -0,0 +1,153 @@ +package cn.iocoder.yudao.module.system.service.social; + +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import jakarta.validation.Valid; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo; +import me.zhyd.oauth.model.AuthUser; + +import java.util.List; + +/** + * 社交应用 Service 接口 + * + * @author 芋道源码 + */ +public interface SocialClientService { + + /** + * 获得社交平台的授权 URL + * + * @param socialType 社交平台的类型 {@link SocialTypeEnum} + * @param userType 用户类型 + * @param redirectUri 重定向 URL + * @return 社交平台的授权 URL + */ + String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri); + + /** + * 请求社交平台,获得授权的用户 + * + * @param socialType 社交平台的类型 + * @param userType 用户类型 + * @param code 授权码 + * @param state 授权 state + * @return 授权的用户 + */ + AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state); + + // =================== 微信公众号独有 =================== + + /** + * 创建微信公众号的 JS SDK 初始化所需的签名 + * + * @param userType 用户类型 + * @param url 访问的 URL 地址 + * @return 签名 + */ + WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url); + + // =================== 微信小程序独有 =================== + + /** + * 获得微信小程序的手机信息 + * + * @param userType 用户类型 + * @param phoneCode 手机授权码 + * @return 手机信息 + */ + WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode); + + /** + * 获得小程序二维码 + * + * @param reqVO 请求信息 + * @return 小程序二维码 + */ + byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO); + + /** + * 获得微信小程订阅模板 + * + * 缓存的目的:考虑到微信小程序订阅消息选择好模版后几乎不会变动,缓存增加查询效率 + * + * @param userType 用户类型 + * @return 微信小程订阅模板 + */ + List getSubscribeTemplateList(Integer userType); + + /** + * 发送微信小程序订阅消息 + * + * @param reqDTO 请求 + * @param templateId 模版编号 + * @param openId 会员 openId + */ + void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId); + + /** + * 上传订单发货到微信小程序 + * + * @param userType 用户类型 + * @param reqDTO 请求 + */ + void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO); + + /** + * 通知订单收货到微信小程序 + * + * @param userType 用户类型 + * @param reqDTO 请求 + */ + void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO); + + // =================== 客户端管理 =================== + + /** + * 创建社交客户端 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createSocialClient(@Valid SocialClientSaveReqVO createReqVO); + + /** + * 更新社交客户端 + * + * @param updateReqVO 更新信息 + */ + void updateSocialClient(@Valid SocialClientSaveReqVO updateReqVO); + + /** + * 删除社交客户端 + * + * @param id 编号 + */ + void deleteSocialClient(Long id); + + /** + * 获得社交客户端 + * + * @param id 编号 + * @return 社交客户端 + */ + SocialClientDO getSocialClient(Long id); + + /** + * 获得社交客户端分页 + * + * @param pageReqVO 分页查询 + * @return 社交客户端分页 + */ + PageResult getSocialClientPage(SocialClientPageReqVO pageReqVO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java new file mode 100644 index 0000000..b5c9d88 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java @@ -0,0 +1,506 @@ +package cn.iocoder.yudao.module.system.service.social; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; +import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; +import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*; +import cn.binarywang.wx.miniapp.bean.shop.response.WxMaOrderShippingInfoBaseResponse; +import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl; +import cn.binarywang.wx.miniapp.constant.WxMaConstants; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.DesensitizedUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.cache.CacheUtils; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper; +import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import cn.iocoder.yudao.module.system.framework.justauth.core.AuthRequestFactory; +import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import jakarta.annotation.Resource; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; +import me.chanjar.weixin.mp.api.WxMpService; +import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; +import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.model.AuthCallback; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthRequest; +import me.zhyd.oauth.utils.AuthStateUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.UTC_MS_WITH_XXX_OFFSET_FORMATTER; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static java.util.Collections.singletonList; + +/** + * 社交应用 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Slf4j +public class SocialClientServiceImpl implements SocialClientService { + + /** + * 小程序码要打开的小程序版本 + * + * 1. release:正式版 + * 2. trial:体验版 + * 3. developer:开发版 + */ + @Value("${yudao.wxa-code.env-version:release}") + public String envVersion; + /** + * 订阅消息跳转小程序类型 + * + * 1. developer:开发版 + * 2. trial:体验版 + * 3. formal:正式版 + */ + @Value("${yudao.wxa-subscribe-message.miniprogram-state:formal}") + public String miniprogramState; + + @Resource + private AuthRequestFactory authRequestFactory; + + @Resource + private WxMpService wxMpService; + @Resource + private WxMpProperties wxMpProperties; + @Resource + private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它 + /** + * 缓存 WxMpService 对象 + * + * key:使用微信公众号的 appId + secret 拼接,即 {@link SocialClientDO} 的 clientId 和 clientSecret 属性。 + * 为什么 key 使用这种格式?因为 {@link SocialClientDO} 在管理后台可以变更,通过这个 key 存储它的单例。 + * + * 为什么要做 WxMpService 缓存?因为 WxMpService 构建成本比较大,所以尽量保证它是单例。 + */ + private final LoadingCache wxMpServiceCache = CacheUtils.buildAsyncReloadingCache( + Duration.ofSeconds(10L), + new CacheLoader() { + + @Override + public WxMpService load(String key) { + String[] keys = key.split(":"); + return buildWxMpService(keys[0], keys[1]); + } + + }); + + @Resource + private WxMaService wxMaService; + @Resource + private WxMaProperties wxMaProperties; + /** + * 缓存 WxMaService 对象 + * + * 说明同 {@link #wxMpServiceCache} 变量 + */ + private final LoadingCache wxMaServiceCache = CacheUtils.buildAsyncReloadingCache( + Duration.ofSeconds(10L), + new CacheLoader() { + + @Override + public WxMaService load(String key) { + String[] keys = key.split(":"); + return buildWxMaService(keys[0], keys[1]); + } + + }); + + @Resource + private SocialClientMapper socialClientMapper; + + @Override + public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) { + // 获得对应的 AuthRequest 实现 + AuthRequest authRequest = buildAuthRequest(socialType, userType); + // 生成跳转地址 + String authorizeUri = authRequest.authorize(AuthStateUtils.createState()); + return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri); + } + + @Override + public AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state) { + // 构建请求 + AuthRequest authRequest = buildAuthRequest(socialType, userType); + AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build(); + // 执行请求 + AuthResponse authResponse = authRequest.login(authCallback); + log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", socialType, + toJsonString(authCallback), toJsonString(authResponse)); + if (!authResponse.ok()) { + throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg()); + } + return (AuthUser) authResponse.getData(); + } + + /** + * 构建 AuthRequest 对象,支持多租户配置 + * + * @param socialType 社交类型 + * @param userType 用户类型 + * @return AuthRequest 对象 + */ + @VisibleForTesting + AuthRequest buildAuthRequest(Integer socialType, Integer userType) { + // 1. 先查找默认的配置项,从 application-*.yaml 中读取 + AuthRequest request = authRequestFactory.get(SocialTypeEnum.valueOfType(socialType).getSource()); + Assert.notNull(request, String.format("社交平台(%d) 不存在", socialType)); + // 2. 查询 DB 的配置项,如果存在则进行覆盖 + SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(socialType, userType); + if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { + // 2.1 构造新的 AuthConfig 对象 + AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(request, "config"); + AuthConfig newAuthConfig = ReflectUtil.newInstance(authConfig.getClass()); + BeanUtil.copyProperties(authConfig, newAuthConfig); + // 2.2 修改对应的 clientId + clientSecret 密钥 + newAuthConfig.setClientId(client.getClientId()); + newAuthConfig.setClientSecret(client.getClientSecret()); + if (client.getAgentId() != null) { // 如果有 agentId 则修改 agentId + newAuthConfig.setAgentId(client.getAgentId()); + } + // 2.3 设置会 request 里,进行后续使用 + ReflectUtil.setFieldValue(request, "config", newAuthConfig); + } + return request; + } + + // =================== 微信公众号独有 =================== + + @Override + @SneakyThrows + public WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url) { + WxMpService service = getWxMpService(userType); + return service.createJsapiSignature(url); + } + + /** + * 获得 clientId + clientSecret 对应的 WxMpService 对象 + * + * @param userType 用户类型 + * @return WxMpService 对象 + */ + @VisibleForTesting + WxMpService getWxMpService(Integer userType) { + // 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象 + SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( + SocialTypeEnum.WECHAT_MP.getType(), userType); + if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { + return wxMpServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret()); + } + // 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMpService 对象 + return wxMpService; + } + + /** + * 创建 clientId + clientSecret 对应的 WxMpService 对象 + * + * @param clientId 微信公众号 appId + * @param clientSecret 微信公众号 secret + * @return WxMpService 对象 + */ + public WxMpService buildWxMpService(String clientId, String clientSecret) { + // 第一步,创建 WxMpRedisConfigImpl 对象 + WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl( + new RedisTemplateWxRedisOps(stringRedisTemplate), + wxMpProperties.getConfigStorage().getKeyPrefix()); + configStorage.setAppId(clientId); + configStorage.setSecret(clientSecret); + + // 第二步,创建 WxMpService 对象 + WxMpService service = new WxMpServiceImpl(); + service.setWxMpConfigStorage(configStorage); + return service; + } + + // =================== 微信小程序独有 =================== + + @Override + public WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode) { + WxMaService service = getWxMaService(userType); + try { + return service.getUserService().getPhoneNoInfo(phoneCode); + } catch (WxErrorException e) { + log.error("[getPhoneNoInfo][userType({}) phoneCode({}) 获得手机号失败]", userType, phoneCode, e); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR); + } + } + + @Override + public byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO) { + WxMaService service = getWxMaService(UserTypeEnum.MEMBER.getValue()); + try { + return service.getQrcodeService().createWxaCodeUnlimitBytes( + ObjUtil.defaultIfEmpty(reqVO.getScene(), SocialWxQrcodeReqDTO.SCENE), + reqVO.getPath(), + ObjUtil.defaultIfNull(reqVO.getCheckPath(), SocialWxQrcodeReqDTO.CHECK_PATH), + envVersion, + ObjUtil.defaultIfNull(reqVO.getWidth(), SocialWxQrcodeReqDTO.WIDTH), + ObjUtil.defaultIfNull(reqVO.getAutoColor(), SocialWxQrcodeReqDTO.AUTO_COLOR), + null, + ObjUtil.defaultIfNull(reqVO.getHyaline(), SocialWxQrcodeReqDTO.HYALINE)); + } catch (WxErrorException e) { + log.error("[getWxQrcode][reqVO({}) 获得小程序码失败]", reqVO, e); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR); + } + } + + @Override + @Cacheable(cacheNames = RedisKeyConstants.WXA_SUBSCRIBE_TEMPLATE, key = "#userType", + unless = "#result == null") + public List getSubscribeTemplateList(Integer userType) { + WxMaService service = getWxMaService(userType); + try { + WxMaSubscribeService subscribeService = service.getSubscribeService(); + return subscribeService.getTemplateList(); + } catch (WxErrorException e) { + log.error("[getSubscribeTemplate][userType({}) 获得小程序订阅消息模版]", userType, e); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR); + } + } + + @Override + public void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId) { + WxMaService service = getWxMaService(reqDTO.getUserType()); + try { + WxMaSubscribeService subscribeService = service.getSubscribeService(); + subscribeService.sendSubscribeMsg(buildMessageSendReqDTO(reqDTO, templateId, openId)); + } catch (WxErrorException e) { + log.error("[sendSubscribeMessage][reqVO({}) templateId({}) openId({}) 发送小程序订阅消息失败]", reqDTO, templateId, openId, e); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR); + } + } + + /** + * 构建发送消息请求参数 + * + * @param reqDTO 请求 + * @param templateId 模版编号 + * @param openId 会员 openId + * @return 微信小程序订阅消息请求参数 + */ + private WxMaSubscribeMessage buildMessageSendReqDTO(SocialWxaSubscribeMessageSendReqDTO reqDTO, + String templateId, String openId) { + // 设置订阅消息基本参数 + WxMaSubscribeMessage subscribeMessage = new WxMaSubscribeMessage().setLang(WxMaConstants.MiniProgramLang.ZH_CN) + .setMiniprogramState(miniprogramState).setTemplateId(templateId).setToUser(openId).setPage(reqDTO.getPage()); + // 设置具体消息参数 + Map messages = reqDTO.getMessages(); + if (CollUtil.isNotEmpty(messages)) { + reqDTO.getMessages().keySet().forEach(key -> findAndThen(messages, key, value -> + subscribeMessage.addData(new WxMaSubscribeMessage.MsgData(key, value)))); + } + return subscribeMessage; + } + + @Override + public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) { + WxMaService service = getWxMaService(userType); + List shippingList; + if (Objects.equals(reqDTO.getLogisticsType(), SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_EXPRESS)) { + shippingList = singletonList(ShippingListBean.builder() + .trackingNo(reqDTO.getLogisticsNo()) + .expressCompany(reqDTO.getExpressCompany()) + .itemDesc(reqDTO.getItemDesc()) + .contact(ContactBean.builder().receiverContact(DesensitizedUtil.mobilePhone(reqDTO.getReceiverContact())).build()) + .build()); + } else { + shippingList = singletonList(ShippingListBean.builder().itemDesc(reqDTO.getItemDesc()).build()); + } + WxMaOrderShippingInfoUploadRequest request = WxMaOrderShippingInfoUploadRequest.builder() + .orderKey(OrderKeyBean.builder() + .orderNumberType(2) // 使用原支付交易对应的微信订单号,即渠道单号 + .transactionId(reqDTO.getTransactionId()) + .build()) + .logisticsType(reqDTO.getLogisticsType()) // 配送方式 + .deliveryMode(1) // 统一发货 + .shippingList(shippingList) + .payer(PayerBean.builder().openid(reqDTO.getOpenid()).build()) + .uploadTime(ZonedDateTime.now().format(UTC_MS_WITH_XXX_OFFSET_FORMATTER)) + .build(); + try { + WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().upload(request); + if (response.getErrCode() != 0) { + log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({}) response({})]", request, response); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, response.getErrMsg()); + } + log.info("[uploadWxaOrderShippingInfo][上传微信小程序发货信息成功:request({}) response({})]", request, response); + } catch (WxErrorException ex) { + log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({})]", request, ex); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, ex.getError().getErrorMsg()); + } + } + + @Override + public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) { + WxMaService service = getWxMaService(userType); + WxMaOrderShippingInfoNotifyConfirmRequest request = WxMaOrderShippingInfoNotifyConfirmRequest.builder() + .transactionId(reqDTO.getTransactionId()) + .receivedTime(LocalDateTimeUtil.toEpochMilli(reqDTO.getReceivedTime())) + .build(); + try { + WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().notifyConfirmReceive(request); + if (response.getErrCode() != 0) { + log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({}) response({})]", request, response); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, response.getErrMsg()); + } + log.info("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序成功:request({}) response({})]", request, response); + } catch (WxErrorException ex) { + log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({})]", request, ex); + throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, ex.getError().getErrorMsg()); + } + } + + /** + * 获得 clientId + clientSecret 对应的 WxMpService 对象 + * + * @param userType 用户类型 + * @return WxMpService 对象 + */ + @VisibleForTesting + WxMaService getWxMaService(Integer userType) { + // 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象 + SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( + SocialTypeEnum.WECHAT_MINI_PROGRAM.getType(), userType); + if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { + return wxMaServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret()); + } + // 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMaService 对象 + return wxMaService; + } + + /** + * 创建 clientId + clientSecret 对应的 WxMaService 对象 + * + * @param clientId 微信小程序 appId + * @param clientSecret 微信小程序 secret + * @return WxMaService 对象 + */ + private WxMaService buildWxMaService(String clientId, String clientSecret) { + // 第一步,创建 WxMaRedisBetterConfigImpl 对象 + WxMaRedisBetterConfigImpl configStorage = new WxMaRedisBetterConfigImpl( + new RedisTemplateWxRedisOps(stringRedisTemplate), + wxMaProperties.getConfigStorage().getKeyPrefix()); + configStorage.setAppid(clientId); + configStorage.setSecret(clientSecret); + + // 第二步,创建 WxMpService 对象 + WxMaService service = new WxMaServiceImpl(); + service.setWxMaConfig(configStorage); + return service; + } + + // =================== 客户端管理 =================== + + @Override + public Long createSocialClient(SocialClientSaveReqVO createReqVO) { + // 校验重复 + validateSocialClientUnique(null, createReqVO.getUserType(), createReqVO.getSocialType()); + + // 插入 + SocialClientDO client = BeanUtils.toBean(createReqVO, SocialClientDO.class); + socialClientMapper.insert(client); + return client.getId(); + } + + @Override + public void updateSocialClient(SocialClientSaveReqVO updateReqVO) { + // 校验存在 + validateSocialClientExists(updateReqVO.getId()); + // 校验重复 + validateSocialClientUnique(updateReqVO.getId(), updateReqVO.getUserType(), updateReqVO.getSocialType()); + + // 更新 + SocialClientDO updateObj = BeanUtils.toBean(updateReqVO, SocialClientDO.class); + socialClientMapper.updateById(updateObj); + } + + @Override + public void deleteSocialClient(Long id) { + // 校验存在 + validateSocialClientExists(id); + // 删除 + socialClientMapper.deleteById(id); + } + + private void validateSocialClientExists(Long id) { + if (socialClientMapper.selectById(id) == null) { + throw exception(SOCIAL_CLIENT_NOT_EXISTS); + } + } + + /** + * 校验社交应用是否重复,需要保证 userType + socialType 唯一 + * + * 原因是,不同端(userType)选择某个社交登录(socialType)时,需要通过 {@link #buildAuthRequest(Integer, Integer)} 构建对应的请求 + * + * @param id 编号 + * @param userType 用户类型 + * @param socialType 社交类型 + */ + private void validateSocialClientUnique(Long id, Integer userType, Integer socialType) { + SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( + socialType, userType); + if (client == null) { + return; + } + if (id == null // 新增时,说明重复 + || ObjUtil.notEqual(id, client.getId())) { // 更新时,如果 id 不一致,说明重复 + throw exception(SOCIAL_CLIENT_UNIQUE); + } + } + + @Override + public SocialClientDO getSocialClient(Long id) { + return socialClientMapper.selectById(id); + } + + @Override + public PageResult getSocialClientPage(SocialClientPageReqVO pageReqVO) { + return socialClientMapper.selectPage(pageReqVO); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java new file mode 100644 index 0000000..739ff2d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java @@ -0,0 +1,89 @@ +package cn.iocoder.yudao.module.system.service.social; + +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; + +import jakarta.validation.Valid; +import java.util.List; + +/** + * 社交用户 Service 接口,例如说社交平台的授权登录 + * + * @author 芋道源码 + */ +public interface SocialUserService { + + /** + * 获得指定用户的社交用户列表 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @return 社交用户列表 + */ + List getSocialUserList(Long userId, Integer userType); + + /** + * 绑定社交用户 + * + * @param reqDTO 绑定信息 + * @return 社交用户 openid + */ + String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO); + + /** + * 取消绑定社交用户 + * + * @param userId 用户编号 + * @param userType 全局用户类型 + * @param socialType 社交平台的类型 {@link SocialTypeEnum} + * @param openid 社交平台的 openid + */ + void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid); + + /** + * 获得社交用户,基于 userId + * + * @param userType 用户类型 + * @param userId 用户编号 + * @param socialType 社交平台的类型 + * @return 社交用户 + */ + SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType); + + /** + * 获得社交用户 + * + * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 + * + * @param userType 用户类型 + * @param socialType 社交平台的类型 + * @param code 授权码 + * @param state state + * @return 社交用户 + */ + SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state); + + // ==================== 社交用户 CRUD ==================== + + /** + * 获得社交用户 + * + * @param id 编号 + * @return 社交用户 + */ + SocialUserDO getSocialUser(Long id); + + /** + * 获得社交用户分页 + * + * @param pageReqVO 分页查询 + * @return 社交用户分页 + */ + PageResult getSocialUserPage(SocialUserPageReqVO pageReqVO); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java new file mode 100644 index 0000000..f3cad58 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java @@ -0,0 +1,173 @@ +package cn.iocoder.yudao.module.system.service.social; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotNull; +import lombok.extern.slf4j.Slf4j; +import me.zhyd.oauth.model.AuthUser; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND; + +/** + * 社交用户 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class SocialUserServiceImpl implements SocialUserService { + + @Resource + private SocialUserBindMapper socialUserBindMapper; + @Resource + private SocialUserMapper socialUserMapper; + + @Resource + private SocialClientService socialClientService; + + @Override + public List getSocialUserList(Long userId, Integer userType) { + // 获得绑定 + List socialUserBinds = socialUserBindMapper.selectListByUserIdAndUserType(userId, userType); + if (CollUtil.isEmpty(socialUserBinds)) { + return Collections.emptyList(); + } + // 获得社交用户 + return socialUserMapper.selectBatchIds(convertSet(socialUserBinds, SocialUserBindDO::getSocialUserId)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String bindSocialUser(SocialUserBindReqDTO reqDTO) { + // 获得社交用户 + SocialUserDO socialUser = authSocialUser(reqDTO.getSocialType(), reqDTO.getUserType(), + reqDTO.getCode(), reqDTO.getState()); + Assert.notNull(socialUser, "社交用户不能为空"); + + // 社交用户可能之前绑定过别的用户,需要进行解绑 + socialUserBindMapper.deleteByUserTypeAndSocialUserId(reqDTO.getUserType(), socialUser.getId()); + + // 用户可能之前已经绑定过该社交类型,需要进行解绑 + socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(reqDTO.getUserType(), reqDTO.getUserId(), + socialUser.getType()); + + // 绑定当前登录的社交用户 + SocialUserBindDO socialUserBind = SocialUserBindDO.builder() + .userId(reqDTO.getUserId()).userType(reqDTO.getUserType()) + .socialUserId(socialUser.getId()).socialType(socialUser.getType()).build(); + socialUserBindMapper.insert(socialUserBind); + return socialUser.getOpenid(); + } + + @Override + public void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid) { + // 获得 openid 对应的 SocialUserDO 社交用户 + SocialUserDO socialUser = socialUserMapper.selectByTypeAndOpenid(socialType, openid); + if (socialUser == null) { + throw exception(SOCIAL_USER_NOT_FOUND); + } + + // 获得对应的社交绑定关系 + socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(userType, userId, socialUser.getType()); + } + + @Override + public SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType) { + // 获得绑定用户 + SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserIdAndUserTypeAndSocialType(userId, userType, socialType); + if (socialUserBind == null) { + return null; + } + // 获得社交用户 + SocialUserDO socialUser = socialUserMapper.selectById(socialUserBind.getSocialUserId()); + Assert.notNull(socialUser, "社交用户不能为空"); + return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(), + socialUserBind.getUserId()); + } + + @Override + public SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state) { + // 获得社交用户 + SocialUserDO socialUser = authSocialUser(socialType, userType, code, state); + Assert.notNull(socialUser, "社交用户不能为空"); + + // 获得绑定用户 + SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType, + socialUser.getId()); + return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(), + socialUserBind != null ? socialUserBind.getUserId() : null); + } + + /** + * 授权获得对应的社交用户 + * 如果授权失败,则会抛出 {@link ServiceException} 异常 + * + * @param socialType 社交平台的类型 {@link SocialTypeEnum} + * @param userType 用户类型 + * @param code 授权码 + * @param state state + * @return 授权用户 + */ + @NotNull + public SocialUserDO authSocialUser(Integer socialType, Integer userType, String code, String state) { + // 优先从 DB 中获取,因为 code 有且可以使用一次。 + // 在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次 + SocialUserDO socialUser = socialUserMapper.selectByTypeAndCodeAnState(socialType, code, state); + if (socialUser != null) { + return socialUser; + } + + // 请求获取 + AuthUser authUser = socialClientService.getAuthUser(socialType, userType, code, state); + Assert.notNull(authUser, "三方用户不能为空"); + + // 保存到 DB 中 + socialUser = socialUserMapper.selectByTypeAndOpenid(socialType, authUser.getUuid()); + if (socialUser == null) { + socialUser = new SocialUserDO(); + } + socialUser.setType(socialType).setCode(code).setState(state) // 需要保存 code + state 字段,保证后续可查询 + .setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken()))) + .setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo())); + if (socialUser.getId() == null) { + socialUserMapper.insert(socialUser); + } else { + socialUserMapper.updateById(socialUser); + } + return socialUser; + } + + // ==================== 社交用户 CRUD ==================== + + @Override + public SocialUserDO getSocialUser(Long id) { + return socialUserMapper.selectById(id); + } + + @Override + public PageResult getSocialUserPage(SocialUserPageReqVO pageReqVO) { + return socialUserMapper.selectPage(pageReqVO); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageService.java new file mode 100755 index 0000000..05318f8 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageService.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.system.service.tenant; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; + +import jakarta.validation.Valid; +import java.util.List; + +/** + * 租户套餐 Service 接口 + * + * @author 芋道源码 + */ +public interface TenantPackageService { + + /** + * 创建租户套餐 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createTenantPackage(@Valid TenantPackageSaveReqVO createReqVO); + + /** + * 更新租户套餐 + * + * @param updateReqVO 更新信息 + */ + void updateTenantPackage(@Valid TenantPackageSaveReqVO updateReqVO); + + /** + * 删除租户套餐 + * + * @param id 编号 + */ + void deleteTenantPackage(Long id); + + /** + * 获得租户套餐 + * + * @param id 编号 + * @return 租户套餐 + */ + TenantPackageDO getTenantPackage(Long id); + + /** + * 获得租户套餐分页 + * + * @param pageReqVO 分页查询 + * @return 租户套餐分页 + */ + PageResult getTenantPackagePage(TenantPackagePageReqVO pageReqVO); + + /** + * 校验租户套餐 + * + * @param id 编号 + * @return 租户套餐 + */ + TenantPackageDO validTenantPackage(Long id); + + /** + * 获得指定状态的租户套餐列表 + * + * @param status 状态 + * @return 租户套餐 + */ + List getTenantPackageListByStatus(Integer status); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java new file mode 100755 index 0000000..34209e5 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java @@ -0,0 +1,139 @@ +package cn.iocoder.yudao.module.system.service.tenant; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; +import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; +import com.google.common.annotations.VisibleForTesting; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 租户套餐 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class TenantPackageServiceImpl implements TenantPackageService { + + @Resource + private TenantPackageMapper tenantPackageMapper; + + @Resource + @Lazy // 避免循环依赖的报错 + private TenantService tenantService; + + @Override + public Long createTenantPackage(TenantPackageSaveReqVO createReqVO) { + // 校验套餐名是否重复 + validateTenantPackageNameUnique(null, createReqVO.getName()); + // 插入 + TenantPackageDO tenantPackage = BeanUtils.toBean(createReqVO, TenantPackageDO.class); + tenantPackageMapper.insert(tenantPackage); + // 返回 + return tenantPackage.getId(); + } + + @Override + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 + public void updateTenantPackage(TenantPackageSaveReqVO updateReqVO) { + // 校验存在 + TenantPackageDO tenantPackage = validateTenantPackageExists(updateReqVO.getId()); + // 校验套餐名是否重复 + validateTenantPackageNameUnique(updateReqVO.getId(), updateReqVO.getName()); + // 更新 + TenantPackageDO updateObj = BeanUtils.toBean(updateReqVO, TenantPackageDO.class); + tenantPackageMapper.updateById(updateObj); + // 如果菜单发生变化,则修改每个租户的菜单 + if (!CollUtil.isEqualList(tenantPackage.getMenuIds(), updateReqVO.getMenuIds())) { + List tenants = tenantService.getTenantListByPackageId(tenantPackage.getId()); + tenants.forEach(tenant -> tenantService.updateTenantRoleMenu(tenant.getId(), updateReqVO.getMenuIds())); + } + } + + @Override + public void deleteTenantPackage(Long id) { + // 校验存在 + validateTenantPackageExists(id); + // 校验正在使用 + validateTenantUsed(id); + // 删除 + tenantPackageMapper.deleteById(id); + } + + private TenantPackageDO validateTenantPackageExists(Long id) { + TenantPackageDO tenantPackage = tenantPackageMapper.selectById(id); + if (tenantPackage == null) { + throw exception(TENANT_PACKAGE_NOT_EXISTS); + } + return tenantPackage; + } + + private void validateTenantUsed(Long id) { + if (tenantService.getTenantCountByPackageId(id) > 0) { + throw exception(TENANT_PACKAGE_USED); + } + } + + @Override + public TenantPackageDO getTenantPackage(Long id) { + return tenantPackageMapper.selectById(id); + } + + @Override + public PageResult getTenantPackagePage(TenantPackagePageReqVO pageReqVO) { + return tenantPackageMapper.selectPage(pageReqVO); + } + + @Override + public TenantPackageDO validTenantPackage(Long id) { + TenantPackageDO tenantPackage = tenantPackageMapper.selectById(id); + if (tenantPackage == null) { + throw exception(TENANT_PACKAGE_NOT_EXISTS); + } + if (tenantPackage.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { + throw exception(TENANT_PACKAGE_DISABLE, tenantPackage.getName()); + } + return tenantPackage; + } + + @Override + public List getTenantPackageListByStatus(Integer status) { + return tenantPackageMapper.selectListByStatus(status); + } + + + @VisibleForTesting + void validateTenantPackageNameUnique(Long id, String name) { + if (StrUtil.isBlank(name)) { + return; + } + TenantPackageDO tenantPackage = tenantPackageMapper.selectByName(name); + if (tenantPackage == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的用户 + if (id == null) { + throw exception(TENANT_PACKAGE_NAME_DUPLICATE); + } + if (!tenantPackage.getId().equals(id)) { + throw exception(TENANT_PACKAGE_NAME_DUPLICATE); + } + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java new file mode 100755 index 0000000..425d18d --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java @@ -0,0 +1,138 @@ +package cn.iocoder.yudao.module.system.service.tenant; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; +import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; +import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; + +import jakarta.validation.Valid; +import java.util.List; +import java.util.Set; + +/** + * 租户 Service 接口 + * + * @author 芋道源码 + */ +public interface TenantService { + + /** + * 创建租户 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createTenant(@Valid TenantSaveReqVO createReqVO); + + /** + * 更新租户 + * + * @param updateReqVO 更新信息 + */ + void updateTenant(@Valid TenantSaveReqVO updateReqVO); + + /** + * 更新租户的角色菜单 + * + * @param tenantId 租户编号 + * @param menuIds 菜单编号数组 + */ + void updateTenantRoleMenu(Long tenantId, Set menuIds); + + /** + * 删除租户 + * + * @param id 编号 + */ + void deleteTenant(Long id); + + /** + * 获得租户 + * + * @param id 编号 + * @return 租户 + */ + TenantDO getTenant(Long id); + + /** + * 获得租户分页 + * + * @param pageReqVO 分页查询 + * @return 租户分页 + */ + PageResult getTenantPage(TenantPageReqVO pageReqVO); + + /** + * 获得名字对应的租户 + * + * @param name 租户名 + * @return 租户 + */ + TenantDO getTenantByName(String name); + + /** + * 获得域名对应的租户 + * + * @param website 域名 + * @return 租户 + */ + TenantDO getTenantByWebsite(String website); + + /** + * 获得使用指定套餐的租户数量 + * + * @param packageId 租户套餐编号 + * @return 租户数量 + */ + Long getTenantCountByPackageId(Long packageId); + + /** + * 获得使用指定套餐的租户数组 + * + * @param packageId 租户套餐编号 + * @return 租户数组 + */ + List getTenantListByPackageId(Long packageId); + + /** + * 获得指定状态的租户列表 + * + * @param status 状态 + * @return 租户列表 + */ + List getTenantListByStatus(Integer status); + + /** + * 进行租户的信息处理逻辑 + * 其中,租户编号从 {@link TenantContextHolder} 上下文中获取 + * + * @param handler 处理器 + */ + void handleTenantInfo(TenantInfoHandler handler); + + /** + * 进行租户的菜单处理逻辑 + * 其中,租户编号从 {@link TenantContextHolder} 上下文中获取 + * + * @param handler 处理器 + */ + void handleTenantMenu(TenantMenuHandler handler); + + /** + * 获得所有租户 + * + * @return 租户编号数组 + */ + List getTenantIdList(); + + /** + * 校验租户是否合法 + * + * @param id 租户编号 + */ + void validTenant(Long id); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java new file mode 100755 index 0000000..5060b16 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java @@ -0,0 +1,313 @@ +package cn.iocoder.yudao.module.system.service.tenant; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; +import cn.iocoder.yudao.framework.tenant.config.TenantProperties; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; +import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; +import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper; +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; +import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; +import cn.iocoder.yudao.module.system.service.permission.MenuService; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import cn.iocoder.yudao.module.system.service.permission.RoleService; +import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; +import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.baomidou.dynamic.datasource.annotation.DSTransactional; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static java.util.Collections.singleton; + +/** + * 租户 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +@Slf4j +public class TenantServiceImpl implements TenantService { + + @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") + @Autowired(required = false) // 由于 yudao.tenant.enable 配置项,可以关闭多租户的功能,所以这里只能不强制注入 + private TenantProperties tenantProperties; + + @Resource + private TenantMapper tenantMapper; + + @Resource + private TenantPackageService tenantPackageService; + @Resource + @Lazy // 延迟,避免循环依赖报错 + private AdminUserService userService; + @Resource + private RoleService roleService; + @Resource + private MenuService menuService; + @Resource + private PermissionService permissionService; + + @Override + public List getTenantIdList() { + List tenants = tenantMapper.selectList(); + return CollectionUtils.convertList(tenants, TenantDO::getId); + } + + @Override + public void validTenant(Long id) { + TenantDO tenant = getTenant(id); + if (tenant == null) { + throw exception(TENANT_NOT_EXISTS); + } + if (tenant.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { + throw exception(TENANT_DISABLE, tenant.getName()); + } + if (DateUtils.isExpired(tenant.getExpireTime())) { + throw exception(TENANT_EXPIRE, tenant.getName()); + } + } + + @Override + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 + @DataPermission(enable = false) // 参见 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1154 说明 + public Long createTenant(TenantSaveReqVO createReqVO) { + // 校验租户名称是否重复 + validTenantNameDuplicate(createReqVO.getName(), null); + // 校验租户域名是否重复 + validTenantWebsiteDuplicate(createReqVO.getWebsite(), null); + // 校验套餐被禁用 + TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId()); + + // 创建租户 + TenantDO tenant = BeanUtils.toBean(createReqVO, TenantDO.class); + tenantMapper.insert(tenant); + // 创建租户的管理员 + TenantUtils.execute(tenant.getId(), () -> { + // 创建角色 + Long roleId = createRole(tenantPackage); + // 创建用户,并分配角色 + Long userId = createUser(roleId, createReqVO); + // 修改租户的管理员 + tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId)); + }); + return tenant.getId(); + } + + private Long createUser(Long roleId, TenantSaveReqVO createReqVO) { + // 创建用户 + Long userId = userService.createUser(TenantConvert.INSTANCE.convert02(createReqVO)); + // 分配角色 + permissionService.assignUserRole(userId, singleton(roleId)); + return userId; + } + + private Long createRole(TenantPackageDO tenantPackage) { + // 创建角色 + RoleSaveReqVO reqVO = new RoleSaveReqVO(); + reqVO.setName(RoleCodeEnum.TENANT_ADMIN.getName()).setCode(RoleCodeEnum.TENANT_ADMIN.getCode()) + .setSort(0).setRemark("系统自动生成"); + Long roleId = roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType()); + // 分配权限 + permissionService.assignRoleMenu(roleId, tenantPackage.getMenuIds()); + return roleId; + } + + @Override + @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 + public void updateTenant(TenantSaveReqVO updateReqVO) { + // 校验存在 + TenantDO tenant = validateUpdateTenant(updateReqVO.getId()); + // 校验租户名称是否重复 + validTenantNameDuplicate(updateReqVO.getName(), updateReqVO.getId()); + // 校验租户域名是否重复 + validTenantWebsiteDuplicate(updateReqVO.getWebsite(), updateReqVO.getId()); + // 校验套餐被禁用 + TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId()); + + // 更新租户 + TenantDO updateObj = BeanUtils.toBean(updateReqVO, TenantDO.class); + tenantMapper.updateById(updateObj); + // 如果套餐发生变化,则修改其角色的权限 + if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) { + updateTenantRoleMenu(tenant.getId(), tenantPackage.getMenuIds()); + } + } + + private void validTenantNameDuplicate(String name, Long id) { + TenantDO tenant = tenantMapper.selectByName(name); + if (tenant == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同名字的租户 + if (id == null) { + throw exception(TENANT_NAME_DUPLICATE, name); + } + if (!tenant.getId().equals(id)) { + throw exception(TENANT_NAME_DUPLICATE, name); + } + } + + private void validTenantWebsiteDuplicate(String website, Long id) { + if (StrUtil.isEmpty(website)) { + return; + } + TenantDO tenant = tenantMapper.selectByWebsite(website); + if (tenant == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同名字的租户 + if (id == null) { + throw exception(TENANT_WEBSITE_DUPLICATE, website); + } + if (!tenant.getId().equals(id)) { + throw exception(TENANT_WEBSITE_DUPLICATE, website); + } + } + + @Override + @DSTransactional + public void updateTenantRoleMenu(Long tenantId, Set menuIds) { + TenantUtils.execute(tenantId, () -> { + // 获得所有角色 + List roles = roleService.getRoleList(); + roles.forEach(role -> Assert.isTrue(tenantId.equals(role.getTenantId()), "角色({}/{}) 租户不匹配", + role.getId(), role.getTenantId(), tenantId)); // 兜底校验 + // 重新分配每个角色的权限 + roles.forEach(role -> { + // 如果是租户管理员,重新分配其权限为租户套餐的权限 + if (Objects.equals(role.getCode(), RoleCodeEnum.TENANT_ADMIN.getCode())) { + permissionService.assignRoleMenu(role.getId(), menuIds); + log.info("[updateTenantRoleMenu][租户管理员({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), menuIds); + return; + } + // 如果是其他角色,则去掉超过套餐的权限 + Set roleMenuIds = permissionService.getRoleMenuListByRoleId(role.getId()); + roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds); + permissionService.assignRoleMenu(role.getId(), roleMenuIds); + log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds); + }); + }); + } + + @Override + public void deleteTenant(Long id) { + // 校验存在 + validateUpdateTenant(id); + // 删除 + tenantMapper.deleteById(id); + } + + private TenantDO validateUpdateTenant(Long id) { + TenantDO tenant = tenantMapper.selectById(id); + if (tenant == null) { + throw exception(TENANT_NOT_EXISTS); + } + // 内置租户,不允许删除 + if (isSystemTenant(tenant)) { + throw exception(TENANT_CAN_NOT_UPDATE_SYSTEM); + } + return tenant; + } + + @Override + public TenantDO getTenant(Long id) { + return tenantMapper.selectById(id); + } + + @Override + public PageResult getTenantPage(TenantPageReqVO pageReqVO) { + return tenantMapper.selectPage(pageReqVO); + } + + @Override + public TenantDO getTenantByName(String name) { + return tenantMapper.selectByName(name); + } + + @Override + public TenantDO getTenantByWebsite(String website) { + return tenantMapper.selectByWebsite(website); + } + + @Override + public Long getTenantCountByPackageId(Long packageId) { + return tenantMapper.selectCountByPackageId(packageId); + } + + @Override + public List getTenantListByPackageId(Long packageId) { + return tenantMapper.selectListByPackageId(packageId); + } + + @Override + public List getTenantListByStatus(Integer status) { + return tenantMapper.selectListByStatus(status); + } + + @Override + public void handleTenantInfo(TenantInfoHandler handler) { + // 如果禁用,则不执行逻辑 + if (isTenantDisable()) { + return; + } + // 获得租户 + TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId()); + // 执行处理器 + handler.handle(tenant); + } + + @Override + public void handleTenantMenu(TenantMenuHandler handler) { + // 如果禁用,则不执行逻辑 + if (isTenantDisable()) { + return; + } + // 获得租户,然后获得菜单 + TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId()); + Set menuIds; + if (isSystemTenant(tenant)) { // 系统租户,菜单是全量的 + menuIds = CollectionUtils.convertSet(menuService.getMenuList(), MenuDO::getId); + } else { + menuIds = tenantPackageService.getTenantPackage(tenant.getPackageId()).getMenuIds(); + } + // 执行处理器 + handler.handle(menuIds); + } + + private static boolean isSystemTenant(TenantDO tenant) { + return Objects.equals(tenant.getPackageId(), TenantDO.PACKAGE_ID_SYSTEM); + } + + private boolean isTenantDisable() { + return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable()); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantInfoHandler.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantInfoHandler.java new file mode 100644 index 0000000..5b5b9fe --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantInfoHandler.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.service.tenant.handler; + +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; + +/** + * 租户信息处理 + * 目的:尽量减少租户逻辑耦合到系统中 + * + * @author 芋道源码 + */ +public interface TenantInfoHandler { + + /** + * 基于传入的租户信息,进行相关逻辑的执行 + * 例如说,创建用户时,超过最大账户配额 + * + * @param tenant 租户信息 + */ + void handle(TenantDO tenant); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantMenuHandler.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantMenuHandler.java new file mode 100644 index 0000000..2e1be43 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantMenuHandler.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.system.service.tenant.handler; + +import java.util.Set; + +/** + * 租户菜单处理 + * 目的:尽量减少租户逻辑耦合到系统中 + * + * @author 芋道源码 + */ +public interface TenantMenuHandler { + + /** + * 基于传入的租户菜单【全】列表,进行相关逻辑的执行 + * 例如说,返回可分配菜单的时候,可以移除多余的 + * + * @param menuIds 菜单列表 + */ + void handle(Set menuIds); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java new file mode 100644 index 0000000..d5c83bc --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java @@ -0,0 +1,210 @@ +package cn.iocoder.yudao.module.system.service.user; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportExcelVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 后台用户 Service 接口 + * + * @author 芋道源码 + */ +public interface AdminUserService { + + /** + * 创建用户 + * + * @param createReqVO 用户信息 + * @return 用户编号 + */ + Long createUser(@Valid UserSaveReqVO createReqVO); + + /** + * 注册用户 + * + * @param registerReqVO 用户信息 + * @return 用户编号 + */ + Long registerUser(@Valid AuthRegisterReqVO registerReqVO); + + /** + * 修改用户 + * + * @param updateReqVO 用户信息 + */ + void updateUser(@Valid UserSaveReqVO updateReqVO); + + /** + * 更新用户的最后登陆信息 + * + * @param id 用户编号 + * @param loginIp 登陆 IP + */ + void updateUserLogin(Long id, String loginIp); + + /** + * 修改用户个人信息 + * + * @param id 用户编号 + * @param reqVO 用户个人信息 + */ + void updateUserProfile(Long id, @Valid UserProfileUpdateReqVO reqVO); + + /** + * 修改用户个人密码 + * + * @param id 用户编号 + * @param reqVO 更新用户个人密码 + */ + void updateUserPassword(Long id, @Valid UserProfileUpdatePasswordReqVO reqVO); + + /** + * 修改密码 + * + * @param id 用户编号 + * @param password 密码 + */ + void updateUserPassword(Long id, String password); + + /** + * 修改状态 + * + * @param id 用户编号 + * @param status 状态 + */ + void updateUserStatus(Long id, Integer status); + + /** + * 删除用户 + * + * @param id 用户编号 + */ + void deleteUser(Long id); + + /** + * 通过用户名查询用户 + * + * @param username 用户名 + * @return 用户对象信息 + */ + AdminUserDO getUserByUsername(String username); + + /** + * 通过手机号获取用户 + * + * @param mobile 手机号 + * @return 用户对象信息 + */ + AdminUserDO getUserByMobile(String mobile); + + /** + * 获得用户分页列表 + * + * @param reqVO 分页条件 + * @return 分页列表 + */ + PageResult getUserPage(UserPageReqVO reqVO); + + /** + * 通过用户 ID 查询用户 + * + * @param id 用户ID + * @return 用户对象信息 + */ + AdminUserDO getUser(Long id); + + /** + * 获得指定部门的用户数组 + * + * @param deptIds 部门数组 + * @return 用户数组 + */ + List getUserListByDeptIds(Collection deptIds); + + /** + * 获得指定岗位的用户数组 + * + * @param postIds 岗位数组 + * @return 用户数组 + */ + List getUserListByPostIds(Collection postIds); + + /** + * 获得用户列表 + * + * @param ids 用户编号数组 + * @return 用户列表 + */ + List getUserList(Collection ids); + + /** + * 校验用户们是否有效。如下情况,视为无效: + * 1. 用户编号不存在 + * 2. 用户被禁用 + * + * @param ids 用户编号数组 + */ + void validateUserList(Collection ids); + + /** + * 获得用户 Map + * + * @param ids 用户编号数组 + * @return 用户 Map + */ + default Map getUserMap(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return new HashMap<>(); + } + return CollectionUtils.convertMap(getUserList(ids), AdminUserDO::getId); + } + + /** + * 获得用户列表,基于昵称模糊匹配 + * + * @param nickname 昵称 + * @return 用户列表 + */ + List getUserListByNickname(String nickname); + + /** + * 批量导入用户 + * + * @param importUsers 导入用户列表 + * @param isUpdateSupport 是否支持更新 + * @return 导入结果 + */ + UserImportRespVO importUserList(List importUsers, boolean isUpdateSupport); + + /** + * 获得指定状态的用户们 + * + * @param status 状态 + * @return 用户们 + */ + List getUserListByStatus(Integer status); + + /** + * 判断密码是否匹配 + * + * @param rawPassword 未加密的密码 + * @param encodedPassword 加密后的密码 + * @return 是否匹配 + */ + boolean isPasswordMatch(String rawPassword, String encodedPassword); + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java new file mode 100644 index 0000000..3f279f3 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java @@ -0,0 +1,521 @@ +package cn.iocoder.yudao.module.system.service.user; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; +import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils; +import cn.iocoder.yudao.module.infra.api.config.ConfigApi; +import cn.iocoder.yudao.module.infra.api.file.FileApi; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportExcelVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper; +import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import cn.iocoder.yudao.module.system.service.dept.PostService; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import cn.iocoder.yudao.module.system.service.tenant.TenantService; +import com.google.common.annotations.VisibleForTesting; +import com.mzt.logapi.context.LogRecordContext; +import com.mzt.logapi.service.impl.DiffParseFunction; +import com.mzt.logapi.starter.annotation.LogRecord; +import jakarta.annotation.Resource; +import jakarta.validation.ConstraintViolationException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Lazy; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*; + +/** + * 后台用户 Service 实现类 + * + * @author 芋道源码 + */ +@Service("adminUserService") +@Slf4j +public class AdminUserServiceImpl implements AdminUserService { + + static final String USER_INIT_PASSWORD_KEY = "system.user.init-password"; + + static final String USER_REGISTER_ENABLED_KEY = "system.user.register-enabled"; + + @Resource + private AdminUserMapper userMapper; + + @Resource + private DeptService deptService; + @Resource + private PostService postService; + @Resource + private PermissionService permissionService; + @Resource + private PasswordEncoder passwordEncoder; + @Resource + @Lazy // 延迟,避免循环依赖报错 + private TenantService tenantService; + + @Resource + private UserPostMapper userPostMapper; + + @Resource + private ConfigApi configApi; + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}", + success = SYSTEM_USER_CREATE_SUCCESS) + public Long createUser(UserSaveReqVO createReqVO) { + // 1.1 校验账户配合 + tenantService.handleTenantInfo(tenant -> { + long count = userMapper.selectCount(); + if (count >= tenant.getAccountCount()) { + throw exception(USER_COUNT_MAX, tenant.getAccountCount()); + } + }); + // 1.2 校验正确性 + validateUserForCreateOrUpdate(null, createReqVO.getUsername(), + createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptId(), createReqVO.getPostIds()); + // 2.1 插入用户 + AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class); + user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 + user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码 + userMapper.insert(user); + // 2.2 插入关联岗位 + if (CollectionUtil.isNotEmpty(user.getPostIds())) { + userPostMapper.insertBatch(convertList(user.getPostIds(), + postId -> new UserPostDO().setUserId(user.getId()).setPostId(postId))); + } + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("user", user); + return user.getId(); + } + + @Override + public Long registerUser(AuthRegisterReqVO registerReqVO) { + // 1.1 校验是否开启注册 + if (ObjUtil.notEqual(configApi.getConfigValueByKey(USER_REGISTER_ENABLED_KEY), "true")) { + throw exception(USER_REGISTER_DISABLED); + } + // 1.2 校验账户配合 + tenantService.handleTenantInfo(tenant -> { + long count = userMapper.selectCount(); + if (count >= tenant.getAccountCount()) { + throw exception(USER_COUNT_MAX, tenant.getAccountCount()); + } + }); + // 1.3 校验正确性 + validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null); + + // 2. 插入用户 + AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class); + user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 + user.setPassword(encodePassword(registerReqVO.getPassword())); // 加密密码 + userMapper.insert(user); + return user.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", + success = SYSTEM_USER_UPDATE_SUCCESS) + public void updateUser(UserSaveReqVO updateReqVO) { + updateReqVO.setPassword(null); // 特殊:此处不更新密码 + // 1. 校验正确性 + AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(), + updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds()); + + // 2.1 更新用户 + AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class); + userMapper.updateById(updateObj); + // 2.2 更新岗位 + updateUserPost(updateReqVO, updateObj); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldUser, UserSaveReqVO.class)); + LogRecordContext.putVariable("user", oldUser); + } + + private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) { + Long userId = reqVO.getId(); + Set dbPostIds = convertSet(userPostMapper.selectListByUserId(userId), UserPostDO::getPostId); + // 计算新增和删除的岗位编号 + Set postIds = CollUtil.emptyIfNull(updateObj.getPostIds()); + Collection createPostIds = CollUtil.subtract(postIds, dbPostIds); + Collection deletePostIds = CollUtil.subtract(dbPostIds, postIds); + // 执行新增和删除。对于已经授权的岗位,不用做任何处理 + if (!CollectionUtil.isEmpty(createPostIds)) { + userPostMapper.insertBatch(convertList(createPostIds, + postId -> new UserPostDO().setUserId(userId).setPostId(postId))); + } + if (!CollectionUtil.isEmpty(deletePostIds)) { + userPostMapper.deleteByUserIdAndPostId(userId, deletePostIds); + } + } + + @Override + public void updateUserLogin(Long id, String loginIp) { + userMapper.updateById(new AdminUserDO().setId(id).setLoginIp(loginIp).setLoginDate(LocalDateTime.now())); + } + + @Override + public void updateUserProfile(Long id, UserProfileUpdateReqVO reqVO) { + // 校验正确性 + validateUserExists(id); + validateEmailUnique(id, reqVO.getEmail()); + validateMobileUnique(id, reqVO.getMobile()); + // 执行更新 + userMapper.updateById(BeanUtils.toBean(reqVO, AdminUserDO.class).setId(id)); + } + + @Override + public void updateUserPassword(Long id, UserProfileUpdatePasswordReqVO reqVO) { + // 校验旧密码密码 + validateOldPassword(id, reqVO.getOldPassword()); + // 执行更新 + AdminUserDO updateObj = new AdminUserDO().setId(id); + updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码 + userMapper.updateById(updateObj); + } + + @Override + @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE, bizNo = "{{#id}}", + success = SYSTEM_USER_UPDATE_PASSWORD_SUCCESS) + public void updateUserPassword(Long id, String password) { + // 1. 校验用户存在 + AdminUserDO user = validateUserExists(id); + + // 2. 更新密码 + AdminUserDO updateObj = new AdminUserDO(); + updateObj.setId(id); + updateObj.setPassword(encodePassword(password)); // 加密密码 + userMapper.updateById(updateObj); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("user", user); + LogRecordContext.putVariable("newPassword", updateObj.getPassword()); + } + + @Override + public void updateUserStatus(Long id, Integer status) { + // 校验用户存在 + validateUserExists(id); + // 更新状态 + AdminUserDO updateObj = new AdminUserDO(); + updateObj.setId(id); + updateObj.setStatus(status); + userMapper.updateById(updateObj); + } + + @Override + @Transactional(rollbackFor = Exception.class) + @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}", + success = SYSTEM_USER_DELETE_SUCCESS) + public void deleteUser(Long id) { + // 1. 校验用户存在 + AdminUserDO user = validateUserExists(id); + + // 2.1 删除用户 + userMapper.deleteById(id); + // 2.2 删除用户关联数据 + permissionService.processUserDeleted(id); + // 2.2 删除用户岗位 + userPostMapper.deleteByUserId(id); + + // 3. 记录操作日志上下文 + LogRecordContext.putVariable("user", user); + } + + @Override + public AdminUserDO getUserByUsername(String username) { + return userMapper.selectByUsername(username); + } + + @Override + public AdminUserDO getUserByMobile(String mobile) { + return userMapper.selectByMobile(mobile); + } + + @Override + public PageResult getUserPage(UserPageReqVO reqVO) { + // 如果有角色编号,查询角色对应的用户编号 + Set userIds = reqVO.getRoleId() != null ? + permissionService.getUserRoleIdListByRoleId(singleton(reqVO.getRoleId())) : null; + + // 分页查询 + return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()), userIds); + } + + @Override + public AdminUserDO getUser(Long id) { + return userMapper.selectById(id); + } + + @Override + public List getUserListByDeptIds(Collection deptIds) { + if (CollUtil.isEmpty(deptIds)) { + return Collections.emptyList(); + } + return userMapper.selectListByDeptIds(deptIds); + } + + @Override + public List getUserListByPostIds(Collection postIds) { + if (CollUtil.isEmpty(postIds)) { + return Collections.emptyList(); + } + Set userIds = convertSet(userPostMapper.selectListByPostIds(postIds), UserPostDO::getUserId); + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyList(); + } + return userMapper.selectBatchIds(userIds); + } + + @Override + public List getUserList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return userMapper.selectBatchIds(ids); + } + + @Override + public void validateUserList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得岗位信息 + List users = userMapper.selectBatchIds(ids); + Map userMap = CollectionUtils.convertMap(users, AdminUserDO::getId); + // 校验 + ids.forEach(id -> { + AdminUserDO user = userMap.get(id); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus())) { + throw exception(USER_IS_DISABLE, user.getNickname()); + } + }); + } + + @Override + public List getUserListByNickname(String nickname) { + return userMapper.selectListByNickname(nickname); + } + + /** + * 获得部门条件:查询指定部门的子部门编号们,包括自身 + * + * @param deptId 部门编号 + * @return 部门编号集合 + */ + private Set getDeptCondition(Long deptId) { + if (deptId == null) { + return Collections.emptySet(); + } + Set deptIds = convertSet(deptService.getChildDeptList(deptId), DeptDO::getId); + deptIds.add(deptId); // 包括自身 + return deptIds; + } + + private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email, + Long deptId, Set postIds) { + // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确 + return DataPermissionUtils.executeIgnore(() -> { + // 校验用户存在 + AdminUserDO user = validateUserExists(id); + // 校验用户名唯一 + validateUsernameUnique(id, username); + // 校验手机号唯一 + validateMobileUnique(id, mobile); + // 校验邮箱唯一 + validateEmailUnique(id, email); + // 校验部门处于开启状态 + deptService.validateDeptList(CollectionUtils.singleton(deptId)); + // 校验岗位处于开启状态 + postService.validatePostList(postIds); + return user; + }); + } + + @VisibleForTesting + AdminUserDO validateUserExists(Long id) { + if (id == null) { + return null; + } + AdminUserDO user = userMapper.selectById(id); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + return user; + } + + @VisibleForTesting + void validateUsernameUnique(Long id, String username) { + if (StrUtil.isBlank(username)) { + return; + } + AdminUserDO user = userMapper.selectByUsername(username); + if (user == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的用户 + if (id == null) { + throw exception(USER_USERNAME_EXISTS); + } + if (!user.getId().equals(id)) { + throw exception(USER_USERNAME_EXISTS); + } + } + + @VisibleForTesting + void validateEmailUnique(Long id, String email) { + if (StrUtil.isBlank(email)) { + return; + } + AdminUserDO user = userMapper.selectByEmail(email); + if (user == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的用户 + if (id == null) { + throw exception(USER_EMAIL_EXISTS); + } + if (!user.getId().equals(id)) { + throw exception(USER_EMAIL_EXISTS); + } + } + + @VisibleForTesting + void validateMobileUnique(Long id, String mobile) { + if (StrUtil.isBlank(mobile)) { + return; + } + AdminUserDO user = userMapper.selectByMobile(mobile); + if (user == null) { + return; + } + // 如果 id 为空,说明不用比较是否为相同 id 的用户 + if (id == null) { + throw exception(USER_MOBILE_EXISTS); + } + if (!user.getId().equals(id)) { + throw exception(USER_MOBILE_EXISTS); + } + } + + /** + * 校验旧密码 + * @param id 用户 id + * @param oldPassword 旧密码 + */ + @VisibleForTesting + void validateOldPassword(Long id, String oldPassword) { + AdminUserDO user = userMapper.selectById(id); + if (user == null) { + throw exception(USER_NOT_EXISTS); + } + if (!isPasswordMatch(oldPassword, user.getPassword())) { + throw exception(USER_PASSWORD_FAILED); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 + public UserImportRespVO importUserList(List importUsers, boolean isUpdateSupport) { + // 1.1 参数校验 + if (CollUtil.isEmpty(importUsers)) { + throw exception(USER_IMPORT_LIST_IS_EMPTY); + } + // 1.2 初始化密码不能为空 + String initPassword = configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY); + if (StrUtil.isEmpty(initPassword)) { + throw exception(USER_IMPORT_INIT_PASSWORD); + } + + // 2. 遍历,逐个创建 or 更新 + UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>()) + .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build(); + importUsers.forEach(importUser -> { + // 2.1.1 校验字段是否符合要求 + try { + ValidationUtils.validate(BeanUtils.toBean(importUser, UserSaveReqVO.class).setPassword(initPassword)); + } catch (ConstraintViolationException ex){ + respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage()); + return; + } + // 2.1.2 校验,判断是否有不符合的原因 + try { + validateUserForCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(), + importUser.getDeptId(), null); + } catch (ServiceException ex) { + respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage()); + return; + } + + // 2.2.1 判断如果不存在,在进行插入 + AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername()); + if (existUser == null) { + userMapper.insert(BeanUtils.toBean(importUser, AdminUserDO.class) + .setPassword(encodePassword(initPassword)).setPostIds(new HashSet<>())); // 设置默认密码及空岗位编号数组 + respVO.getCreateUsernames().add(importUser.getUsername()); + return; + } + // 2.2.2 如果存在,判断是否允许更新 + if (!isUpdateSupport) { + respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg()); + return; + } + AdminUserDO updateUser = BeanUtils.toBean(importUser, AdminUserDO.class); + updateUser.setId(existUser.getId()); + userMapper.updateById(updateUser); + respVO.getUpdateUsernames().add(importUser.getUsername()); + }); + return respVO; + } + + @Override + public List getUserListByStatus(Integer status) { + return userMapper.selectListByStatus(status); + } + + @Override + public boolean isPasswordMatch(String rawPassword, String encodedPassword) { + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 对密码进行加密 + * + * @param password 密码 + * @return 加密后的密码 + */ + private String encodePassword(String password) { + return passwordEncoder.encode(password); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java new file mode 100644 index 0000000..2a6a46f --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java @@ -0,0 +1,103 @@ +package cn.iocoder.yudao.module.system.util.oauth2; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; + +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.*; + +/** + * OAuth2 相关的工具类 + * + * @author 芋道源码 + */ +public class OAuth2Utils { + + /** + * 构建授权码模式下,重定向的 URI + * + * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 getSuccessfulRedirect 方法 + * + * @param redirectUri 重定向 URI + * @param authorizationCode 授权码 + * @param state 状态 + * @return 授权码模式下的重定向 URI + */ + public static String buildAuthorizationCodeRedirectUri(String redirectUri, String authorizationCode, String state) { + Map query = new LinkedHashMap<>(); + query.put("code", authorizationCode); + if (state != null) { + query.put("state", state); + } + return HttpUtils.append(redirectUri, query, null, false); + } + + /** + * 构建简化模式下,重定向的 URI + * + * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 appendAccessToken 方法 + * + * @param redirectUri 重定向 URI + * @param accessToken 访问令牌 + * @param state 状态 + * @param expireTime 过期时间 + * @param scopes 授权范围 + * @param additionalInformation 附加信息 + * @return 简化授权模式下的重定向 URI + */ + public static String buildImplicitRedirectUri(String redirectUri, String accessToken, String state, LocalDateTime expireTime, + Collection scopes, Map additionalInformation) { + Map vars = new LinkedHashMap(); + Map keys = new HashMap(); + vars.put("access_token", accessToken); + vars.put("token_type", SecurityFrameworkUtils.AUTHORIZATION_BEARER.toLowerCase()); + if (state != null) { + vars.put("state", state); + } + if (expireTime != null) { + vars.put("expires_in", getExpiresIn(expireTime)); + } + if (CollUtil.isNotEmpty(scopes)) { + vars.put("scope", buildScopeStr(scopes)); + } + if (CollUtil.isNotEmpty(additionalInformation)) { + for (String key : additionalInformation.keySet()) { + Object value = additionalInformation.get(key); + if (value != null) { + keys.put("extra_" + key, key); + vars.put("extra_" + key, value); + } + } + } + // Do not include the refresh token (even if there is one) + return HttpUtils.append(redirectUri, vars, keys, true); + } + + public static String buildUnsuccessfulRedirect(String redirectUri, String responseType, String state, + String error, String description) { + Map query = new LinkedHashMap(); + query.put("error", error); + query.put("error_description", description); + if (state != null) { + query.put("state", state); + } + return HttpUtils.append(redirectUri, query, null, !responseType.contains("code")); + } + + public static long getExpiresIn(LocalDateTime expireTime) { + return LocalDateTimeUtil.between(LocalDateTime.now(), expireTime, ChronoUnit.SECONDS); + } + + public static String buildScopeStr(Collection scopes) { + return CollUtil.join(scopes, " "); + } + + public static List buildScopes(String scope) { + return StrUtil.split(scope, ' '); + } + +} diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/util/package-info.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/util/package-info.java new file mode 100644 index 0000000..97ca1f1 --- /dev/null +++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/util/package-info.java @@ -0,0 +1,4 @@ +/** + * 每个模块的 util 包,放专属当前模块的 Utils 工具类 + */ +package cn.iocoder.yudao.module.system.util; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService b/yudao-module-system/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService index fa9d5b2..fa9d5b2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService +++ b/yudao-module-system/src/main/resources/META-INF/services/com.anji.captcha.service.CaptchaCacheService diff --git a/yudao-module-system/src/main/resources/images/jigsaw/original/bg1.png b/yudao-module-system/src/main/resources/images/jigsaw/original/bg1.png new file mode 100644 index 0000000..c481457 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/original/bg1.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/original/bg2.png b/yudao-module-system/src/main/resources/images/jigsaw/original/bg2.png new file mode 100644 index 0000000..bf8fb38 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/original/bg2.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/original/bg3.png b/yudao-module-system/src/main/resources/images/jigsaw/original/bg3.png new file mode 100644 index 0000000..f871d3d --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/original/bg3.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/original/bg4.png b/yudao-module-system/src/main/resources/images/jigsaw/original/bg4.png new file mode 100644 index 0000000..2e3d871 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/original/bg4.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/original/bg5.png b/yudao-module-system/src/main/resources/images/jigsaw/original/bg5.png new file mode 100644 index 0000000..fe383b7 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/original/bg5.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/original/bg6.png b/yudao-module-system/src/main/resources/images/jigsaw/original/bg6.png new file mode 100644 index 0000000..5024ceb --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/original/bg6.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/original/bg7.png b/yudao-module-system/src/main/resources/images/jigsaw/original/bg7.png new file mode 100644 index 0000000..efe76f8 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/original/bg7.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/original/bg8.png b/yudao-module-system/src/main/resources/images/jigsaw/original/bg8.png new file mode 100644 index 0000000..2727aa3 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/original/bg8.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/original/bg9.png b/yudao-module-system/src/main/resources/images/jigsaw/original/bg9.png new file mode 100644 index 0000000..4463aa2 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/original/bg9.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/1.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/1.png new file mode 100644 index 0000000..ef11324 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/1.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/10.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/10.png new file mode 100644 index 0000000..297e44c --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/10.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/11.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/11.png new file mode 100644 index 0000000..d9b1da8 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/11.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/12.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/12.png new file mode 100644 index 0000000..07e7313 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/12.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/13.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/13.png new file mode 100644 index 0000000..82c3dd9 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/13.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/14.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/14.png new file mode 100644 index 0000000..0b9a866 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/14.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/15.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/15.png new file mode 100644 index 0000000..86b0d1c --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/15.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/16.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/16.png new file mode 100644 index 0000000..e90a6e2 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/16.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/17.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/17.png new file mode 100644 index 0000000..a82cbc7 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/17.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/18.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/18.png new file mode 100644 index 0000000..d3f3cfd --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/18.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/19.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/19.png new file mode 100644 index 0000000..eb2855b --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/19.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/8.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/8.png new file mode 100644 index 0000000..3cb5ce1 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/8.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/9.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/9.png new file mode 100644 index 0000000..384d354 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/11/9.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/2.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/2.png new file mode 100644 index 0000000..baf3f06 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/2.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/3.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/3.png new file mode 100644 index 0000000..ccaf617 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/3.png diff --git a/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/4.png b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/4.png new file mode 100644 index 0000000..7dab162 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/jigsaw/slidingBlock/4.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg1.png b/yudao-module-system/src/main/resources/images/pic-click/bg1.png new file mode 100644 index 0000000..14e7345 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg1.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg10.png b/yudao-module-system/src/main/resources/images/pic-click/bg10.png new file mode 100644 index 0000000..1ea1d6d --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg10.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg2.png b/yudao-module-system/src/main/resources/images/pic-click/bg2.png new file mode 100644 index 0000000..0edb329 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg2.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg3.png b/yudao-module-system/src/main/resources/images/pic-click/bg3.png new file mode 100644 index 0000000..9167996 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg3.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg4.png b/yudao-module-system/src/main/resources/images/pic-click/bg4.png new file mode 100644 index 0000000..e8e8e6c --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg4.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg5.png b/yudao-module-system/src/main/resources/images/pic-click/bg5.png new file mode 100644 index 0000000..66a3181 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg5.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg6.png b/yudao-module-system/src/main/resources/images/pic-click/bg6.png new file mode 100644 index 0000000..9b0f5d8 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg6.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg7.png b/yudao-module-system/src/main/resources/images/pic-click/bg7.png new file mode 100644 index 0000000..db41c74 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg7.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg8.png b/yudao-module-system/src/main/resources/images/pic-click/bg8.png new file mode 100644 index 0000000..3496813 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg8.png diff --git a/yudao-module-system/src/main/resources/images/pic-click/bg9.png b/yudao-module-system/src/main/resources/images/pic-click/bg9.png new file mode 100644 index 0000000..4e7b477 --- /dev/null +++ b/yudao-module-system/src/main/resources/images/pic-click/bg9.png diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java new file mode 100644 index 0000000..7848272 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java @@ -0,0 +1,337 @@ +package cn.iocoder.yudao.module.system.controller.admin.oauth2; + +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.util.collection.SetUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2GrantTypeEnum; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; +import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import jakarta.servlet.http.HttpServletRequest; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static java.util.Arrays.asList; +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * {@link OAuth2OpenController} 的单元测试 + * + * @author 芋道源码 + */ +public class OAuth2OpenControllerTest extends BaseMockitoUnitTest { + + @InjectMocks + private OAuth2OpenController oauth2OpenController; + + @Mock + private OAuth2GrantService oauth2GrantService; + @Mock + private OAuth2ClientService oauth2ClientService; + @Mock + private OAuth2ApproveService oauth2ApproveService; + @Mock + private OAuth2TokenService oauth2TokenService; + + @Test + public void testPostAccessToken_authorizationCode() { + // 准备参数 + String granType = OAuth2GrantTypeEnum.AUTHORIZATION_CODE.getGrantType(); + String code = randomString(); + String redirectUri = randomString(); + String state = randomString(); + HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); + // mock 方法(client) + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); + when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), eq(granType), eq(new ArrayList<>()), eq(redirectUri))).thenReturn(client); + + // mock 方法(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) + .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS)); + when(oauth2GrantService.grantAuthorizationCodeForAccessToken(eq("test_client_id"), + eq(code), eq(redirectUri), eq(state))).thenReturn(accessTokenDO); + + // 调用 + CommonResult result = oauth2OpenController.postAccessToken(request, granType, + code, redirectUri, state, null, null, null, null); + // 断言 + assertEquals(0, result.getCode()); + assertPojoEquals(accessTokenDO, result.getData()); + assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L)); // 执行过程会过去几毫秒 + } + + @Test + public void testPostAccessToken_password() { + // 准备参数 + String granType = OAuth2GrantTypeEnum.PASSWORD.getGrantType(); + String username = randomString(); + String password = randomString(); + String scope = "write read"; + HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); + // mock 方法(client) + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); + when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), + eq(granType), eq(Lists.newArrayList("write", "read")), isNull())).thenReturn(client); + + // mock 方法(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) + .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS)); + when(oauth2GrantService.grantPassword(eq(username), eq(password), eq("test_client_id"), + eq(Lists.newArrayList("write", "read")))).thenReturn(accessTokenDO); + + // 调用 + CommonResult result = oauth2OpenController.postAccessToken(request, granType, + null, null, null, username, password, scope, null); + // 断言 + assertEquals(0, result.getCode()); + assertPojoEquals(accessTokenDO, result.getData()); + assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L)); // 执行过程会过去几毫秒 + } + + @Test + public void testPostAccessToken_refreshToken() { + // 准备参数 + String granType = OAuth2GrantTypeEnum.REFRESH_TOKEN.getGrantType(); + String refreshToken = randomString(); + String password = randomString(); + HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); + // mock 方法(client) + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); + when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), + eq(granType), eq(Lists.newArrayList()), isNull())).thenReturn(client); + + // mock 方法(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) + .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS)); + when(oauth2GrantService.grantRefreshToken(eq(refreshToken), eq("test_client_id"))).thenReturn(accessTokenDO); + + // 调用 + CommonResult result = oauth2OpenController.postAccessToken(request, granType, + null, null, null, null, password, null, refreshToken); + // 断言 + assertEquals(0, result.getCode()); + assertPojoEquals(accessTokenDO, result.getData()); + assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L)); // 执行过程会过去几毫秒 + } + + @Test + public void testPostAccessToken_implicit() { + // 调用,并断言 + assertServiceException(() -> oauth2OpenController.postAccessToken(null, + OAuth2GrantTypeEnum.IMPLICIT.getGrantType(), null, null, null, + null, null, null, null), + new ErrorCode(400, "Token 接口不支持 implicit 授权模式")); + } + + @Test + public void testRevokeToken() { + // 准备参数 + HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret"); + String token = randomString(); + // mock 方法(client) + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id"); + when(oauth2ClientService.validOAuthClientFromCache(eq("demo_client_id"), + eq("demo_client_secret"), isNull(), isNull(), isNull())).thenReturn(client); + // mock 方法(移除) + when(oauth2GrantService.revokeToken(eq("demo_client_id"), eq(token))).thenReturn(true); + + // 调用 + CommonResult result = oauth2OpenController.revokeToken(request, token); + // 断言 + assertEquals(0, result.getCode()); + assertTrue(result.getData()); + } + + @Test + public void testCheckToken() { + // 准备参数 + HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret"); + String token = randomString(); + // mock 方法 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setUserType(UserTypeEnum.ADMIN.getValue()).setExpiresTime(LocalDateTimeUtil.of(1653485731195L)); + when(oauth2TokenService.checkAccessToken(eq(token))).thenReturn(accessTokenDO); + + // 调用 + CommonResult result = oauth2OpenController.checkToken(request, token); + // 断言 + assertEquals(0, result.getCode()); + assertPojoEquals(accessTokenDO, result.getData()); + assertEquals(1653485731L, result.getData().getExp()); // 执行过程会过去几毫秒 + } + + @Test + public void testAuthorize() { + // 准备参数 + String clientId = randomString(); + // mock 方法(client) + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id").setScopes(ListUtil.toList("read", "write", "all")); + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(client); + // mock 方法(approve) + List approves = asList( + randomPojo(OAuth2ApproveDO.class).setScope("read").setApproved(true), + randomPojo(OAuth2ApproveDO.class).setScope("write").setApproved(false)); + when(oauth2ApproveService.getApproveList(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId))).thenReturn(approves); + + // 调用 + CommonResult result = oauth2OpenController.authorize(clientId); + // 断言 + assertEquals(0, result.getCode()); + assertPojoEquals(client, result.getData().getClient()); + assertEquals(new KeyValue<>("read", true), result.getData().getScopes().get(0)); + assertEquals(new KeyValue<>("write", false), result.getData().getScopes().get(1)); + assertEquals(new KeyValue<>("all", false), result.getData().getScopes().get(2)); + } + + @Test + public void testApproveOrDeny_grantTypeError() { + // 调用,并断言 + assertServiceException(() -> oauth2OpenController.approveOrDeny(randomString(), null, + null, null, null, null), + new ErrorCode(400, "response_type 参数值只允许 code 和 token")); + } + + @Test // autoApprove = true,但是不通过 + public void testApproveOrDeny_autoApproveNo() { + // 准备参数 + String responseType = "code"; + String clientId = randomString(); + String scope = "{\"read\": true, \"write\": false}"; + String redirectUri = randomString(); + String state = randomString(); + // mock 方法 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class); + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"), + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); + + // 调用 + CommonResult result = oauth2OpenController.approveOrDeny(responseType, clientId, + scope, redirectUri, true, state); + // 断言 + assertEquals(0, result.getCode()); + assertNull(result.getData()); + } + + @Test // autoApprove = false,但是不通过 + public void testApproveOrDeny_ApproveNo() { + // 准备参数 + String responseType = "token"; + String clientId = randomString(); + String scope = "{\"read\": true, \"write\": false}"; + String redirectUri = "https://www.iocoder.cn"; + String state = "test"; + // mock 方法 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class); + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"), + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); + + // 调用 + CommonResult result = oauth2OpenController.approveOrDeny(responseType, clientId, + scope, redirectUri, false, state); + // 断言 + assertEquals(0, result.getCode()); + assertEquals("https://www.iocoder.cn#error=access_denied&error_description=User%20denied%20access&state=test", result.getData()); + } + + @Test // autoApprove = true,通过 + token + public void testApproveOrDeny_autoApproveWithToken() { + // 准备参数 + String responseType = "token"; + String clientId = randomString(); + String scope = "{\"read\": true, \"write\": false}"; + String redirectUri = "https://www.iocoder.cn"; + String state = "test"; + // mock 方法(client) + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null); + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"), + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); + // mock 方法(场景一) + when(oauth2ApproveService.checkForPreApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), + eq(clientId), eq(SetUtils.asSet("read", "write")))).thenReturn(true); + // mock 方法(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) + .setAccessToken("test_access_token").setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30010L, ChronoUnit.MILLIS)); + when(oauth2GrantService.grantImplicit(isNull(), eq(UserTypeEnum.ADMIN.getValue()), + eq(clientId), eq(ListUtil.toList("read")))).thenReturn(accessTokenDO); + + // 调用 + CommonResult result = oauth2OpenController.approveOrDeny(responseType, clientId, + scope, redirectUri, true, state); + // 断言 + assertEquals(0, result.getCode()); + assertThat(result.getData(), anyOf( // 29 和 30 都有一定概率,主要是时间计算 + is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=29&scope=read"), + is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read") + )); + } + + @Test // autoApprove = false,通过 + code + public void testApproveOrDeny_approveWithCode() { + // 准备参数 + String responseType = "code"; + String clientId = randomString(); + String scope = "{\"read\": true, \"write\": false}"; + String redirectUri = "https://www.iocoder.cn"; + String state = "test"; + // mock 方法(client) + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null); + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"), + eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); + // mock 方法(场景二) + when(oauth2ApproveService.updateAfterApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId), + eq(MapUtil.builder(new LinkedHashMap()).put("read", true).put("write", false).build()))) + .thenReturn(true); + // mock 方法(访问令牌) + String authorizationCode = "test_code"; + when(oauth2GrantService.grantAuthorizationCodeForCode(isNull(), eq(UserTypeEnum.ADMIN.getValue()), + eq(clientId), eq(ListUtil.toList("read")), eq(redirectUri), eq(state))).thenReturn(authorizationCode); + + // 调用 + CommonResult result = oauth2OpenController.approveOrDeny(responseType, clientId, + scope, redirectUri, false, state); + // 断言 + assertEquals(0, result.getCode()); + assertEquals("https://www.iocoder.cn?code=test_code&state=test", result.getData()); + } + + private HttpServletRequest mockRequest(String clientId, String secret) { + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getParameter(eq("client_id"))).thenReturn(clientId); + when(request.getParameter(eq("client_secret"))).thenReturn(secret); + return request; + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java new file mode 100644 index 0000000..b04f426 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java @@ -0,0 +1,160 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.MockedStatic; +import org.mockito.stubbing.Answer; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; + +/** + * {@link cn.iocoder.yudao.module.system.framework.sms.core.client.impl.AliyunSmsClient} 的单元测试 + * + * @author 芋道源码 + */ +public class AliyunSmsClientTest extends BaseMockitoUnitTest { + + private final SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey(randomString()) // 随机一个 apiKey,避免构建报错 + .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 + .setSignature("芋道源码"); + + @InjectMocks + private final AliyunSmsClient smsClient = new AliyunSmsClient(properties); + + @Test + public void tesSendSms_success() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("code", 1234), new KeyValue<>("op", "login")); + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\"Message\":\"OK\",\"RequestId\":\"30067CE9-3710-5984-8881-909B21D8DB28\",\"Code\":\"OK\",\"BizId\":\"800025323183427988\"}"); + httpUtilsMockedStatic.when(() -> HttpUtils.encodeUtf8(anyString())) + .then((Answer) invocationOnMock -> (String) invocationOnMock.getArguments()[0]); + + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, + apiTemplateId, templateParams); + // 断言 + assertTrue(result.getSuccess()); + assertEquals("30067CE9-3710-5984-8881-909B21D8DB28", result.getApiRequestId()); + assertEquals("OK", result.getApiCode()); + assertEquals("OK", result.getApiMsg()); + assertEquals("800025323183427988", result.getSerialNo()); + } + } + + @Test + public void tesSendSms_fail() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("code", 1234), new KeyValue<>("op", "login")); + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\"Message\":\"手机号码格式错误\",\"RequestId\":\"B7700B8E-227E-5886-9564-26036172F01F\",\"Code\":\"isv.MOBILE_NUMBER_ILLEGAL\"}"); + httpUtilsMockedStatic.when(() -> HttpUtils.encodeUtf8(anyString())) + .then((Answer) invocationOnMock -> (String) invocationOnMock.getArguments()[0]); + + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams); + // 断言 + assertFalse(result.getSuccess()); + assertEquals("B7700B8E-227E-5886-9564-26036172F01F", result.getApiRequestId()); + assertEquals("isv.MOBILE_NUMBER_ILLEGAL", result.getApiCode()); + assertEquals("手机号码格式错误", result.getApiMsg()); + assertNull(result.getSerialNo()); + } + } + + @Test + public void testParseSmsReceiveStatus() { + // 准备参数 + String text = "[\n" + + " {\n" + + " \"phone_number\" : \"13900000001\",\n" + + " \"send_time\" : \"2017-01-01 11:12:13\",\n" + + " \"report_time\" : \"2017-02-02 22:23:24\",\n" + + " \"success\" : true,\n" + + " \"err_code\" : \"DELIVERED\",\n" + + " \"err_msg\" : \"用户接收成功\",\n" + + " \"sms_size\" : \"1\",\n" + + " \"biz_id\" : \"12345\",\n" + + " \"out_id\" : \"67890\"\n" + + " }\n" + + "]"; + // mock 方法 + + // 调用 + List statuses = smsClient.parseSmsReceiveStatus(text); + // 断言 + assertEquals(1, statuses.size()); + assertTrue(statuses.get(0).getSuccess()); + assertEquals("DELIVERED", statuses.get(0).getErrorCode()); + assertEquals("用户接收成功", statuses.get(0).getErrorMsg()); + assertEquals("13900000001", statuses.get(0).getMobile()); + assertEquals(LocalDateTime.of(2017, 2, 2, 22, 23, 24), + statuses.get(0).getReceiveTime()); + assertEquals("12345", statuses.get(0).getSerialNo()); + assertEquals(67890L, statuses.get(0).getLogId()); + } + + @Test + public void testGetSmsTemplate() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + String apiTemplateId = randomString(); + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\"TemplateCode\":\"SMS_207945135\",\"RequestId\":\"6F4CC077-29C8-5BA5-AB62-5FF95068A5AC\",\"Message\":\"OK\",\"TemplateContent\":\"您的验证码${code},该验证码5分钟内有效,请勿泄漏于他人!\",\"TemplateName\":\"公告通知\",\"TemplateType\":0,\"Code\":\"OK\",\"CreateDate\":\"2020-12-23 17:34:42\",\"Reason\":\"无审批备注\",\"TemplateStatus\":1}"); + httpUtilsMockedStatic.when(() -> HttpUtils.encodeUtf8(anyString())) + .then((Answer) invocationOnMock -> (String) invocationOnMock.getArguments()[0]); + + // 调用 + SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); + // 断言 + assertEquals("SMS_207945135", result.getId()); + assertEquals("您的验证码${code},该验证码5分钟内有效,请勿泄漏于他人!", result.getContent()); + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); + assertEquals("无审批备注", result.getAuditReason()); + } + } + + @Test + public void testConvertSmsTemplateAuditStatus() { + assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), + smsClient.convertSmsTemplateAuditStatus(0)); + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), + smsClient.convertSmsTemplateAuditStatus(1)); + assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), + smsClient.convertSmsTemplateAuditStatus(2)); + assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3), + "未知审核状态(3)"); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java new file mode 100644 index 0000000..9d24d09 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java @@ -0,0 +1,127 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import com.google.common.collect.Lists; + +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.MockedStatic; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; + +/** + * {@link HuaweiSmsClient} 的单元测试 + * + * @author scholar + */ +public class HuaweiSmsClientTest extends BaseMockitoUnitTest { + + private final SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错 + .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 + .setSignature("芋道源码"); + + @InjectMocks + private HuaweiSmsClient smsClient = new HuaweiSmsClient(properties); + + @Test + public void testDoSendSms_success() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString() + " " + randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); + + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\"result\":[{\"originTo\":\"+86155****5678\",\"createTime\":\"2018-05-25T16:34:34Z\",\"from\":\"1069********0012\",\"smsMsgId\":\"d6e3cdd0-522b-4692-8304-a07553cdf591_8539659\",\"status\":\"000000\",\"countryId\":\"CN\",\"total\":2}],\"code\":\"000000\",\"description\":\"Success\"}\n"); + + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, + apiTemplateId, templateParams); + // 断言 + assertTrue(result.getSuccess()); + assertEquals("d6e3cdd0-522b-4692-8304-a07553cdf591_8539659", result.getSerialNo()); + assertEquals("000000", result.getApiCode()); + } + } + + @Test + public void testDoSendSms_fail_01() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString() + " " + randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); + + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\"result\":[{\"total\":1,\"originTo\":\"17321315478\",\"createTime\":\"2024-08-18T11:32:20Z\",\"from\":\"x8824060312575\",\"smsMsgId\":\"06e4b966-ad87-479f-8b74-f57fb7aafb60_304613461\",\"countryId\":\"CN\",\"status\":\"E200033\"}],\"code\":\"E000510\",\"description\":\"The SMS fails to be sent. For details, see status.\"}"); + + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, + apiTemplateId, templateParams); + // 断言 + assertFalse(result.getSuccess()); + assertEquals("06e4b966-ad87-479f-8b74-f57fb7aafb60_304613461", result.getSerialNo()); + assertEquals("E200033", result.getApiCode()); + } + } + + @Test + public void testDoSendSms_fail_02() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString() + " " + randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); + + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\"code\":\"E000102\",\"description\":\"Invalid app_key.\"}"); + + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, + apiTemplateId, templateParams); + // 断言 + assertFalse(result.getSuccess()); + assertEquals("E000102", result.getApiCode()); + assertEquals("Invalid app_key.", result.getApiMsg()); + } + } + + @Test + public void testParseSmsReceiveStatus() { + // 准备参数 + String text = "sequence=1&total=1&statusDesc=%E7%94%A8%E6%88%B7%E5%B7%B2%E6%88%90%E5%8A%9F%E6%94%B6%E5%88%B0%E7%9F%AD%E4%BF%A1&updateTime=2024-08-15T03%3A00%3A34Z&source=2&smsMsgId=70207ed7-1d02-41b0-8537-bb25fd1c2364_143684459&status=DELIVRD&extend=176"; + + // 调用 + List statuses = smsClient.parseSmsReceiveStatus(text); + // 断言 + assertEquals(1, statuses.size()); + SmsReceiveRespDTO status = statuses.get(0); + assertTrue(status.getSuccess()); + assertEquals("DELIVRD", status.getErrorCode()); + assertEquals(LocalDateTime.of(2024, 8, 15, 3, 0, 34), status.getReceiveTime()); + assertEquals("70207ed7-1d02-41b0-8537-bb25fd1c2364_143684459", status.getSerialNo()); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java new file mode 100644 index 0000000..50c38c1 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java @@ -0,0 +1,131 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import com.google.common.collect.Lists; + +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.MockedStatic; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; + +/** + * {@link QiniuSmsClient} 的单元测试 + * + * @author scholar + */ +public class QiniuSmsClientTest extends BaseMockitoUnitTest { + + private final SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey(randomString())// 随机一个 apiKey,避免构建报错 + .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 + .setSignature("芋道源码"); + + @InjectMocks + private QiniuSmsClient smsClient = new QiniuSmsClient(properties); + + @Test + public void testDoSendSms_success() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString() + " " + randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\"message_id\":\"17245678901\"}"); + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, + apiTemplateId, templateParams); + // 断言 + assertTrue(result.getSuccess()); + assertEquals("17245678901", result.getSerialNo()); + } + } + + @Test + public void testDoSendSms_fail() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString() + " " + randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\"error\":\"BadToken\",\"message\":\"Your authorization token is invalid\",\"request_id\":\"etziWcJFo1C8Ne8X\"}"); + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, + apiTemplateId, templateParams); + // 断言 + assertFalse(result.getSuccess()); + assertEquals("BadToken", result.getApiCode()); + assertEquals("Your authorization token is invalid", result.getApiMsg()); + assertEquals("etziWcJFo1C8Ne8X", result.getApiRequestId()); + } + } + + @Test + public void testGetSmsTemplate() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + String apiTemplateId = randomString(); + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.get(anyString(), anyMap())) + .thenReturn("{\"audit_status\":\"passed\",\"created_at\":1724231187,\"description\":\"\",\"disable_broadcast\":false,\"disable_broadcast_reason\":\"\",\"disable_reason\":\"\",\"disabled\":false,\"id\":\"1826184073773596672\",\"is_oversea\":false,\"name\":\"dd\",\"parameters\":[\"code\"],\"reject_reason\":\"\",\"signature_id\":\"1826099896017498112\",\"signature_text\":\"yudao\",\"template\":\"您的验证码为:${code}\",\"type\":\"verification\",\"uid\":1383022432,\"updated_at\":1724288561,\"variable_count\":0}"); + // 调用 + SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); + // 断言 + assertEquals("1826184073773596672", result.getId()); + assertEquals("您的验证码为:${code}", result.getContent()); + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); + assertEquals("", result.getAuditReason()); + } + } + + @Test + public void testParseSmsReceiveStatus() { + // 准备参数 + String text = "{\"items\":[{\"mobile\":\"18881234567\",\"message_id\":\"10135515063508004167\",\"status\":\"DELIVRD\",\"delivrd_at\":1724591666,\"error\":\"DELIVRD\",\"seq\":\"123\"}]}"; + // 调用 + List statuses = smsClient.parseSmsReceiveStatus(text); + // 断言 + assertEquals(1, statuses.size()); + SmsReceiveRespDTO status = statuses.get(0); + assertTrue(status.getSuccess()); + assertEquals("DELIVRD", status.getErrorMsg()); + assertEquals(LocalDateTime.of(2024, 8, 25, 21, 14, 26), status.getReceiveTime()); + assertEquals("18881234567", status.getMobile()); + assertEquals("10135515063508004167", status.getSerialNo()); + assertEquals(123, status.getLogId()); + } + + @Test + public void testConvertSmsTemplateAuditStatus() { + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), + smsClient.convertSmsTemplateAuditStatus("passed")); + assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), + smsClient.convertSmsTemplateAuditStatus("reviewing")); + assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), + smsClient.convertSmsTemplateAuditStatus("rejected")); + assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus("unknown"), + "未知审核状态(3)"); + } +} \ No newline at end of file diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java new file mode 100644 index 0000000..faba754 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java @@ -0,0 +1,151 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.hutool.core.collection.ListUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.List; + +/** + * 各种 {@link SmsClient} 的集成测试 + * + * @author 芋道源码 + */ +public class SmsClientTests { + + // ========== 阿里云 ========== + + @Test + @Disabled + public void testAliyunSmsClient_getSmsTemplate() throws Throwable { + SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey(System.getenv("SMS_ALIYUN_ACCESS_KEY")) + .setApiSecret(System.getenv("SMS_ALIYUN_SECRET_KEY")); + AliyunSmsClient client = new AliyunSmsClient(properties); + // 准备参数 + String apiTemplateId = "SMS_207945135"; + // 调用 + SmsTemplateRespDTO template = client.getSmsTemplate(apiTemplateId); + // 打印结果 + System.out.println(template); + } + + @Test + @Disabled + public void testAliyunSmsClient_sendSms() throws Throwable { + SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey(System.getenv("SMS_ALIYUN_ACCESS_KEY")) + .setApiSecret(System.getenv("SMS_ALIYUN_SECRET_KEY")) + .setSignature("Ballcat"); + AliyunSmsClient client = new AliyunSmsClient(properties); + // 准备参数 + Long sendLogId = System.currentTimeMillis(); + String mobile = "15601691323"; + String apiTemplateId = "SMS_207945135"; + // 调用 + SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, ListUtil.of(new KeyValue<>("code", "1024"))); + // 打印结果 + System.out.println(sendRespDTO); + } + + // ========== 腾讯云 ========== + + @Test + @Disabled + public void testTencentSmsClient_sendSms() throws Throwable { + String sdkAppId = "1400500458"; + SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey(System.getenv("SMS_TENCENT_ACCESS_KEY") + " " + sdkAppId) + .setApiSecret(System.getenv("SMS_TENCENT_SECRET_KEY")) + .setSignature("芋道源码"); + TencentSmsClient client = new TencentSmsClient(properties); + // 准备参数 + Long sendLogId = System.currentTimeMillis(); + String mobile = "15601691323"; + String apiTemplateId = "358212"; + // 调用 + SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, ListUtil.of(new KeyValue<>("code", "1024"))); + // 打印结果 + System.out.println(sendRespDTO); + } + + @Test + @Disabled + public void testTencentSmsClient_getSmsTemplate() throws Throwable { + String sdkAppId = "1400500458"; + SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey(System.getenv("SMS_TENCENT_ACCESS_KEY") + " " + sdkAppId) + .setApiSecret(System.getenv("SMS_TENCENT_SECRET_KEY")) + .setSignature("芋道源码"); + TencentSmsClient client = new TencentSmsClient(properties); + // 准备参数 + String apiTemplateId = "358212"; + // 调用 + SmsTemplateRespDTO template = client.getSmsTemplate(apiTemplateId); + // 打印结果 + System.out.println(template); + } + + // ========== 华为云 ========== + + @Test + @Disabled + public void testHuaweiSmsClient_sendSms() throws Throwable { + String sender = "x8824060312575"; + SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey(System.getenv("SMS_HUAWEI_ACCESS_KEY") + " " + sender) + .setApiSecret(System.getenv("SMS_HUAWEI_SECRET_KEY")) + .setSignature("runpu"); + HuaweiSmsClient client = new HuaweiSmsClient(properties); + // 准备参数 + Long sendLogId = System.currentTimeMillis(); + String mobile = "17321315478"; + String apiTemplateId = "3644cdab863546a3b718d488659a99ef"; + List> templateParams = ListUtil.of(new KeyValue<>("code", "1024")); + // 调用 + SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams); + // 打印结果 + System.out.println(smsSendRespDTO); + } + + // ========== 七牛云 ========== + + @Test + @Disabled + public void testQiniuSmsClient_sendSms() throws Throwable { + SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey("SMS_QINIU_ACCESS_KEY") + .setApiSecret("SMS_QINIU_SECRET_KEY"); + QiniuSmsClient client = new QiniuSmsClient(properties); + // 准备参数 + Long sendLogId = System.currentTimeMillis(); + String mobile = "17321315478"; + String apiTemplateId = "3644cdab863546a3b718d488659a99ef"; + List> templateParams = ListUtil.of(new KeyValue<>("code", "1122")); + // 调用 + SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams); + // 打印结果 + System.out.println(smsSendRespDTO); + } + + @Test + @Disabled + public void testQiniuSmsClient_getSmsTemplate() throws Throwable { + SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey("SMS_QINIU_ACCESS_KEY") + .setApiSecret("SMS_QINIU_SECRET_KEY"); + QiniuSmsClient client = new QiniuSmsClient(properties); + // 准备参数 + String apiTemplateId = "3644cdab863546a3b718d488659a99ef"; + // 调用 + SmsTemplateRespDTO template = client.getSmsTemplate(apiTemplateId); + // 打印结果 + System.out.println(template); + } +} + diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClientTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClientTest.java new file mode 100644 index 0000000..060a345 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClientTest.java @@ -0,0 +1,218 @@ +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; + +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import com.google.common.collect.Lists; + +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.MockedStatic; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; + +/** + * {@link TencentSmsClient} 的单元测试 + * + * @author shiwp + */ +public class TencentSmsClientTest extends BaseMockitoUnitTest { + + private final SmsChannelProperties properties = new SmsChannelProperties() + .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错 + .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 + .setSignature("芋道源码"); + + @InjectMocks + private TencentSmsClient smsClient = new TencentSmsClient(properties); + + @Test + public void testDoSendSms_success() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\n" + + " \"Response\": {\n" + + " \"SendStatusSet\": [\n" + + " {\n" + + " \"SerialNo\": \"5000:1045710669157053657849499619\",\n" + + " \"PhoneNumber\": \"+8618511122233\",\n" + + " \"Fee\": 1,\n" + + " \"SessionContext\": \"test\",\n" + + " \"Code\": \"Ok\",\n" + + " \"Message\": \"send success\",\n" + + " \"IsoCode\": \"CN\"\n" + + " },\n" + + " ],\n" + + " \"RequestId\": \"a0aabda6-cf91-4f3e-a81f-9198114a2279\"\n" + + " }\n" + + "}"); + + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, + apiTemplateId, templateParams); + // 断言 + assertTrue(result.getSuccess()); + assertEquals("5000:1045710669157053657849499619", result.getSerialNo()); + assertEquals("a0aabda6-cf91-4f3e-a81f-9198114a2279", result.getApiRequestId()); + assertEquals("send success", result.getApiMsg()); + } + } + + @Test + public void testDoSendSms_fail_01() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); + + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\n" + + " \"Response\": {\n" + + " \"SendStatusSet\": [\n" + + " {\n" + + " \"SerialNo\": \"5000:1045710669157053657849499619\",\n" + + " \"PhoneNumber\": \"+8618511122233\",\n" + + " \"Fee\": 1,\n" + + " \"SessionContext\": \"test\",\n" + + " \"Code\": \"ERROR\",\n" + + " \"Message\": \"send success\",\n" + + " \"IsoCode\": \"CN\"\n" + + " },\n" + + " ],\n" + + " \"RequestId\": \"a0aabda6-cf91-4f3e-a81f-9198114a2279\"\n" + + " }\n" + + "}"); + + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, + apiTemplateId, templateParams); + // 断言 + assertFalse(result.getSuccess()); + assertEquals("5000:1045710669157053657849499619", result.getSerialNo()); + assertEquals("a0aabda6-cf91-4f3e-a81f-9198114a2279", result.getApiRequestId()); + assertEquals("send success", result.getApiMsg()); + } + } + + @Test + public void testDoSendSms_fail_02() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString(); + List> templateParams = Lists.newArrayList( + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); + + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{\"Response\":{\"Error\":{\"Code\":\"AuthFailure.SecretIdNotFound\",\"Message\":\"The SecretId is not found, please ensure that your SecretId is correct.\"},\"RequestId\":\"2a88f82a-261c-4ac6-9fa9-c7d01aaa486a\"}}"); + + // 调用 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, + apiTemplateId, templateParams); + // 断言 + assertFalse(result.getSuccess()); + assertEquals("2a88f82a-261c-4ac6-9fa9-c7d01aaa486a", result.getApiRequestId()); + assertEquals("AuthFailure.SecretIdNotFound", result.getApiCode()); + assertEquals("The SecretId is not found, please ensure that your SecretId is correct.", result.getApiMsg()); + } + } + + @Test + public void testParseSmsReceiveStatus() { + // 准备参数 + String text = "[\n" + + " {\n" + + " \"user_receive_time\": \"2015-10-17 08:03:04\",\n" + + " \"nationcode\": \"86\",\n" + + " \"mobile\": \"13900000001\",\n" + + " \"report_status\": \"SUCCESS\",\n" + + " \"errmsg\": \"DELIVRD\",\n" + + " \"description\": \"用户短信送达成功\",\n" + + " \"sid\": \"12345\",\n" + + " \"ext\": {\"logId\":\"67890\"}\n" + + " }\n" + + "]"; + + // 调用 + List statuses = smsClient.parseSmsReceiveStatus(text); + // 断言 + assertEquals(1, statuses.size()); + assertTrue(statuses.get(0).getSuccess()); + assertEquals("DELIVRD", statuses.get(0).getErrorCode()); + assertEquals("13900000001", statuses.get(0).getMobile()); + assertEquals(LocalDateTime.of(2015, 10, 17, 8, 3, 4), statuses.get(0).getReceiveTime()); + assertEquals("12345", statuses.get(0).getSerialNo()); + } + + @Test + public void testGetSmsTemplate() throws Throwable { + try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { + // 准备参数 + String apiTemplateId = "1122"; + + // mock 方法 + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) + .thenReturn("{ \"Response\": {\n" + + " \"DescribeTemplateStatusSet\": [\n" + + " {\n" + + " \"TemplateName\": \"验证码\",\n" + + " \"TemplateId\": 1122,\n" + + " \"International\": 0,\n" + + " \"ReviewReply\": \"审批备注\",\n" + + " \"CreateTime\": 1617379200,\n" + + " \"TemplateContent\": \"您的验证码是{1}\",\n" + + " \"StatusCode\": 0\n" + + " },\n" + + " \n" + + " ],\n" + + " \"RequestId\": \"f36e4f00-605e-49b1-ad0d-bfaba81c7325\"\n" + + " }}"); + + // 调用 + SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); + // 断言 + assertEquals("1122", result.getId()); + assertEquals("您的验证码是{1}", result.getContent()); + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); + assertEquals("审批备注", result.getAuditReason()); + } + } + + @Test + public void testConvertSmsTemplateAuditStatus() { + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), + smsClient.convertSmsTemplateAuditStatus(0)); + assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), + smsClient.convertSmsTemplateAuditStatus(1)); + assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), + smsClient.convertSmsTemplateAuditStatus(-1)); + assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3), + "未知审核状态(3)"); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java new file mode 100644 index 0000000..62baea3 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java @@ -0,0 +1,358 @@ +package cn.iocoder.yudao.module.system.service.auth; + +import cn.hutool.core.util.ReflectUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; +import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import cn.iocoder.yudao.module.system.service.logger.LoginLogService; +import cn.iocoder.yudao.module.system.service.member.MemberService; +import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; +import cn.iocoder.yudao.module.system.service.social.SocialUserService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import com.anji.captcha.model.common.ResponseModel; +import com.anji.captcha.service.CaptchaService; +import jakarta.annotation.Resource; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +@Import(AdminAuthServiceImpl.class) +public class AdminAuthServiceImplTest extends BaseDbUnitTest { + + @Resource + private AdminAuthServiceImpl authService; + + @MockBean + private AdminUserService userService; + @MockBean + private CaptchaService captchaService; + @MockBean + private LoginLogService loginLogService; + @MockBean + private SocialUserService socialUserService; + @MockBean + private SmsCodeApi smsCodeApi; + @MockBean + private OAuth2TokenService oauth2TokenService; + @MockBean + private MemberService memberService; + @MockBean + private Validator validator; + + @BeforeEach + public void setUp() { + authService.setCaptchaEnable(true); + // 注入一个 Validator 对象 + ReflectUtil.setFieldValue(authService, "validator", + Validation.buildDefaultValidatorFactory().getValidator()); + } + + @Test + public void testAuthenticate_success() { + // 准备参数 + String username = randomString(); + String password = randomString(); + // mock user 数据 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) + .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(userService.getUserByUsername(eq(username))).thenReturn(user); + // mock password 匹配 + when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); + + // 调用 + AdminUserDO loginUser = authService.authenticate(username, password); + // 校验 + assertPojoEquals(user, loginUser); + } + + @Test + public void testAuthenticate_userNotFound() { + // 准备参数 + String username = randomString(); + String password = randomString(); + + // 调用, 并断言异常 + assertServiceException(() -> authService.authenticate(username, password), + AUTH_LOGIN_BAD_CREDENTIALS); + verify(loginLogService).createLoginLog( + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) + && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()) + && o.getUserId() == null) + ); + } + + @Test + public void testAuthenticate_badCredentials() { + // 准备参数 + String username = randomString(); + String password = randomString(); + // mock user 数据 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) + .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(userService.getUserByUsername(eq(username))).thenReturn(user); + + // 调用, 并断言异常 + assertServiceException(() -> authService.authenticate(username, password), + AUTH_LOGIN_BAD_CREDENTIALS); + verify(loginLogService).createLoginLog( + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) + && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()) + && o.getUserId().equals(user.getId())) + ); + } + + @Test + public void testAuthenticate_userDisabled() { + // 准备参数 + String username = randomString(); + String password = randomString(); + // mock user 数据 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) + .setPassword(password).setStatus(CommonStatusEnum.DISABLE.getStatus())); + when(userService.getUserByUsername(eq(username))).thenReturn(user); + // mock password 匹配 + when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); + + // 调用, 并断言异常 + assertServiceException(() -> authService.authenticate(username, password), + AUTH_LOGIN_USER_DISABLED); + verify(loginLogService).createLoginLog( + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) + && o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult()) + && o.getUserId().equals(user.getId())) + ); + } + + @Test + public void testLogin_success() { + // 准备参数 + AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o -> + o.setUsername("test_username").setPassword("test_password") + .setSocialType(randomEle(SocialTypeEnum.values()).getType())); + + // mock 验证码正确 + authService.setCaptchaEnable(false); + // mock user 数据 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username") + .setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(userService.getUserByUsername(eq("test_username"))).thenReturn(user); + // mock password 匹配 + when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true); + // mock 缓存登录用户到 Redis + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) + .setUserType(UserTypeEnum.ADMIN.getValue())); + when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) + .thenReturn(accessTokenDO); + + // 调用,并校验 + AuthLoginRespVO loginRespVO = authService.login(reqVO); + assertPojoEquals(accessTokenDO, loginRespVO); + // 校验调用参数 + verify(loginLogService).createLoginLog( + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) + && o.getUserId().equals(user.getId())) + ); + verify(socialUserService).bindSocialUser(eq(new SocialUserBindReqDTO( + user.getId(), UserTypeEnum.ADMIN.getValue(), + reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()))); + } + + @Test + public void testSendSmsCode() { + // 准备参数 + String mobile = randomString(); + Integer scene = SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(); + AuthSmsSendReqVO reqVO = new AuthSmsSendReqVO(mobile, scene); + // mock 方法(用户信息) + AdminUserDO user = randomPojo(AdminUserDO.class); + when(userService.getUserByMobile(eq(mobile))).thenReturn(user); + + // 调用 + authService.sendSmsCode(reqVO); + // 断言 + verify(smsCodeApi).sendSmsCode(argThat(sendReqDTO -> { + assertEquals(mobile, sendReqDTO.getMobile()); + assertEquals(scene, sendReqDTO.getScene()); + return true; + })); + } + + @Test + public void testSmsLogin_success() { + // 准备参数 + String mobile = randomString(); + String code = randomString(); + AuthSmsLoginReqVO reqVO = new AuthSmsLoginReqVO(mobile, code); + // mock 方法(验证码) + doNothing().when(smsCodeApi).useSmsCode((argThat(smsCodeUseReqDTO -> { + assertEquals(mobile, smsCodeUseReqDTO.getMobile()); + assertEquals(code, smsCodeUseReqDTO.getCode()); + assertEquals(SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), smsCodeUseReqDTO.getScene()); + return true; + }))); + // mock 方法(用户信息) + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L)); + when(userService.getUserByMobile(eq(mobile))).thenReturn(user); + // mock 缓存登录用户到 Redis + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) + .setUserType(UserTypeEnum.ADMIN.getValue())); + when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) + .thenReturn(accessTokenDO); + + // 调用,并断言 + AuthLoginRespVO loginRespVO = authService.smsLogin(reqVO); + assertPojoEquals(accessTokenDO, loginRespVO); + // 断言调用 + verify(loginLogService).createLoginLog( + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_MOBILE.getType()) + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) + && o.getUserId().equals(user.getId())) + ); + } + + @Test + public void testSocialLogin_success() { + // 准备参数 + AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class); + // mock 方法(绑定的用户编号) + Long userId = 1L; + when(socialUserService.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()), + eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), userId)); + // mock(用户) + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId)); + when(userService.getUser(eq(userId))).thenReturn(user); + // mock 缓存登录用户到 Redis + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) + .setUserType(UserTypeEnum.ADMIN.getValue())); + when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) + .thenReturn(accessTokenDO); + + // 调用,并断言 + AuthLoginRespVO loginRespVO = authService.socialLogin(reqVO); + assertPojoEquals(accessTokenDO, loginRespVO); + // 断言调用 + verify(loginLogService).createLoginLog( + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_SOCIAL.getType()) + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) + && o.getUserId().equals(user.getId())) + ); + } + + @Test + public void testValidateCaptcha_successWithEnable() { + // 准备参数 + AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); + + // mock 验证通过 + when(captchaService.verification(argThat(captchaVO -> { + assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification()); + return true; + }))).thenReturn(ResponseModel.success()); + + // 调用,无需断言 + authService.validateCaptcha(reqVO); + } + + @Test + public void testValidateCaptcha_successWithDisable() { + // 准备参数 + AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); + + // mock 验证码关闭 + authService.setCaptchaEnable(false); + + // 调用,无需断言 + authService.validateCaptcha(reqVO); + } + + @Test + public void testCaptcha_fail() { + // 准备参数 + AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); + + // mock 验证通过 + when(captchaService.verification(argThat(captchaVO -> { + assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification()); + return true; + }))).thenReturn(ResponseModel.errorMsg("就是不对")); + + // 调用, 并断言异常 + assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对"); + // 校验调用参数 + verify(loginLogService).createLoginLog( + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) + && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())) + ); + } + + @Test + public void testRefreshToken() { + // 准备参数 + String refreshToken = randomString(); + // mock 方法 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); + when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq("default"))) + .thenReturn(accessTokenDO); + + // 调用 + AuthLoginRespVO loginRespVO = authService.refreshToken(refreshToken); + // 断言 + assertPojoEquals(accessTokenDO, loginRespVO); + } + + @Test + public void testLogout_success() { + // 准备参数 + String token = randomString(); + // mock + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) + .setUserType(UserTypeEnum.ADMIN.getValue())); + when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO); + + // 调用 + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); + // 校验调用参数 + verify(loginLogService).createLoginLog( + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType()) + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) + ); + // 调用,并校验 + + } + + @Test + public void testLogout_fail() { + // 准备参数 + String token = randomString(); + + // 调用 + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); + // 校验调用参数 + verify(loginLogService, never()).createLoginLog(any()); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java new file mode 100644 index 0000000..bcf55bd --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java @@ -0,0 +1,296 @@ +package cn.iocoder.yudao.module.system.service.dept; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link DeptServiceImpl} 的单元测试类 + * + * @author niudehua + */ +@Import(DeptServiceImpl.class) +public class DeptServiceImplTest extends BaseDbUnitTest { + + @Resource + private DeptServiceImpl deptService; + @Resource + private DeptMapper deptMapper; + + @Test + public void testCreateDept() { + // 准备参数 + DeptSaveReqVO reqVO = randomPojo(DeptSaveReqVO.class, o -> { + o.setId(null); // 防止 id 被设置 + o.setParentId(DeptDO.PARENT_ID_ROOT); + o.setStatus(randomCommonStatus()); + }); + + // 调用 + Long deptId = deptService.createDept(reqVO); + // 断言 + assertNotNull(deptId); + // 校验记录的属性是否正确 + DeptDO deptDO = deptMapper.selectById(deptId); + assertPojoEquals(reqVO, deptDO, "id"); + } + + @Test + public void testUpdateDept() { + // mock 数据 + DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); + deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 + // 准备参数 + DeptSaveReqVO reqVO = randomPojo(DeptSaveReqVO.class, o -> { + // 设置更新的 ID + o.setParentId(DeptDO.PARENT_ID_ROOT); + o.setId(dbDeptDO.getId()); + o.setStatus(randomCommonStatus()); + }); + + // 调用 + deptService.updateDept(reqVO); + // 校验是否更新正确 + DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, deptDO); + } + + @Test + public void testDeleteDept_success() { + // mock 数据 + DeptDO dbDeptDO = randomPojo(DeptDO.class); + deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbDeptDO.getId(); + + // 调用 + deptService.deleteDept(id); + // 校验数据不存在了 + assertNull(deptMapper.selectById(id)); + } + + @Test + public void testDeleteDept_exitsChildren() { + // mock 数据 + DeptDO parentDept = randomPojo(DeptDO.class); + deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据 + // 准备参数 + DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> { + o.setParentId(parentDept.getId()); + o.setStatus(randomCommonStatus()); + }); + // 插入子部门 + deptMapper.insert(childrenDeptDO); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN); + } + + @Test + public void testValidateDeptExists_notFound() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateDeptExists(id), DEPT_NOT_FOUND); + } + + @Test + public void testValidateParentDept_parentError() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateParentDept(id, id), + DEPT_PARENT_ERROR); + } + + @Test + public void testValidateParentDept_parentIsChild() { + // mock 数据(父节点) + DeptDO parentDept = randomPojo(DeptDO.class); + deptMapper.insert(parentDept); + // mock 数据(子节点) + DeptDO childDept = randomPojo(DeptDO.class, o -> { + o.setParentId(parentDept.getId()); + }); + deptMapper.insert(childDept); + + // 准备参数 + Long id = parentDept.getId(); + Long parentId = childDept.getId(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateParentDept(id, parentId), DEPT_PARENT_IS_CHILD); + } + + @Test + public void testValidateNameUnique_duplicate() { + // mock 数据 + DeptDO deptDO = randomPojo(DeptDO.class); + deptMapper.insert(deptDO); + + // 准备参数 + Long id = randomLongId(); + Long parentId = deptDO.getParentId(); + String name = deptDO.getName(); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name), + DEPT_NAME_DUPLICATE); + } + + @Test + public void testGetDept() { + // mock 数据 + DeptDO deptDO = randomPojo(DeptDO.class); + deptMapper.insert(deptDO); + // 准备参数 + Long id = deptDO.getId(); + + // 调用 + DeptDO dbDept = deptService.getDept(id); + // 断言 + assertEquals(deptDO, dbDept); + } + + @Test + public void testGetDeptList_ids() { + // mock 数据 + DeptDO deptDO01 = randomPojo(DeptDO.class); + deptMapper.insert(deptDO01); + DeptDO deptDO02 = randomPojo(DeptDO.class); + deptMapper.insert(deptDO02); + // 准备参数 + List ids = Arrays.asList(deptDO01.getId(), deptDO02.getId()); + + // 调用 + List deptDOList = deptService.getDeptList(ids); + // 断言 + assertEquals(2, deptDOList.size()); + assertEquals(deptDO01, deptDOList.get(0)); + assertEquals(deptDO02, deptDOList.get(1)); + } + + @Test + public void testGetDeptList_reqVO() { + // mock 数据 + DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到 + o.setName("开发部"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + deptMapper.insert(dept); + // 测试 name 不匹配 + deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setName("发"))); + // 测试 status 不匹配 + deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 准备参数 + DeptListReqVO reqVO = new DeptListReqVO(); + reqVO.setName("开"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + List sysDeptDOS = deptService.getDeptList(reqVO); + // 断言 + assertEquals(1, sysDeptDOS.size()); + assertPojoEquals(dept, sysDeptDOS.get(0)); + } + + @Test + public void testGetChildDeptList() { + // mock 数据(1 级别子节点) + DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); + deptMapper.insert(dept1); + DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); + deptMapper.insert(dept2); + // mock 数据(2 级子节点) + DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); + deptMapper.insert(dept1a); + DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); + deptMapper.insert(dept2a); + // 准备参数 + Long id = dept1.getParentId(); + + // 调用 + List result = deptService.getChildDeptList(id); + // 断言 + assertEquals(result.size(), 2); + assertPojoEquals(dept1, result.get(0)); + assertPojoEquals(dept1a, result.get(1)); + } + + @Test + public void testGetChildDeptListFromCache() { + // mock 数据(1 级别子节点) + DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); + deptMapper.insert(dept1); + DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); + deptMapper.insert(dept2); + // mock 数据(2 级子节点) + DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); + deptMapper.insert(dept1a); + DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); + deptMapper.insert(dept2a); + // 准备参数 + Long id = dept1.getParentId(); + + // 调用 + Set result = deptService.getChildDeptIdListFromCache(id); + // 断言 + assertEquals(result.size(), 2); + assertTrue(result.contains(dept1.getId())); + assertTrue(result.contains(dept1a.getId())); + } + + @Test + public void testValidateDeptList_success() { + // mock 数据 + DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.ENABLE.getStatus()); + deptMapper.insert(deptDO); + // 准备参数 + List ids = singletonList(deptDO.getId()); + + // 调用,无需断言 + deptService.validateDeptList(ids); + } + + @Test + public void testValidateDeptList_notFound() { + // 准备参数 + List ids = singletonList(randomLongId()); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_FOUND); + } + + @Test + public void testValidateDeptList_notEnable() { + // mock 数据 + DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.DISABLE.getStatus()); + deptMapper.insert(deptDO); + // 准备参数 + List ids = singletonList(deptDO.getId()); + + // 调用, 并断言异常 + assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_ENABLE, deptDO.getName()); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImplTest.java new file mode 100644 index 0000000..657c09d --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImplTest.java @@ -0,0 +1,248 @@ +package cn.iocoder.yudao.module.system.service.dept; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link PostServiceImpl} 的单元测试类 + * + * @author niudehua + */ +@Import(PostServiceImpl.class) +public class PostServiceImplTest extends BaseDbUnitTest { + + @Resource + private PostServiceImpl postService; + + @Resource + private PostMapper postMapper; + + @Test + public void testCreatePost_success() { + // 准备参数 + PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, + o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus())) + .setId(null); // 防止 id 被设置 + // 调用 + Long postId = postService.createPost(reqVO); + + // 断言 + assertNotNull(postId); + // 校验记录的属性是否正确 + PostDO post = postMapper.selectById(postId); + assertPojoEquals(reqVO, post, "id"); + } + + @Test + public void testUpdatePost_success() { + // mock 数据 + PostDO postDO = randomPostDO(); + postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据 + // 准备参数 + PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, o -> { + // 设置更新的 ID + o.setId(postDO.getId()); + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); + }); + + // 调用 + postService.updatePost(reqVO); + // 校验是否更新正确 + PostDO post = postMapper.selectById(reqVO.getId()); + assertPojoEquals(reqVO, post); + } + + @Test + public void testDeletePost_success() { + // mock 数据 + PostDO postDO = randomPostDO(); + postMapper.insert(postDO); + // 准备参数 + Long id = postDO.getId(); + + // 调用 + postService.deletePost(id); + assertNull(postMapper.selectById(id)); + } + + @Test + public void testValidatePost_notFoundForDelete() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> postService.deletePost(id), POST_NOT_FOUND); + } + + @Test + public void testValidatePost_nameDuplicateForCreate() { + // mock 数据 + PostDO postDO = randomPostDO(); + postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据 + // 准备参数 + PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, + // 模拟 name 重复 + o -> o.setName(postDO.getName())); + assertServiceException(() -> postService.createPost(reqVO), POST_NAME_DUPLICATE); + } + + @Test + public void testValidatePost_codeDuplicateForUpdate() { + // mock 数据 + PostDO postDO = randomPostDO(); + postMapper.insert(postDO); + // mock 数据:稍后模拟重复它的 code + PostDO codePostDO = randomPostDO(); + postMapper.insert(codePostDO); + // 准备参数 + PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, o -> { + // 设置更新的 ID + o.setId(postDO.getId()); + // 模拟 code 重复 + o.setCode(codePostDO.getCode()); + }); + + // 调用, 并断言异常 + assertServiceException(() -> postService.updatePost(reqVO), POST_CODE_DUPLICATE); + } + + @Test + public void testGetPostPage() { + // mock 数据 + PostDO postDO = randomPojo(PostDO.class, o -> { + o.setName("码仔"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + postMapper.insert(postDO); + // 测试 name 不匹配 + postMapper.insert(cloneIgnoreId(postDO, o -> o.setName("程序员"))); + // 测试 status 不匹配 + postMapper.insert(cloneIgnoreId(postDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 准备参数 + PostPageReqVO reqVO = new PostPageReqVO(); + reqVO.setName("码"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + PageResult pageResult = postService.getPostPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(postDO, pageResult.getList().get(0)); + } + + @Test + public void testGetPostList() { + // mock 数据 + PostDO postDO01 = randomPojo(PostDO.class); + postMapper.insert(postDO01); + // 测试 id 不匹配 + PostDO postDO02 = randomPojo(PostDO.class); + postMapper.insert(postDO02); + // 准备参数 + List ids = singletonList(postDO01.getId()); + + // 调用 + List list = postService.getPostList(ids); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(postDO01, list.get(0)); + } + + @Test + public void testGetPostList_idsAndStatus() { + // mock 数据 + PostDO postDO01 = randomPojo(PostDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + postMapper.insert(postDO01); + // 测试 status 不匹配 + PostDO postDO02 = randomPojo(PostDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + postMapper.insert(postDO02); + // 准备参数 + List ids = Arrays.asList(postDO01.getId(), postDO02.getId()); + + // 调用 + List list = postService.getPostList(ids, singletonList(CommonStatusEnum.ENABLE.getStatus())); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(postDO01, list.get(0)); + } + + @Test + public void testGetPost() { + // mock 数据 + PostDO dbPostDO = randomPostDO(); + postMapper.insert(dbPostDO); + // 准备参数 + Long id = dbPostDO.getId(); + // 调用 + PostDO post = postService.getPost(id); + // 断言 + assertNotNull(post); + assertPojoEquals(dbPostDO, post); + } + + @Test + public void testValidatePostList_success() { + // mock 数据 + PostDO postDO = randomPostDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); + postMapper.insert(postDO); + // 准备参数 + List ids = singletonList(postDO.getId()); + + // 调用,无需断言 + postService.validatePostList(ids); + } + + @Test + public void testValidatePostList_notFound() { + // 准备参数 + List ids = singletonList(randomLongId()); + + // 调用, 并断言异常 + assertServiceException(() -> postService.validatePostList(ids), POST_NOT_FOUND); + } + + @Test + public void testValidatePostList_notEnable() { + // mock 数据 + PostDO postDO = randomPostDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); + postMapper.insert(postDO); + // 准备参数 + List ids = singletonList(postDO.getId()); + + // 调用, 并断言异常 + assertServiceException(() -> postService.validatePostList(ids), POST_NOT_ENABLE, + postDO.getName()); + } + + @SafeVarargs + private static PostDO randomPostDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setStatus(randomCommonStatus()); // 保证 status 的范围 + }; + return randomPojo(PostDO.class, ArrayUtils.append(consumer, consumers)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java new file mode 100644 index 0000000..999570e --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java @@ -0,0 +1,352 @@ +package cn.iocoder.yudao.module.system.service.dict; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; +import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.function.Consumer; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Import(DictDataServiceImpl.class) +public class DictDataServiceImplTest extends BaseDbUnitTest { + + @Resource + private DictDataServiceImpl dictDataService; + + @Resource + private DictDataMapper dictDataMapper; + @MockBean + private DictTypeService dictTypeService; + + @Test + public void testGetDictDataList() { + // mock 数据 + DictDataDO dictDataDO01 = randomDictDataDO().setDictType("yunai").setSort(2) + .setStatus(CommonStatusEnum.ENABLE.getStatus()); + dictDataMapper.insert(dictDataDO01); + DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setSort(1) + .setStatus(CommonStatusEnum.ENABLE.getStatus()); + dictDataMapper.insert(dictDataDO02); + DictDataDO dictDataDO03 = randomDictDataDO().setDictType("yunai").setSort(3) + .setStatus(CommonStatusEnum.DISABLE.getStatus()); + dictDataMapper.insert(dictDataDO03); + DictDataDO dictDataDO04 = randomDictDataDO().setDictType("yunai2").setSort(3) + .setStatus(CommonStatusEnum.DISABLE.getStatus()); + dictDataMapper.insert(dictDataDO04); + // 准备参数 + Integer status = CommonStatusEnum.ENABLE.getStatus(); + String dictType = "yunai"; + + // 调用 + List dictDataDOList = dictDataService.getDictDataList(status, dictType); + // 断言 + assertEquals(2, dictDataDOList.size()); + assertPojoEquals(dictDataDO02, dictDataDOList.get(0)); + assertPojoEquals(dictDataDO01, dictDataDOList.get(1)); + } + + @Test + public void testGetDictDataPage() { + // mock 数据 + DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> { // 等会查询到 + o.setLabel("芋艿"); + o.setDictType("yunai"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + dictDataMapper.insert(dbDictData); + // 测试 label 不匹配 + dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setLabel("艿"))); + // 测试 dictType 不匹配 + dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setDictType("nai"))); + // 测试 status 不匹配 + dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 准备参数 + DictDataPageReqVO reqVO = new DictDataPageReqVO(); + reqVO.setLabel("芋"); + reqVO.setDictType("yunai"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + PageResult pageResult = dictDataService.getDictDataPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbDictData, pageResult.getList().get(0)); + } + + @Test + public void testGetDictData() { + // mock 数据 + DictDataDO dbDictData = randomDictDataDO(); + dictDataMapper.insert(dbDictData); + // 准备参数 + Long id = dbDictData.getId(); + + // 调用 + DictDataDO dictData = dictDataService.getDictData(id); + // 断言 + assertPojoEquals(dbDictData, dictData); + } + + @Test + public void testCreateDictData_success() { + // 准备参数 + DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, + o -> o.setStatus(randomCommonStatus())) + .setId(null); // 防止 id 被赋值 + // mock 方法 + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); + + // 调用 + Long dictDataId = dictDataService.createDictData(reqVO); + // 断言 + assertNotNull(dictDataId); + // 校验记录的属性是否正确 + DictDataDO dictData = dictDataMapper.selectById(dictDataId); + assertPojoEquals(reqVO, dictData, "id"); + } + + @Test + public void testUpdateDictData_success() { + // mock 数据 + DictDataDO dbDictData = randomDictDataDO(); + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 + // 准备参数 + DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, o -> { + o.setId(dbDictData.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + }); + // mock 方法,字典类型 + when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); + + // 调用 + dictDataService.updateDictData(reqVO); + // 校验是否更新正确 + DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, dictData); + } + + @Test + public void testDeleteDictData_success() { + // mock 数据 + DictDataDO dbDictData = randomDictDataDO(); + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbDictData.getId(); + + // 调用 + dictDataService.deleteDictData(id); + // 校验数据不存在了 + assertNull(dictDataMapper.selectById(id)); + } + + @Test + public void testValidateDictDataExists_success() { + // mock 数据 + DictDataDO dbDictData = randomDictDataDO(); + dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 + + // 调用成功 + dictDataService.validateDictDataExists(dbDictData.getId()); + } + + @Test + public void testValidateDictDataExists_notExists() { + assertServiceException(() -> dictDataService.validateDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS); + } + + @Test + public void testValidateDictTypeExists_success() { + // mock 方法,数据类型被禁用 + String type = randomString(); + when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type)); + + // 调用, 成功 + dictDataService.validateDictTypeExists(type); + } + + @Test + public void testValidateDictTypeExists_notExists() { + assertServiceException(() -> dictDataService.validateDictTypeExists(randomString()), DICT_TYPE_NOT_EXISTS); + } + + @Test + public void testValidateDictTypeExists_notEnable() { + // mock 方法,数据类型被禁用 + String dictType = randomString(); + when(dictTypeService.getDictType(eq(dictType))).thenReturn( + randomPojo(DictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + + // 调用, 并断言异常 + assertServiceException(() -> dictDataService.validateDictTypeExists(dictType), DICT_TYPE_NOT_ENABLE); + } + + @Test + public void testValidateDictDataValueUnique_success() { + // 调用,成功 + dictDataService.validateDictDataValueUnique(randomLongId(), randomString(), randomString()); + } + + @Test + public void testValidateDictDataValueUnique_valueDuplicateForCreate() { + // 准备参数 + String dictType = randomString(); + String value = randomString(); + // mock 数据 + dictDataMapper.insert(randomDictDataDO(o -> { + o.setDictType(dictType); + o.setValue(value); + })); + + // 调用,校验异常 + assertServiceException(() -> dictDataService.validateDictDataValueUnique(null, dictType, value), + DICT_DATA_VALUE_DUPLICATE); + } + + @Test + public void testValidateDictDataValueUnique_valueDuplicateForUpdate() { + // 准备参数 + Long id = randomLongId(); + String dictType = randomString(); + String value = randomString(); + // mock 数据 + dictDataMapper.insert(randomDictDataDO(o -> { + o.setDictType(dictType); + o.setValue(value); + })); + + // 调用,校验异常 + assertServiceException(() -> dictDataService.validateDictDataValueUnique(id, dictType, value), + DICT_DATA_VALUE_DUPLICATE); + } + + @Test + public void testGetDictDataCountByDictType() { + // mock 数据 + dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai"))); + dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("tudou"))); + dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai"))); + // 准备参数 + String dictType = "yunai"; + + // 调用 + long count = dictDataService.getDictDataCountByDictType(dictType); + // 校验 + assertEquals(2L, count); + } + + @Test + public void testValidateDictDataList_success() { + // mock 数据 + DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); + dictDataMapper.insert(dictDataDO); + // 准备参数 + String dictType = dictDataDO.getDictType(); + List values = singletonList(dictDataDO.getValue()); + + // 调用,无需断言 + dictDataService.validateDictDataList(dictType, values); + } + + @Test + public void testValidateDictDataList_notFound() { + // 准备参数 + String dictType = randomString(); + List values = singletonList(randomString()); + + // 调用, 并断言异常 + assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), DICT_DATA_NOT_EXISTS); + } + + @Test + public void testValidateDictDataList_notEnable() { + // mock 数据 + DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); + dictDataMapper.insert(dictDataDO); + // 准备参数 + String dictType = dictDataDO.getDictType(); + List values = singletonList(dictDataDO.getValue()); + + // 调用, 并断言异常 + assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), + DICT_DATA_NOT_ENABLE, dictDataDO.getLabel()); + } + + @Test + public void testGetDictData_dictType() { + // mock 数据 + DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setValue("1"); + dictDataMapper.insert(dictDataDO); + DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setValue("2"); + dictDataMapper.insert(dictDataDO02); + // 准备参数 + String dictType = "yunai"; + String value = "1"; + + // 调用 + DictDataDO dbDictData = dictDataService.getDictData(dictType, value); + // 断言 + assertEquals(dictDataDO, dbDictData); + } + + @Test + public void testParseDictData() { + // mock 数据 + DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setLabel("1"); + dictDataMapper.insert(dictDataDO); + DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setLabel("2"); + dictDataMapper.insert(dictDataDO02); + // 准备参数 + String dictType = "yunai"; + String label = "1"; + + // 调用 + DictDataDO dbDictData = dictDataService.parseDictData(dictType, label); + // 断言 + assertEquals(dictDataDO, dbDictData); + } + + // ========== 随机对象 ========== + + @SafeVarargs + private static DictDataDO randomDictDataDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setStatus(randomCommonStatus()); // 保证 status 的范围 + }; + return randomPojo(DictDataDO.class, ArrayUtils.append(consumer, consumers)); + } + + /** + * 生成一个有效的字典类型 + * + * @param type 字典类型 + * @return DictTypeDO 对象 + */ + private static DictTypeDO randomDictTypeDO(String type) { + return randomPojo(DictTypeDO.class, o -> { + o.setType(type); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启 + }); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImplTest.java new file mode 100644 index 0000000..e04b96f --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImplTest.java @@ -0,0 +1,271 @@ +package cn.iocoder.yudao.module.system.service.dict; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; +import cn.iocoder.yudao.module.system.dal.mysql.dict.DictTypeMapper; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.function.Consumer; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Import(DictTypeServiceImpl.class) +public class DictTypeServiceImplTest extends BaseDbUnitTest { + + @Resource + private DictTypeServiceImpl dictTypeService; + + @Resource + private DictTypeMapper dictTypeMapper; + @MockBean + private DictDataService dictDataService; + + @Test + public void testGetDictTypePage() { + // mock 数据 + DictTypeDO dbDictType = randomPojo(DictTypeDO.class, o -> { // 等会查询到 + o.setName("yunai"); + o.setType("芋艿"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2021, 1, 15)); + }); + dictTypeMapper.insert(dbDictType); + // 测试 name 不匹配 + dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setName("tudou"))); + // 测试 type 不匹配 + dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setType("土豆"))); + // 测试 status 不匹配 + dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1)))); + // 准备参数 + DictTypePageReqVO reqVO = new DictTypePageReqVO(); + reqVO.setName("nai"); + reqVO.setType("艿"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2021, 1, 10, 2021, 1, 20)); + + // 调用 + PageResult pageResult = dictTypeService.getDictTypePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbDictType, pageResult.getList().get(0)); + } + + @Test + public void testGetDictType_id() { + // mock 数据 + DictTypeDO dbDictType = randomDictTypeDO(); + dictTypeMapper.insert(dbDictType); + // 准备参数 + Long id = dbDictType.getId(); + + // 调用 + DictTypeDO dictType = dictTypeService.getDictType(id); + // 断言 + assertNotNull(dictType); + assertPojoEquals(dbDictType, dictType); + } + + @Test + public void testGetDictType_type() { + // mock 数据 + DictTypeDO dbDictType = randomDictTypeDO(); + dictTypeMapper.insert(dbDictType); + // 准备参数 + String type = dbDictType.getType(); + + // 调用 + DictTypeDO dictType = dictTypeService.getDictType(type); + // 断言 + assertNotNull(dictType); + assertPojoEquals(dbDictType, dictType); + } + + @Test + public void testCreateDictType_success() { + // 准备参数 + DictTypeSaveReqVO reqVO = randomPojo(DictTypeSaveReqVO.class, + o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus())) + .setId(null); // 避免 id 被赋值 + + // 调用 + Long dictTypeId = dictTypeService.createDictType(reqVO); + // 断言 + assertNotNull(dictTypeId); + // 校验记录的属性是否正确 + DictTypeDO dictType = dictTypeMapper.selectById(dictTypeId); + assertPojoEquals(reqVO, dictType, "id"); + } + + @Test + public void testUpdateDictType_success() { + // mock 数据 + DictTypeDO dbDictType = randomDictTypeDO(); + dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 + // 准备参数 + DictTypeSaveReqVO reqVO = randomPojo(DictTypeSaveReqVO.class, o -> { + o.setId(dbDictType.getId()); // 设置更新的 ID + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); + }); + + // 调用 + dictTypeService.updateDictType(reqVO); + // 校验是否更新正确 + DictTypeDO dictType = dictTypeMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, dictType); + } + + @Test + public void testDeleteDictType_success() { + // mock 数据 + DictTypeDO dbDictType = randomDictTypeDO(); + dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbDictType.getId(); + + // 调用 + dictTypeService.deleteDictType(id); + // 校验数据不存在了 + assertNull(dictTypeMapper.selectById(id)); + } + + @Test + public void testDeleteDictType_hasChildren() { + // mock 数据 + DictTypeDO dbDictType = randomDictTypeDO(); + dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbDictType.getId(); + // mock 方法 + when(dictDataService.getDictDataCountByDictType(eq(dbDictType.getType()))).thenReturn(1L); + + // 调用, 并断言异常 + assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN); + } + + @Test + public void testGetDictTypeList() { + // 准备参数 + DictTypeDO dictTypeDO01 = randomDictTypeDO(); + dictTypeMapper.insert(dictTypeDO01); + DictTypeDO dictTypeDO02 = randomDictTypeDO(); + dictTypeMapper.insert(dictTypeDO02); + // mock 方法 + + // 调用 + List dictTypeDOList = dictTypeService.getDictTypeList(); + // 断言 + assertEquals(2, dictTypeDOList.size()); + assertPojoEquals(dictTypeDO01, dictTypeDOList.get(0)); + assertPojoEquals(dictTypeDO02, dictTypeDOList.get(1)); + } + + @Test + public void testValidateDictDataExists_success() { + // mock 数据 + DictTypeDO dbDictType = randomDictTypeDO(); + dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 + + // 调用成功 + dictTypeService.validateDictTypeExists(dbDictType.getId()); + } + + @Test + public void testValidateDictDataExists_notExists() { + assertServiceException(() -> dictTypeService.validateDictTypeExists(randomLongId()), DICT_TYPE_NOT_EXISTS); + } + + @Test + public void testValidateDictTypeUnique_success() { + // 调用,成功 + dictTypeService.validateDictTypeUnique(randomLongId(), randomString()); + } + + @Test + public void testValidateDictTypeUnique_valueDuplicateForCreate() { + // 准备参数 + String type = randomString(); + // mock 数据 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type))); + + // 调用,校验异常 + assertServiceException(() -> dictTypeService.validateDictTypeUnique(null, type), + DICT_TYPE_TYPE_DUPLICATE); + } + + @Test + public void testValidateDictTypeUnique_valueDuplicateForUpdate() { + // 准备参数 + Long id = randomLongId(); + String type = randomString(); + // mock 数据 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type))); + + // 调用,校验异常 + assertServiceException(() -> dictTypeService.validateDictTypeUnique(id, type), + DICT_TYPE_TYPE_DUPLICATE); + } + + @Test + public void testValidateDictTypNameUnique_success() { + // 调用,成功 + dictTypeService.validateDictTypeNameUnique(randomLongId(), randomString()); + } + + @Test + public void testValidateDictTypeNameUnique_nameDuplicateForCreate() { + // 准备参数 + String name = randomString(); + // mock 数据 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); + + // 调用,校验异常 + assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(null, name), + DICT_TYPE_NAME_DUPLICATE); + } + + @Test + public void testValidateDictTypeNameUnique_nameDuplicateForUpdate() { + // 准备参数 + Long id = randomLongId(); + String name = randomString(); + // mock 数据 + dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); + + // 调用,校验异常 + assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(id, name), + DICT_TYPE_NAME_DUPLICATE); + } + + // ========== 随机对象 ========== + + @SafeVarargs + private static DictTypeDO randomDictTypeDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + }; + return randomPojo(DictTypeDO.class, ArrayUtils.append(consumer, consumers)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImplTest.java new file mode 100644 index 0000000..f5135da --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImplTest.java @@ -0,0 +1,76 @@ +package cn.iocoder.yudao.module.system.service.logger; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; +import cn.iocoder.yudao.module.system.dal.mysql.logger.LoginLogMapper; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.CAPTCHA_CODE_ERROR; +import static cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.SUCCESS; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Import(LoginLogServiceImpl.class) +public class LoginLogServiceImplTest extends BaseDbUnitTest { + + @Resource + private LoginLogServiceImpl loginLogService; + + @Resource + private LoginLogMapper loginLogMapper; + + @Test + public void testGetLoginLogPage() { + // mock 数据 + LoginLogDO loginLogDO = randomPojo(LoginLogDO.class, o -> { + o.setUserIp("192.168.199.16"); + o.setUsername("wang"); + o.setResult(SUCCESS.getResult()); + o.setCreateTime(buildTime(2021, 3, 6)); + }); + loginLogMapper.insert(loginLogDO); + // 测试 status 不匹配 + loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setResult(CAPTCHA_CODE_ERROR.getResult()))); + // 测试 ip 不匹配 + loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setUserIp("192.168.128.18"))); + // 测试 username 不匹配 + loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setUsername("yunai"))); + // 测试 createTime 不匹配 + loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setCreateTime(buildTime(2021, 2, 6)))); + // 构造调用参数 + LoginLogPageReqVO reqVO = new LoginLogPageReqVO(); + reqVO.setUsername("wang"); + reqVO.setUserIp("192.168.199"); + reqVO.setStatus(true); + reqVO.setCreateTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7)); + + // 调用 + PageResult pageResult = loginLogService.getLoginLogPage(reqVO); + // 断言,只查到了一条符合条件的 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(loginLogDO, pageResult.getList().get(0)); + } + + @Test + public void testCreateLoginLog() { + LoginLogCreateReqDTO reqDTO = randomPojo(LoginLogCreateReqDTO.class); + + // 调用 + loginLogService.createLoginLog(reqDTO); + // 断言 + LoginLogDO loginLogDO = loginLogMapper.selectOne(null); + assertPojoEquals(reqDTO, loginLogDO); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java new file mode 100644 index 0000000..6951b69 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java @@ -0,0 +1,113 @@ +package cn.iocoder.yudao.module.system.service.logger; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.framework.test.core.util.RandomUtils; +import cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO; +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; +import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; +import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Import({OperateLogServiceImpl.class}) +public class OperateLogServiceImplTest extends BaseDbUnitTest { + + @Resource + private OperateLogService operateLogServiceImpl; + + @Resource + private OperateLogMapper operateLogMapper; + + @Test + public void testCreateOperateLog() { + OperateLogCreateReqDTO reqVO = RandomUtils.randomPojo(OperateLogCreateReqDTO.class); + + // 调研 + operateLogServiceImpl.createOperateLog(reqVO); + // 断言 + OperateLogDO operateLogDO = operateLogMapper.selectOne(null); + assertPojoEquals(reqVO, operateLogDO); + } + + @Test + public void testGetOperateLogPage_vo() { + // 构造操作日志 + OperateLogDO operateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> { + o.setUserId(2048L); + o.setBizId(999L); + o.setType("订单"); + o.setSubType("创建订单"); + o.setAction("修改编号为 1 的用户信息"); + o.setCreateTime(buildTime(2021, 3, 6)); + }); + operateLogMapper.insert(operateLogDO); + // 测试 userId 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(1024L))); + // 测试 bizId 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setBizId(888L))); + // 测试 type 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType("退款"))); + // 测试 subType 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setSubType("创建退款"))); + // 测试 action 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setAction("修改编号为 1 退款信息"))); + // 测试 createTime 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setCreateTime(buildTime(2021, 2, 6)))); + + // 构造调用参数 + OperateLogPageReqVO reqVO = new OperateLogPageReqVO(); + reqVO.setUserId(2048L); + reqVO.setBizId(999L); + reqVO.setType("订"); + reqVO.setSubType("订单"); + reqVO.setAction("用户信息"); + reqVO.setCreateTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7)); + + // 调用 + PageResult pageResult = operateLogServiceImpl.getOperateLogPage(reqVO); + // 断言,只查到了一条符合条件的 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(operateLogDO, pageResult.getList().get(0)); + } + + @Test + public void testGetOperateLogPage_dto() { + // 构造操作日志 + OperateLogDO operateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> { + o.setUserId(2048L); + o.setBizId(999L); + o.setType("订单"); + }); + operateLogMapper.insert(operateLogDO); + // 测试 userId 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(1024L))); + // 测试 bizId 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setBizId(888L))); + // 测试 type 不匹配 + operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType("退款"))); + + // 构造调用参数 + OperateLogPageReqDTO reqDTO = new OperateLogPageReqDTO(); + reqDTO.setUserId(2048L); + reqDTO.setBizId(999L); + reqDTO.setType("订单"); + + // 调用 + PageResult pageResult = operateLogServiceImpl.getOperateLogPage(reqDTO); + // 断言,只查到了一条符合条件的 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(operateLogDO, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java new file mode 100755 index 0000000..a3f237e --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java @@ -0,0 +1,179 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +/** + * {@link MailAccountServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(MailAccountServiceImpl.class) +public class MailAccountServiceImplTest extends BaseDbUnitTest { + + @Resource + private MailAccountServiceImpl mailAccountService; + + @Resource + private MailAccountMapper mailAccountMapper; + + @MockBean + private MailTemplateService mailTemplateService; + + @Test + public void testCreateMailAccount_success() { + // 准备参数 + MailAccountSaveReqVO reqVO = randomPojo(MailAccountSaveReqVO.class, o -> o.setMail(randomEmail())) + .setId(null); // 防止 id 被赋值 + + // 调用 + Long mailAccountId = mailAccountService.createMailAccount(reqVO); + // 断言 + assertNotNull(mailAccountId); + // 校验记录的属性是否正确 + MailAccountDO mailAccount = mailAccountMapper.selectById(mailAccountId); + assertPojoEquals(reqVO, mailAccount, "id"); + } + + @Test + public void testUpdateMailAccount_success() { + // mock 数据 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); + mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 + // 准备参数 + MailAccountSaveReqVO reqVO = randomPojo(MailAccountSaveReqVO.class, o -> { + o.setId(dbMailAccount.getId()); // 设置更新的 ID + o.setMail(randomEmail()); + }); + + // 调用 + mailAccountService.updateMailAccount(reqVO); + // 校验是否更新正确 + MailAccountDO mailAccount = mailAccountMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, mailAccount); + } + + @Test + public void testUpdateMailAccount_notExists() { + // 准备参数 + MailAccountSaveReqVO reqVO = randomPojo(MailAccountSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> mailAccountService.updateMailAccount(reqVO), MAIL_ACCOUNT_NOT_EXISTS); + } + + @Test + public void testDeleteMailAccount_success() { + // mock 数据 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); + mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbMailAccount.getId(); + // mock 方法(无关联模版) + when(mailTemplateService.getMailTemplateCountByAccountId(eq(id))).thenReturn(0L); + + // 调用 + mailAccountService.deleteMailAccount(id); + // 校验数据不存在了 + assertNull(mailAccountMapper.selectById(id)); + } + + @Test + public void testGetMailAccountFromCache() { + // mock 数据 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); + mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbMailAccount.getId(); + + // 调用 + MailAccountDO mailAccount = mailAccountService.getMailAccountFromCache(id); + // 断言 + assertPojoEquals(dbMailAccount, mailAccount); + } + + @Test + public void testDeleteMailAccount_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> mailAccountService.deleteMailAccount(id), MAIL_ACCOUNT_NOT_EXISTS); + } + + @Test + public void testGetMailAccountPage() { + // mock 数据 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到 + o.setMail("768@qq.com"); + o.setUsername("yunai"); + }); + mailAccountMapper.insert(dbMailAccount); + // 测试 mail 不匹配 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com"))); + // 测试 username 不匹配 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou"))); + // 准备参数 + MailAccountPageReqVO reqVO = new MailAccountPageReqVO(); + reqVO.setMail("768"); + reqVO.setUsername("yu"); + + // 调用 + PageResult pageResult = mailAccountService.getMailAccountPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbMailAccount, pageResult.getList().get(0)); + } + + @Test + public void testGetMailAccount() { + // mock 数据 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); + mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbMailAccount.getId(); + + // 调用 + MailAccountDO mailAccount = mailAccountService.getMailAccount(id); + // 断言 + assertPojoEquals(dbMailAccount, mailAccount); + } + + @Test + public void testGetMailAccountList() { + // mock 数据 + MailAccountDO dbMailAccount01 = randomPojo(MailAccountDO.class); + mailAccountMapper.insert(dbMailAccount01); + MailAccountDO dbMailAccount02 = randomPojo(MailAccountDO.class); + mailAccountMapper.insert(dbMailAccount02); + // 准备参数 + + // 调用 + List list = mailAccountService.getMailAccountList(); + // 断言 + assertEquals(2, list.size()); + assertPojoEquals(dbMailAccount01, list.get(0)); + assertPojoEquals(dbMailAccount02, list.get(1)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java new file mode 100755 index 0000000..482aa5a --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java @@ -0,0 +1,183 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper; +import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.Map; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link MailLogServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(MailLogServiceImpl.class) +public class MailLogServiceImplTest extends BaseDbUnitTest { + + @Resource + private MailLogServiceImpl mailLogService; + + @Resource + private MailLogMapper mailLogMapper; + + @Test + public void testCreateMailLog() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String toMail = randomEmail(); + MailAccountDO account = randomPojo(MailAccountDO.class); + MailTemplateDO template = randomPojo(MailTemplateDO.class); + String templateContent = randomString(); + Map templateParams = randomTemplateParams(); + Boolean isSend = true; + // mock 方法 + + // 调用 + Long logId = mailLogService.createMailLog(userId, userType, toMail, account, template, templateContent, templateParams, isSend); + // 断言 + MailLogDO log = mailLogMapper.selectById(logId); + assertNotNull(log); + assertEquals(MailSendStatusEnum.INIT.getStatus(), log.getSendStatus()); + assertEquals(userId, log.getUserId()); + assertEquals(userType, log.getUserType()); + assertEquals(toMail, log.getToMail()); + assertEquals(account.getId(), log.getAccountId()); + assertEquals(account.getMail(), log.getFromMail()); + assertEquals(template.getId(), log.getTemplateId()); + assertEquals(template.getCode(), log.getTemplateCode()); + assertEquals(template.getNickname(), log.getTemplateNickname()); + assertEquals(template.getTitle(), log.getTemplateTitle()); + assertEquals(templateContent, log.getTemplateContent()); + assertEquals(templateParams, log.getTemplateParams()); + } + + @Test + public void testUpdateMailSendResult_success() { + // mock 数据 + MailLogDO log = randomPojo(MailLogDO.class, o -> { + o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); + o.setSendTime(null).setSendMessageId(null).setSendException(null) + .setTemplateParams(randomTemplateParams()); + }); + mailLogMapper.insert(log); + // 准备参数 + Long logId = log.getId(); + String messageId = randomString(); + + // 调用 + mailLogService.updateMailSendResult(logId, messageId, null); + // 断言 + MailLogDO dbLog = mailLogMapper.selectById(logId); + assertEquals(MailSendStatusEnum.SUCCESS.getStatus(), dbLog.getSendStatus()); + assertNotNull(dbLog.getSendTime()); + assertEquals(messageId, dbLog.getSendMessageId()); + assertNull(dbLog.getSendException()); + } + + @Test + public void testUpdateMailSendResult_exception() { + // mock 数据 + MailLogDO log = randomPojo(MailLogDO.class, o -> { + o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); + o.setSendTime(null).setSendMessageId(null).setSendException(null) + .setTemplateParams(randomTemplateParams()); + }); + mailLogMapper.insert(log); + // 准备参数 + Long logId = log.getId(); + Exception exception = new NullPointerException("测试异常"); + + // 调用 + mailLogService.updateMailSendResult(logId, null, exception); + // 断言 + MailLogDO dbLog = mailLogMapper.selectById(logId); + assertEquals(MailSendStatusEnum.FAILURE.getStatus(), dbLog.getSendStatus()); + assertNotNull(dbLog.getSendTime()); + assertNull(dbLog.getSendMessageId()); + assertEquals("NullPointerException: 测试异常", dbLog.getSendException()); + } + + @Test + public void testGetMailLog() { + // mock 数据 + MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> o.setTemplateParams(randomTemplateParams())); + mailLogMapper.insert(dbMailLog); + // 准备参数 + Long id = dbMailLog.getId(); + + // 调用 + MailLogDO mailLog = mailLogService.getMailLog(id); + // 断言 + assertPojoEquals(dbMailLog, mailLog); + } + + @Test + public void testGetMailLogPage() { + // mock 数据 + MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> { // 等会查询到 + o.setUserId(1L); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setToMail("768@qq.com"); + o.setAccountId(10L); + o.setTemplateId(100L); + o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); + o.setSendTime(buildTime(2023, 2, 10)); + o.setTemplateParams(randomTemplateParams()); + }); + mailLogMapper.insert(dbMailLog); + // 测试 userId 不匹配 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserId(2L))); + // 测试 userType 不匹配 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 toMail 不匹配 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setToMail("788@.qq.com"))); + // 测试 accountId 不匹配 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setAccountId(11L))); + // 测试 templateId 不匹配 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setTemplateId(101L))); + // 测试 sendStatus 不匹配 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setSendStatus(MailSendStatusEnum.SUCCESS.getStatus()))); + // 测试 sendTime 不匹配 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setSendTime(buildTime(2023, 3, 10)))); + // 准备参数 + MailLogPageReqVO reqVO = new MailLogPageReqVO(); + reqVO.setUserId(1L); + reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); + reqVO.setToMail("768"); + reqVO.setAccountId(10L); + reqVO.setTemplateId(100L); + reqVO.setSendStatus(MailSendStatusEnum.INIT.getStatus()); + reqVO.setSendTime((buildBetweenTime(2023, 2, 1, 2023, 2, 15))); + + // 调用 + PageResult pageResult = mailLogService.getMailLogPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbMailLog, pageResult.getList().get(0)); + } + + private static Map randomTemplateParams() { + return MapUtil.builder().put(randomString(), randomString()) + .put(randomString(), randomString()).build(); + } +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java new file mode 100644 index 0000000..1ef1e12 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java @@ -0,0 +1,329 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.framework.test.core.util.RandomUtils; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; +import cn.iocoder.yudao.module.system.service.member.MemberService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import org.assertj.core.util.Lists; +import org.dromara.hutool.extra.mail.*; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; + +import java.util.HashMap; +import java.util.Map; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +public class MailSendServiceImplTest extends BaseMockitoUnitTest { + + @InjectMocks + private MailSendServiceImpl mailSendService; + + @Mock + private AdminUserService adminUserService; + @Mock + private MemberService memberService; + @Mock + private MailAccountService mailAccountService; + @Mock + private MailTemplateService mailTemplateService; + @Mock + private MailLogService mailLogService; + @Mock + private MailProducer mailProducer; + + /** + * 用于快速测试你的邮箱账号是否正常 + */ + @Test + @Disabled + public void testDemo() { + MailAccount mailAccount = new MailAccount() +// .setFrom("奥特曼 ") + .setFrom("ydym_test@163.com") // 邮箱地址 + .setHost("smtp.163.com").setPort(465).setSslEnable(true) // SMTP 服务器 + .setAuth(true).setUser("ydym_test@163.com").setPass("WBZTEINMIFVRYSOE".toCharArray()); // 登录账号密码 + String messageId = MailUtil.send(mailAccount, "7685413@qq.com", "主题", "内容", false); + System.out.println("发送结果:" + messageId); + } + + @Test + public void testSendSingleMailToAdmin() { + // 准备参数 + Long userId = randomLongId(); + String templateCode = RandomUtils.randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock adminUserService 的方法 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setMobile("15601691300")); + when(adminUserService.getUser(eq(userId))).thenReturn(user); + + // mock MailTemplateService 的方法 + MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String title = RandomUtils.randomString(); + when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) + .thenReturn(title); + String content = RandomUtils.randomString(); + when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock MailAccountService 的方法 + MailAccountDO account = randomPojo(MailAccountDO.class); + when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); + // mock MailLogService 的方法 + Long mailLogId = randomLongId(); + when(mailLogService.createMailLog(eq(userId), eq(UserTypeEnum.ADMIN.getValue()), eq(user.getEmail()), + eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); + + // 调用 + Long resultMailLogId = mailSendService.sendSingleMailToAdmin(null, userId, templateCode, templateParams); + // 断言 + assertEquals(mailLogId, resultMailLogId); + // 断言调用 + verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(user.getEmail()), + eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); + } + + @Test + public void testSendSingleMailToMember() { + // 准备参数 + Long userId = randomLongId(); + String templateCode = RandomUtils.randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock memberService 的方法 + String mail = randomEmail(); + when(memberService.getMemberUserEmail(eq(userId))).thenReturn(mail); + + // mock MailTemplateService 的方法 + MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String title = RandomUtils.randomString(); + when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) + .thenReturn(title); + String content = RandomUtils.randomString(); + when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock MailAccountService 的方法 + MailAccountDO account = randomPojo(MailAccountDO.class); + when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); + // mock MailLogService 的方法 + Long mailLogId = randomLongId(); + when(mailLogService.createMailLog(eq(userId), eq(UserTypeEnum.MEMBER.getValue()), eq(mail), + eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); + + // 调用 + Long resultMailLogId = mailSendService.sendSingleMailToMember(null, userId, templateCode, templateParams); + // 断言 + assertEquals(mailLogId, resultMailLogId); + // 断言调用 + verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(mail), + eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); + } + + /** + * 发送成功,当短信模板开启时 + */ + @Test + public void testSendSingleMail_successWhenMailTemplateEnable() { + // 准备参数 + String mail = randomEmail(); + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String templateCode = RandomUtils.randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock MailTemplateService 的方法 + MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String title = RandomUtils.randomString(); + when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) + .thenReturn(title); + String content = RandomUtils.randomString(); + when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock MailAccountService 的方法 + MailAccountDO account = randomPojo(MailAccountDO.class); + when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); + // mock MailLogService 的方法 + Long mailLogId = randomLongId(); + when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail), + eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); + + // 调用 + Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams); + // 断言 + assertEquals(mailLogId, resultMailLogId); + // 断言调用 + verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(mail), + eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); + } + + /** + * 发送成功,当短信模板关闭时 + */ + @Test + public void testSendSingleMail_successWhenSmsTemplateDisable() { + // 准备参数 + String mail = randomEmail(); + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String templateCode = RandomUtils.randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock MailTemplateService 的方法 + MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String title = RandomUtils.randomString(); + when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) + .thenReturn(title); + String content = RandomUtils.randomString(); + when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock MailAccountService 的方法 + MailAccountDO account = randomPojo(MailAccountDO.class); + when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); + // mock MailLogService 的方法 + Long mailLogId = randomLongId(); + when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail), + eq(account), eq(template), eq(content), eq(templateParams), eq(false))).thenReturn(mailLogId); + + // 调用 + Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams); + // 断言 + assertEquals(mailLogId, resultMailLogId); + // 断言调用 + verify(mailProducer, times(0)).sendMailSendMessage(anyLong(), anyString(), + anyLong(), anyString(), anyString(), anyString()); + } + + @Test + public void testValidateMailTemplateValid_notExists() { + // 准备参数 + String templateCode = RandomUtils.randomString(); + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> mailSendService.validateMailTemplate(templateCode), + MAIL_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testValidateTemplateParams_paramMiss() { + // 准备参数 + MailTemplateDO template = randomPojo(MailTemplateDO.class, + o -> o.setParams(Lists.newArrayList("code"))); + Map templateParams = new HashMap<>(); + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> mailSendService.validateTemplateParams(template, templateParams), + MAIL_SEND_TEMPLATE_PARAM_MISS, "code"); + } + + @Test + public void testValidateMail_notExists() { + // 准备参数 + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> mailSendService.validateMail(null), + MAIL_SEND_MAIL_NOT_EXISTS); + } + + @Test + public void testDoSendMail_success() { + try (final MockedStatic mailUtilMock = mockStatic(MailUtil.class)) { + // 准备参数 + MailSendMessage message = randomPojo(MailSendMessage.class, o -> o.setNickname("芋艿")); + // mock 方法(获得邮箱账号) + MailAccountDO account = randomPojo(MailAccountDO.class, o -> o.setMail("7685@qq.com")); + when(mailAccountService.getMailAccountFromCache(eq(message.getAccountId()))) + .thenReturn(account); + + // mock 方法(发送邮件) + String messageId = randomString(); + mailUtilMock.when(() -> MailUtil.send( + argThat(mailAccount -> { + assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom()); + assertTrue(mailAccount.isAuth()); + assertEquals(account.getUsername(), mailAccount.getUser()); + assertArrayEquals(account.getPassword().toCharArray(), mailAccount.getPass()); + assertEquals(account.getHost(), mailAccount.getHost()); + assertEquals(account.getPort(), mailAccount.getPort()); + assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); + return true; + }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))) + .thenReturn(messageId); + + // 调用 + mailSendService.doSendMail(message); + // 断言 + verify(mailLogService).updateMailSendResult(eq(message.getLogId()), eq(messageId), isNull()); + } + } + + @Test + public void testDoSendMail_exception() { + try (MockedStatic mailUtilMock = mockStatic(MailUtil.class)) { + // 准备参数 + MailSendMessage message = randomPojo(MailSendMessage.class, o -> o.setNickname("芋艿")); + // mock 方法(获得邮箱账号) + MailAccountDO account = randomPojo(MailAccountDO.class, o -> o.setMail("7685@qq.com")); + when(mailAccountService.getMailAccountFromCache(eq(message.getAccountId()))) + .thenReturn(account); + + // mock 方法(发送邮件) + Exception e = new NullPointerException("啦啦啦"); + mailUtilMock.when(() -> MailUtil.send(argThat(mailAccount -> { + assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom()); + assertTrue(mailAccount.isAuth()); + assertEquals(account.getUsername(), mailAccount.getUser()); + assertArrayEquals(account.getPassword().toCharArray(), mailAccount.getPass()); + assertEquals(account.getHost(), mailAccount.getHost()); + assertEquals(account.getPort(), mailAccount.getPort()); + assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); + return true; + }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))).thenThrow(e); + + // 调用 + mailSendService.doSendMail(message); + // 断言 + verify(mailLogService).updateMailSendResult(eq(message.getLogId()), isNull(), same(e)); + } + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java new file mode 100755 index 0000000..70059b2 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java @@ -0,0 +1,215 @@ +package cn.iocoder.yudao.module.system.service.mail; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateSaveReqVO; +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link MailTemplateServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(MailTemplateServiceImpl.class) +public class MailTemplateServiceImplTest extends BaseDbUnitTest { + + @Resource + private MailTemplateServiceImpl mailTemplateService; + + @Resource + private MailTemplateMapper mailTemplateMapper; + + @Test + public void testCreateMailTemplate_success() { + // 准备参数 + MailTemplateSaveReqVO reqVO = randomPojo(MailTemplateSaveReqVO.class) + .setId(null); // 防止 id 被赋值 + + // 调用 + Long mailTemplateId = mailTemplateService.createMailTemplate(reqVO); + // 断言 + assertNotNull(mailTemplateId); + // 校验记录的属性是否正确 + MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId); + assertPojoEquals(reqVO, mailTemplate, "id"); + } + + @Test + public void testUpdateMailTemplate_success() { + // mock 数据 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); + mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + MailTemplateSaveReqVO reqVO = randomPojo(MailTemplateSaveReqVO.class, o -> { + o.setId(dbMailTemplate.getId()); // 设置更新的 ID + }); + + // 调用 + mailTemplateService.updateMailTemplate(reqVO); + // 校验是否更新正确 + MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, mailTemplate); + } + + @Test + public void testUpdateMailTemplate_notExists() { + // 准备参数 + MailTemplateSaveReqVO reqVO = randomPojo(MailTemplateSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> mailTemplateService.updateMailTemplate(reqVO), MAIL_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testDeleteMailTemplate_success() { + // mock 数据 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); + mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbMailTemplate.getId(); + + // 调用 + mailTemplateService.deleteMailTemplate(id); + // 校验数据不存在了 + assertNull(mailTemplateMapper.selectById(id)); + } + + @Test + public void testDeleteMailTemplate_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> mailTemplateService.deleteMailTemplate(id), MAIL_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testGetMailTemplatePage() { + // mock 数据 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到 + o.setName("源码"); + o.setCode("test_01"); + o.setAccountId(1L); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2023, 2, 3)); + }); + mailTemplateMapper.insert(dbMailTemplate); + // 测试 name 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道"))); + // 测试 code 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02"))); + // 测试 accountId 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); + // 测试 status 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5)))); + // 准备参数 + MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO(); + reqVO.setName("源"); + reqVO.setCode("est_01"); + reqVO.setAccountId(1L); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5)); + + // 调用 + PageResult pageResult = mailTemplateService.getMailTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbMailTemplate, pageResult.getList().get(0)); + } + + @Test + public void testGetMailTemplateList() { + // mock 数据 + MailTemplateDO dbMailTemplate01 = randomPojo(MailTemplateDO.class); + mailTemplateMapper.insert(dbMailTemplate01); + MailTemplateDO dbMailTemplate02 = randomPojo(MailTemplateDO.class); + mailTemplateMapper.insert(dbMailTemplate02); + + // 调用 + List list = mailTemplateService.getMailTemplateList(); + // 断言 + assertEquals(2, list.size()); + assertEquals(dbMailTemplate01, list.get(0)); + assertEquals(dbMailTemplate02, list.get(1)); + } + + @Test + public void testGetMailTemplate() { + // mock 数据 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); + mailTemplateMapper.insert(dbMailTemplate); + // 准备参数 + Long id = dbMailTemplate.getId(); + + // 调用 + MailTemplateDO mailTemplate = mailTemplateService.getMailTemplate(id); + // 断言 + assertPojoEquals(dbMailTemplate, mailTemplate); + } + + @Test + public void testGetMailTemplateByCodeFromCache() { + // mock 数据 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); + mailTemplateMapper.insert(dbMailTemplate); + // 准备参数 + String code = dbMailTemplate.getCode(); + + // 调用 + MailTemplateDO mailTemplate = mailTemplateService.getMailTemplateByCodeFromCache(code); + // 断言 + assertPojoEquals(dbMailTemplate, mailTemplate); + } + + @Test + public void testFormatMailTemplateContent() { + // 准备参数 + Map params = new HashMap<>(); + params.put("name", "小红"); + params.put("what", "饭"); + + // 调用,并断言 + assertEquals("小红,你好,饭吃了吗?", + mailTemplateService.formatMailTemplateContent("{name},你好,{what}吃了吗?", params)); + } + + @Test + public void testCountByAccountId() { + // mock 数据 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); + mailTemplateMapper.insert(dbMailTemplate); + // 测试 accountId 不匹配 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); + // 准备参数 + Long accountId = dbMailTemplate.getAccountId(); + + // 调用 + long count = mailTemplateService.getMailTemplateCountByAccountId(accountId); + // 断言 + assertEquals(1, count); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImplTest.java new file mode 100644 index 0000000..7b40af3 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImplTest.java @@ -0,0 +1,130 @@ +package cn.iocoder.yudao.module.system.service.notice; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; +import cn.iocoder.yudao.module.system.dal.mysql.notice.NoticeMapper; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND; +import static org.junit.jupiter.api.Assertions.*; + +@Import(NoticeServiceImpl.class) +class NoticeServiceImplTest extends BaseDbUnitTest { + + @Resource + private NoticeServiceImpl noticeService; + + @Resource + private NoticeMapper noticeMapper; + + @Test + public void testGetNoticePage_success() { + // 插入前置数据 + NoticeDO dbNotice = randomPojo(NoticeDO.class, o -> { + o.setTitle("尼古拉斯赵四来啦!"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + noticeMapper.insert(dbNotice); + // 测试 title 不匹配 + noticeMapper.insert(cloneIgnoreId(dbNotice, o -> o.setTitle("尼古拉斯凯奇也来啦!"))); + // 测试 status 不匹配 + noticeMapper.insert(cloneIgnoreId(dbNotice, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 准备参数 + NoticePageReqVO reqVO = new NoticePageReqVO(); + reqVO.setTitle("尼古拉斯赵四来啦!"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + PageResult pageResult = noticeService.getNoticePage(reqVO); + // 验证查询结果经过筛选 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbNotice, pageResult.getList().get(0)); + } + + @Test + public void testGetNotice_success() { + // 插入前置数据 + NoticeDO dbNotice = randomPojo(NoticeDO.class); + noticeMapper.insert(dbNotice); + + // 查询 + NoticeDO notice = noticeService.getNotice(dbNotice.getId()); + + // 验证插入与读取对象是否一致 + assertNotNull(notice); + assertPojoEquals(dbNotice, notice); + } + + @Test + public void testCreateNotice_success() { + // 准备参数 + NoticeSaveReqVO reqVO = randomPojo(NoticeSaveReqVO.class) + .setId(null); // 避免 id 被赋值 + + // 调用 + Long noticeId = noticeService.createNotice(reqVO); + // 校验插入属性是否正确 + assertNotNull(noticeId); + NoticeDO notice = noticeMapper.selectById(noticeId); + assertPojoEquals(reqVO, notice, "id"); + } + + @Test + public void testUpdateNotice_success() { + // 插入前置数据 + NoticeDO dbNoticeDO = randomPojo(NoticeDO.class); + noticeMapper.insert(dbNoticeDO); + + // 准备更新参数 + NoticeSaveReqVO reqVO = randomPojo(NoticeSaveReqVO.class, o -> o.setId(dbNoticeDO.getId())); + + // 更新 + noticeService.updateNotice(reqVO); + // 检验是否更新成功 + NoticeDO notice = noticeMapper.selectById(reqVO.getId()); + assertPojoEquals(reqVO, notice); + } + + @Test + public void testDeleteNotice_success() { + // 插入前置数据 + NoticeDO dbNotice = randomPojo(NoticeDO.class); + noticeMapper.insert(dbNotice); + + // 删除 + noticeService.deleteNotice(dbNotice.getId()); + + // 检查是否删除成功 + assertNull(noticeMapper.selectById(dbNotice.getId())); + } + + @Test + public void testValidateNoticeExists_success() { + // 插入前置数据 + NoticeDO dbNotice = randomPojo(NoticeDO.class); + noticeMapper.insert(dbNotice); + + // 成功调用 + noticeService.validateNoticeExists(dbNotice.getId()); + } + + @Test + public void testValidateNoticeExists_noExists() { + assertServiceException(() -> + noticeService.validateNoticeExists(randomLongId()), NOTICE_NOT_FOUND); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java new file mode 100644 index 0000000..cfd193b --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java @@ -0,0 +1,276 @@ +package cn.iocoder.yudao.module.system.service.notify; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.*; + +/** +* {@link NotifyMessageServiceImpl} 的单元测试类 +* +* @author 芋道源码 +*/ +@Import(NotifyMessageServiceImpl.class) +public class NotifyMessageServiceImplTest extends BaseDbUnitTest { + + @Resource + private NotifyMessageServiceImpl notifyMessageService; + + @Resource + private NotifyMessageMapper notifyMessageMapper; + + @Test + public void testCreateNotifyMessage_success() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class); + String templateContent = randomString(); + Map templateParams = randomTemplateParams(); + // mock 方法 + + // 调用 + Long messageId = notifyMessageService.createNotifyMessage(userId, userType, + template, templateContent, templateParams); + // 断言 + NotifyMessageDO message = notifyMessageMapper.selectById(messageId); + assertNotNull(message); + assertEquals(userId, message.getUserId()); + assertEquals(userType, message.getUserType()); + assertEquals(template.getId(), message.getTemplateId()); + assertEquals(template.getCode(), message.getTemplateCode()); + assertEquals(template.getType(), message.getTemplateType()); + assertEquals(template.getNickname(), message.getTemplateNickname()); + assertEquals(templateContent, message.getTemplateContent()); + assertEquals(templateParams, message.getTemplateParams()); + assertEquals(false, message.getReadStatus()); + assertNull(message.getReadTime()); + } + + @Test + public void testGetNotifyMessagePage() { + // mock 数据 + NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 + o.setUserId(1L); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setTemplateCode("test_01"); + o.setTemplateType(10); + o.setCreateTime(buildTime(2022, 1, 2)); + o.setTemplateParams(randomTemplateParams()); + }); + notifyMessageMapper.insert(dbNotifyMessage); + // 测试 userId 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); + // 测试 userType 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 templateCode 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateCode("test_11"))); + // 测试 templateType 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateType(20))); + // 测试 createTime 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1)))); + // 准备参数 + NotifyMessagePageReqVO reqVO = new NotifyMessagePageReqVO(); + reqVO.setUserId(1L); + reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); + reqVO.setTemplateCode("est_01"); + reqVO.setTemplateType(10); + reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10)); + + // 调用 + PageResult pageResult = notifyMessageService.getNotifyMessagePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0)); + } + + @Test + public void testGetNotifyMessage() { + // mock 数据 + NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, + o -> o.setTemplateParams(randomTemplateParams())); + notifyMessageMapper.insert(dbNotifyMessage); + // 准备参数 + Long id = dbNotifyMessage.getId(); + + // 调用 + NotifyMessageDO notifyMessage = notifyMessageService.getNotifyMessage(id); + assertPojoEquals(dbNotifyMessage, notifyMessage); + } + + @Test + public void testGetMyNotifyMessagePage() { + // mock 数据 + NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 + o.setUserId(1L); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setReadStatus(true); + o.setCreateTime(buildTime(2022, 1, 2)); + o.setTemplateParams(randomTemplateParams()); + }); + notifyMessageMapper.insert(dbNotifyMessage); + // 测试 userId 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); + // 测试 userType 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 readStatus 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(false))); + // 测试 createTime 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1)))); + // 准备参数 + Long userId = 1L; + Integer userType = UserTypeEnum.ADMIN.getValue(); + NotifyMessageMyPageReqVO reqVO = new NotifyMessageMyPageReqVO(); + reqVO.setReadStatus(true); + reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10)); + + // 调用 + PageResult pageResult = notifyMessageService.getMyMyNotifyMessagePage(reqVO, userId, userType); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0)); + } + + @Test + public void testGetUnreadNotifyMessageList() { + // mock 数据 + NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 + o.setUserId(1L); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setReadStatus(false); + o.setTemplateParams(randomTemplateParams()); + }); + notifyMessageMapper.insert(dbNotifyMessage); + // 测试 userId 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); + // 测试 userType 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 readStatus 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); + // 准备参数 + Long userId = 1L; + Integer userType = UserTypeEnum.ADMIN.getValue(); + Integer size = 10; + + // 调用 + List list = notifyMessageService.getUnreadNotifyMessageList(userId, userType, size); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbNotifyMessage, list.get(0)); + } + + @Test + public void testGetUnreadNotifyMessageCount() { + // mock 数据 + NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 + o.setUserId(1L); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setReadStatus(false); + o.setTemplateParams(randomTemplateParams()); + }); + notifyMessageMapper.insert(dbNotifyMessage); + // 测试 userId 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); + // 测试 userType 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 readStatus 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); + // 准备参数 + Long userId = 1L; + Integer userType = UserTypeEnum.ADMIN.getValue(); + + // 调用,并断言 + assertEquals(1, notifyMessageService.getUnreadNotifyMessageCount(userId, userType)); + } + + @Test + public void testUpdateNotifyMessageRead() { + // mock 数据 + NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 + o.setUserId(1L); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setReadStatus(false); + o.setReadTime(null); + o.setTemplateParams(randomTemplateParams()); + }); + notifyMessageMapper.insert(dbNotifyMessage); + // 测试 userId 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); + // 测试 userType 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 readStatus 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); + // 准备参数 + Collection ids = Arrays.asList(dbNotifyMessage.getId(), dbNotifyMessage.getId() + 1, + dbNotifyMessage.getId() + 2, dbNotifyMessage.getId() + 3); + Long userId = 1L; + Integer userType = UserTypeEnum.ADMIN.getValue(); + + // 调用 + int updateCount = notifyMessageService.updateNotifyMessageRead(ids, userId, userType); + // 断言 + assertEquals(1, updateCount); + NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId()); + assertTrue(notifyMessage.getReadStatus()); + assertNotNull(notifyMessage.getReadTime()); + } + + @Test + public void testUpdateAllNotifyMessageRead() { + // mock 数据 + NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 + o.setUserId(1L); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setReadStatus(false); + o.setReadTime(null); + o.setTemplateParams(randomTemplateParams()); + }); + notifyMessageMapper.insert(dbNotifyMessage); + // 测试 userId 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); + // 测试 userType 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 readStatus 不匹配 + notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); + // 准备参数 + Long userId = 1L; + Integer userType = UserTypeEnum.ADMIN.getValue(); + + // 调用 + int updateCount = notifyMessageService.updateAllNotifyMessageRead(userId, userType); + // 断言 + assertEquals(1, updateCount); + NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId()); + assertTrue(notifyMessage.getReadStatus()); + assertNotNull(notifyMessage.getReadTime()); + } + + private static Map randomTemplateParams() { + return MapUtil.builder().put(randomString(), randomString()) + .put(randomString(), randomString()).build(); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java new file mode 100644 index 0000000..9dbdac4 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java @@ -0,0 +1,190 @@ +package cn.iocoder.yudao.module.system.service.notify; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; +import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.HashMap; +import java.util.Map; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_SEND_TEMPLATE_PARAM_MISS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +class NotifySendServiceImplTest extends BaseMockitoUnitTest { + + @InjectMocks + private NotifySendServiceImpl notifySendService; + + @Mock + private NotifyTemplateService notifyTemplateService; + @Mock + private NotifyMessageService notifyMessageService; + + @Test + public void testSendSingleNotifyToAdmin() { + // 准备参数 + Long userId = randomLongId(); + String templateCode = randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock NotifyTemplateService 的方法 + NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String content = randomString(); + when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock NotifyMessageService 的方法 + Long messageId = randomLongId(); + when(notifyMessageService.createNotifyMessage(eq(userId), eq(UserTypeEnum.ADMIN.getValue()), + eq(template), eq(content), eq(templateParams))).thenReturn(messageId); + + // 调用 + Long resultMessageId = notifySendService.sendSingleNotifyToAdmin(userId, templateCode, templateParams); + // 断言 + assertEquals(messageId, resultMessageId); + } + + @Test + public void testSendSingleNotifyToMember() { + // 准备参数 + Long userId = randomLongId(); + String templateCode = randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock NotifyTemplateService 的方法 + NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String content = randomString(); + when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock NotifyMessageService 的方法 + Long messageId = randomLongId(); + when(notifyMessageService.createNotifyMessage(eq(userId), eq(UserTypeEnum.MEMBER.getValue()), + eq(template), eq(content), eq(templateParams))).thenReturn(messageId); + + // 调用 + Long resultMessageId = notifySendService.sendSingleNotifyToMember(userId, templateCode, templateParams); + // 断言 + assertEquals(messageId, resultMessageId); + } + + /** + * 发送成功,当短信模板开启时 + */ + @Test + public void testSendSingleNotify_successWhenMailTemplateEnable() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String templateCode = randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock NotifyTemplateService 的方法 + NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String content = randomString(); + when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock NotifyMessageService 的方法 + Long messageId = randomLongId(); + when(notifyMessageService.createNotifyMessage(eq(userId), eq(userType), + eq(template), eq(content), eq(templateParams))).thenReturn(messageId); + + // 调用 + Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams); + // 断言 + assertEquals(messageId, resultMessageId); + } + + /** + * 发送成功,当短信模板关闭时 + */ + @Test + public void testSendSingleMail_successWhenSmsTemplateDisable() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String templateCode = randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock NotifyTemplateService 的方法 + NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + + // 调用 + Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams); + // 断言 + assertNull(resultMessageId); + verify(notifyTemplateService, never()).formatNotifyTemplateContent(anyString(), anyMap()); + verify(notifyMessageService, never()).createNotifyMessage(anyLong(), anyInt(), any(), anyString(), anyMap()); + } + + @Test + public void testCheckMailTemplateValid_notExists() { + // 准备参数 + String templateCode = randomString(); + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> notifySendService.validateNotifyTemplate(templateCode), + NOTICE_NOT_FOUND); + } + + @Test + public void testCheckTemplateParams_paramMiss() { + // 准备参数 + NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, + o -> o.setParams(Lists.newArrayList("code"))); + Map templateParams = new HashMap<>(); + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> notifySendService.validateTemplateParams(template, templateParams), + NOTIFY_SEND_TEMPLATE_PARAM_MISS, "code"); + } + + @Test + public void testSendBatchNotify() { + // 准备参数 + // mock 方法 + + // 调用 + UnsupportedOperationException exception = Assertions.assertThrows( + UnsupportedOperationException.class, + () -> notifySendService.sendBatchNotify(null, null, null, null, null) + ); + // 断言 + assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage()); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java new file mode 100644 index 0000000..ba1f98e --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java @@ -0,0 +1,178 @@ +package cn.iocoder.yudao.module.system.service.notify; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.HashMap; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link NotifyTemplateServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(NotifyTemplateServiceImpl.class) +public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { + + @Resource + private NotifyTemplateServiceImpl notifyTemplateService; + + @Resource + private NotifyTemplateMapper notifyTemplateMapper; + + @Test + public void testCreateNotifyTemplate_success() { + // 准备参数 + NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class, + o -> o.setStatus(randomCommonStatus())) + .setId(null); // 防止 id 被赋值 + + // 调用 + Long notifyTemplateId = notifyTemplateService.createNotifyTemplate(reqVO); + // 断言 + assertNotNull(notifyTemplateId); + // 校验记录的属性是否正确 + NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId); + assertPojoEquals(reqVO, notifyTemplate, "id"); + } + + @Test + public void testUpdateNotifyTemplate_success() { + // mock 数据 + NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); + notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class, o -> { + o.setId(dbNotifyTemplate.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + }); + + // 调用 + notifyTemplateService.updateNotifyTemplate(reqVO); + // 校验是否更新正确 + NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, notifyTemplate); + } + + @Test + public void testUpdateNotifyTemplate_notExists() { + // 准备参数 + NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> notifyTemplateService.updateNotifyTemplate(reqVO), NOTIFY_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testDeleteNotifyTemplate_success() { + // mock 数据 + NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); + notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbNotifyTemplate.getId(); + + // 调用 + notifyTemplateService.deleteNotifyTemplate(id); + // 校验数据不存在了 + assertNull(notifyTemplateMapper.selectById(id)); + } + + @Test + public void testDeleteNotifyTemplate_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> notifyTemplateService.deleteNotifyTemplate(id), NOTIFY_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testGetNotifyTemplatePage() { + // mock 数据 + NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到 + o.setName("芋头"); + o.setCode("test_01"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2022, 2, 3)); + }); + notifyTemplateMapper.insert(dbNotifyTemplate); + // 测试 name 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投"))); + // 测试 code 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02"))); + // 测试 status 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5)))); + // 准备参数 + NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO(); + reqVO.setName("芋"); + reqVO.setCode("est_01"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5)); + + // 调用 + PageResult pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0)); + } + + @Test + public void testGetNotifyTemplate() { + // mock 数据 + NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); + notifyTemplateMapper.insert(dbNotifyTemplate); + // 准备参数 + Long id = dbNotifyTemplate.getId(); + + // 调用 + NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplate(id); + // 断言 + assertPojoEquals(dbNotifyTemplate, notifyTemplate); + } + + @Test + public void testGetNotifyTemplateByCodeFromCache() { + // mock 数据 + NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); + notifyTemplateMapper.insert(dbNotifyTemplate); + // 准备参数 + String code = dbNotifyTemplate.getCode(); + + // 调用 + NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplateByCodeFromCache(code); + // 断言 + assertPojoEquals(dbNotifyTemplate, notifyTemplate); + } + + @Test + public void testFormatNotifyTemplateContent() { + // 准备参数 + Map params = new HashMap<>(); + params.put("name", "小红"); + params.put("what", "饭"); + + // 调用,并断言 + assertEquals("小红,你好,饭吃了吗?", + notifyTemplateService.formatNotifyTemplateContent("{name},你好,{what}吃了吗?", params)); + } +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImplTest.java new file mode 100644 index 0000000..142201c --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImplTest.java @@ -0,0 +1,270 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ApproveMapper; +import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.util.*; + +import static cn.hutool.core.util.RandomUtil.*; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +/** + * {@link OAuth2ApproveServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(OAuth2ApproveServiceImpl.class) +public class OAuth2ApproveServiceImplTest extends BaseDbUnitTest { + + @Resource + private OAuth2ApproveServiceImpl oauth2ApproveService; + + @Resource + private OAuth2ApproveMapper oauth2ApproveMapper; + + @MockBean + private OAuth2ClientService oauth2ClientService; + + @Test + public void checkForPreApproval_clientAutoApprove() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + List requestedScopes = Lists.newArrayList("read"); + // mock 方法 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))) + .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(requestedScopes)); + + // 调用 + boolean success = oauth2ApproveService.checkForPreApproval(userId, userType, + clientId, requestedScopes); + // 断言 + assertTrue(success); + List result = oauth2ApproveMapper.selectList(); + assertEquals(1, result.size()); + assertEquals(userId, result.get(0).getUserId()); + assertEquals(userType, result.get(0).getUserType()); + assertEquals(clientId, result.get(0).getClientId()); + assertEquals("read", result.get(0).getScope()); + assertTrue(result.get(0).getApproved()); + assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime())); + } + + @Test + public void checkForPreApproval_approve() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + List requestedScopes = Lists.newArrayList("read"); + // mock 方法 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))) + .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null)); + // mock 数据 + OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId) + .setUserType(userType).setClientId(clientId).setScope("read") + .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)).setApproved(true); // 同意 + oauth2ApproveMapper.insert(approve); + + // 调用 + boolean success = oauth2ApproveService.checkForPreApproval(userId, userType, + clientId, requestedScopes); + // 断言 + assertTrue(success); + } + + @Test + public void checkForPreApproval_reject() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + List requestedScopes = Lists.newArrayList("read"); + // mock 方法 + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))) + .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null)); + // mock 数据 + OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId) + .setUserType(userType).setClientId(clientId).setScope("read") + .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)).setApproved(false); // 拒绝 + oauth2ApproveMapper.insert(approve); + + // 调用 + boolean success = oauth2ApproveService.checkForPreApproval(userId, userType, + clientId, requestedScopes); + // 断言 + assertFalse(success); + } + + @Test + public void testUpdateAfterApproval_none() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + + // 调用 + boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId, + null); + // 断言 + assertTrue(success); + List result = oauth2ApproveMapper.selectList(); + assertEquals(0, result.size()); + } + + @Test + public void testUpdateAfterApproval_approved() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + Map requestedScopes = new LinkedHashMap<>(); // 有序,方便判断 + requestedScopes.put("read", true); + requestedScopes.put("write", false); + // mock 方法 + + // 调用 + boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId, + requestedScopes); + // 断言 + assertTrue(success); + List result = oauth2ApproveMapper.selectList(); + assertEquals(2, result.size()); + // read + assertEquals(userId, result.get(0).getUserId()); + assertEquals(userType, result.get(0).getUserType()); + assertEquals(clientId, result.get(0).getClientId()); + assertEquals("read", result.get(0).getScope()); + assertTrue(result.get(0).getApproved()); + assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime())); + // write + assertEquals(userId, result.get(1).getUserId()); + assertEquals(userType, result.get(1).getUserType()); + assertEquals(clientId, result.get(1).getClientId()); + assertEquals("write", result.get(1).getScope()); + assertFalse(result.get(1).getApproved()); + assertFalse(DateUtils.isExpired(result.get(1).getExpiresTime())); + } + + @Test + public void testUpdateAfterApproval_reject() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + Map requestedScopes = new LinkedHashMap<>(); + requestedScopes.put("write", false); + // mock 方法 + + // 调用 + boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId, + requestedScopes); + // 断言 + assertFalse(success); + List result = oauth2ApproveMapper.selectList(); + assertEquals(1, result.size()); + // write + assertEquals(userId, result.get(0).getUserId()); + assertEquals(userType, result.get(0).getUserType()); + assertEquals(clientId, result.get(0).getClientId()); + assertEquals("write", result.get(0).getScope()); + assertFalse(result.get(0).getApproved()); + assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime())); + } + + @Test + public void testGetApproveList() { + // 准备参数 + Long userId = 10L; + Integer userType = UserTypeEnum.ADMIN.getValue(); + String clientId = randomString(); + // mock 数据 + OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId) + .setUserType(userType).setClientId(clientId).setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)); + oauth2ApproveMapper.insert(approve); // 未过期 + oauth2ApproveMapper.insert(ObjectUtil.clone(approve).setId(null) + .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), -1L, ChronoUnit.DAYS))); // 已过期 + + // 调用 + List result = oauth2ApproveService.getApproveList(userId, userType, clientId); + // 断言 + assertEquals(1, result.size()); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(approve, result.get(0), "expiresTime"); + } + + @Test + public void testSaveApprove_insert() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + String scope = randomString(); + Boolean approved = randomBoolean(); + LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()); + // mock 方法 + + // 调用 + oauth2ApproveService.saveApprove(userId, userType, clientId, + scope, approved, expireTime); + // 断言 + List result = oauth2ApproveMapper.selectList(); + assertEquals(1, result.size()); + assertEquals(userId, result.get(0).getUserId()); + assertEquals(userType, result.get(0).getUserType()); + assertEquals(clientId, result.get(0).getClientId()); + assertEquals(scope, result.get(0).getScope()); + assertEquals(approved, result.get(0).getApproved()); + assertEquals(expireTime, result.get(0).getExpiresTime()); + } + + @Test + public void testSaveApprove_update() { + // mock 数据 + OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class); + oauth2ApproveMapper.insert(approve); + // 准备参数 + Long userId = approve.getUserId(); + Integer userType = approve.getUserType(); + String clientId = approve.getClientId(); + String scope = approve.getScope(); + Boolean approved = randomBoolean(); + LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()); + // mock 方法 + + // 调用 + oauth2ApproveService.saveApprove(userId, userType, clientId, + scope, approved, expireTime); + // 断言 + List result = oauth2ApproveMapper.selectList(); + assertEquals(1, result.size()); + assertEquals(approve.getId(), result.get(0).getId()); + assertEquals(userId, result.get(0).getUserId()); + assertEquals(userType, result.get(0).getUserType()); + assertEquals(clientId, result.get(0).getClientId()); + assertEquals(scope, result.get(0).getScope()); + assertEquals(approved, result.get(0).getApproved()); + assertEquals(expireTime, result.get(0).getExpiresTime()); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java new file mode 100755 index 0000000..05e85cb --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java @@ -0,0 +1,220 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.Collections; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; + +/** + * {@link OAuth2ClientServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(OAuth2ClientServiceImpl.class) +public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { + + @Resource + private OAuth2ClientServiceImpl oauth2ClientService; + + @Resource + private OAuth2ClientMapper oauth2ClientMapper; + + @Test + public void testCreateOAuth2Client_success() { + // 准备参数 + OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class, + o -> o.setLogo(randomString())) + .setId(null); // 防止 id 被赋值 + + // 调用 + Long oauth2ClientId = oauth2ClientService.createOAuth2Client(reqVO); + // 断言 + assertNotNull(oauth2ClientId); + // 校验记录的属性是否正确 + OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(oauth2ClientId); + assertPojoEquals(reqVO, oAuth2Client, "id"); + } + + @Test + public void testUpdateOAuth2Client_success() { + // mock 数据 + OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class); + oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据 + // 准备参数 + OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class, o -> { + o.setId(dbOAuth2Client.getId()); // 设置更新的 ID + o.setLogo(randomString()); + }); + + // 调用 + oauth2ClientService.updateOAuth2Client(reqVO); + // 校验是否更新正确 + OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, oAuth2Client); + } + + @Test + public void testUpdateOAuth2Client_notExists() { + // 准备参数 + OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> oauth2ClientService.updateOAuth2Client(reqVO), OAUTH2_CLIENT_NOT_EXISTS); + } + + @Test + public void testDeleteOAuth2Client_success() { + // mock 数据 + OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class); + oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbOAuth2Client.getId(); + + // 调用 + oauth2ClientService.deleteOAuth2Client(id); + // 校验数据不存在了 + assertNull(oauth2ClientMapper.selectById(id)); + } + + @Test + public void testDeleteOAuth2Client_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> oauth2ClientService.deleteOAuth2Client(id), OAUTH2_CLIENT_NOT_EXISTS); + } + + @Test + public void testValidateClientIdExists_withId() { + // mock 数据 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("tudou"); + oauth2ClientMapper.insert(client); + // 准备参数 + Long id = randomLongId(); + String clientId = "tudou"; + + // 调用,不会报错 + assertServiceException(() -> oauth2ClientService.validateClientIdExists(id, clientId), OAUTH2_CLIENT_EXISTS); + } + + @Test + public void testValidateClientIdExists_noId() { + // mock 数据 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("tudou"); + oauth2ClientMapper.insert(client); + // 准备参数 + String clientId = "tudou"; + + // 调用,不会报错 + assertServiceException(() -> oauth2ClientService.validateClientIdExists(null, clientId), OAUTH2_CLIENT_EXISTS); + } + + @Test + public void testGetOAuth2Client() { + // mock 数据 + OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class); + oauth2ClientMapper.insert(clientDO); + // 准备参数 + Long id = clientDO.getId(); + + // 调用,并断言 + OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2Client(id); + assertPojoEquals(clientDO, dbClientDO); + } + + @Test + public void testGetOAuth2ClientFromCache() { + // mock 数据 + OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class); + oauth2ClientMapper.insert(clientDO); + // 准备参数 + String clientId = clientDO.getClientId(); + + // 调用,并断言 + OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2ClientFromCache(clientId); + assertPojoEquals(clientDO, dbClientDO); + } + + @Test + public void testGetOAuth2ClientPage() { + // mock 数据 + OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到 + o.setName("潜龙"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + oauth2ClientMapper.insert(dbOAuth2Client); + // 测试 name 不匹配 + oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰"))); + // 测试 status 不匹配 + oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 准备参数 + OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO(); + reqVO.setName("龙"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + PageResult pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0)); + } + + @Test + public void testValidOAuthClientFromCache() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(OAuth2ClientServiceImpl.class))) + .thenReturn(oauth2ClientService); + + // mock 方法 + OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default") + .setStatus(CommonStatusEnum.ENABLE.getStatus()); + oauth2ClientMapper.insert(client); + OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable") + .setStatus(CommonStatusEnum.DISABLE.getStatus()); + oauth2ClientMapper.insert(client02); + + // 调用,并断言 + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(), + null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable", + null, null, null, null), OAUTH2_CLIENT_DISABLE); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER); + assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", + null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test"); + // 成功调用(1:参数完整) + OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(), + client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0)); + assertPojoEquals(client, result); + // 成功调用(2:只有 clientId 参数) + result = oauth2ClientService.validOAuthClientFromCache(client.getClientId()); + assertPojoEquals(client, result); + } + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImplTest.java new file mode 100644 index 0000000..cba9d3e --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImplTest.java @@ -0,0 +1,101 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.core.util.RandomUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2CodeMapper; +import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_EXPIRE; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link OAuth2CodeServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(OAuth2CodeServiceImpl.class) +class OAuth2CodeServiceImplTest extends BaseDbUnitTest { + + @Resource + private OAuth2CodeServiceImpl oauth2CodeService; + + @Resource + private OAuth2CodeMapper oauth2CodeMapper; + + @Test + public void testCreateAuthorizationCode() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = RandomUtil.randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + List scopes = Lists.newArrayList("read", "write"); + String redirectUri = randomString(); + String state = randomString(); + + // 调用 + OAuth2CodeDO codeDO = oauth2CodeService.createAuthorizationCode(userId, userType, clientId, + scopes, redirectUri, state); + // 断言 + OAuth2CodeDO dbCodeDO = oauth2CodeMapper.selectByCode(codeDO.getCode()); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(codeDO, dbCodeDO, "expiresTime", "createTime", "updateTime", "deleted"); + assertEquals(userId, codeDO.getUserId()); + assertEquals(userType, codeDO.getUserType()); + assertEquals(clientId, codeDO.getClientId()); + assertEquals(scopes, codeDO.getScopes()); + assertEquals(redirectUri, codeDO.getRedirectUri()); + assertEquals(state, codeDO.getState()); + assertFalse(DateUtils.isExpired(codeDO.getExpiresTime())); + } + + @Test + public void testConsumeAuthorizationCode_null() { + // 调用,并断言 + assertServiceException(() -> oauth2CodeService.consumeAuthorizationCode(randomString()), + OAUTH2_CODE_NOT_EXISTS); + } + + @Test + public void testConsumeAuthorizationCode_expired() { + // 准备参数 + String code = "test_code"; + // mock 数据 + OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class).setCode(code) + .setExpiresTime(LocalDateTime.now().minusDays(1)); + oauth2CodeMapper.insert(codeDO); + + // 调用,并断言 + assertServiceException(() -> oauth2CodeService.consumeAuthorizationCode(code), + OAUTH2_CODE_EXPIRE); + } + + @Test + public void testConsumeAuthorizationCode_success() { + // 准备参数 + String code = "test_code"; + // mock 数据 + OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class).setCode(code) + .setExpiresTime(LocalDateTime.now().plusDays(1)); + oauth2CodeMapper.insert(codeDO); + + // 调用 + OAuth2CodeDO result = oauth2CodeService.consumeAuthorizationCode(code); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(codeDO, result, "expiresTime"); + assertNull(oauth2CodeMapper.selectByCode(code)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java new file mode 100644 index 0000000..52c7228 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java @@ -0,0 +1,173 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.List; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static java.util.Collections.emptyList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +/** + * {@link OAuth2GrantServiceImpl} 的单元测试 + * + * @author 芋道源码 + */ +public class OAuth2GrantServiceImplTest extends BaseMockitoUnitTest { + + @InjectMocks + private OAuth2GrantServiceImpl oauth2GrantService; + + @Mock + private OAuth2TokenService oauth2TokenService; + @Mock + private OAuth2CodeService oauth2CodeService; + @Mock + private AdminAuthService adminAuthService; + + @Test + public void testGrantImplicit() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + List scopes = Lists.newArrayList("read", "write"); + // mock 方法 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); + when(oauth2TokenService.createAccessToken(eq(userId), eq(userType), + eq(clientId), eq(scopes))).thenReturn(accessTokenDO); + + // 调用,并断言 + assertPojoEquals(accessTokenDO, oauth2GrantService.grantImplicit( + userId, userType, clientId, scopes)); + } + + @Test + public void testGrantAuthorizationCodeForCode() { + // 准备参数 + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String clientId = randomString(); + List scopes = Lists.newArrayList("read", "write"); + String redirectUri = randomString(); + String state = randomString(); + // mock 方法 + OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class); + when(oauth2CodeService.createAuthorizationCode(eq(userId), eq(userType), + eq(clientId), eq(scopes), eq(redirectUri), eq(state))).thenReturn(codeDO); + + // 调用,并断言 + assertEquals(codeDO.getCode(), oauth2GrantService.grantAuthorizationCodeForCode(userId, userType, + clientId, scopes, redirectUri, state)); + } + + @Test + public void testGrantAuthorizationCodeForAccessToken() { + // 准备参数 + String clientId = randomString(); + String code = randomString(); + List scopes = Lists.newArrayList("read", "write"); + String redirectUri = randomString(); + String state = randomString(); + // mock 方法(code) + OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class, o -> { + o.setClientId(clientId); + o.setRedirectUri(redirectUri); + o.setState(state); + o.setScopes(scopes); + }); + when(oauth2CodeService.consumeAuthorizationCode(eq(code))).thenReturn(codeDO); + // mock 方法(创建令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); + when(oauth2TokenService.createAccessToken(eq(codeDO.getUserId()), eq(codeDO.getUserType()), + eq(codeDO.getClientId()), eq(codeDO.getScopes()))).thenReturn(accessTokenDO); + + // 调用,并断言 + assertPojoEquals(accessTokenDO, oauth2GrantService.grantAuthorizationCodeForAccessToken( + clientId, code, redirectUri, state)); + } + + @Test + public void testGrantPassword() { + // 准备参数 + String username = randomString(); + String password = randomString(); + String clientId = randomString(); + List scopes = Lists.newArrayList("read", "write"); + // mock 方法(认证) + AdminUserDO user = randomPojo(AdminUserDO.class); + when(adminAuthService.authenticate(eq(username), eq(password))).thenReturn(user); + // mock 方法(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); + when(oauth2TokenService.createAccessToken(eq(user.getId()), eq(UserTypeEnum.ADMIN.getValue()), + eq(clientId), eq(scopes))).thenReturn(accessTokenDO); + + // 调用,并断言 + assertPojoEquals(accessTokenDO, oauth2GrantService.grantPassword( + username, password, clientId, scopes)); + } + + @Test + public void testGrantRefreshToken() { + // 准备参数 + String refreshToken = randomString(); + String clientId = randomString(); + // mock 方法 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); + when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq(clientId))) + .thenReturn(accessTokenDO); + + // 调用,并断言 + assertPojoEquals(accessTokenDO, oauth2GrantService.grantRefreshToken( + refreshToken, clientId)); + } + + @Test + public void testGrantClientCredentials() { + assertThrows(UnsupportedOperationException.class, + () -> oauth2GrantService.grantClientCredentials(randomString(), emptyList()), + "暂时不支持 client_credentials 授权模式"); + } + + @Test + public void testRevokeToken_clientIdError() { + // 准备参数 + String clientId = randomString(); + String accessToken = randomString(); + // mock 方法 + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); + when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO); + + // 调用,并断言 + assertFalse(oauth2GrantService.revokeToken(clientId, accessToken)); + } + + @Test + public void testRevokeToken_success() { + // 准备参数 + String clientId = randomString(); + String accessToken = randomString(); + // mock 方法(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setClientId(clientId); + when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO); + // mock 方法(移除) + when(oauth2TokenService.removeAccessToken(eq(accessToken))).thenReturn(accessTokenDO); + + // 调用,并断言 + assertTrue(oauth2GrantService.revokeToken(clientId, accessToken)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java new file mode 100644 index 0000000..03f78b4 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java @@ -0,0 +1,329 @@ +package cn.iocoder.yudao.module.system.service.oauth2; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; +import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper; +import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper; +import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import jakarta.annotation.Resource; +import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import java.time.LocalDateTime; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +/** + * {@link OAuth2TokenServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import({OAuth2TokenServiceImpl.class, OAuth2AccessTokenRedisDAO.class}) +public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest { + + @Resource + private OAuth2TokenServiceImpl oauth2TokenService; + + @Resource + private OAuth2AccessTokenMapper oauth2AccessTokenMapper; + @Resource + private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper; + + @Resource + private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO; + + @MockBean + private OAuth2ClientService oauth2ClientService; + @MockBean + private AdminUserService adminUserService; + + @Test + public void testCreateAccessToken() { + TenantContextHolder.setTenantId(0L); + // 准备参数 + Long userId = randomLongId(); + Integer userType = UserTypeEnum.ADMIN.getValue(); + String clientId = randomString(); + List scopes = Lists.newArrayList("read", "write"); + // mock 方法 + OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId) + .setAccessTokenValiditySeconds(30).setRefreshTokenValiditySeconds(60); + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); + // mock 数据(用户) + AdminUserDO user = randomPojo(AdminUserDO.class); + when(adminUserService.getUser(userId)).thenReturn(user); + + // 调用 + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, userType, clientId, scopes); + // 断言访问令牌 + OAuth2AccessTokenDO dbAccessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken()); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(accessTokenDO, dbAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted"); + assertEquals(userId, accessTokenDO.getUserId()); + assertEquals(userType, accessTokenDO.getUserType()); + assertEquals(2, accessTokenDO.getUserInfo().size()); + assertEquals(user.getNickname(), accessTokenDO.getUserInfo().get("nickname")); + assertEquals(user.getDeptId().toString(), accessTokenDO.getUserInfo().get("deptId")); + assertEquals(clientId, accessTokenDO.getClientId()); + assertEquals(scopes, accessTokenDO.getScopes()); + assertFalse(DateUtils.isExpired(accessTokenDO.getExpiresTime())); + // 断言访问令牌的缓存 + OAuth2AccessTokenDO redisAccessTokenDO = oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken()); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(accessTokenDO, redisAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted"); + // 断言刷新令牌 + OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectList().get(0); + assertPojoEquals(accessTokenDO, refreshTokenDO, "id", "expiresTime", "createTime", "updateTime", "deleted"); + assertFalse(DateUtils.isExpired(refreshTokenDO.getExpiresTime())); + } + + @Test + public void testRefreshAccessToken_null() { + // 准备参数 + String refreshToken = randomString(); + String clientId = randomString(); + // mock 方法 + + // 调用,并断言 + assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), + new ErrorCode(400, "无效的刷新令牌")); + } + + @Test + public void testRefreshAccessToken_clientIdError() { + // 准备参数 + String refreshToken = randomString(); + String clientId = randomString(); + // mock 方法 + OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId); + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); + // mock 数据(访问令牌) + OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) + .setRefreshToken(refreshToken).setClientId("error"); + oauth2RefreshTokenMapper.insert(refreshTokenDO); + + // 调用,并断言 + assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), + new ErrorCode(400, "刷新令牌的客户端编号不正确")); + } + + @Test + public void testRefreshAccessToken_expired() { + // 准备参数 + String refreshToken = randomString(); + String clientId = randomString(); + // mock 方法 + OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId); + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); + // mock 数据(访问令牌) + OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) + .setRefreshToken(refreshToken).setClientId(clientId) + .setExpiresTime(LocalDateTime.now().minusDays(1)); + oauth2RefreshTokenMapper.insert(refreshTokenDO); + + // 调用,并断言 + assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), + new ErrorCode(401, "刷新令牌已过期")); + assertEquals(0, oauth2AccessTokenMapper.selectCount()); + } + + @Test + public void testRefreshAccessToken_success() { + TenantContextHolder.setTenantId(0L); + // 准备参数 + String refreshToken = randomString(); + String clientId = randomString(); + // mock 方法 + OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId) + .setAccessTokenValiditySeconds(30); + when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); + // mock 数据(访问令牌) + OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class, o -> + o.setRefreshToken(refreshToken).setClientId(clientId) + .setExpiresTime(LocalDateTime.now().plusDays(1)) + .setUserType(UserTypeEnum.ADMIN.getValue()) + .setTenantId(TenantContextHolder.getTenantId())); + oauth2RefreshTokenMapper.insert(refreshTokenDO); + // mock 数据(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setRefreshToken(refreshToken) + .setUserType(refreshTokenDO.getUserType()); + oauth2AccessTokenMapper.insert(accessTokenDO); + oauth2AccessTokenRedisDAO.set(accessTokenDO); + // mock 数据(用户) + AdminUserDO user = randomPojo(AdminUserDO.class); + when(adminUserService.getUser(refreshTokenDO.getUserId())).thenReturn(user); + + // 调用 + OAuth2AccessTokenDO newAccessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId); + // 断言,老的访问令牌被删除 + assertNull(oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken())); + assertNull(oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken())); + // 断言,新的访问令牌 + OAuth2AccessTokenDO dbAccessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(newAccessTokenDO.getAccessToken()); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(newAccessTokenDO, dbAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted"); + assertPojoEquals(newAccessTokenDO, refreshTokenDO, "id", "expiresTime", "createTime", "updateTime", "deleted", + "creator", "updater"); + assertFalse(DateUtils.isExpired(newAccessTokenDO.getExpiresTime())); + // 断言,新的访问令牌的缓存 + OAuth2AccessTokenDO redisAccessTokenDO = oauth2AccessTokenRedisDAO.get(newAccessTokenDO.getAccessToken()); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(newAccessTokenDO, redisAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted"); + } + + @Test + public void testGetAccessToken() { + // mock 数据(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) + .setExpiresTime(LocalDateTime.now().plusDays(1)); + oauth2AccessTokenMapper.insert(accessTokenDO); + // 准备参数 + String accessToken = accessTokenDO.getAccessToken(); + + // 调用 + OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken); + // 断言 + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(accessTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted", + "creator", "updater"); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(accessTokenDO, oauth2AccessTokenRedisDAO.get(accessToken), "expiresTime", "createTime", "updateTime", "deleted", + "creator", "updater"); + } + + @Test + public void testCheckAccessToken_null() { + // 调研,并断言 + assertServiceException(() -> oauth2TokenService.checkAccessToken(randomString()), + new ErrorCode(401, "访问令牌不存在")); + } + + @Test + public void testCheckAccessToken_expired() { + // mock 数据(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) + .setExpiresTime(LocalDateTime.now().minusDays(1)); + oauth2AccessTokenMapper.insert(accessTokenDO); + // 准备参数 + String accessToken = accessTokenDO.getAccessToken(); + + // 调研,并断言 + assertServiceException(() -> oauth2TokenService.checkAccessToken(accessToken), + new ErrorCode(401, "访问令牌已过期")); + } + + @Test + public void testCheckAccessToken_refreshToken() { + // mock 数据(访问令牌) + OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) + .setExpiresTime(LocalDateTime.now().plusDays(1)); + oauth2RefreshTokenMapper.insert(refreshTokenDO); + // 准备参数 + String accessToken = refreshTokenDO.getRefreshToken(); + + // 调研,并断言 + OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken); + // 断言 + assertPojoEquals(refreshTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted", + "creator", "updater"); + } + + @Test + public void testCheckAccessToken_success() { + // mock 数据(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) + .setExpiresTime(LocalDateTime.now().plusDays(1)); + oauth2AccessTokenMapper.insert(accessTokenDO); + // 准备参数 + String accessToken = accessTokenDO.getAccessToken(); + + // 调研,并断言 + OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken); + // 断言 + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(accessTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted", + "creator", "updater"); + } + + @Test + public void testRemoveAccessToken_null() { + // 调用,并断言 + assertNull(oauth2TokenService.removeAccessToken(randomString())); + } + + @Test + public void testRemoveAccessToken_success() { + // mock 数据(访问令牌) + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) + .setExpiresTime(LocalDateTime.now().plusDays(1)); + oauth2AccessTokenMapper.insert(accessTokenDO); + // mock 数据(刷新令牌) + OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) + .setRefreshToken(accessTokenDO.getRefreshToken()); + oauth2RefreshTokenMapper.insert(refreshTokenDO); + // 调用 + OAuth2AccessTokenDO result = oauth2TokenService.removeAccessToken(accessTokenDO.getAccessToken()); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(accessTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted", + "creator", "updater"); + // 断言数据 + assertNull(oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken())); + assertNull(oauth2RefreshTokenMapper.selectByRefreshToken(accessTokenDO.getRefreshToken())); + assertNull(oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken())); + } + + + @Test + public void testGetAccessTokenPage() { + // mock 数据 + OAuth2AccessTokenDO dbAccessToken = randomPojo(OAuth2AccessTokenDO.class, o -> { // 等会查询到 + o.setUserId(10L); + o.setUserType(1); + o.setClientId("test_client"); + o.setExpiresTime(LocalDateTime.now().plusDays(1)); + }); + oauth2AccessTokenMapper.insert(dbAccessToken); + // 测试 userId 不匹配 + oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setUserId(20L))); + // 测试 userType 不匹配 + oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setUserType(2))); + // 测试 userType 不匹配 + oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setClientId("it_client"))); + // 测试 expireTime 不匹配 + oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setExpiresTime(LocalDateTimeUtil.now()))); + // 准备参数 + OAuth2AccessTokenPageReqVO reqVO = new OAuth2AccessTokenPageReqVO(); + reqVO.setUserId(10L); + reqVO.setUserType(1); + reqVO.setClientId("test"); + + // 调用 + PageResult pageResult = oauth2TokenService.getAccessTokenPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 + assertPojoEquals(dbAccessToken, pageResult.getList().get(0), "expiresTime"); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java new file mode 100644 index 0000000..cc39379 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java @@ -0,0 +1,331 @@ +package cn.iocoder.yudao.module.system.service.permission; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper; +import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; +import cn.iocoder.yudao.module.system.service.tenant.TenantService; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; + +@Import(MenuServiceImpl.class) +public class MenuServiceImplTest extends BaseDbUnitTest { + + @Resource + private MenuServiceImpl menuService; + + @Resource + private MenuMapper menuMapper; + + @MockBean + private PermissionService permissionService; + @MockBean + private TenantService tenantService; + + @Test + public void testCreateMenu_success() { + // mock 数据(构造父菜单) + MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, + "parent", 0L); + menuMapper.insert(menuDO); + Long parentId = menuDO.getId(); + // 准备参数 + MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> { + o.setParentId(parentId); + o.setName("testSonName"); + o.setType(MenuTypeEnum.MENU.getType()); + }).setId(null); // 防止 id 被赋值 + Long menuId = menuService.createMenu(reqVO); + + // 校验记录的属性是否正确 + MenuDO dbMenu = menuMapper.selectById(menuId); + assertPojoEquals(reqVO, dbMenu, "id"); + } + + @Test + public void testUpdateMenu_success() { + // mock 数据(构造父子菜单) + MenuDO sonMenuDO = createParentAndSonMenu(); + Long sonId = sonMenuDO.getId(); + // 准备参数 + MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> { + o.setId(sonId); + o.setName("testSonName"); // 修改名字 + o.setParentId(sonMenuDO.getParentId()); + o.setType(MenuTypeEnum.MENU.getType()); + }); + + // 调用 + menuService.updateMenu(reqVO); + // 校验记录的属性是否正确 + MenuDO dbMenu = menuMapper.selectById(sonId); + assertPojoEquals(reqVO, dbMenu); + } + + @Test + public void testUpdateMenu_sonIdNotExist() { + // 准备参数 + MenuSaveVO reqVO = randomPojo(MenuSaveVO.class); + // 调用,并断言异常 + assertServiceException(() -> menuService.updateMenu(reqVO), MENU_NOT_EXISTS); + } + + @Test + public void testDeleteMenu_success() { + // mock 数据 + MenuDO menuDO = randomPojo(MenuDO.class); + menuMapper.insert(menuDO); + // 准备参数 + Long id = menuDO.getId(); + + // 调用 + menuService.deleteMenu(id); + // 断言 + MenuDO dbMenuDO = menuMapper.selectById(id); + assertNull(dbMenuDO); + verify(permissionService).processMenuDeleted(id); + } + + @Test + public void testDeleteMenu_menuNotExist() { + assertServiceException(() -> menuService.deleteMenu(randomLongId()), + MENU_NOT_EXISTS); + } + + @Test + public void testDeleteMenu_existChildren() { + // mock 数据(构造父子菜单) + MenuDO sonMenu = createParentAndSonMenu(); + // 准备参数 + Long parentId = sonMenu.getParentId(); + + // 调用并断言异常 + assertServiceException(() -> menuService.deleteMenu(parentId), MENU_EXISTS_CHILDREN); + } + + @Test + public void testGetMenuList_all() { + // mock 数据 + MenuDO menu100 = randomPojo(MenuDO.class); + menuMapper.insert(menu100); + MenuDO menu101 = randomPojo(MenuDO.class); + menuMapper.insert(menu101); + // 准备参数 + + // 调用 + List list = menuService.getMenuList(); + // 断言 + assertEquals(2, list.size()); + assertPojoEquals(menu100, list.get(0)); + assertPojoEquals(menu101, list.get(1)); + } + + @Test + public void testGetMenuList() { + // mock 数据 + MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setName("芋艿").setStatus(CommonStatusEnum.ENABLE.getStatus())); + menuMapper.insert(menuDO); + // 测试 status 不匹配 + menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 name 不匹配 + menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setName("艿"))); + // 准备参数 + MenuListReqVO reqVO = new MenuListReqVO().setName("芋").setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + List result = menuService.getMenuList(reqVO); + // 断言 + assertEquals(1, result.size()); + assertPojoEquals(menuDO, result.get(0)); + } + + @Test + public void testGetMenuListByTenant() { + // mock 数据 + MenuDO menu100 = randomPojo(MenuDO.class, o -> o.setId(100L).setStatus(CommonStatusEnum.ENABLE.getStatus())); + menuMapper.insert(menu100); + MenuDO menu101 = randomPojo(MenuDO.class, o -> o.setId(101L).setStatus(CommonStatusEnum.DISABLE.getStatus())); + menuMapper.insert(menu101); + MenuDO menu102 = randomPojo(MenuDO.class, o -> o.setId(102L).setStatus(CommonStatusEnum.ENABLE.getStatus())); + menuMapper.insert(menu102); + // mock 过滤菜单 + Set menuIds = asSet(100L, 101L); + doNothing().when(tenantService).handleTenantMenu(argThat(handler -> { + handler.handle(menuIds); + return true; + })); + // 准备参数 + MenuListReqVO reqVO = new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + List result = menuService.getMenuListByTenant(reqVO); + // 断言 + assertEquals(1, result.size()); + assertPojoEquals(menu100, result.get(0)); + } + + @Test + public void testGetMenuIdListByPermissionFromCache() { + // mock 数据 + MenuDO menu100 = randomPojo(MenuDO.class); + menuMapper.insert(menu100); + MenuDO menu101 = randomPojo(MenuDO.class); + menuMapper.insert(menu101); + // 准备参数 + String permission = menu100.getPermission(); + + // 调用 + List ids = menuService.getMenuIdListByPermissionFromCache(permission); + // 断言 + assertEquals(1, ids.size()); + assertEquals(menu100.getId(), ids.get(0)); + } + + @Test + public void testGetMenuList_ids() { + // mock 数据 + MenuDO menu100 = randomPojo(MenuDO.class); + menuMapper.insert(menu100); + MenuDO menu101 = randomPojo(MenuDO.class); + menuMapper.insert(menu101); + // 准备参数 + Collection ids = Collections.singleton(menu100.getId()); + + // 调用 + List list = menuService.getMenuList(ids); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(menu100, list.get(0)); + } + + @Test + public void testGetMenu() { + // mock 数据 + MenuDO menu = randomPojo(MenuDO.class); + menuMapper.insert(menu); + // 准备参数 + Long id = menu.getId(); + + // 调用 + MenuDO dbMenu = menuService.getMenu(id); + // 断言 + assertPojoEquals(menu, dbMenu); + } + + @Test + public void testValidateParentMenu_success() { + // mock 数据 + MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L); + menuMapper.insert(menuDO); + // 准备参数 + Long parentId = menuDO.getId(); + + // 调用,无需断言 + menuService.validateParentMenu(parentId, null); + } + + @Test + public void testValidateParentMenu_canNotSetSelfToBeParent() { + // 调用,并断言异常 + assertServiceException(() -> menuService.validateParentMenu(1L, 1L), + MENU_PARENT_ERROR); + } + + @Test + public void testValidateParentMenu_parentNotExist() { + // 调用,并断言异常 + assertServiceException(() -> menuService.validateParentMenu(randomLongId(), null), + MENU_PARENT_NOT_EXISTS); + } + + @Test + public void testValidateParentMenu_parentTypeError() { + // mock 数据 + MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); + menuMapper.insert(menuDO); + // 准备参数 + Long parentId = menuDO.getId(); + + // 调用,并断言异常 + assertServiceException(() -> menuService.validateParentMenu(parentId, null), + MENU_PARENT_NOT_DIR_OR_MENU); + } + + @Test + public void testValidateMenu_Name_success() { + // mock 父子菜单 + MenuDO sonMenu = createParentAndSonMenu(); + // 准备参数 + Long parentId = sonMenu.getParentId(); + Long otherSonMenuId = randomLongId(); + String otherSonMenuName = randomString(); + + // 调用,无需断言 + menuService.validateMenuName(parentId, otherSonMenuName, otherSonMenuId); + } + + @Test + public void testValidateMenu_sonMenuNameNameDuplicate() { + // mock 父子菜单 + MenuDO sonMenu = createParentAndSonMenu(); + // 准备参数 + Long parentId = sonMenu.getParentId(); + Long otherSonMenuId = randomLongId(); + String otherSonMenuName = sonMenu.getName(); //相同名称 + + // 调用,并断言异常 + assertServiceException(() -> menuService.validateMenuName(parentId, otherSonMenuName, otherSonMenuId), + MENU_NAME_DUPLICATE); + } + + // ====================== 初始化方法 ====================== + + /** + * 插入父子菜单,返回子菜单 + * + * @return 子菜单 + */ + private MenuDO createParentAndSonMenu() { + // 构造父子菜单 + MenuDO parentMenuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT); + menuMapper.insert(parentMenuDO); + // 构建子菜单 + MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName", + parentMenuDO.getParentId()); + menuMapper.insert(sonMenuDO); + return sonMenuDO; + } + + private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) { + return buildMenuDO(type, name, parentId, randomCommonStatus()); + } + + private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) { + return randomPojo(MenuDO.class, o -> o.setId(null).setName(name).setParentId(parentId) + .setType(type.getType()).setStatus(status)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java new file mode 100644 index 0000000..9180444 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java @@ -0,0 +1,527 @@ +package cn.iocoder.yudao.module.system.service.permission; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; +import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static cn.hutool.core.collection.ListUtil.toList; +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +@Import({PermissionServiceImpl.class}) +public class PermissionServiceTest extends BaseDbUnitTest { + + @Resource + private PermissionServiceImpl permissionService; + + @Resource + private RoleMenuMapper roleMenuMapper; + @Resource + private UserRoleMapper userRoleMapper; + + @MockBean + private RoleService roleService; + @MockBean + private MenuService menuService; + @MockBean + private DeptService deptService; + @MockBean + private AdminUserService userService; + + @Test + public void testHasAnyPermissions_superAdmin() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + String[] roles = new String[]{"system:user:query", "system:user:create"}; + // mock 用户登录的角色 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); + // mock 其它方法 + when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); + + // 调用,并断言 + assertTrue(permissionService.hasAnyPermissions(userId, roles)); + } + } + + @Test + public void testHasAnyPermissions_normal() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + String[] roles = new String[]{"system:user:query", "system:user:create"}; + // mock 用户登录的角色 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); + // mock 菜单 + Long menuId = 1000L; + when(menuService.getMenuIdListByPermissionFromCache( + eq("system:user:create"))).thenReturn(singletonList(menuId)); + roleMenuMapper.insert(randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1000L)); + + // 调用,并断言 + assertTrue(permissionService.hasAnyPermissions(userId, roles)); + } + } + + @Test + public void testHasAnyRoles() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + String[] roles = new String[]{"yunai", "tudou"}; + // mock 用户与角色的缓存 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("tudou") + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); + + // 调用,并断言 + assertTrue(permissionService.hasAnyRoles(userId, roles)); + } + } + + // ========== 角色-菜单的相关方法 ========== + + @Test + public void testAssignRoleMenu() { + // 准备参数 + Long roleId = 1L; + Set menuIds = asSet(200L, 300L); + // mock 数据 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(100L); + roleMenuMapper.insert(roleMenu01); + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(200L); + roleMenuMapper.insert(roleMenu02); + + // 调用 + permissionService.assignRoleMenu(roleId, menuIds); + // 断言 + List roleMenuList = roleMenuMapper.selectList(); + assertEquals(2, roleMenuList.size()); + assertEquals(1L, roleMenuList.get(0).getRoleId()); + assertEquals(200L, roleMenuList.get(0).getMenuId()); + assertEquals(1L, roleMenuList.get(1).getRoleId()); + assertEquals(300L, roleMenuList.get(1).getMenuId()); + } + + @Test + public void testProcessRoleDeleted() { + // 准备参数 + Long roleId = randomLongId(); + // mock 数据 UserRole + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setRoleId(roleId)); // 被删除 + userRoleMapper.insert(userRoleDO01); + UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除 + userRoleMapper.insert(userRoleDO02); + // mock 数据 RoleMenu + RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(roleId)); // 被删除 + roleMenuMapper.insert(roleMenuDO01); + RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除 + roleMenuMapper.insert(roleMenuDO02); + + // 调用 + permissionService.processRoleDeleted(roleId); + // 断言数据 RoleMenuDO + List dbRoleMenus = roleMenuMapper.selectList(); + assertEquals(1, dbRoleMenus.size()); + assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02); + // 断言数据 UserRoleDO + List dbUserRoles = userRoleMapper.selectList(); + assertEquals(1, dbUserRoles.size()); + assertPojoEquals(dbUserRoles.get(0), userRoleDO02); + } + + @Test + public void testProcessMenuDeleted() { + // 准备参数 + Long menuId = randomLongId(); + // mock 数据 + RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setMenuId(menuId)); // 被删除 + roleMenuMapper.insert(roleMenuDO01); + RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除 + roleMenuMapper.insert(roleMenuDO02); + + // 调用 + permissionService.processMenuDeleted(menuId); + // 断言数据 + List dbRoleMenus = roleMenuMapper.selectList(); + assertEquals(1, dbRoleMenus.size()); + assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02); + } + + @Test + public void testGetRoleMenuIds_superAdmin() { + // 准备参数 + Long roleId = 100L; + // mock 方法 + when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true); + List menuList = singletonList(randomPojo(MenuDO.class).setId(1L)); + when(menuService.getMenuList()).thenReturn(menuList); + + // 调用 + Set menuIds = permissionService.getRoleMenuListByRoleId(roleId); + // 断言 + assertEquals(singleton(1L), menuIds); + } + + @Test + public void testGetRoleMenuIds_normal() { + // 准备参数 + Long roleId = 100L; + // mock 数据 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); + roleMenuMapper.insert(roleMenu01); + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(2L); + roleMenuMapper.insert(roleMenu02); + + // 调用 + Set menuIds = permissionService.getRoleMenuListByRoleId(roleId); + // 断言 + assertEquals(asSet(1L, 2L), menuIds); + } + + @Test + public void testGetMenuRoleIdListByMenuIdFromCache() { + // 准备参数 + Long menuId = 1L; + // mock 数据 + RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); + roleMenuMapper.insert(roleMenu01); + RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(200L).setMenuId(1L); + roleMenuMapper.insert(roleMenu02); + + // 调用 + Set roleIds = permissionService.getMenuRoleIdListByMenuIdFromCache(menuId); + // 断言 + assertEquals(asSet(100L, 200L), roleIds); + } + + // ========== 用户-角色的相关方法 ========== + + @Test + public void testAssignUserRole() { + // 准备参数 + Long userId = 1L; + Set roleIds = asSet(200L, 300L); + // mock 数据 + UserRoleDO userRole01 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(100L); + userRoleMapper.insert(userRole01); + UserRoleDO userRole02 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(200L); + userRoleMapper.insert(userRole02); + + // 调用 + permissionService.assignUserRole(userId, roleIds); + // 断言 + List userRoleDOList = userRoleMapper.selectList(); + assertEquals(2, userRoleDOList.size()); + assertEquals(1L, userRoleDOList.get(0).getUserId()); + assertEquals(200L, userRoleDOList.get(0).getRoleId()); + assertEquals(1L, userRoleDOList.get(1).getUserId()); + assertEquals(300L, userRoleDOList.get(1).getRoleId()); + } + + @Test + public void testProcessUserDeleted() { + // 准备参数 + Long userId = randomLongId(); + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(userId)); // 被删除 + userRoleMapper.insert(userRoleDO01); + UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除 + userRoleMapper.insert(userRoleDO02); + + // 调用 + permissionService.processUserDeleted(userId); + // 断言数据 + List dbUserRoles = userRoleMapper.selectList(); + assertEquals(1, dbUserRoles.size()); + assertPojoEquals(dbUserRoles.get(0), userRoleDO02); + } + + @Test + public void testGetUserRoleIdListByUserId() { + // 准备参数 + Long userId = 1L; + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); + + // 调用 + Set result = permissionService.getUserRoleIdListByUserId(userId); + // 断言 + assertEquals(asSet(10L, 20L), result); + } + + @Test + public void testGetUserRoleIdListByUserIdFromCache() { + // 准备参数 + Long userId = 1L; + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); + + // 调用 + Set result = permissionService.getUserRoleIdListByUserIdFromCache(userId); + // 断言 + assertEquals(asSet(10L, 20L), result); + } + + @Test + public void testGetUserRoleIdsFromCache() { + // 准备参数 + Long userId = 1L; + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); + + // 调用 + Set result = permissionService.getUserRoleIdListByUserIdFromCache(userId); + // 断言 + assertEquals(asSet(10L, 20L), result); + } + + @Test + public void testGetUserRoleIdListByRoleId() { + // 准备参数 + Collection roleIds = asSet(10L, 20L); + // mock 数据 + UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); + userRoleMapper.insert(userRoleDO01); + UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L)); + userRoleMapper.insert(roleMenuDO02); + + // 调用 + Set result = permissionService.getUserRoleIdListByRoleId(roleIds); + // 断言 + assertEquals(asSet(1L, 2L), result); + } + + @Test + public void testGetEnableUserRoleListByUserIdFromCache() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + // mock 用户登录的角色 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(200L)); + RoleDO role01 = randomPojo(RoleDO.class, o -> o.setId(100L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + RoleDO role02 = randomPojo(RoleDO.class, o -> o.setId(200L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(asSet(100L, 200L)))) + .thenReturn(toList(role01, role02)); + + // 调用 + List result = permissionService.getEnableUserRoleListByUserIdFromCache(userId); + // 断言 + assertEquals(1, result.size()); + assertPojoEquals(role01, result.get(0)); + } + } + + // ========== 用户-部门的相关方法 ========== + + @Test + public void testAssignRoleDataScope() { + // 准备参数 + Long roleId = 1L; + Integer dataScope = 2; + Set dataScopeDeptIds = asSet(10L, 20L); + + // 调用 + permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds); + // 断言 + verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds)); + } + + @Test + public void testGetDeptDataPermission_All() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertTrue(result.getAll()); + assertFalse(result.getSelf()); + assertTrue(CollUtil.isEmpty(result.getDeptIds())); + } + } + + @Test + public void testGetDeptDataPermission_DeptCustom() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + // mock 部门的返回 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), + null, null); // 最后返回 null 的目的,看看会不会重复调用 + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertFalse(result.getSelf()); + assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); + assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + } + } + + @Test + public void testGetDeptDataPermission_DeptOnly() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + // mock 部门的返回 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), + null, null); // 最后返回 null 的目的,看看会不会重复调用 + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertFalse(result.getSelf()); + assertEquals(1, result.getDeptIds().size()); + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + } + } + + @Test + public void testGetDeptDataPermission_DeptAndChild() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + // mock 部门的返回 + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), + null, null); // 最后返回 null 的目的,看看会不会重复调用 + // mock 方法(部门) + DeptDO deptDO = randomPojo(DeptDO.class); + when(deptService.getChildDeptIdListFromCache(eq(3L))).thenReturn(singleton(deptDO.getId())); + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertFalse(result.getSelf()); + assertEquals(2, result.getDeptIds().size()); + assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); + } + } + + @Test + public void testGetDeptDataPermission_Self() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) + .thenReturn(permissionService); + + // 准备参数 + Long userId = 1L; + // mock 用户的角色编号 + userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); + // mock 获得用户的角色 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); + + // 调用 + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); + // 断言 + assertFalse(result.getAll()); + assertTrue(result.getSelf()); + assertTrue(CollUtil.isEmpty(result.getDeptIds())); + } + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java new file mode 100644 index 0000000..fc87193 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java @@ -0,0 +1,372 @@ +package cn.iocoder.yudao.module.system.service.permission; + +import cn.hutool.extra.spring.SpringUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; +import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; + +@Import(RoleServiceImpl.class) +public class RoleServiceImplTest extends BaseDbUnitTest { + + @Resource + private RoleServiceImpl roleService; + + @Resource + private RoleMapper roleMapper; + + @MockBean + private PermissionService permissionService; + + @Test + public void testCreateRole() { + // 准备参数 + RoleSaveReqVO reqVO = randomPojo(RoleSaveReqVO.class) + .setId(null) // 防止 id 被赋值 + .setStatus(randomCommonStatus()); + + // 调用 + Long roleId = roleService.createRole(reqVO, null); + // 断言 + RoleDO roleDO = roleMapper.selectById(roleId); + assertPojoEquals(reqVO, roleDO, "id"); + assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType()); + assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope()); + } + + @Test + public void testUpdateRole() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); + roleMapper.insert(roleDO); + // 准备参数 + Long id = roleDO.getId(); + RoleSaveReqVO reqVO = randomPojo(RoleSaveReqVO.class, o -> o.setId(id) + .setStatus(randomCommonStatus())); + + // 调用 + roleService.updateRole(reqVO); + // 断言 + RoleDO newRoleDO = roleMapper.selectById(id); + assertPojoEquals(reqVO, newRoleDO); + } + + @Test + public void testUpdateRoleDataScope() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); + roleMapper.insert(roleDO); + // 准备参数 + Long id = roleDO.getId(); + Integer dataScope = randomEle(DataScopeEnum.values()).getScope(); + Set dataScopeRoleIds = randomSet(Long.class); + + // 调用 + roleService.updateRoleDataScope(id, dataScope, dataScopeRoleIds); + // 断言 + RoleDO dbRoleDO = roleMapper.selectById(id); + assertEquals(dataScope, dbRoleDO.getDataScope()); + assertEquals(dataScopeRoleIds, dbRoleDO.getDataScopeDeptIds()); + } + + @Test + public void testDeleteRole() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); + roleMapper.insert(roleDO); + // 参数准备 + Long id = roleDO.getId(); + + // 调用 + roleService.deleteRole(id); + // 断言 + assertNull(roleMapper.selectById(id)); + // verify 删除相关数据 + verify(permissionService).processRoleDeleted(id); + } + + @Test + public void testValidateRoleDuplicate_success() { + // 调用,不会抛异常 + roleService.validateRoleDuplicate(randomString(), randomString(), null); + } + + @Test + public void testValidateRoleDuplicate_nameDuplicate() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name")); + roleMapper.insert(roleDO); + // 准备参数 + String name = "role_name"; + + // 调用,并断言异常 + assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null), + ROLE_NAME_DUPLICATE, name); + } + + @Test + public void testValidateRoleDuplicate_codeDuplicate() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code")); + roleMapper.insert(roleDO); + // 准备参数 + String code = "code"; + + // 调用,并断言异常 + assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null), + ROLE_CODE_DUPLICATE, code); + } + + @Test + public void testValidateUpdateRole_success() { + RoleDO roleDO = randomPojo(RoleDO.class); + roleMapper.insert(roleDO); + // 准备参数 + Long id = roleDO.getId(); + + // 调用,无异常 + roleService.validateRoleForUpdate(id); + } + + @Test + public void testValidateUpdateRole_roleIdNotExist() { + assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS); + } + + @Test + public void testValidateUpdateRole_systemRoleCanNotBeUpdate() { + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType())); + roleMapper.insert(roleDO); + // 准备参数 + Long id = roleDO.getId(); + + assertServiceException(() -> roleService.validateRoleForUpdate(id), + ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); + } + + @Test + public void testGetRole() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class); + roleMapper.insert(roleDO); + // 参数准备 + Long id = roleDO.getId(); + + // 调用 + RoleDO dbRoleDO = roleService.getRole(id); + // 断言 + assertPojoEquals(roleDO, dbRoleDO); + } + + @Test + public void testGetRoleFromCache() { + // mock 数据(缓存) + RoleDO roleDO = randomPojo(RoleDO.class); + roleMapper.insert(roleDO); + // 参数准备 + Long id = roleDO.getId(); + + // 调用 + RoleDO dbRoleDO = roleService.getRoleFromCache(id); + // 断言 + assertPojoEquals(roleDO, dbRoleDO); + } + + @Test + public void testGetRoleListByStatus() { + // mock 数据 + RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + roleMapper.insert(dbRole01); + RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + roleMapper.insert(dbRole02); + + // 调用 + List list = roleService.getRoleListByStatus( + singleton(CommonStatusEnum.ENABLE.getStatus())); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbRole01, list.get(0)); + } + + @Test + public void testGetRoleList() { + // mock 数据 + RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + roleMapper.insert(dbRole01); + RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + roleMapper.insert(dbRole02); + + // 调用 + List list = roleService.getRoleList(); + // 断言 + assertEquals(2, list.size()); + assertPojoEquals(dbRole01, list.get(0)); + assertPojoEquals(dbRole02, list.get(1)); + } + + @Test + public void testGetRoleList_ids() { + // mock 数据 + RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + roleMapper.insert(dbRole01); + RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + roleMapper.insert(dbRole02); + // 准备参数 + Collection ids = singleton(dbRole01.getId()); + + // 调用 + List list = roleService.getRoleList(ids); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbRole01, list.get(0)); + } + + @Test + public void testGetRoleListFromCache() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) + .thenReturn(roleService); + + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + roleMapper.insert(dbRole); + // 测试 id 不匹配 + roleMapper.insert(cloneIgnoreId(dbRole, o -> {})); + // 准备参数 + Collection ids = singleton(dbRole.getId()); + + // 调用 + List list = roleService.getRoleListFromCache(ids); + // 断言 + assertEquals(1, list.size()); + assertPojoEquals(dbRole, list.get(0)); + } + } + + @Test + public void testGetRolePage() { + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class, o -> { // 等会查询到 + o.setName("土豆"); + o.setCode("tudou"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2022, 2, 8)); + }); + roleMapper.insert(dbRole); + // 测试 name 不匹配 + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setName("红薯"))); + // 测试 code 不匹配 + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCode("hong"))); + // 测试 createTime 不匹配 + roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCreateTime(buildTime(2022, 2, 16)))); + // 准备参数 + RolePageReqVO reqVO = new RolePageReqVO(); + reqVO.setName("土豆"); + reqVO.setCode("tu"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 12)); + + // 调用 + PageResult pageResult = roleService.getRolePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbRole, pageResult.getList().get(0)); + } + + @Test + public void testHasAnySuperAdmin_true() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) + .thenReturn(roleService); + + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class).setCode("super_admin"); + roleMapper.insert(dbRole); + // 准备参数 + Long id = dbRole.getId(); + + // 调用,并调用 + assertTrue(roleService.hasAnySuperAdmin(singletonList(id))); + } + } + + @Test + public void testHasAnySuperAdmin_false() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) + .thenReturn(roleService); + + // mock 数据 + RoleDO dbRole = randomPojo(RoleDO.class).setCode("tenant_admin"); + roleMapper.insert(dbRole); + // 准备参数 + Long id = dbRole.getId(); + + // 调用,并调用 + assertFalse(roleService.hasAnySuperAdmin(singletonList(id))); + } + } + + @Test + public void testValidateRoleList_success() { + // mock 数据 + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + roleMapper.insert(roleDO); + // 准备参数 + List ids = singletonList(roleDO.getId()); + + // 调用,无需断言 + roleService.validateRoleList(ids); + } + + @Test + public void testValidateRoleList_notFound() { + // 准备参数 + List ids = singletonList(randomLongId()); + + // 调用, 并断言异常 + assertServiceException(() -> roleService.validateRoleList(ids), ROLE_NOT_EXISTS); + } + + @Test + public void testValidateRoleList_notEnable() { + // mock 数据 + RoleDO RoleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + roleMapper.insert(RoleDO); + // 准备参数 + List ids = singletonList(RoleDO.getId()); + + // 调用, 并断言异常 + assertServiceException(() -> roleService.validateRoleList(ids), ROLE_IS_DISABLE, RoleDO.getName()); + } +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java new file mode 100644 index 0000000..295911a --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java @@ -0,0 +1,217 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClientFactory; +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; +import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +@Import(SmsChannelServiceImpl.class) +public class SmsChannelServiceTest extends BaseDbUnitTest { + + @Resource + private SmsChannelServiceImpl smsChannelService; + + @Resource + private SmsChannelMapper smsChannelMapper; + + @MockBean + private SmsClientFactory smsClientFactory; + @MockBean + private SmsTemplateService smsTemplateService; + + @Test + public void testCreateSmsChannel_success() { + // 准备参数 + SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class, o -> o.setStatus(randomCommonStatus())) + .setId(null); // 防止 id 被赋值 + + // 调用 + Long smsChannelId = smsChannelService.createSmsChannel(reqVO); + // 断言 + assertNotNull(smsChannelId); + // 校验记录的属性是否正确 + SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId); + assertPojoEquals(reqVO, smsChannel, "id"); + } + + @Test + public void testUpdateSmsChannel_success() { + // mock 数据 + SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); + smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class, o -> { + o.setId(dbSmsChannel.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + o.setCallbackUrl(randomString()); + }); + + // 调用 + smsChannelService.updateSmsChannel(reqVO); + // 校验是否更新正确 + SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, smsChannel); + } + + @Test + public void testUpdateSmsChannel_notExists() { + // 准备参数 + SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> smsChannelService.updateSmsChannel(reqVO), SMS_CHANNEL_NOT_EXISTS); + } + + @Test + public void testDeleteSmsChannel_success() { + // mock 数据 + SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); + smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbSmsChannel.getId(); + + // 调用 + smsChannelService.deleteSmsChannel(id); + // 校验数据不存在了 + assertNull(smsChannelMapper.selectById(id)); + } + + @Test + public void testDeleteSmsChannel_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_NOT_EXISTS); + } + + @Test + public void testDeleteSmsChannel_hasChildren() { + // mock 数据 + SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); + smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbSmsChannel.getId(); + // mock 方法 + when(smsTemplateService.getSmsTemplateCountByChannelId(eq(id))).thenReturn(10L); + + // 调用, 并断言异常 + assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_HAS_CHILDREN); + } + + @Test + public void testGetSmsChannel() { + // mock 数据 + SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); + smsChannelMapper.insert(dbSmsChannel); // @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbSmsChannel.getId(); + + // 调用,并断言 + assertPojoEquals(dbSmsChannel, smsChannelService.getSmsChannel(id)); + } + + @Test + public void testGetSmsChannelList() { + // mock 数据 + SmsChannelDO dbSmsChannel01 = randomPojo(SmsChannelDO.class); + smsChannelMapper.insert(dbSmsChannel01); + SmsChannelDO dbSmsChannel02 = randomPojo(SmsChannelDO.class); + smsChannelMapper.insert(dbSmsChannel02); + // 准备参数 + + // 调用 + List list = smsChannelService.getSmsChannelList(); + // 断言 + assertEquals(2, list.size()); + assertPojoEquals(dbSmsChannel01, list.get(0)); + assertPojoEquals(dbSmsChannel02, list.get(1)); + } + + @Test + public void testGetSmsChannelPage() { + // mock 数据 + SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class, o -> { // 等会查询到 + o.setSignature("芋道源码"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2020, 12, 12)); + }); + smsChannelMapper.insert(dbSmsChannel); + // 测试 signature 不匹配 + smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setSignature("源码"))); + // 测试 status 不匹配 + smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11)))); + // 准备参数 + SmsChannelPageReqVO reqVO = new SmsChannelPageReqVO(); + reqVO.setSignature("芋道"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); + + // 调用 + PageResult pageResult = smsChannelService.getSmsChannelPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbSmsChannel, pageResult.getList().get(0)); + } + + @Test + public void testGetSmsClient_id() { + // mock 数据 + SmsChannelDO channel = randomPojo(SmsChannelDO.class); + smsChannelMapper.insert(channel); + // 准备参数 + Long id = channel.getId(); + // mock 方法 + SmsClient mockClient = mock(SmsClient.class); + SmsChannelProperties properties = BeanUtils.toBean(channel, SmsChannelProperties.class); + when(smsClientFactory.createOrUpdateSmsClient(eq(properties))).thenReturn(mockClient); + + // 调用 + SmsClient client = smsChannelService.getSmsClient(id); + // 断言 + assertSame(client, mockClient); + } + + @Test + public void testGetSmsClient_code() { + // 准备参数 + String code = randomString(); + // mock 方法 + SmsClient mockClient = mock(SmsClient.class); + when(smsClientFactory.getSmsClient(eq(code))).thenReturn(mockClient); + + // 调用 + SmsClient client = smsChannelService.getSmsClient(code); + // 断言 + assertSame(client, mockClient); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImplTest.java new file mode 100644 index 0000000..093a5ae --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImplTest.java @@ -0,0 +1,191 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; +import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO; +import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsCodeMapper; +import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; +import cn.iocoder.yudao.module.system.framework.sms.config.SmsCodeProperties; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import java.time.Duration; +import java.time.LocalDateTime; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@Import(SmsCodeServiceImpl.class) +public class SmsCodeServiceImplTest extends BaseDbUnitTest { + + @Resource + private SmsCodeServiceImpl smsCodeService; + + @Resource + private SmsCodeMapper smsCodeMapper; + + @MockBean + private SmsCodeProperties smsCodeProperties; + @MockBean + private SmsSendService smsSendService; + + @BeforeEach + public void setUp() { + when(smsCodeProperties.getExpireTimes()).thenReturn(Duration.ofMinutes(5)); + when(smsCodeProperties.getSendFrequency()).thenReturn(Duration.ofMinutes(1)); + when(smsCodeProperties.getSendMaximumQuantityPerDay()).thenReturn(10); + when(smsCodeProperties.getBeginCode()).thenReturn(9999); + when(smsCodeProperties.getEndCode()).thenReturn(9999); + } + + @Test + public void sendSmsCode_success() { + // 准备参数 + SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> { + o.setMobile("15601691300"); + o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene()); + }); + + // 调用 + smsCodeService.sendSmsCode(reqDTO); + // 断言 code 验证码 + SmsCodeDO smsCodeDO = smsCodeMapper.selectOne(null); + assertPojoEquals(reqDTO, smsCodeDO); + assertEquals("9999", smsCodeDO.getCode()); + assertEquals(1, smsCodeDO.getTodayIndex()); + assertFalse(smsCodeDO.getUsed()); + // 断言调用 + verify(smsSendService).sendSingleSms(eq(reqDTO.getMobile()), isNull(), isNull(), + eq("user-sms-login"), eq(MapUtil.of("code", "9999"))); + } + + @Test + public void sendSmsCode_tooFast() { + // mock 数据 + SmsCodeDO smsCodeDO = randomPojo(SmsCodeDO.class, + o -> o.setMobile("15601691300").setTodayIndex(1)); + smsCodeMapper.insert(smsCodeDO); + // 准备参数 + SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> { + o.setMobile("15601691300"); + o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene()); + }); + + // 调用,并断言异常 + assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), + SMS_CODE_SEND_TOO_FAST); + } + + @Test + public void sendSmsCode_exceedDay() { + // mock 数据 + SmsCodeDO smsCodeDO = randomPojo(SmsCodeDO.class, + o -> o.setMobile("15601691300").setTodayIndex(10).setCreateTime(LocalDateTime.now())); + smsCodeMapper.insert(smsCodeDO); + // 准备参数 + SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> { + o.setMobile("15601691300"); + o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene()); + }); + when(smsCodeProperties.getSendFrequency()).thenReturn(Duration.ofMillis(0)); + + // 调用,并断言异常 + assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), + SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); + } + + @Test + public void testUseSmsCode_success() { + // 准备参数 + SmsCodeUseReqDTO reqDTO = randomPojo(SmsCodeUseReqDTO.class, o -> { + o.setMobile("15601691300"); + o.setScene(randomEle(SmsSceneEnum.values()).getScene()); + }); + smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> { + o.setMobile(reqDTO.getMobile()).setScene(reqDTO.getScene()) + .setCode(reqDTO.getCode()).setUsed(false); + })); + + // 调用 + smsCodeService.useSmsCode(reqDTO); + // 断言 + SmsCodeDO smsCodeDO = smsCodeMapper.selectOne(null); + assertTrue(smsCodeDO.getUsed()); + assertNotNull(smsCodeDO.getUsedTime()); + assertEquals(reqDTO.getUsedIp(), smsCodeDO.getUsedIp()); + } + + @Test + public void validateSmsCode_success() { + // 准备参数 + SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { + o.setMobile("15601691300"); + o.setScene(randomEle(SmsSceneEnum.values()).getScene()); + }); + smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile()) + .setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(false))); + + // 调用 + smsCodeService.validateSmsCode(reqDTO); + } + + @Test + public void validateSmsCode_notFound() { + // 准备参数 + SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { + o.setMobile("15601691300"); + o.setScene(randomEle(SmsSceneEnum.values()).getScene()); + }); + + // 调用,并断言异常 + assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO), + SMS_CODE_NOT_FOUND); + } + + @Test + public void validateSmsCode_expired() { + // 准备参数 + SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { + o.setMobile("15601691300"); + o.setScene(randomEle(SmsSceneEnum.values()).getScene()); + }); + smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile()) + .setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(false) + .setCreateTime(LocalDateTime.now().minusMinutes(6)))); + + // 调用,并断言异常 + assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO), + SMS_CODE_EXPIRED); + } + + @Test + public void validateSmsCode_used() { + // 准备参数 + SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { + o.setMobile("15601691300"); + o.setScene(randomEle(SmsSceneEnum.values()).getScene()); + }); + smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile()) + .setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(true) + .setCreateTime(LocalDateTime.now()))); + + // 调用,并断言异常 + assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO), + SMS_CODE_USED); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java new file mode 100644 index 0000000..570e8ce --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java @@ -0,0 +1,190 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper; +import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum; +import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum; +import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.function.Consumer; + +import static cn.hutool.core.util.RandomUtil.randomBoolean; +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@Import(SmsLogServiceImpl.class) +public class SmsLogServiceImplTest extends BaseDbUnitTest { + + @Resource + private SmsLogServiceImpl smsLogService; + + @Resource + private SmsLogMapper smsLogMapper; + + @Test + public void testGetSmsLogPage() { + // mock 数据 + SmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到 + o.setChannelId(1L); + o.setTemplateId(10L); + o.setMobile("15601691300"); + o.setSendStatus(SmsSendStatusEnum.INIT.getStatus()); + o.setSendTime(buildTime(2020, 11, 11)); + o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus()); + o.setReceiveTime(buildTime(2021, 11, 11)); + }); + smsLogMapper.insert(dbSmsLog); + // 测试 channelId 不匹配 + smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setChannelId(2L))); + // 测试 templateId 不匹配 + smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setTemplateId(20L))); + // 测试 mobile 不匹配 + smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setMobile("18818260999"))); + // 测试 sendStatus 不匹配 + smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus()))); + // 测试 sendTime 不匹配 + smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setSendTime(buildTime(2020, 12, 12)))); + // 测试 receiveStatus 不匹配 + smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setReceiveStatus(SmsReceiveStatusEnum.SUCCESS.getStatus()))); + // 测试 receiveTime 不匹配 + smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setReceiveTime(buildTime(2021, 12, 12)))); + // 准备参数 + SmsLogPageReqVO reqVO = new SmsLogPageReqVO(); + reqVO.setChannelId(1L); + reqVO.setTemplateId(10L); + reqVO.setMobile("156"); + reqVO.setSendStatus(SmsSendStatusEnum.INIT.getStatus()); + reqVO.setSendTime(buildBetweenTime(2020, 11, 1, 2020, 11, 30)); + reqVO.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus()); + reqVO.setReceiveTime(buildBetweenTime(2021, 11, 1, 2021, 11, 30)); + + // 调用 + PageResult pageResult = smsLogService.getSmsLogPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbSmsLog, pageResult.getList().get(0)); + } + + @Test + public void testCreateSmsLog() { + // 准备参数 + String mobile = randomString(); + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + Boolean isSend = randomBoolean(); + SmsTemplateDO templateDO = randomPojo(SmsTemplateDO.class, + o -> o.setType(randomEle(SmsTemplateTypeEnum.values()).getType())); + String templateContent = randomString(); + Map templateParams = randomTemplateParams(); + // mock 方法 + + // 调用 + Long logId = smsLogService.createSmsLog(mobile, userId, userType, isSend, + templateDO, templateContent, templateParams); + // 断言 + SmsLogDO logDO = smsLogMapper.selectById(logId); + assertEquals(isSend ? SmsSendStatusEnum.INIT.getStatus() : SmsSendStatusEnum.IGNORE.getStatus(), + logDO.getSendStatus()); + assertEquals(mobile, logDO.getMobile()); + assertEquals(userType, logDO.getUserType()); + assertEquals(userId, logDO.getUserId()); + assertEquals(templateDO.getId(), logDO.getTemplateId()); + assertEquals(templateDO.getCode(), logDO.getTemplateCode()); + assertEquals(templateDO.getType(), logDO.getTemplateType()); + assertEquals(templateDO.getChannelId(), logDO.getChannelId()); + assertEquals(templateDO.getChannelCode(), logDO.getChannelCode()); + assertEquals(templateContent, logDO.getTemplateContent()); + assertEquals(templateParams, logDO.getTemplateParams()); + assertEquals(SmsReceiveStatusEnum.INIT.getStatus(), logDO.getReceiveStatus()); + } + + @Test + public void testUpdateSmsSendResult() { + // mock 数据 + SmsLogDO dbSmsLog = randomSmsLogDO( + o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus())); + smsLogMapper.insert(dbSmsLog); + // 准备参数 + Long id = dbSmsLog.getId(); + Boolean success = randomBoolean(); + String apiSendCode = randomString(); + String apiSendMsg = randomString(); + String apiRequestId = randomString(); + String apiSerialNo = randomString(); + + // 调用 + smsLogService.updateSmsSendResult(id, success, + apiSendCode, apiSendMsg, apiRequestId, apiSerialNo); + // 断言 + dbSmsLog = smsLogMapper.selectById(id); + assertEquals(success ? SmsSendStatusEnum.SUCCESS.getStatus() : SmsSendStatusEnum.FAILURE.getStatus(), + dbSmsLog.getSendStatus()); + assertNotNull(dbSmsLog.getSendTime()); + assertEquals(apiSendCode, dbSmsLog.getApiSendCode()); + assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg()); + assertEquals(apiRequestId, dbSmsLog.getApiRequestId()); + assertEquals(apiSerialNo, dbSmsLog.getApiSerialNo()); + } + + @Test + public void testUpdateSmsReceiveResult() { + // mock 数据 + SmsLogDO dbSmsLog = randomSmsLogDO( + o -> o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus())); + smsLogMapper.insert(dbSmsLog); + // 准备参数 + Long id = dbSmsLog.getId(); + Boolean success = randomBoolean(); + LocalDateTime receiveTime = randomLocalDateTime(); + String apiReceiveCode = randomString(); + String apiReceiveMsg = randomString(); + + // 调用 + smsLogService.updateSmsReceiveResult(id, success, receiveTime, apiReceiveCode, apiReceiveMsg); + // 断言 + dbSmsLog = smsLogMapper.selectById(id); + assertEquals(success ? SmsReceiveStatusEnum.SUCCESS.getStatus() + : SmsReceiveStatusEnum.FAILURE.getStatus(), dbSmsLog.getReceiveStatus()); + assertEquals(receiveTime, dbSmsLog.getReceiveTime()); + assertEquals(apiReceiveCode, dbSmsLog.getApiReceiveCode()); + assertEquals(apiReceiveMsg, dbSmsLog.getApiReceiveMsg()); + } + + // ========== 随机对象 ========== + + @SafeVarargs + private static SmsLogDO randomSmsLogDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setTemplateParams(randomTemplateParams()); + o.setTemplateType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 templateType 的范围 + o.setUserType(randomEle(UserTypeEnum.values()).getValue()); // 保证 userType 的范围 + o.setSendStatus(randomEle(SmsSendStatusEnum.values()).getStatus()); // 保证 sendStatus 的范围 + o.setReceiveStatus(randomEle(SmsReceiveStatusEnum.values()).getStatus()); // 保证 receiveStatus 的范围 + }; + return randomPojo(SmsLogDO.class, ArrayUtils.append(consumer, consumers)); + } + + private static Map randomTemplateParams() { + return MapUtil.builder().put(randomString(), randomString()) + .put(randomString(), randomString()).build(); + } +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java new file mode 100644 index 0000000..487c6f7 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java @@ -0,0 +1,298 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; +import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; +import cn.iocoder.yudao.module.system.service.member.MemberService; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +public class SmsSendServiceImplTest extends BaseMockitoUnitTest { + + @InjectMocks + private SmsSendServiceImpl smsSendService; + + @Mock + private AdminUserService adminUserService; + @Mock + private MemberService memberService; + @Mock + private SmsChannelService smsChannelService; + @Mock + private SmsTemplateService smsTemplateService; + @Mock + private SmsLogService smsLogService; + @Mock + private SmsProducer smsProducer; + + @Test + public void testSendSingleSmsToAdmin() { + // 准备参数 + Long userId = randomLongId(); + String templateCode = randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock adminUserService 的方法 + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setMobile("15601691300")); + when(adminUserService.getUser(eq(userId))).thenReturn(user); + + // mock SmsTemplateService 的方法 + SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String content = randomString(); + when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock SmsChannelService 的方法 + SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); + // mock SmsLogService 的方法 + Long smsLogId = randomLongId(); + when(smsLogService.createSmsLog(eq(user.getMobile()), eq(userId), eq(UserTypeEnum.ADMIN.getValue()), eq(Boolean.TRUE), eq(template), + eq(content), eq(templateParams))).thenReturn(smsLogId); + + // 调用 + Long resultSmsLogId = smsSendService.sendSingleSmsToAdmin(null, userId, templateCode, templateParams); + // 断言 + assertEquals(smsLogId, resultSmsLogId); + // 断言调用 + verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(user.getMobile()), + eq(template.getChannelId()), eq(template.getApiTemplateId()), + eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); + } + + @Test + public void testSendSingleSmsToUser() { + // 准备参数 + Long userId = randomLongId(); + String templateCode = randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock memberService 的方法 + String mobile = "15601691300"; + when(memberService.getMemberUserMobile(eq(userId))).thenReturn(mobile); + + // mock SmsTemplateService 的方法 + SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String content = randomString(); + when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock SmsChannelService 的方法 + SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); + // mock SmsLogService 的方法 + Long smsLogId = randomLongId(); + when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(UserTypeEnum.MEMBER.getValue()), eq(Boolean.TRUE), eq(template), + eq(content), eq(templateParams))).thenReturn(smsLogId); + + // 调用 + Long resultSmsLogId = smsSendService.sendSingleSmsToMember(null, userId, templateCode, templateParams); + // 断言 + assertEquals(smsLogId, resultSmsLogId); + // 断言调用 + verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(mobile), + eq(template.getChannelId()), eq(template.getApiTemplateId()), + eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); + } + + /** + * 发送成功,当短信模板开启时 + */ + @Test + public void testSendSingleSms_successWhenSmsTemplateEnable() { + // 准备参数 + String mobile = randomString(); + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String templateCode = randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock SmsTemplateService 的方法 + SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String content = randomString(); + when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock SmsChannelService 的方法 + SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); + // mock SmsLogService 的方法 + Long smsLogId = randomLongId(); + when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.TRUE), eq(template), + eq(content), eq(templateParams))).thenReturn(smsLogId); + + // 调用 + Long resultSmsLogId = smsSendService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); + // 断言 + assertEquals(smsLogId, resultSmsLogId); + // 断言调用 + verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(mobile), + eq(template.getChannelId()), eq(template.getApiTemplateId()), + eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); + } + + /** + * 发送成功,当短信模板关闭时 + */ + @Test + public void testSendSingleSms_successWhenSmsTemplateDisable() { + // 准备参数 + String mobile = randomString(); + Long userId = randomLongId(); + Integer userType = randomEle(UserTypeEnum.values()).getValue(); + String templateCode = randomString(); + Map templateParams = MapUtil.builder().put("code", "1234") + .put("op", "login").build(); + // mock SmsTemplateService 的方法 + SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> { + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); + o.setContent("验证码为{code}, 操作为{op}"); + o.setParams(Lists.newArrayList("code", "op")); + }); + when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); + String content = randomString(); + when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) + .thenReturn(content); + // mock SmsChannelService 的方法 + SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); + // mock SmsLogService 的方法 + Long smsLogId = randomLongId(); + when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.FALSE), eq(template), + eq(content), eq(templateParams))).thenReturn(smsLogId); + + // 调用 + Long resultSmsLogId = smsSendService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); + // 断言 + assertEquals(smsLogId, resultSmsLogId); + // 断言调用 + verify(smsProducer, times(0)).sendSmsSendMessage(anyLong(), anyString(), + anyLong(), any(), anyList()); + } + + @Test + public void testCheckSmsTemplateValid_notExists() { + // 准备参数 + String templateCode = randomString(); + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> smsSendService.validateSmsTemplate(templateCode), + SMS_SEND_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testBuildTemplateParams_paramMiss() { + // 准备参数 + SmsTemplateDO template = randomPojo(SmsTemplateDO.class, + o -> o.setParams(Lists.newArrayList("code"))); + Map templateParams = new HashMap<>(); + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> smsSendService.buildTemplateParams(template, templateParams), + SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code"); + } + + @Test + public void testCheckMobile_notExists() { + // 准备参数 + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> smsSendService.validateMobile(null), + SMS_SEND_MOBILE_NOT_EXISTS); + } + + @Test + public void testSendBatchNotify() { + // 准备参数 + // mock 方法 + + // 调用 + UnsupportedOperationException exception = Assertions.assertThrows( + UnsupportedOperationException.class, + () -> smsSendService.sendBatchSms(null, null, null, null, null) + ); + // 断言 + assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage()); + } + + @Test + @SuppressWarnings("unchecked") + public void testDoSendSms() throws Throwable { + // 准备参数 + SmsSendMessage message = randomPojo(SmsSendMessage.class); + // mock SmsClientFactory 的方法 + SmsClient smsClient = spy(SmsClient.class); + when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient); + // mock SmsClient 的方法 + SmsSendRespDTO sendResult = randomPojo(SmsSendRespDTO.class); + when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()), + eq(message.getTemplateParams()))).thenReturn(sendResult); + + // 调用 + smsSendService.doSendSms(message); + // 断言 + verify(smsLogService).updateSmsSendResult(eq(message.getLogId()), + eq(sendResult.getSuccess()), eq(sendResult.getApiCode()), + eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getSerialNo())); + } + + @Test + public void testReceiveSmsStatus() throws Throwable { + // 准备参数 + String channelCode = randomString(); + String text = randomString(); + // mock SmsClientFactory 的方法 + SmsClient smsClient = spy(SmsClient.class); + when(smsChannelService.getSmsClient(eq(channelCode))).thenReturn(smsClient); + // mock SmsClient 的方法 + List receiveResults = randomPojoList(SmsReceiveRespDTO.class); + + // 调用 + smsSendService.receiveSmsStatus(channelCode, text); + // 断言 + receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()), + eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode()))); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java new file mode 100644 index 0000000..22f737d --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java @@ -0,0 +1,348 @@ +package cn.iocoder.yudao.module.system.service.sms; + +import cn.hutool.core.map.MapUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; +import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; +import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; +import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Import(SmsTemplateServiceImpl.class) +public class SmsTemplateServiceImplTest extends BaseDbUnitTest { + + @Resource + private SmsTemplateServiceImpl smsTemplateService; + + @Resource + private SmsTemplateMapper smsTemplateMapper; + + @MockBean + private SmsChannelService smsChannelService; + @MockBean + private SmsClient smsClient; + + @Test + public void testFormatSmsTemplateContent() { + // 准备参数 + String content = "正在进行登录操作{operation},您的验证码是{code}"; + Map params = MapUtil.builder("operation", "登录") + .put("code", "1234").build(); + + // 调用 + String result = smsTemplateService.formatSmsTemplateContent(content, params); + // 断言 + assertEquals("正在进行登录操作登录,您的验证码是1234", result); + } + + @Test + public void testParseTemplateContentParams() { + // 准备参数 + String content = "正在进行登录操作{operation},您的验证码是{code}"; + // mock 方法 + + // 调用 + List params = smsTemplateService.parseTemplateContentParams(content); + // 断言 + assertEquals(Lists.newArrayList("operation", "code"), params); + } + + @Test + @SuppressWarnings("unchecked") + public void testCreateSmsTemplate_success() throws Throwable { + // 准备参数 + SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class, o -> { + o.setContent("正在进行登录操作{operation},您的验证码是{code}"); + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 + }).setId(null); // 防止 id 被赋值 + // mock Channel 的方法 + SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { + o.setId(reqVO.getChannelId()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 + }); + when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); + // mock 获得 API 短信模板成功 + when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); + when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn( + randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()))); + + // 调用 + Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO); + // 断言 + assertNotNull(smsTemplateId); + // 校验记录的属性是否正确 + SmsTemplateDO smsTemplate = smsTemplateMapper.selectById(smsTemplateId); + assertPojoEquals(reqVO, smsTemplate, "id"); + assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); + assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); + } + + @Test + @SuppressWarnings("unchecked") + public void testUpdateSmsTemplate_success() throws Throwable { + // mock 数据 + SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); + smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class, o -> { + o.setId(dbSmsTemplate.getId()); // 设置更新的 ID + o.setContent("正在进行登录操作{operation},您的验证码是{code}"); + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 + }); + // mock 方法 + SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { + o.setId(reqVO.getChannelId()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 + }); + when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); + // mock 获得 API 短信模板成功 + when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); + when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn( + randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()))); + + // 调用 + smsTemplateService.updateSmsTemplate(reqVO); + // 校验是否更新正确 + SmsTemplateDO smsTemplate = smsTemplateMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, smsTemplate); + assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); + assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); + } + + @Test + public void testUpdateSmsTemplate_notExists() { + // 准备参数 + SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> smsTemplateService.updateSmsTemplate(reqVO), SMS_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testDeleteSmsTemplate_success() { + // mock 数据 + SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); + smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbSmsTemplate.getId(); + + // 调用 + smsTemplateService.deleteSmsTemplate(id); + // 校验数据不存在了 + assertNull(smsTemplateMapper.selectById(id)); + } + + @Test + public void testDeleteSmsTemplate_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> smsTemplateService.deleteSmsTemplate(id), SMS_TEMPLATE_NOT_EXISTS); + } + + @Test + public void testGetSmsTemplate() { + // mock 数据 + SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); + smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbSmsTemplate.getId(); + + // 调用 + SmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplate(id); + // 校验 + assertPojoEquals(dbSmsTemplate, smsTemplate); + } + + @Test + public void testGetSmsTemplateByCodeFromCache() { + // mock 数据 + SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); + smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 + // 准备参数 + String code = dbSmsTemplate.getCode(); + + // 调用 + SmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplateByCodeFromCache(code); + // 校验 + assertPojoEquals(dbSmsTemplate, smsTemplate); + } + + @Test + public void testGetSmsTemplatePage() { + // mock 数据 + SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到 + o.setType(SmsTemplateTypeEnum.PROMOTION.getType()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCode("tudou"); + o.setContent("芋道源码"); + o.setApiTemplateId("yunai"); + o.setChannelId(1L); + o.setCreateTime(buildTime(2021, 11, 11)); + }); + smsTemplateMapper.insert(dbSmsTemplate); + // 测试 type 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType()))); + // 测试 status 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 code 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma"))); + // 测试 content 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码"))); + // 测试 apiTemplateId 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai"))); + // 测试 channelId 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); + // 测试 createTime 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12)))); + // 准备参数 + SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO(); + reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType()); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCode("tu"); + reqVO.setContent("芋道"); + reqVO.setApiTemplateId("yu"); + reqVO.setChannelId(1L); + reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); + + // 调用 + PageResult pageResult = smsTemplateService.getSmsTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0)); + } + + @Test + public void testGetSmsTemplateCountByChannelId() { + // mock 数据 + SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> o.setChannelId(1L)); + smsTemplateMapper.insert(dbSmsTemplate); + // 测试 channelId 不匹配 + smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); + // 准备参数 + Long channelId = 1L; + + // 调用 + Long count = smsTemplateService.getSmsTemplateCountByChannelId(channelId); + // 断言 + assertEquals(1, count); + } + + @Test + public void testValidateSmsChannel_success() { + // 准备参数 + Long channelId = randomLongId(); + // mock 方法 + SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { + o.setId(channelId); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 + }); + when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO); + + // 调用 + SmsChannelDO returnChannelDO = smsTemplateService.validateSmsChannel(channelId); + // 断言 + assertPojoEquals(returnChannelDO, channelDO); + } + + @Test + public void testValidateSmsChannel_notExists() { + // 准备参数 + Long channelId = randomLongId(); + + // 调用,校验异常 + assertServiceException(() -> smsTemplateService.validateSmsChannel(channelId), + SMS_CHANNEL_NOT_EXISTS); + } + + @Test + public void testValidateSmsChannel_disable() { + // 准备参数 + Long channelId = randomLongId(); + // mock 方法 + SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { + o.setId(channelId); + o.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 保证 status 禁用,触发失败 + }); + when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO); + + // 调用,校验异常 + assertServiceException(() -> smsTemplateService.validateSmsChannel(channelId), + SMS_CHANNEL_DISABLE); + } + + @Test + public void testValidateDictDataValueUnique_success() { + // 调用,成功 + smsTemplateService.validateSmsTemplateCodeDuplicate(randomLongId(), randomString()); + } + + @Test + public void testValidateSmsTemplateCodeDuplicate_valueDuplicateForCreate() { + // 准备参数 + String code = randomString(); + // mock 数据 + smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code))); + + // 调用,校验异常 + assertServiceException(() -> smsTemplateService.validateSmsTemplateCodeDuplicate(null, code), + SMS_TEMPLATE_CODE_DUPLICATE, code); + } + + @Test + public void testValidateDictDataValueUnique_valueDuplicateForUpdate() { + // 准备参数 + Long id = randomLongId(); + String code = randomString(); + // mock 数据 + smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code))); + + // 调用,校验异常 + assertServiceException(() -> smsTemplateService.validateSmsTemplateCodeDuplicate(id, code), + SMS_TEMPLATE_CODE_DUPLICATE, code); + } + + // ========== 随机对象 ========== + + @SafeVarargs + private static SmsTemplateDO randomSmsTemplateDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 + }; + return randomPojo(SmsTemplateDO.class, ArrayUtils.append(consumer, consumers)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java new file mode 100644 index 0000000..02f7a61 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java @@ -0,0 +1,471 @@ +package cn.iocoder.yudao.module.system.service.social; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.api.WxMaUserService; +import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; +import cn.hutool.core.util.ReflectUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import cn.iocoder.yudao.module.system.framework.justauth.core.AuthRequestFactory; +import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; +import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; +import jakarta.annotation.Resource; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import me.zhyd.oauth.config.AuthConfig; +import me.zhyd.oauth.model.AuthResponse; +import me.zhyd.oauth.model.AuthUser; +import me.zhyd.oauth.request.AuthDefaultRequest; +import me.zhyd.oauth.request.AuthRequest; +import me.zhyd.oauth.utils.AuthStateUtils; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.data.redis.core.StringRedisTemplate; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * {@link SocialClientServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(SocialClientServiceImpl.class) +public class SocialClientServiceImplTest extends BaseDbUnitTest { + + @Resource + private SocialClientServiceImpl socialClientService; + + @Resource + private SocialClientMapper socialClientMapper; + + @MockBean + private AuthRequestFactory authRequestFactory; + + @MockBean + private WxMpService wxMpService; + @MockBean + private WxMpProperties wxMpProperties; + @MockBean + private StringRedisTemplate stringRedisTemplate; + @MockBean + private WxMaService wxMaService; + @MockBean + private WxMaProperties wxMaProperties; + + @Test + public void testGetAuthorizeUrl() { + try (MockedStatic authStateUtilsMock = mockStatic(AuthStateUtils.class)) { + // 准备参数 + Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + String redirectUri = "sss"; + // mock 获得对应的 AuthRequest 实现 + AuthRequest authRequest = mock(AuthRequest.class); + when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); + // mock 方法 + authStateUtilsMock.when(AuthStateUtils::createState).thenReturn("aoteman"); + when(authRequest.authorize(eq("aoteman"))).thenReturn("https://www.iocoder.cn?redirect_uri=yyy"); + + // 调用 + String url = socialClientService.getAuthorizeUrl(socialType, userType, redirectUri); + // 断言 + assertEquals("https://www.iocoder.cn?redirect_uri=sss", url); + } + } + + @Test + public void testAuthSocialUser_success() { + // 准备参数 + Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + String code = randomString(); + String state = randomString(); + // mock 方法(AuthRequest) + AuthRequest authRequest = mock(AuthRequest.class); + when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); + // mock 方法(AuthResponse) + AuthUser authUser = randomPojo(AuthUser.class); + AuthResponse authResponse = new AuthResponse<>(2000, null, authUser); + when(authRequest.login(argThat(authCallback -> { + assertEquals(code, authCallback.getCode()); + assertEquals(state, authCallback.getState()); + return true; + }))).thenReturn(authResponse); + + // 调用 + AuthUser result = socialClientService.getAuthUser(socialType, userType, code, state); + // 断言 + assertSame(authUser, result); + } + + @Test + public void testAuthSocialUser_fail() { + // 准备参数 + Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + String code = randomString(); + String state = randomString(); + // mock 方法(AuthRequest) + AuthRequest authRequest = mock(AuthRequest.class); + when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); + // mock 方法(AuthResponse) + AuthResponse authResponse = new AuthResponse<>(0, "模拟失败", null); + when(authRequest.login(argThat(authCallback -> { + assertEquals(code, authCallback.getCode()); + assertEquals(state, authCallback.getState()); + return true; + }))).thenReturn(authResponse); + + // 调用并断言 + assertServiceException( + () -> socialClientService.getAuthUser(socialType, userType, code, state), + SOCIAL_USER_AUTH_FAILURE, "模拟失败"); + } + + @Test + public void testBuildAuthRequest_clientNull() { + // 准备参数 + Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); + Integer userType = randomPojo(SocialTypeEnum.class).getType(); + // mock 获得对应的 AuthRequest 实现 + AuthRequest authRequest = mock(AuthDefaultRequest.class); + AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(authRequest, "config"); + when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); + + // 调用 + AuthRequest result = socialClientService.buildAuthRequest(socialType, userType); + // 断言 + assertSame(authRequest, result); + assertSame(authConfig, ReflectUtil.getFieldValue(authConfig, "config")); + } + + @Test + public void testBuildAuthRequest_clientDisable() { + // 准备参数 + Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); + Integer userType = randomPojo(SocialTypeEnum.class).getType(); + // mock 获得对应的 AuthRequest 实现 + AuthRequest authRequest = mock(AuthDefaultRequest.class); + AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(authRequest, "config"); + when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); + // mock 数据 + SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()) + .setUserType(userType).setSocialType(socialType)); + socialClientMapper.insert(client); + + // 调用 + AuthRequest result = socialClientService.buildAuthRequest(socialType, userType); + // 断言 + assertSame(authRequest, result); + assertSame(authConfig, ReflectUtil.getFieldValue(authConfig, "config")); + } + + @Test + public void testBuildAuthRequest_clientEnable() { + // 准备参数 + Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); + Integer userType = randomPojo(SocialTypeEnum.class).getType(); + // mock 获得对应的 AuthRequest 实现 + AuthConfig authConfig = mock(AuthConfig.class); + AuthRequest authRequest = mock(AuthDefaultRequest.class); + ReflectUtil.setFieldValue(authRequest, "config", authConfig); + when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); + // mock 数据 + SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) + .setUserType(userType).setSocialType(socialType)); + socialClientMapper.insert(client); + + // 调用 + AuthRequest result = socialClientService.buildAuthRequest(socialType, userType); + // 断言 + assertSame(authRequest, result); + assertNotSame(authConfig, ReflectUtil.getFieldValue(authRequest, "config")); + } + + // =================== 微信公众号独有 =================== + + @Test + public void testCreateWxMpJsapiSignature() throws WxErrorException { + // 准备参数 + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + String url = randomString(); + // mock 方法 + WxJsapiSignature signature = randomPojo(WxJsapiSignature.class); + when(wxMpService.createJsapiSignature(eq(url))).thenReturn(signature); + + // 调用 + WxJsapiSignature result = socialClientService.createWxMpJsapiSignature(userType, url); + // 断言 + assertSame(signature, result); + } + + @Test + public void testGetWxMpService_clientNull() { + // 准备参数 + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + // mock 方法 + + // 调用 + WxMpService result = socialClientService.getWxMpService(userType); + // 断言 + assertSame(wxMpService, result); + } + + @Test + public void testGetWxMpService_clientDisable() { + // 准备参数 + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + // mock 数据 + SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()) + .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MP.getType())); + socialClientMapper.insert(client); + + // 调用 + WxMpService result = socialClientService.getWxMpService(userType); + // 断言 + assertSame(wxMpService, result); + } + + @Test + public void testGetWxMpService_clientEnable() { + // 准备参数 + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + // mock 数据 + SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) + .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MP.getType())); + socialClientMapper.insert(client); + // mock 方法 + WxMpProperties.ConfigStorage configStorage = mock(WxMpProperties.ConfigStorage.class); + when(wxMpProperties.getConfigStorage()).thenReturn(configStorage); + + // 调用 + WxMpService result = socialClientService.getWxMpService(userType); + // 断言 + assertNotSame(wxMpService, result); + assertEquals(client.getClientId(), result.getWxMpConfigStorage().getAppId()); + assertEquals(client.getClientSecret(), result.getWxMpConfigStorage().getSecret()); + } + + // =================== 微信小程序独有 =================== + + @Test + public void testGetWxMaPhoneNumberInfo_success() throws WxErrorException { + // 准备参数 + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + String phoneCode = randomString(); + // mock 方法 + WxMaUserService userService = mock(WxMaUserService.class); + when(wxMaService.getUserService()).thenReturn(userService); + WxMaPhoneNumberInfo phoneNumber = randomPojo(WxMaPhoneNumberInfo.class); + when(userService.getPhoneNoInfo(eq(phoneCode))).thenReturn(phoneNumber); + + // 调用 + WxMaPhoneNumberInfo result = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode); + // 断言 + assertSame(phoneNumber, result); + } + + @Test + public void testGetWxMaPhoneNumberInfo_exception() throws WxErrorException { + // 准备参数 + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + String phoneCode = randomString(); + // mock 方法 + WxMaUserService userService = mock(WxMaUserService.class); + when(wxMaService.getUserService()).thenReturn(userService); + WxErrorException wxErrorException = new WxErrorException(new NullPointerException()); + when(userService.getPhoneNoInfo(eq(phoneCode))).thenThrow(wxErrorException); + + // 调用并断言异常 + assertServiceException(() -> socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode), + SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR); + } + + @Test + public void testGetWxMaService_clientNull() { + // 准备参数 + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + // mock 方法 + + // 调用 + WxMaService result = socialClientService.getWxMaService(userType); + // 断言 + assertSame(wxMaService, result); + } + + @Test + public void testGetWxMaService_clientDisable() { + // 准备参数 + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + // mock 数据 + SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()) + .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MINI_PROGRAM.getType())); + socialClientMapper.insert(client); + + // 调用 + WxMaService result = socialClientService.getWxMaService(userType); + // 断言 + assertSame(wxMaService, result); + } + + @Test + public void testGetWxMaService_clientEnable() { + // 准备参数 + Integer userType = randomPojo(UserTypeEnum.class).getValue(); + // mock 数据 + SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) + .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MINI_PROGRAM.getType())); + socialClientMapper.insert(client); + // mock 方法 + WxMaProperties.ConfigStorage configStorage = mock(WxMaProperties.ConfigStorage.class); + when(wxMaProperties.getConfigStorage()).thenReturn(configStorage); + + // 调用 + WxMaService result = socialClientService.getWxMaService(userType); + // 断言 + assertNotSame(wxMaService, result); + assertEquals(client.getClientId(), result.getWxMaConfig().getAppid()); + assertEquals(client.getClientSecret(), result.getWxMaConfig().getSecret()); + } + + // =================== 客户端管理 =================== + + @Test + public void testCreateSocialClient_success() { + // 准备参数 + SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, + o -> o.setSocialType(randomEle(SocialTypeEnum.values()).getType()) + .setUserType(randomEle(UserTypeEnum.values()).getValue()) + .setStatus(randomCommonStatus())) + .setId(null); // 防止 id 被赋值 + + // 调用 + Long socialClientId = socialClientService.createSocialClient(reqVO); + // 断言 + assertNotNull(socialClientId); + // 校验记录的属性是否正确 + SocialClientDO socialClient = socialClientMapper.selectById(socialClientId); + assertPojoEquals(reqVO, socialClient, "id"); + } + + @Test + public void testUpdateSocialClient_success() { + // mock 数据 + SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class); + socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据 + // 准备参数 + SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, o -> { + o.setId(dbSocialClient.getId()); // 设置更新的 ID + o.setSocialType(randomEle(SocialTypeEnum.values()).getType()) + .setUserType(randomEle(UserTypeEnum.values()).getValue()) + .setStatus(randomCommonStatus()); + }); + + // 调用 + socialClientService.updateSocialClient(reqVO); + // 校验是否更新正确 + SocialClientDO socialClient = socialClientMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, socialClient); + } + + @Test + public void testUpdateSocialClient_notExists() { + // 准备参数 + SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> socialClientService.updateSocialClient(reqVO), SOCIAL_CLIENT_NOT_EXISTS); + } + + @Test + public void testDeleteSocialClient_success() { + // mock 数据 + SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class); + socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbSocialClient.getId(); + + // 调用 + socialClientService.deleteSocialClient(id); + // 校验数据不存在了 + assertNull(socialClientMapper.selectById(id)); + } + + @Test + public void testDeleteSocialClient_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> socialClientService.deleteSocialClient(id), SOCIAL_CLIENT_NOT_EXISTS); + } + + @Test + public void testGetSocialClient() { + // mock 数据 + SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class); + socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbSocialClient.getId(); + + // 调用 + SocialClientDO socialClient = socialClientService.getSocialClient(id); + // 校验数据正确 + assertPojoEquals(dbSocialClient, socialClient); + } + + @Test + public void testGetSocialClientPage() { + // mock 数据 + SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class, o -> { // 等会查询到 + o.setName("芋头"); + o.setSocialType(SocialTypeEnum.GITEE.getType()); + o.setUserType(UserTypeEnum.ADMIN.getValue()); + o.setClientId("yudao"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + socialClientMapper.insert(dbSocialClient); + // 测试 name 不匹配 + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setName(randomString()))); + // 测试 socialType 不匹配 + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setSocialType(SocialTypeEnum.DINGTALK.getType()))); + // 测试 userType 不匹配 + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); + // 测试 clientId 不匹配 + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientId("dao"))); + // 测试 status 不匹配 + socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 准备参数 + SocialClientPageReqVO reqVO = new SocialClientPageReqVO(); + reqVO.setName("芋"); + reqVO.setSocialType(SocialTypeEnum.GITEE.getType()); + reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); + reqVO.setClientId("yu"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + + // 调用 + PageResult pageResult = socialClientService.getSocialClientPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbSocialClient, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java new file mode 100644 index 0000000..8416415 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java @@ -0,0 +1,288 @@ +package cn.iocoder.yudao.module.system.service.social; + +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; +import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; +import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; +import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; +import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; +import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; +import jakarta.annotation.Resource; +import me.zhyd.oauth.model.AuthUser; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import java.util.List; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.hutool.core.util.RandomUtil.randomLong; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.when; + +/** + * {@link SocialUserServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(SocialUserServiceImpl.class) +public class SocialUserServiceImplTest extends BaseDbUnitTest { + + @Resource + private SocialUserServiceImpl socialUserService; + + @Resource + private SocialUserMapper socialUserMapper; + @Resource + private SocialUserBindMapper socialUserBindMapper; + + @MockBean + private SocialClientService socialClientService; + + @Test + public void testGetSocialUserList() { + Long userId = 1L; + Integer userType = UserTypeEnum.ADMIN.getValue(); + // mock 获得社交用户 + SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(SocialTypeEnum.GITEE.getType()); + socialUserMapper.insert(socialUser); // 可被查到 + socialUserMapper.insert(randomPojo(SocialUserDO.class)); // 不可被查到 + // mock 获得绑定 + socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 可被查询到 + .setUserId(userId).setUserType(userType).setSocialType(SocialTypeEnum.GITEE.getType()) + .setSocialUserId(socialUser.getId())); + socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 不可被查询到 + .setUserId(2L).setUserType(userType).setSocialType(SocialTypeEnum.DINGTALK.getType())); + + // 调用 + List result = socialUserService.getSocialUserList(userId, userType); + // 断言 + assertEquals(1, result.size()); + assertPojoEquals(socialUser, result.get(0)); + } + + @Test + public void testBindSocialUser() { + // 准备参数 + SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO() + .setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue()) + .setSocialType(SocialTypeEnum.GITEE.getType()).setCode("test_code").setState("test_state"); + // mock 数据:获得社交用户 + SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(reqDTO.getSocialType()) + .setCode(reqDTO.getCode()).setState(reqDTO.getState()); + socialUserMapper.insert(socialUser); + // mock 数据:用户可能之前已经绑定过该社交类型 + socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue()) + .setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(-1L)); + // mock 数据:社交用户可能之前绑定过别的用户 + socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserType(UserTypeEnum.ADMIN.getValue()) + .setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId())); + + // 调用 + String openid = socialUserService.bindSocialUser(reqDTO); + // 断言 + List socialUserBinds = socialUserBindMapper.selectList(); + assertEquals(1, socialUserBinds.size()); + assertEquals(socialUser.getOpenid(), openid); + } + + @Test + public void testUnbindSocialUser_success() { + // 准备参数 + Long userId = 1L; + Integer userType = UserTypeEnum.ADMIN.getValue(); + Integer type = SocialTypeEnum.GITEE.getType(); + String openid = "test_openid"; + // mock 数据:社交用户 + SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setOpenid(openid); + socialUserMapper.insert(socialUser); + // mock 数据:社交绑定关系 + SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType) + .setUserId(userId).setSocialType(type); + socialUserBindMapper.insert(socialUserBind); + + // 调用 + socialUserService.unbindSocialUser(userId, userType, type, openid); + // 断言 + assertEquals(0, socialUserBindMapper.selectCount(null).intValue()); + } + + @Test + public void testUnbindSocialUser_notFound() { + // 调用,并断言 + assertServiceException( + () -> socialUserService.unbindSocialUser(randomLong(), UserTypeEnum.ADMIN.getValue(), + SocialTypeEnum.GITEE.getType(), "test_openid"), + SOCIAL_USER_NOT_FOUND); + } + + @Test + public void testGetSocialUser() { + // 准备参数 + Integer userType = UserTypeEnum.ADMIN.getValue(); + Integer type = SocialTypeEnum.GITEE.getType(); + String code = "tudou"; + String state = "yuanma"; + // mock 社交用户 + SocialUserDO socialUserDO = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state); + socialUserMapper.insert(socialUserDO); + // mock 社交用户的绑定 + Long userId = randomLong(); + SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId) + .setSocialType(type).setSocialUserId(socialUserDO.getId()); + socialUserBindMapper.insert(socialUserBind); + + // 调用 + SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(userType, type, code, state); + // 断言 + assertEquals(userId, socialUser.getUserId()); + assertEquals(socialUserDO.getOpenid(), socialUser.getOpenid()); + } + + @Test + public void testAuthSocialUser_exists() { + // 准备参数 + Integer socialType = SocialTypeEnum.GITEE.getType(); + Integer userType = randomEle(SocialTypeEnum.values()).getType(); + String code = "tudou"; + String state = "yuanma"; + // mock 方法 + SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(socialType).setCode(code).setState(state); + socialUserMapper.insert(socialUser); + + // 调用 + SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); + // 断言 + assertPojoEquals(socialUser, result); + } + + @Test + public void testAuthSocialUser_notNull() { + // mock 数据 + SocialUserDO socialUser = randomPojo(SocialUserDO.class, + o -> o.setType(SocialTypeEnum.GITEE.getType()).setCode("tudou").setState("yuanma")); + socialUserMapper.insert(socialUser); + // 准备参数 + Integer socialType = SocialTypeEnum.GITEE.getType(); + Integer userType = randomEle(SocialTypeEnum.values()).getType(); + String code = "tudou"; + String state = "yuanma"; + + // 调用 + SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); + // 断言 + assertPojoEquals(socialUser, result); + } + + @Test + public void testAuthSocialUser_insert() { + // 准备参数 + Integer socialType = SocialTypeEnum.GITEE.getType(); + Integer userType = randomEle(SocialTypeEnum.values()).getType(); + String code = "tudou"; + String state = "yuanma"; + // mock 方法 + AuthUser authUser = randomPojo(AuthUser.class); + when(socialClientService.getAuthUser(eq(socialType), eq(userType), eq(code), eq(state))).thenReturn(authUser); + + // 调用 + SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); + // 断言 + assertBindSocialUser(socialType, result, authUser); + assertEquals(code, result.getCode()); + assertEquals(state, result.getState()); + } + + @Test + public void testAuthSocialUser_update() { + // 准备参数 + Integer socialType = SocialTypeEnum.GITEE.getType(); + Integer userType = randomEle(SocialTypeEnum.values()).getType(); + String code = "tudou"; + String state = "yuanma"; + // mock 数据 + socialUserMapper.insert(randomPojo(SocialUserDO.class).setType(socialType).setOpenid("test_openid")); + // mock 方法 + AuthUser authUser = randomPojo(AuthUser.class); + when(socialClientService.getAuthUser(eq(socialType), eq(userType), eq(code), eq(state))).thenReturn(authUser); + + // 调用 + SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); + // 断言 + assertBindSocialUser(socialType, result, authUser); + assertEquals(code, result.getCode()); + assertEquals(state, result.getState()); + } + + private void assertBindSocialUser(Integer type, SocialUserDO socialUser, AuthUser authUser) { + assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken()); + assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo()); + assertEquals(authUser.getNickname(), socialUser.getNickname()); + assertEquals(authUser.getAvatar(), socialUser.getAvatar()); + assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo()); + assertEquals(type, socialUser.getType()); + assertEquals(authUser.getUuid(), socialUser.getOpenid()); + } + + @Test + public void testGetSocialUser_id() { + // mock 数据 + SocialUserDO socialUserDO = randomPojo(SocialUserDO.class); + socialUserMapper.insert(socialUserDO); + // 参数准备 + Long id = socialUserDO.getId(); + + // 调用 + SocialUserDO dbSocialUserDO = socialUserService.getSocialUser(id); + // 断言 + assertPojoEquals(socialUserDO, dbSocialUserDO); + } + + @Test + public void testGetSocialUserPage() { + // mock 数据 + SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, o -> { // 等会查询到 + o.setType(SocialTypeEnum.GITEE.getType()); + o.setNickname("芋艿"); + o.setOpenid("yudaoyuanma"); + o.setCreateTime(buildTime(2020, 1, 15)); + }); + socialUserMapper.insert(dbSocialUser); + // 测试 type 不匹配 + socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setType(SocialTypeEnum.DINGTALK.getType()))); + // 测试 nickname 不匹配 + socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setNickname(randomString()))); + // 测试 openid 不匹配 + socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setOpenid("java"))); + // 测试 createTime 不匹配 + socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setCreateTime(buildTime(2020, 1, 21)))); + // 准备参数 + SocialUserPageReqVO reqVO = new SocialUserPageReqVO(); + reqVO.setType(SocialTypeEnum.GITEE.getType()); + reqVO.setNickname("芋"); + reqVO.setOpenid("yudao"); + reqVO.setCreateTime(buildBetweenTime(2020, 1, 10, 2020, 1, 20)); + + // 调用 + PageResult pageResult = socialUserService.getSocialUserPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbSocialUser, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImplTest.java new file mode 100755 index 0000000..0e030f3 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImplTest.java @@ -0,0 +1,236 @@ +package cn.iocoder.yudao.module.system.service.tenant; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; +import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** +* {@link TenantPackageServiceImpl} 的单元测试类 +* +* @author 芋道源码 +*/ +@Import(TenantPackageServiceImpl.class) +public class TenantPackageServiceImplTest extends BaseDbUnitTest { + + @Resource + private TenantPackageServiceImpl tenantPackageService; + + @Resource + private TenantPackageMapper tenantPackageMapper; + + @MockBean + private TenantService tenantService; + + @Test + public void testCreateTenantPackage_success() { + // 准备参数 + TenantPackageSaveReqVO reqVO = randomPojo(TenantPackageSaveReqVO.class, + o -> o.setStatus(randomCommonStatus())) + .setId(null); // 防止 id 被赋值 + + // 调用 + Long tenantPackageId = tenantPackageService.createTenantPackage(reqVO); + // 断言 + assertNotNull(tenantPackageId); + // 校验记录的属性是否正确 + TenantPackageDO tenantPackage = tenantPackageMapper.selectById(tenantPackageId); + assertPojoEquals(reqVO, tenantPackage, "id"); + } + + @Test + public void testUpdateTenantPackage_success() { + // mock 数据 + TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, + o -> o.setStatus(randomCommonStatus())); + tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 + // 准备参数 + TenantPackageSaveReqVO reqVO = randomPojo(TenantPackageSaveReqVO.class, o -> { + o.setId(dbTenantPackage.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + }); + // mock 方法 + Long tenantId01 = randomLongId(); + Long tenantId02 = randomLongId(); + when(tenantService.getTenantListByPackageId(eq(reqVO.getId()))).thenReturn( + asList(randomPojo(TenantDO.class, o -> o.setId(tenantId01)), + randomPojo(TenantDO.class, o -> o.setId(tenantId02)))); + + // 调用 + tenantPackageService.updateTenantPackage(reqVO); + // 校验是否更新正确 + TenantPackageDO tenantPackage = tenantPackageMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, tenantPackage); + // 校验调用租户的菜单 + verify(tenantService).updateTenantRoleMenu(eq(tenantId01), eq(reqVO.getMenuIds())); + verify(tenantService).updateTenantRoleMenu(eq(tenantId02), eq(reqVO.getMenuIds())); + } + + @Test + public void testUpdateTenantPackage_notExists() { + // 准备参数 + TenantPackageSaveReqVO reqVO = randomPojo(TenantPackageSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> tenantPackageService.updateTenantPackage(reqVO), TENANT_PACKAGE_NOT_EXISTS); + } + + @Test + public void testDeleteTenantPackage_success() { + // mock 数据 + TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class); + tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbTenantPackage.getId(); + // mock 租户未使用该套餐 + when(tenantService.getTenantCountByPackageId(eq(id))).thenReturn(0L); + + // 调用 + tenantPackageService.deleteTenantPackage(id); + // 校验数据不存在了 + assertNull(tenantPackageMapper.selectById(id)); + } + + @Test + public void testDeleteTenantPackage_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> tenantPackageService.deleteTenantPackage(id), TENANT_PACKAGE_NOT_EXISTS); + } + + @Test + public void testDeleteTenantPackage_used() { + // mock 数据 + TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class); + tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbTenantPackage.getId(); + // mock 租户在使用该套餐 + when(tenantService.getTenantCountByPackageId(eq(id))).thenReturn(1L); + + // 调用, 并断言异常 + assertServiceException(() -> tenantPackageService.deleteTenantPackage(id), TENANT_PACKAGE_USED); + } + + @Test + public void testGetTenantPackagePage() { + // mock 数据 + TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, o -> { // 等会查询到 + o.setName("芋道源码"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setRemark("源码解析"); + o.setCreateTime(buildTime(2022, 10, 10)); + }); + tenantPackageMapper.insert(dbTenantPackage); + // 测试 name 不匹配 + tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setName("源码"))); + // 测试 status 不匹配 + tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 remark 不匹配 + tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setRemark("解析"))); + // 测试 createTime 不匹配 + tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setCreateTime(buildTime(2022, 11, 11)))); + // 准备参数 + TenantPackagePageReqVO reqVO = new TenantPackagePageReqVO(); + reqVO.setName("芋道"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setRemark("源码"); + reqVO.setCreateTime(buildBetweenTime(2022, 10, 9, 2022, 10, 11)); + + // 调用 + PageResult pageResult = tenantPackageService.getTenantPackagePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbTenantPackage, pageResult.getList().get(0)); + } + + @Test + public void testValidTenantPackage_success() { + // mock 数据 + TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, + o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 + + // 调用 + TenantPackageDO result = tenantPackageService.validTenantPackage(dbTenantPackage.getId()); + // 断言 + assertPojoEquals(dbTenantPackage, result); + } + + @Test + public void testValidTenantPackage_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> tenantPackageService.validTenantPackage(id), TENANT_PACKAGE_NOT_EXISTS); + } + + @Test + public void testValidTenantPackage_disable() { + // mock 数据 + TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, + o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 + + // 调用, 并断言异常 + assertServiceException(() -> tenantPackageService.validTenantPackage(dbTenantPackage.getId()), + TENANT_PACKAGE_DISABLE, dbTenantPackage.getName()); + } + + @Test + public void testGetTenantPackage() { + // mock 数据 + TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class); + tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 + + // 调用 + TenantPackageDO result = tenantPackageService.getTenantPackage(dbTenantPackage.getId()); + // 断言 + assertPojoEquals(result, dbTenantPackage); + } + + @Test + public void testGetTenantPackageListByStatus() { + // mock 数据 + TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, + o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); + tenantPackageMapper.insert(dbTenantPackage); + // 测试 status 不匹配 + tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, + o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + + // 调用 + List list = tenantPackageService.getTenantPackageListByStatus( + CommonStatusEnum.ENABLE.getStatus()); + assertEquals(1, list.size()); + assertPojoEquals(dbTenantPackage, list.get(0)); + } + +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java new file mode 100644 index 0000000..f4d4f86 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java @@ -0,0 +1,458 @@ +package cn.iocoder.yudao.module.system.service.tenant; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.tenant.config.TenantProperties; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; +import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper; +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; +import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; +import cn.iocoder.yudao.module.system.service.permission.MenuService; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import cn.iocoder.yudao.module.system.service.permission.RoleService; +import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; +import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; +import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO.PACKAGE_ID_SYSTEM; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static java.util.Arrays.asList; +import static java.util.Collections.singleton; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +/** + * {@link TenantServiceImpl} 的单元测试类 + * + * @author 芋道源码 + */ +@Import(TenantServiceImpl.class) +public class TenantServiceImplTest extends BaseDbUnitTest { + + @Resource + private TenantServiceImpl tenantService; + + @Resource + private TenantMapper tenantMapper; + + @MockBean + private TenantProperties tenantProperties; + @MockBean + private TenantPackageService tenantPackageService; + @MockBean + private AdminUserService userService; + @MockBean + private RoleService roleService; + @MockBean + private MenuService menuService; + @MockBean + private PermissionService permissionService; + + @BeforeEach + public void setUp() { + // 清理租户上下文 + TenantContextHolder.clear(); + } + + @Test + public void testGetTenantIdList() { + // mock 数据 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L)); + tenantMapper.insert(tenant); + + // 调用,并断言业务异常 + List result = tenantService.getTenantIdList(); + assertEquals(Collections.singletonList(1L), result); + } + + @Test + public void testValidTenant_notExists() { + assertServiceException(() -> tenantService.validTenant(randomLongId()), TENANT_NOT_EXISTS); + } + + @Test + public void testValidTenant_disable() { + // mock 数据 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.DISABLE.getStatus())); + tenantMapper.insert(tenant); + + // 调用,并断言业务异常 + assertServiceException(() -> tenantService.validTenant(1L), TENANT_DISABLE, tenant.getName()); + } + + @Test + public void testValidTenant_expired() { + // mock 数据 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus()) + .setExpireTime(buildTime(2020, 2, 2))); + tenantMapper.insert(tenant); + + // 调用,并断言业务异常 + assertServiceException(() -> tenantService.validTenant(1L), TENANT_EXPIRE, tenant.getName()); + } + + @Test + public void testValidTenant_success() { + // mock 数据 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus()) + .setExpireTime(LocalDateTime.now().plusDays(1))); + tenantMapper.insert(tenant); + + // 调用,并断言业务异常 + tenantService.validTenant(1L); + } + + @Test + public void testCreateTenant() { + // mock 套餐 100L + TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class, o -> o.setId(100L)); + when(tenantPackageService.validTenantPackage(eq(100L))).thenReturn(tenantPackage); + // mock 角色 200L + when(roleService.createRole(argThat(role -> { + assertEquals(RoleCodeEnum.TENANT_ADMIN.getName(), role.getName()); + assertEquals(RoleCodeEnum.TENANT_ADMIN.getCode(), role.getCode()); + assertEquals(0, role.getSort()); + assertEquals("系统自动生成", role.getRemark()); + return true; + }), eq(RoleTypeEnum.SYSTEM.getType()))).thenReturn(200L); + // mock 用户 300L + when(userService.createUser(argThat(user -> { + assertEquals("yunai", user.getUsername()); + assertEquals("yuanma", user.getPassword()); + assertEquals("芋道", user.getNickname()); + assertEquals("15601691300", user.getMobile()); + return true; + }))).thenReturn(300L); + + // 准备参数 + TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> { + o.setContactName("芋道"); + o.setContactMobile("15601691300"); + o.setPackageId(100L); + o.setStatus(randomCommonStatus()); + o.setWebsite("https://www.iocoder.cn"); + o.setUsername("yunai"); + o.setPassword("yuanma"); + }).setId(null); // 设置为 null,方便后面校验 + + // 调用 + Long tenantId = tenantService.createTenant(reqVO); + // 断言 + assertNotNull(tenantId); + // 校验记录的属性是否正确 + TenantDO tenant = tenantMapper.selectById(tenantId); + assertPojoEquals(reqVO, tenant, "id"); + assertEquals(300L, tenant.getContactUserId()); + // verify 分配权限 + verify(permissionService).assignRoleMenu(eq(200L), same(tenantPackage.getMenuIds())); + // verify 分配角色 + verify(permissionService).assignUserRole(eq(300L), eq(singleton(200L))); + } + + @Test + public void testUpdateTenant_success() { + // mock 数据 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setStatus(randomCommonStatus())); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + // 准备参数 + TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> { + o.setId(dbTenant.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + o.setWebsite(randomString()); + }); + + // mock 套餐 + TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class, + o -> o.setMenuIds(asSet(200L, 201L))); + when(tenantPackageService.validTenantPackage(eq(reqVO.getPackageId()))).thenReturn(tenantPackage); + // mock 所有角色 + RoleDO role100 = randomPojo(RoleDO.class, o -> o.setId(100L).setCode(RoleCodeEnum.TENANT_ADMIN.getCode())); + role100.setTenantId(dbTenant.getId()); + RoleDO role101 = randomPojo(RoleDO.class, o -> o.setId(101L)); + role101.setTenantId(dbTenant.getId()); + when(roleService.getRoleList()).thenReturn(asList(role100, role101)); + // mock 每个角色的权限 + when(permissionService.getRoleMenuListByRoleId(eq(101L))).thenReturn(asSet(201L, 202L)); + + // 调用 + tenantService.updateTenant(reqVO); + // 校验是否更新正确 + TenantDO tenant = tenantMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, tenant); + // verify 设置角色权限 + verify(permissionService).assignRoleMenu(eq(100L), eq(asSet(200L, 201L))); + verify(permissionService).assignRoleMenu(eq(101L), eq(asSet(201L))); + } + + @Test + public void testUpdateTenant_notExists() { + // 准备参数 + TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> tenantService.updateTenant(reqVO), TENANT_NOT_EXISTS); + } + + @Test + public void testUpdateTenant_system() { + // mock 数据 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM)); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + // 准备参数 + TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> { + o.setId(dbTenant.getId()); // 设置更新的 ID + }); + + // 调用,校验业务异常 + assertServiceException(() -> tenantService.updateTenant(reqVO), TENANT_CAN_NOT_UPDATE_SYSTEM); + } + + @Test + public void testDeleteTenant_success() { + // mock 数据 + TenantDO dbTenant = randomPojo(TenantDO.class, + o -> o.setStatus(randomCommonStatus())); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbTenant.getId(); + + // 调用 + tenantService.deleteTenant(id); + // 校验数据不存在了 + assertNull(tenantMapper.selectById(id)); + } + + @Test + public void testDeleteTenant_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> tenantService.deleteTenant(id), TENANT_NOT_EXISTS); + } + + @Test + public void testDeleteTenant_system() { + // mock 数据 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM)); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbTenant.getId(); + + // 调用, 并断言异常 + assertServiceException(() -> tenantService.deleteTenant(id), TENANT_CAN_NOT_UPDATE_SYSTEM); + } + + @Test + public void testGetTenant() { + // mock 数据 + TenantDO dbTenant = randomPojo(TenantDO.class); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbTenant.getId(); + + // 调用 + TenantDO result = tenantService.getTenant(id); + // 校验存在 + assertPojoEquals(result, dbTenant); + } + + @Test + public void testGetTenantPage() { + // mock 数据 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> { // 等会查询到 + o.setName("芋道源码"); + o.setContactName("芋艿"); + o.setContactMobile("15601691300"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2020, 12, 12)); + }); + tenantMapper.insert(dbTenant); + // 测试 name 不匹配 + tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setName(randomString()))); + // 测试 contactName 不匹配 + tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactName(randomString()))); + // 测试 contactMobile 不匹配 + tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactMobile(randomString()))); + // 测试 status 不匹配 + tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setCreateTime(buildTime(2021, 12, 12)))); + // 准备参数 + TenantPageReqVO reqVO = new TenantPageReqVO(); + reqVO.setName("芋道"); + reqVO.setContactName("艿"); + reqVO.setContactMobile("1560"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); + + // 调用 + PageResult pageResult = tenantService.getTenantPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbTenant, pageResult.getList().get(0)); + } + + @Test + public void testGetTenantByName() { + // mock 数据 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setName("芋道")); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + + // 调用 + TenantDO result = tenantService.getTenantByName("芋道"); + // 校验存在 + assertPojoEquals(result, dbTenant); + } + + @Test + public void testGetTenantByWebsite() { + // mock 数据 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setWebsite("https://www.iocoder.cn")); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + + // 调用 + TenantDO result = tenantService.getTenantByWebsite("https://www.iocoder.cn"); + // 校验存在 + assertPojoEquals(result, dbTenant); + } + + @Test + public void testGetTenantListByPackageId() { + // mock 数据 + TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L)); + tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据 + TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L)); + tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据 + + // 调用 + List result = tenantService.getTenantListByPackageId(1L); + assertEquals(1, result.size()); + assertPojoEquals(dbTenant1, result.get(0)); + } + + @Test + public void testGetTenantCountByPackageId() { + // mock 数据 + TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L)); + tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据 + TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L)); + tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据 + + // 调用 + Long count = tenantService.getTenantCountByPackageId(1L); + assertEquals(1, count); + } + + @Test + public void testHandleTenantInfo_disable() { + // 准备参数 + TenantInfoHandler handler = mock(TenantInfoHandler.class); + // mock 禁用 + when(tenantProperties.getEnable()).thenReturn(false); + + // 调用 + tenantService.handleTenantInfo(handler); + // 断言 + verify(handler, never()).handle(any()); + } + + @Test + public void testHandleTenantInfo_success() { + // 准备参数 + TenantInfoHandler handler = mock(TenantInfoHandler.class); + // mock 未禁用 + when(tenantProperties.getEnable()).thenReturn(true); + // mock 租户 + TenantDO dbTenant = randomPojo(TenantDO.class); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + TenantContextHolder.setTenantId(dbTenant.getId()); + + // 调用 + tenantService.handleTenantInfo(handler); + // 断言 + verify(handler).handle(argThat(argument -> { + assertPojoEquals(dbTenant, argument); + return true; + })); + } + + @Test + public void testHandleTenantMenu_disable() { + // 准备参数 + TenantMenuHandler handler = mock(TenantMenuHandler.class); + // mock 禁用 + when(tenantProperties.getEnable()).thenReturn(false); + + // 调用 + tenantService.handleTenantMenu(handler); + // 断言 + verify(handler, never()).handle(any()); + } + + @Test // 系统租户的情况 + public void testHandleTenantMenu_system() { + // 准备参数 + TenantMenuHandler handler = mock(TenantMenuHandler.class); + // mock 未禁用 + when(tenantProperties.getEnable()).thenReturn(true); + // mock 租户 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM)); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + TenantContextHolder.setTenantId(dbTenant.getId()); + // mock 菜单 + when(menuService.getMenuList()).thenReturn(Arrays.asList(randomPojo(MenuDO.class, o -> o.setId(100L)), + randomPojo(MenuDO.class, o -> o.setId(101L)))); + + // 调用 + tenantService.handleTenantMenu(handler); + // 断言 + verify(handler).handle(asSet(100L, 101L)); + } + + @Test // 普通租户的情况 + public void testHandleTenantMenu_normal() { + // 准备参数 + TenantMenuHandler handler = mock(TenantMenuHandler.class); + // mock 未禁用 + when(tenantProperties.getEnable()).thenReturn(true); + // mock 租户 + TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(200L)); + tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 + TenantContextHolder.setTenantId(dbTenant.getId()); + // mock 菜单 + when(tenantPackageService.getTenantPackage(eq(200L))).thenReturn(randomPojo(TenantPackageDO.class, + o -> o.setMenuIds(asSet(100L, 101L)))); + + // 调用 + tenantService.handleTenantMenu(handler); + // 断言 + verify(handler).handle(asSet(100L, 101L)); + } +} diff --git a/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java new file mode 100644 index 0000000..6256c42 --- /dev/null +++ b/yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java @@ -0,0 +1,763 @@ +package cn.iocoder.yudao.module.system.service.user; + +import cn.hutool.core.util.RandomUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; +import cn.iocoder.yudao.module.infra.api.config.ConfigApi; +import cn.iocoder.yudao.module.infra.api.file.FileApi; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportExcelVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper; +import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper; +import cn.iocoder.yudao.module.system.enums.common.SexEnum; +import cn.iocoder.yudao.module.system.service.dept.DeptService; +import cn.iocoder.yudao.module.system.service.dept.PostService; +import cn.iocoder.yudao.module.system.service.permission.PermissionService; +import cn.iocoder.yudao.module.system.service.tenant.TenantService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.stubbing.Answer; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.security.crypto.password.PasswordEncoder; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import static cn.hutool.core.util.RandomUtil.randomEle; +import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.service.user.AdminUserServiceImpl.USER_INIT_PASSWORD_KEY; +import static java.util.Collections.singleton; +import static java.util.Collections.singletonList; +import static org.assertj.core.util.Lists.newArrayList; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@Import(AdminUserServiceImpl.class) +public class AdminUserServiceImplTest extends BaseDbUnitTest { + + @Resource + private AdminUserServiceImpl userService; + + @Resource + private AdminUserMapper userMapper; + @Resource + private UserPostMapper userPostMapper; + + @MockBean + private DeptService deptService; + @MockBean + private PostService postService; + @MockBean + private PermissionService permissionService; + @MockBean + private PasswordEncoder passwordEncoder; + @MockBean + private TenantService tenantService; + @MockBean + private FileApi fileApi; + @MockBean + private ConfigApi configApi; + + @BeforeEach + public void before() { + // mock 初始化密码 + when(configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY)).thenReturn("yudaoyuanma"); + } + + @Test + public void testCreatUser_success() { + // 准备参数 + UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> { + o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); + o.setMobile(randomString()); + o.setPostIds(asSet(1L, 2L)); + }).setId(null); // 避免 id 被赋值 + // mock 账户额度充足 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(1)); + doNothing().when(tenantService).handleTenantInfo(argThat(handler -> { + handler.handle(tenant); + return true; + })); + // mock deptService 的方法 + DeptDO dept = randomPojo(DeptDO.class, o -> { + o.setId(reqVO.getDeptId()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); + // mock postService 的方法 + List posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> + randomPojo(PostDO.class, o -> { + o.setId(postId); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + })); + when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); + // mock passwordEncoder 的方法 + when(passwordEncoder.encode(eq(reqVO.getPassword()))).thenReturn("yudaoyuanma"); + + // 调用 + Long userId = userService.createUser(reqVO); + // 断言 + AdminUserDO user = userMapper.selectById(userId); + assertPojoEquals(reqVO, user, "password", "id"); + assertEquals("yudaoyuanma", user.getPassword()); + assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus()); + // 断言关联岗位 + List userPosts = userPostMapper.selectListByUserId(user.getId()); + assertEquals(1L, userPosts.get(0).getPostId()); + assertEquals(2L, userPosts.get(1).getPostId()); + } + + @Test + public void testCreatUser_max() { + // 准备参数 + UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class); + // mock 账户额度不足 + TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(-1)); + doNothing().when(tenantService).handleTenantInfo(argThat(handler -> { + handler.handle(tenant); + return true; + })); + + // 调用,并断言异常 + assertServiceException(() -> userService.createUser(reqVO), USER_COUNT_MAX, -1); + } + + @Test + public void testUpdateUser_success() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(o -> o.setPostIds(asSet(1L, 2L))); + userMapper.insert(dbUser); + userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(1L)); + userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(2L)); + // 准备参数 + UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> { + o.setId(dbUser.getId()); + o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); + o.setMobile(randomString()); + o.setPostIds(asSet(2L, 3L)); + }); + // mock deptService 的方法 + DeptDO dept = randomPojo(DeptDO.class, o -> { + o.setId(reqVO.getDeptId()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); + // mock postService 的方法 + List posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> + randomPojo(PostDO.class, o -> { + o.setId(postId); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + })); + when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); + + // 调用 + userService.updateUser(reqVO); + // 断言 + AdminUserDO user = userMapper.selectById(reqVO.getId()); + assertPojoEquals(reqVO, user, "password"); + // 断言关联岗位 + List userPosts = userPostMapper.selectListByUserId(user.getId()); + assertEquals(2L, userPosts.get(0).getPostId()); + assertEquals(3L, userPosts.get(1).getPostId()); + } + + @Test + public void testUpdateUserLogin() { + // mock 数据 + AdminUserDO user = randomAdminUserDO(o -> o.setLoginDate(null)); + userMapper.insert(user); + // 准备参数 + Long id = user.getId(); + String loginIp = randomString(); + + // 调用 + userService.updateUserLogin(id, loginIp); + // 断言 + AdminUserDO dbUser = userMapper.selectById(id); + assertEquals(loginIp, dbUser.getLoginIp()); + assertNotNull(dbUser.getLoginDate()); + } + + @Test + public void testUpdateUserProfile_success() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(); + userMapper.insert(dbUser); + // 准备参数 + Long userId = dbUser.getId(); + UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> { + o.setMobile(randomString()); + o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); + o.setAvatar(randomURL()); + }); + + // 调用 + userService.updateUserProfile(userId, reqVO); + // 断言 + AdminUserDO user = userMapper.selectById(userId); + assertPojoEquals(reqVO, user); + } + + @Test + public void testUpdateUserPassword_success() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(o -> o.setPassword("encode:tudou")); + userMapper.insert(dbUser); + // 准备参数 + Long userId = dbUser.getId(); + UserProfileUpdatePasswordReqVO reqVO = randomPojo(UserProfileUpdatePasswordReqVO.class, o -> { + o.setOldPassword("tudou"); + o.setNewPassword("yuanma"); + }); + // mock 方法 + when(passwordEncoder.encode(anyString())).then( + (Answer) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0)); + when(passwordEncoder.matches(eq(reqVO.getOldPassword()), eq(dbUser.getPassword()))).thenReturn(true); + + // 调用 + userService.updateUserPassword(userId, reqVO); + // 断言 + AdminUserDO user = userMapper.selectById(userId); + assertEquals("encode:yuanma", user.getPassword()); + } + + @Test + public void testUpdateUserPassword02_success() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(); + userMapper.insert(dbUser); + // 准备参数 + Long userId = dbUser.getId(); + String password = "yudao"; + // mock 方法 + when(passwordEncoder.encode(anyString())).then( + (Answer) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0)); + + // 调用 + userService.updateUserPassword(userId, password); + // 断言 + AdminUserDO user = userMapper.selectById(userId); + assertEquals("encode:" + password, user.getPassword()); + } + + @Test + public void testUpdateUserStatus() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(); + userMapper.insert(dbUser); + // 准备参数 + Long userId = dbUser.getId(); + Integer status = randomCommonStatus(); + + // 调用 + userService.updateUserStatus(userId, status); + // 断言 + AdminUserDO user = userMapper.selectById(userId); + assertEquals(status, user.getStatus()); + } + + @Test + public void testDeleteUser_success(){ + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(); + userMapper.insert(dbUser); + // 准备参数 + Long userId = dbUser.getId(); + + // 调用数据 + userService.deleteUser(userId); + // 校验结果 + assertNull(userMapper.selectById(userId)); + // 校验调用次数 + verify(permissionService, times(1)).processUserDeleted(eq(userId)); + } + + @Test + public void testGetUserByUsername() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(); + userMapper.insert(dbUser); + // 准备参数 + String username = dbUser.getUsername(); + + // 调用 + AdminUserDO user = userService.getUserByUsername(username); + // 断言 + assertPojoEquals(dbUser, user); + } + + @Test + public void testGetUserByMobile() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(); + userMapper.insert(dbUser); + // 准备参数 + String mobile = dbUser.getMobile(); + + // 调用 + AdminUserDO user = userService.getUserByMobile(mobile); + // 断言 + assertPojoEquals(dbUser, user); + } + + @Test + public void testGetUserPage() { + // mock 数据 + AdminUserDO dbUser = initGetUserPageData(); + // 准备参数 + UserPageReqVO reqVO = new UserPageReqVO(); + reqVO.setUsername("tu"); + reqVO.setMobile("1560"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); + reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 + // mock 方法 + List deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L))); + when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList); + + // 调用 + PageResult pageResult = userService.getUserPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbUser, pageResult.getList().get(0)); + } + + /** + * 初始化 getUserPage 方法的测试数据 + */ + private AdminUserDO initGetUserPageData() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(o -> { // 等会查询到 + o.setUsername("tudou"); + o.setMobile("15601691300"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2020, 12, 12)); + o.setDeptId(2L); + }); + userMapper.insert(dbUser); + // 测试 username 不匹配 + userMapper.insert(cloneIgnoreId(dbUser, o -> o.setUsername("dou"))); + // 测试 mobile 不匹配 + userMapper.insert(cloneIgnoreId(dbUser, o -> o.setMobile("18818260888"))); + // 测试 status 不匹配 + userMapper.insert(cloneIgnoreId(dbUser, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + userMapper.insert(cloneIgnoreId(dbUser, o -> o.setCreateTime(buildTime(2020, 11, 11)))); + // 测试 dept 不匹配 + userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(0L))); + return dbUser; + } + + @Test + public void testGetUser() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(); + userMapper.insert(dbUser); + // 准备参数 + Long userId = dbUser.getId(); + + // 调用 + AdminUserDO user = userService.getUser(userId); + // 断言 + assertPojoEquals(dbUser, user); + } + + @Test + public void testGetUserListByDeptIds() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(o -> o.setDeptId(1L)); + userMapper.insert(dbUser); + // 测试 deptId 不匹配 + userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(2L))); + // 准备参数 + Collection deptIds = singleton(1L); + + // 调用 + List list = userService.getUserListByDeptIds(deptIds); + // 断言 + assertEquals(1, list.size()); + assertEquals(dbUser, list.get(0)); + } + + /** + * 情况一,校验不通过,导致插入失败 + */ + @Test + public void testImportUserList_01() { + // 准备参数 + UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { + o.setEmail(randomEmail()); + o.setMobile(randomMobile()); + }); + // mock 方法,模拟失败 + doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any()); + + // 调用 + UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true); + // 断言 + assertEquals(0, respVO.getCreateUsernames().size()); + assertEquals(0, respVO.getUpdateUsernames().size()); + assertEquals(1, respVO.getFailureUsernames().size()); + assertEquals(DEPT_NOT_FOUND.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername())); + } + + /** + * 情况二,不存在,进行插入 + */ + @Test + public void testImportUserList_02() { + // 准备参数 + UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 + o.setEmail(randomEmail()); + o.setMobile(randomMobile()); + }); + // mock deptService 的方法 + DeptDO dept = randomPojo(DeptDO.class, o -> { + o.setId(importUser.getDeptId()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); + // mock passwordEncoder 的方法 + when(passwordEncoder.encode(eq("yudaoyuanma"))).thenReturn("java"); + + // 调用 + UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true); + // 断言 + assertEquals(1, respVO.getCreateUsernames().size()); + AdminUserDO user = userMapper.selectByUsername(respVO.getCreateUsernames().get(0)); + assertPojoEquals(importUser, user); + assertEquals("java", user.getPassword()); + assertEquals(0, respVO.getUpdateUsernames().size()); + assertEquals(0, respVO.getFailureUsernames().size()); + } + + /** + * 情况三,存在,但是不强制更新 + */ + @Test + public void testImportUserList_03() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(); + userMapper.insert(dbUser); + // 准备参数 + UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 + o.setUsername(dbUser.getUsername()); + o.setEmail(randomEmail()); + o.setMobile(randomMobile()); + }); + // mock deptService 的方法 + DeptDO dept = randomPojo(DeptDO.class, o -> { + o.setId(importUser.getDeptId()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); + + // 调用 + UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), false); + // 断言 + assertEquals(0, respVO.getCreateUsernames().size()); + assertEquals(0, respVO.getUpdateUsernames().size()); + assertEquals(1, respVO.getFailureUsernames().size()); + assertEquals(USER_USERNAME_EXISTS.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername())); + } + + /** + * 情况四,存在,强制更新 + */ + @Test + public void testImportUserList_04() { + // mock 数据 + AdminUserDO dbUser = randomAdminUserDO(); + userMapper.insert(dbUser); + // 准备参数 + UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 + o.setUsername(dbUser.getUsername()); + o.setEmail(randomEmail()); + o.setMobile(randomMobile()); + }); + // mock deptService 的方法 + DeptDO dept = randomPojo(DeptDO.class, o -> { + o.setId(importUser.getDeptId()); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + }); + when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); + + // 调用 + UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true); + // 断言 + assertEquals(0, respVO.getCreateUsernames().size()); + assertEquals(1, respVO.getUpdateUsernames().size()); + AdminUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0)); + assertPojoEquals(importUser, user); + assertEquals(0, respVO.getFailureUsernames().size()); + } + + @Test + public void testValidateUserExists_notExists() { + assertServiceException(() -> userService.validateUserExists(randomLongId()), USER_NOT_EXISTS); + } + + @Test + public void testValidateUsernameUnique_usernameExistsForCreate() { + // 准备参数 + String username = randomString(); + // mock 数据 + userMapper.insert(randomAdminUserDO(o -> o.setUsername(username))); + + // 调用,校验异常 + assertServiceException(() -> userService.validateUsernameUnique(null, username), + USER_USERNAME_EXISTS); + } + + @Test + public void testValidateUsernameUnique_usernameExistsForUpdate() { + // 准备参数 + Long id = randomLongId(); + String username = randomString(); + // mock 数据 + userMapper.insert(randomAdminUserDO(o -> o.setUsername(username))); + + // 调用,校验异常 + assertServiceException(() -> userService.validateUsernameUnique(id, username), + USER_USERNAME_EXISTS); + } + + @Test + public void testValidateEmailUnique_emailExistsForCreate() { + // 准备参数 + String email = randomString(); + // mock 数据 + userMapper.insert(randomAdminUserDO(o -> o.setEmail(email))); + + // 调用,校验异常 + assertServiceException(() -> userService.validateEmailUnique(null, email), + USER_EMAIL_EXISTS); + } + + @Test + public void testValidateEmailUnique_emailExistsForUpdate() { + // 准备参数 + Long id = randomLongId(); + String email = randomString(); + // mock 数据 + userMapper.insert(randomAdminUserDO(o -> o.setEmail(email))); + + // 调用,校验异常 + assertServiceException(() -> userService.validateEmailUnique(id, email), + USER_EMAIL_EXISTS); + } + + @Test + public void testValidateMobileUnique_mobileExistsForCreate() { + // 准备参数 + String mobile = randomString(); + // mock 数据 + userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile))); + + // 调用,校验异常 + assertServiceException(() -> userService.validateMobileUnique(null, mobile), + USER_MOBILE_EXISTS); + } + + @Test + public void testValidateMobileUnique_mobileExistsForUpdate() { + // 准备参数 + Long id = randomLongId(); + String mobile = randomString(); + // mock 数据 + userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile))); + + // 调用,校验异常 + assertServiceException(() -> userService.validateMobileUnique(id, mobile), + USER_MOBILE_EXISTS); + } + + @Test + public void testValidateOldPassword_notExists() { + assertServiceException(() -> userService.validateOldPassword(randomLongId(), randomString()), + USER_NOT_EXISTS); + } + + @Test + public void testValidateOldPassword_passwordFailed() { + // mock 数据 + AdminUserDO user = randomAdminUserDO(); + userMapper.insert(user); + // 准备参数 + Long id = user.getId(); + String oldPassword = user.getPassword(); + + // 调用,校验异常 + assertServiceException(() -> userService.validateOldPassword(id, oldPassword), + USER_PASSWORD_FAILED); + // 校验调用 + verify(passwordEncoder, times(1)).matches(eq(oldPassword), eq(user.getPassword())); + } + + @Test + public void testUserListByPostIds() { + // 准备参数 + Collection postIds = asSet(10L, 20L); + // mock user1 数据 + AdminUserDO user1 = randomAdminUserDO(o -> o.setPostIds(asSet(10L, 30L))); + userMapper.insert(user1); + userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(10L)); + userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(30L)); + // mock user2 数据 + AdminUserDO user2 = randomAdminUserDO(o -> o.setPostIds(singleton(100L))); + userMapper.insert(user2); + userPostMapper.insert(new UserPostDO().setUserId(user2.getId()).setPostId(100L)); + + // 调用 + List result = userService.getUserListByPostIds(postIds); + // 断言 + assertEquals(1, result.size()); + assertEquals(user1, result.get(0)); + } + + @Test + public void testGetUserList() { + // mock 数据 + AdminUserDO user = randomAdminUserDO(); + userMapper.insert(user); + // 测试 id 不匹配 + userMapper.insert(randomAdminUserDO()); + // 准备参数 + Collection ids = singleton(user.getId()); + + // 调用 + List result = userService.getUserList(ids); + // 断言 + assertEquals(1, result.size()); + assertEquals(user, result.get(0)); + } + + @Test + public void testGetUserMap() { + // mock 数据 + AdminUserDO user = randomAdminUserDO(); + userMapper.insert(user); + // 测试 id 不匹配 + userMapper.insert(randomAdminUserDO()); + // 准备参数 + Collection ids = singleton(user.getId()); + + // 调用 + Map result = userService.getUserMap(ids); + // 断言 + assertEquals(1, result.size()); + assertEquals(user, result.get(user.getId())); + } + + @Test + public void testGetUserListByNickname() { + // mock 数据 + AdminUserDO user = randomAdminUserDO(o -> o.setNickname("芋头")); + userMapper.insert(user); + // 测试 nickname 不匹配 + userMapper.insert(randomAdminUserDO(o -> o.setNickname("源码"))); + // 准备参数 + String nickname = "芋"; + + // 调用 + List result = userService.getUserListByNickname(nickname); + // 断言 + assertEquals(1, result.size()); + assertEquals(user, result.get(0)); + } + + @Test + public void testGetUserListByStatus() { + // mock 数据 + AdminUserDO user = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); + userMapper.insert(user); + // 测试 status 不匹配 + userMapper.insert(randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()))); + // 准备参数 + Integer status = CommonStatusEnum.DISABLE.getStatus(); + + // 调用 + List result = userService.getUserListByStatus(status); + // 断言 + assertEquals(1, result.size()); + assertEquals(user, result.get(0)); + } + + @Test + public void testValidateUserList_success() { + // mock 数据 + AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); + userMapper.insert(userDO); + // 准备参数 + List ids = singletonList(userDO.getId()); + + // 调用,无需断言 + userService.validateUserList(ids); + } + + @Test + public void testValidateUserList_notFound() { + // 准备参数 + List ids = singletonList(randomLongId()); + + // 调用, 并断言异常 + assertServiceException(() -> userService.validateUserList(ids), USER_NOT_EXISTS); + } + + @Test + public void testValidateUserList_notEnable() { + // mock 数据 + AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); + userMapper.insert(userDO); + // 准备参数 + List ids = singletonList(userDO.getId()); + + // 调用, 并断言异常 + assertServiceException(() -> userService.validateUserList(ids), USER_IS_DISABLE, + userDO.getNickname()); + } + + // ========== 随机对象 ========== + + @SafeVarargs + private static AdminUserDO randomAdminUserDO(Consumer... consumers) { + Consumer consumer = (o) -> { + o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 + o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 + }; + return randomPojo(AdminUserDO.class, ArrayUtils.append(consumer, consumers)); + } + +} diff --git a/yudao-module-system/src/test/resources/application-unit-test.yaml b/yudao-module-system/src/test/resources/application-unit-test.yaml new file mode 100644 index 0000000..c1f6573 --- /dev/null +++ b/yudao-module-system/src/test/resources/application-unit-test.yaml @@ -0,0 +1,48 @@ +spring: + main: + lazy-initialization: true # 开启懒加载,加快速度 + banner-mode: off # 单元测试,禁用 Banner + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 + initial-size: 1 # 单元测试,配置为 1,提升启动速度 + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 127.0.0.1 # 地址 + port: 16379 # 端口(单元测试,使用 16379 端口) + database: 0 # 数据库索引 + +mybatis: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + +--- #################### 定时任务相关配置 #################### + +--- #################### 配置中心相关配置 #################### + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项(单元测试,禁用 Lock4j) + +--- #################### 监控相关配置 #################### + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +yudao: + info: + base-package: cn.iocoder.yudao.module diff --git a/yudao-module-system/src/test/resources/logback.xml b/yudao-module-system/src/test/resources/logback.xml new file mode 100644 index 0000000..daf756b --- /dev/null +++ b/yudao-module-system/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/yudao-module-system/src/test/resources/sql/clean.sql b/yudao-module-system/src/test/resources/sql/clean.sql new file mode 100644 index 0000000..e7946a1 --- /dev/null +++ b/yudao-module-system/src/test/resources/sql/clean.sql @@ -0,0 +1,33 @@ +DELETE FROM "system_dept"; +DELETE FROM "system_dict_data"; +DELETE FROM "system_role"; +DELETE FROM "system_role_menu"; +DELETE FROM "system_menu"; +DELETE FROM "system_user_role"; +DELETE FROM "system_dict_type"; +DELETE FROM "system_user_session"; +DELETE FROM "system_post"; +DELETE FROM "system_user_post"; +DELETE FROM "system_notice"; +DELETE FROM "system_login_log"; +DELETE FROM "system_operate_log"; +DELETE FROM "system_users"; +DELETE FROM "system_sms_channel"; +DELETE FROM "system_sms_template"; +DELETE FROM "system_sms_log"; +DELETE FROM "system_sms_code"; +DELETE FROM "system_social_client"; +DELETE FROM "system_social_user"; +DELETE FROM "system_social_user_bind"; +DELETE FROM "system_tenant"; +DELETE FROM "system_tenant_package"; +DELETE FROM "system_oauth2_client"; +DELETE FROM "system_oauth2_approve"; +DELETE FROM "system_oauth2_access_token"; +DELETE FROM "system_oauth2_refresh_token"; +DELETE FROM "system_oauth2_code"; +DELETE FROM "system_mail_account"; +DELETE FROM "system_mail_template"; +DELETE FROM "system_mail_log"; +DELETE FROM "system_notify_template"; +DELETE FROM "system_notify_message"; diff --git a/yudao-module-system/src/test/resources/sql/create_tables.sql b/yudao-module-system/src/test/resources/sql/create_tables.sql new file mode 100644 index 0000000..58f029f --- /dev/null +++ b/yudao-module-system/src/test/resources/sql/create_tables.sql @@ -0,0 +1,615 @@ +CREATE TABLE IF NOT EXISTS "system_dept" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(30) NOT NULL DEFAULT '', + "parent_id" bigint NOT NULL DEFAULT '0', + "sort" int NOT NULL DEFAULT '0', + "leader_user_id" bigint DEFAULT NULL, + "phone" varchar(11) DEFAULT NULL, + "email" varchar(50) DEFAULT NULL, + "status" tinyint NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '部门表'; + +CREATE TABLE IF NOT EXISTS "system_dict_data" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "sort" int NOT NULL DEFAULT '0', + "label" varchar(100) NOT NULL DEFAULT '', + "value" varchar(100) NOT NULL DEFAULT '', + "dict_type" varchar(100) NOT NULL DEFAULT '', + "status" tinyint NOT NULL DEFAULT '0', + "color_type" varchar(100) NOT NULL DEFAULT '', + "css_class" varchar(100) NOT NULL DEFAULT '', + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '字典数据表'; + +CREATE TABLE IF NOT EXISTS "system_role" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(30) NOT NULL, + "code" varchar(100) NOT NULL, + "sort" int NOT NULL, + "data_scope" tinyint NOT NULL DEFAULT '1', + "data_scope_dept_ids" varchar(500) NOT NULL DEFAULT '', + "status" tinyint NOT NULL, + "type" tinyint NOT NULL, + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '角色信息表'; + +CREATE TABLE IF NOT EXISTS "system_role_menu" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "role_id" bigint NOT NULL, + "menu_id" bigint NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '角色和菜单关联表'; + +CREATE TABLE IF NOT EXISTS "system_menu" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(50) NOT NULL, + "permission" varchar(100) NOT NULL DEFAULT '', + "type" tinyint NOT NULL, + "sort" int NOT NULL DEFAULT '0', + "parent_id" bigint NOT NULL DEFAULT '0', + "path" varchar(200) DEFAULT '', + "icon" varchar(100) DEFAULT '#', + "component" varchar(255) DEFAULT NULL, + "component_name" varchar(255) DEFAULT NULL, + "status" tinyint NOT NULL DEFAULT '0', + "visible" bit NOT NULL DEFAULT TRUE, + "keep_alive" bit NOT NULL DEFAULT TRUE, + "always_show" bit NOT NULL DEFAULT TRUE, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '菜单权限表'; + +CREATE TABLE IF NOT EXISTS "system_user_role" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "role_id" bigint NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp DEFAULT NULL, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp DEFAULT NULL, + "deleted" bit DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '用户和角色关联表'; + +CREATE TABLE IF NOT EXISTS "system_dict_type" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(100) NOT NULL DEFAULT '', + "type" varchar(100) NOT NULL DEFAULT '', + "status" tinyint NOT NULL DEFAULT '0', + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "deleted_time" timestamp NOT NULL, + PRIMARY KEY ("id") +) COMMENT '字典类型表'; + +CREATE TABLE IF NOT EXISTS `system_user_session` ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + `token` varchar(32) NOT NULL, + `user_id` bigint DEFAULT NULL, + "user_type" tinyint NOT NULL, + `username` varchar(50) NOT NULL DEFAULT '', + `user_ip` varchar(50) DEFAULT NULL, + `user_agent` varchar(512) DEFAULT NULL, + `session_timeout` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater` varchar(64) DEFAULT '' , + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY (`id`) +) COMMENT '用户在线 Session'; + +CREATE TABLE IF NOT EXISTS "system_post" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "code" varchar(64) NOT NULL, + "name" varchar(50) NOT NULL, + "sort" integer NOT NULL, + "status" tinyint NOT NULL, + "remark" varchar(500) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '岗位信息表'; + +CREATE TABLE IF NOT EXISTS `system_user_post`( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint DEFAULT NULL, + "post_id" bigint DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY (`id`) +) COMMENT ='用户岗位表'; + +CREATE TABLE IF NOT EXISTS "system_notice" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "title" varchar(50) NOT NULL COMMENT '公告标题', + "content" text NOT NULL COMMENT '公告内容', + "type" tinyint NOT NULL COMMENT '公告类型(1通知 2公告)', + "status" tinyint NOT NULL DEFAULT '0' COMMENT '公告状态(0正常 1关闭)', + "creator" varchar(64) DEFAULT '' COMMENT '创建者', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + "updater" varchar(64) DEFAULT '' COMMENT '更新者', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + "deleted" bit NOT NULL DEFAULT 0 COMMENT '是否删除', + "tenant_id" bigint not null default '0', + PRIMARY KEY("id") +) COMMENT '通知公告表'; + +CREATE TABLE IF NOT EXISTS `system_login_log` ( + `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, + `log_type` bigint(4) NOT NULL, + "user_id" bigint not null default '0', + "user_type" tinyint NOT NULL, + `trace_id` varchar(64) NOT NULL DEFAULT '', + `username` varchar(50) NOT NULL DEFAULT '', + `result` tinyint(4) NOT NULL, + `user_ip` varchar(50) NOT NULL, + `user_agent` varchar(512) NOT NULL, + `creator` varchar(64) DEFAULT '', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater` varchar(64) DEFAULT '', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` bit(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) COMMENT ='系统访问记录'; + +CREATE TABLE IF NOT EXISTS `system_operate_log` ( + `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, + `trace_id` varchar(64) NOT NULL DEFAULT '', + `user_id` bigint(20) NOT NULL, + "user_type" tinyint not null default '0', + `type` varchar(50) NOT NULL, + `sub_type` varchar(50) NOT NULL, + `biz_id` bigint(20) NOT NULL, + `action` varchar(2000) NOT NULL DEFAULT '', + `extra` varchar(512) NOT NULL DEFAULT '', + `request_method` varchar(16) DEFAULT '', + `request_url` varchar(255) DEFAULT '', + `user_ip` varchar(50) DEFAULT NULL, + `user_agent` varchar(200) DEFAULT NULL, + `creator` varchar(64) DEFAULT '', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updater` varchar(64) DEFAULT '', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `deleted` bit(1) NOT NULL DEFAULT '0', + "tenant_id" bigint not null default '0', + PRIMARY KEY (`id`) +) COMMENT ='操作日志记录'; + +CREATE TABLE IF NOT EXISTS "system_users" ( + "id" bigint not null GENERATED BY DEFAULT AS IDENTITY, + "username" varchar(30) not null, + "password" varchar(100) not null default '', + "nickname" varchar(30) not null, + "remark" varchar(500) default null, + "dept_id" bigint default null, + "post_ids" varchar(255) default null, + "email" varchar(50) default '', + "mobile" varchar(11) default '', + "sex" tinyint default '0', + "avatar" varchar(100) default '', + "status" tinyint not null default '0', + "login_ip" varchar(50) default '', + "login_date" timestamp default null, + "creator" varchar(64) default '', + "create_time" timestamp not null default current_timestamp, + "updater" varchar(64) default '', + "update_time" timestamp not null default current_timestamp, + "deleted" bit not null default false, + "tenant_id" bigint not null default '0', + primary key ("id") +) comment '用户信息表'; + +CREATE TABLE IF NOT EXISTS "system_sms_channel" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "signature" varchar(10) NOT NULL, + "code" varchar(63) NOT NULL, + "status" tinyint NOT NULL, + "remark" varchar(255) DEFAULT NULL, + "api_key" varchar(63) NOT NULL, + "api_secret" varchar(63) DEFAULT NULL, + "callback_url" varchar(255) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '短信渠道'; + +CREATE TABLE IF NOT EXISTS "system_sms_template" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "type" tinyint NOT NULL, + "status" tinyint NOT NULL, + "code" varchar(63) NOT NULL, + "name" varchar(63) NOT NULL, + "content" varchar(255) NOT NULL, + "params" varchar(255) NOT NULL, + "remark" varchar(255) DEFAULT NULL, + "api_template_id" varchar(63) NOT NULL, + "channel_id" bigint NOT NULL, + "channel_code" varchar(63) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '短信模板'; + +CREATE TABLE IF NOT EXISTS "system_sms_log" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "channel_id" bigint NOT NULL, + "channel_code" varchar(63) NOT NULL, + "template_id" bigint NOT NULL, + "template_code" varchar(63) NOT NULL, + "template_type" tinyint NOT NULL, + "template_content" varchar(255) NOT NULL, + "template_params" varchar(255) NOT NULL, + "api_template_id" varchar(63) NOT NULL, + "mobile" varchar(11) NOT NULL, + "user_id" bigint DEFAULT '0', + "user_type" tinyint DEFAULT '0', + "send_status" tinyint NOT NULL DEFAULT '0', + "send_time" timestamp DEFAULT NULL, + "send_code" int DEFAULT NULL, + "send_msg" varchar(255) DEFAULT NULL, + "api_send_code" varchar(63) DEFAULT NULL, + "api_send_msg" varchar(255) DEFAULT NULL, + "api_request_id" varchar(255) DEFAULT NULL, + "api_serial_no" varchar(255) DEFAULT NULL, + "receive_status" tinyint NOT NULL DEFAULT '0', + "receive_time" timestamp DEFAULT NULL, + "api_receive_code" varchar(63) DEFAULT NULL, + "api_receive_msg" varchar(255) DEFAULT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '短信日志'; + +CREATE TABLE IF NOT EXISTS "system_sms_code" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "mobile" varchar(11) NOT NULL, + "code" varchar(11) NOT NULL, + "scene" bigint NOT NULL, + "create_ip" varchar NOT NULL, + "today_index" int NOT NULL, + "used" bit NOT NULL DEFAULT FALSE, + "used_time" timestamp DEFAULT NULL, + "used_ip" varchar NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '短信日志'; + +CREATE TABLE IF NOT EXISTS "system_social_client" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(255) NOT NULL, + "social_type" int NOT NULL, + "user_type" int NOT NULL, + "client_id" varchar(255) NOT NULL, + "client_secret" varchar(255) NOT NULL, + "agent_id" varchar(255) NOT NULL, + "status" int NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '社交客户端表'; + +CREATE TABLE IF NOT EXISTS "system_social_user" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "type" tinyint NOT NULL, + "openid" varchar(64) NOT NULL, + "token" varchar(256) DEFAULT NULL, + "raw_token_info" varchar(1024) NOT NULL, + "nickname" varchar(32) NOT NULL, + "avatar" varchar(255) DEFAULT NULL, + "raw_user_info" varchar(1024) NOT NULL, + "code" varchar(64) NOT NULL, + "state" varchar(64), + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '社交用户'; + +CREATE TABLE IF NOT EXISTS "system_social_user_bind" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "user_type" tinyint NOT NULL, + "social_type" tinyint NOT NULL, + "social_user_id" number NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '社交用户的绑定'; + +CREATE TABLE IF NOT EXISTS "system_tenant" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(63) NOT NULL, + "contact_user_id" bigint NOT NULL DEFAULT '0', + "contact_name" varchar(255) NOT NULL, + "contact_mobile" varchar(255), + "status" tinyint NOT NULL, + "website" varchar(63) DEFAULT '', + "package_id" bigint NOT NULL, + "expire_time" timestamp NOT NULL, + "account_count" int NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '租户'; + +CREATE TABLE IF NOT EXISTS "system_tenant_package" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(30) NOT NULL, + "status" tinyint NOT NULL, + "remark" varchar(256), + "menu_ids" varchar(2048) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '租户套餐表'; + +CREATE TABLE IF NOT EXISTS "system_oauth2_client" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "client_id" varchar NOT NULL, + "secret" varchar NOT NULL, + "name" varchar NOT NULL, + "logo" varchar NOT NULL, + "description" varchar, + "status" int NOT NULL, + "access_token_validity_seconds" int NOT NULL, + "refresh_token_validity_seconds" int NOT NULL, + "redirect_uris" varchar NOT NULL, + "authorized_grant_types" varchar NOT NULL, + "scopes" varchar NOT NULL DEFAULT '', + "auto_approve_scopes" varchar NOT NULL DEFAULT '', + "authorities" varchar NOT NULL DEFAULT '', + "resource_ids" varchar NOT NULL DEFAULT '', + "additional_information" varchar NOT NULL DEFAULT '', + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT 'OAuth2 客户端表'; + +CREATE TABLE IF NOT EXISTS "system_oauth2_approve" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "user_type" tinyint NOT NULL, + "client_id" varchar NOT NULL, + "scope" varchar NOT NULL, + "approved" bit NOT NULL DEFAULT FALSE, + "expires_time" datetime NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT 'OAuth2 批准表'; + +CREATE TABLE IF NOT EXISTS "system_oauth2_access_token" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "user_type" tinyint NOT NULL, + "user_info" varchar NOT NULL, + "access_token" varchar NOT NULL, + "refresh_token" varchar NOT NULL, + "client_id" varchar NOT NULL, + "scopes" varchar NOT NULL, + "approved" bit NOT NULL DEFAULT FALSE, + "expires_time" datetime NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null, + PRIMARY KEY ("id") +) COMMENT 'OAuth2 访问令牌'; + +CREATE TABLE IF NOT EXISTS "system_oauth2_refresh_token" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "user_type" tinyint NOT NULL, + "refresh_token" varchar NOT NULL, + "client_id" varchar NOT NULL, + "scopes" varchar NOT NULL, + "approved" bit NOT NULL DEFAULT FALSE, + "expires_time" datetime NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT 'OAuth2 刷新令牌'; + +CREATE TABLE IF NOT EXISTS "system_oauth2_code" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "user_type" tinyint NOT NULL, + "code" varchar NOT NULL, + "client_id" varchar NOT NULL, + "scopes" varchar NOT NULL, + "expires_time" datetime NOT NULL, + "redirect_uri" varchar NOT NULL, + "state" varchar NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT 'OAuth2 刷新令牌'; + +CREATE TABLE IF NOT EXISTS "system_mail_account" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "mail" varchar NOT NULL, + "username" varchar NOT NULL, + "password" varchar NOT NULL, + "host" varchar NOT NULL, + "port" int NOT NULL, + "ssl_enable" bit NOT NULL, + "starttls_enable" bit NOT NULL, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '邮箱账号表'; + +CREATE TABLE IF NOT EXISTS "system_mail_template" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "code" varchar NOT NULL, + "account_id" bigint NOT NULL, + "nickname" varchar, + "title" varchar NOT NULL, + "content" varchar NOT NULL, + "params" varchar NOT NULL, + "status" varchar NOT NULL, + "remark" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '邮件模版表'; + +CREATE TABLE IF NOT EXISTS "system_mail_log" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint, + "user_type" varchar, + "to_mail" varchar NOT NULL, + "account_id" bigint NOT NULL, + "from_mail" varchar NOT NULL, + "template_id" bigint NOT NULL, + "template_code" varchar NOT NULL, + "template_nickname" varchar, + "template_title" varchar NOT NULL, + "template_content" varchar NOT NULL, + "template_params" varchar NOT NULL, + "send_status" varchar NOT NULL, + "send_time" datetime, + "send_message_id" varchar, + "send_exception" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '邮件日志表'; + +-- 将该建表 SQL 语句,添加到 yudao-module-system-biz 模块的 test/resources/sql/create_tables.sql 文件里 +CREATE TABLE IF NOT EXISTS "system_notify_template" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar NOT NULL, + "code" varchar NOT NULL, + "nickname" varchar NOT NULL, + "content" varchar NOT NULL, + "type" varchar NOT NULL, + "params" varchar, + "status" varchar NOT NULL, + "remark" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '站内信模板表'; + +CREATE TABLE IF NOT EXISTS "system_notify_message" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "user_id" bigint NOT NULL, + "user_type" varchar NOT NULL, + "template_id" bigint NOT NULL, + "template_code" varchar NOT NULL, + "template_nickname" varchar NOT NULL, + "template_content" varchar NOT NULL, + "template_type" int NOT NULL, + "template_params" varchar NOT NULL, + "read_status" bit NOT NULL, + "read_time" varchar, + "creator" varchar DEFAULT '', + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar DEFAULT '', + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + "tenant_id" bigint not null default '0', + PRIMARY KEY ("id") +) COMMENT '站内信消息表'; diff --git a/yudao-module-system/yudao-module-system-api/pom.xml b/yudao-module-system/yudao-module-system-api/pom.xml deleted file mode 100644 index c98baf3..0000000 --- a/yudao-module-system/yudao-module-system-api/pom.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - cn.iocoder.boot - yudao-module-system - ${revision} - - 4.0.0 - yudao-module-system-api - jar - - ${project.artifactId} - - system 模块 API,暴露给其它模块调用 - - - - - cn.iocoder.boot - yudao-common - - - - - org.springframework.boot - spring-boot-starter-validation - true - - - - diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java deleted file mode 100644 index bdc3ba5..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.yudao.module.system.api.dept; - -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * 部门 API 接口 - * - * @author 芋道源码 - */ -public interface DeptApi { - - /** - * 获得部门信息 - * - * @param id 部门编号 - * @return 部门信息 - */ - DeptRespDTO getDept(Long id); - - /** - * 获得部门信息数组 - * - * @param ids 部门编号数组 - * @return 部门信息数组 - */ - List getDeptList(Collection ids); - - /** - * 校验部门们是否有效。如下情况,视为无效: - * 1. 部门编号不存在 - * 2. 部门被禁用 - * - * @param ids 角色编号数组 - */ - void validateDeptList(Collection ids); - - /** - * 获得指定编号的部门 Map - * - * @param ids 部门编号数组 - * @return 部门 Map - */ - default Map getDeptMap(Collection ids) { - List list = getDeptList(ids); - return CollectionUtils.convertMap(list, DeptRespDTO::getId); - } - - /** - * 获得指定部门的所有子部门 - * - * @param id 部门编号 - * @return 子部门列表 - */ - List getChildDeptList(Long id); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java deleted file mode 100644 index c38f4cd..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApi.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.api.dept; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * 岗位 API 接口 - * - * @author 芋道源码 - */ -public interface PostApi { - - /** - * 校验岗位们是否有效。如下情况,视为无效: - * 1. 岗位编号不存在 - * 2. 岗位被禁用 - * - * @param ids 岗位编号数组 - */ - void validPostList(Collection ids); - - List getPostList(Collection ids); - - default Map getPostMap(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return MapUtil.empty(); - } - - List list = getPostList(ids); - return CollectionUtils.convertMap(list, PostRespDTO::getId); - } - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java deleted file mode 100644 index d3e66fd..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.system.api.dept.dto; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import lombok.Data; - -/** - * 部门 Response DTO - * - * @author 芋道源码 - */ -@Data -public class DeptRespDTO { - - /** - * 部门编号 - */ - private Long id; - /** - * 部门名称 - */ - private String name; - /** - * 父部门编号 - */ - private Long parentId; - /** - * 负责人的用户编号 - */ - private Long leaderUserId; - /** - * 部门状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java deleted file mode 100644 index cf2cc25..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/PostRespDTO.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.system.api.dept.dto; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import lombok.Data; - -/** - * 岗位 Response DTO - * - * @author 芋道源码 - */ -@Data -public class PostRespDTO { - - /** - * 岗位序号 - */ - private Long id; - /** - * 岗位名称 - */ - private String name; - /** - * 岗位编码 - */ - private String code; - /** - * 岗位排序 - */ - private Integer sort; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java deleted file mode 100644 index 239f1fe..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApi.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.system.api.dict; - -import cn.iocoder.yudao.framework.common.biz.system.dict.DictDataCommonApi; - -import java.util.Collection; - -/** - * 字典数据 API 接口 - * - * @author 芋道源码 - */ -public interface DictDataApi extends DictDataCommonApi { - - /** - * 校验字典数据们是否有效。如下情况,视为无效: - * 1. 字典数据不存在 - * 2. 字典数据被禁用 - * - * @param dictType 字典类型 - * @param values 字典数据值的数组 - */ - void validateDictDataList(String dictType, Collection values); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApi.java deleted file mode 100644 index f24d154..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApi.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.api.logger; - -import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; - -import jakarta.validation.Valid; - -/** - * 登录日志的 API 接口 - * - * @author 芋道源码 - */ -public interface LoginLogApi { - - /** - * 创建登录日志 - * - * @param reqDTO 日志信息 - */ - void createLoginLog(@Valid LoginLogCreateReqDTO reqDTO); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java deleted file mode 100644 index e6894a9..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApi.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.api.logger; - -import cn.iocoder.yudao.framework.common.biz.system.logger.OperateLogCommonApi; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO; - -/** - * 操作日志 API 接口 - * - * @author 芋道源码 - */ -public interface OperateLogApi extends OperateLogCommonApi { - - /** - * 获取指定模块的指定数据的操作日志分页 - * - * @param pageReqDTO 请求 - * @return 操作日志分页 - */ - PageResult getOperateLogPage(OperateLogPageReqDTO pageReqDTO); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/LoginLogCreateReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/LoginLogCreateReqDTO.java deleted file mode 100644 index 539c8ee..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/LoginLogCreateReqDTO.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.yudao.module.system.api.logger.dto; - -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; - -/** - * 登录日志创建 Request DTO - * - * @author 芋道源码 - */ -@Data -public class LoginLogCreateReqDTO { - - /** - * 日志类型 - */ - @NotNull(message = "日志类型不能为空") - private Integer logType; - /** - * 链路追踪编号 - */ - private String traceId; - - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - */ - @NotNull(message = "用户类型不能为空") - private Integer userType; - /** - * 用户账号 - * - * 不再强制校验 username 非空,因为 Member 社交登录时,此时暂时没有 username(mobile)! - */ - private String username; - - /** - * 登录结果 - */ - @NotNull(message = "登录结果不能为空") - private Integer result; - - /** - * 用户 IP - */ - @NotEmpty(message = "用户 IP 不能为空") - private String userIp; - /** - * 浏览器 UserAgent - * - * 允许空,原因:Job 过期登出时,是无法传递 UserAgent 的 - */ - private String userAgent; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogPageReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogPageReqDTO.java deleted file mode 100644 index 80e52cd..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogPageReqDTO.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.api.logger.dto; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import lombok.Data; - -/** - * 操作日志分页 Request DTO - * - * @author HUIHUI - */ -@Data -public class OperateLogPageReqDTO extends PageParam { - - /** - * 模块类型 - */ - private String type; - /** - * 模块数据编号 - */ - private Long bizId; - - /** - * 用户编号 - */ - private Long userId; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogRespDTO.java deleted file mode 100644 index 41ff437..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/logger/dto/OperateLogRespDTO.java +++ /dev/null @@ -1,83 +0,0 @@ -package cn.iocoder.yudao.module.system.api.logger.dto; - -import com.fhs.core.trans.anno.Trans; -import com.fhs.core.trans.constant.TransType; -import com.fhs.core.trans.vo.VO; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 系统操作日志 Resp DTO - * - * @author HUIHUI - */ -@Data -public class OperateLogRespDTO implements VO { - - /** - * 日志编号 - */ - private Long id; - /** - * 链路追踪编号 - */ - private String traceId; - /** - * 用户编号 - */ - @Trans(type = TransType.SIMPLE, targetClassName = "cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO", - fields = "nickname", ref = "userName") - private Long userId; - /** - * 用户名称 - */ - private String userName; - /** - * 用户类型 - */ - private Integer userType; - /** - * 操作模块类型 - */ - private String type; - /** - * 操作名 - */ - private String subType; - /** - * 操作模块业务编号 - */ - private Long bizId; - /** - * 操作内容 - */ - private String action; - /** - * 拓展字段 - */ - private String extra; - - /** - * 请求方法名 - */ - private String requestMethod; - /** - * 请求地址 - */ - private String requestUrl; - /** - * 用户 IP - */ - private String userIp; - /** - * 浏览器 UA - */ - private String userAgent; - - /** - * 创建时间 - */ - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java deleted file mode 100644 index 9fb4b0e..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.api.mail; - -import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleToUserReqDTO; - -import jakarta.validation.Valid; - -/** - * 邮箱发送 API 接口 - * - * @author 芋道源码 - */ -public interface MailSendApi { - - /** - * 发送单条邮箱给 Admin 用户 - * - * 在 mail 为空时,使用 userId 加载对应 Admin 的邮箱 - * - * @param reqDTO 发送请求 - * @return 发送日志编号 - */ - Long sendSingleMailToAdmin(@Valid MailSendSingleToUserReqDTO reqDTO); - - /** - * 发送单条邮箱给 Member 用户 - * - * 在 mail 为空时,使用 userId 加载对应 Member 的邮箱 - * - * @param reqDTO 发送请求 - * @return 发送日志编号 - */ - Long sendSingleMailToMember(@Valid MailSendSingleToUserReqDTO reqDTO); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendSingleToUserReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendSingleToUserReqDTO.java deleted file mode 100644 index 0481d59..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendSingleToUserReqDTO.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.system.api.mail.dto; - -import lombok.Data; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotNull; -import java.util.Map; - -/** - * 邮件发送 Request DTO - * - * @author wangjingqi - */ -@Data -public class MailSendSingleToUserReqDTO { - - /** - * 用户编号 - */ - private Long userId; - /** - * 邮箱 - */ - @Email - private String mail; - - /** - * 邮件模板编号 - */ - @NotNull(message = "邮件模板编号不能为空") - private String templateCode; - /** - * 邮件模板参数 - */ - private Map templateParams; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java deleted file mode 100644 index ca8e0c9..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.system.api.notify; - -import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; - -import jakarta.validation.Valid; - -/** - * 站内信发送 API 接口 - * - * @author xrcoder - */ -public interface NotifyMessageSendApi { - - /** - * 发送单条站内信给 Admin 用户 - * - * @param reqDTO 发送请求 - * @return 发送消息 ID - */ - Long sendSingleMessageToAdmin(@Valid NotifySendSingleToUserReqDTO reqDTO); - - /** - * 发送单条站内信给 Member 用户 - * - * @param reqDTO 发送请求 - * @return 发送消息 ID - */ - Long sendSingleMessageToMember(@Valid NotifySendSingleToUserReqDTO reqDTO); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifySendSingleToUserReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifySendSingleToUserReqDTO.java deleted file mode 100644 index 635cc16..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifySendSingleToUserReqDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.api.notify.dto; - -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import java.util.Map; - -/** - * 站内信发送给 Admin 或者 Member 用户 - * - * @author xrcoder - */ -@Data -public class NotifySendSingleToUserReqDTO { - - /** - * 用户编号 - */ - @NotNull(message = "用户编号不能为空") - private Long userId; - - /** - * 站内信模板编号 - */ - @NotEmpty(message = "站内信模板编号不能为空") - private String templateCode; - - /** - * 站内信模板参数 - */ - private Map templateParams; -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifyTemplateReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifyTemplateReqDTO.java deleted file mode 100644 index 688e091..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifyTemplateReqDTO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.api.notify.dto; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -@Data -public class NotifyTemplateReqDTO { - - @NotEmpty(message = "模版名称不能为空") - private String name; - - @NotNull(message = "模版编码不能为空") - private String code; - - @NotNull(message = "模版类型不能为空") - private Integer type; - - @NotEmpty(message = "发送人名称不能为空") - private String nickname; - - @NotEmpty(message = "模版内容不能为空") - private String content; - - @NotNull(message = "状态不能为空") - @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") - private Integer status; - - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/package-info.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/package-info.java deleted file mode 100644 index 25bfc44..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * System API 包,定义暴露给其它模块的 API - */ -package cn.iocoder.yudao.module.system.api; diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApi.java deleted file mode 100644 index 41d8837..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApi.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.api.permission; - -import cn.iocoder.yudao.framework.common.biz.system.permission.PermissionCommonApi; - -import java.util.Collection; -import java.util.Set; - -/** - * 权限 API 接口 - * - * @author 芋道源码 - */ -public interface PermissionApi extends PermissionCommonApi { - - /** - * 获得拥有多个角色的用户编号集合 - * - * @param roleIds 角色编号集合 - * @return 用户编号集合 - */ - Set getUserRoleIdListByRoleIds(Collection roleIds); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApi.java deleted file mode 100644 index 309c9ef..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApi.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.api.permission; - -import java.util.Collection; - -/** - * 角色 API 接口 - * - * @author 芋道源码 - */ -public interface RoleApi { - - /** - * 校验角色们是否有效。如下情况,视为无效: - * 1. 角色编号不存在 - * 2. 角色被禁用 - * - * @param ids 角色编号数组 - */ - void validRoleList(Collection ids); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java deleted file mode 100644 index eefe9da..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApi.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.system.api.sms; - -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; - -import jakarta.validation.Valid; - -/** - * 短信验证码 API 接口 - * - * @author 芋道源码 - */ -public interface SmsCodeApi { - - /** - * 创建短信验证码,并进行发送 - * - * @param reqDTO 发送请求 - */ - void sendSmsCode(@Valid SmsCodeSendReqDTO reqDTO); - - /** - * 验证短信验证码,并进行使用 - * 如果正确,则将验证码标记成已使用 - * 如果错误,则抛出 {@link ServiceException} 异常 - * - * @param reqDTO 使用请求 - */ - void useSmsCode(@Valid SmsCodeUseReqDTO reqDTO); - - /** - * 检查验证码是否有效 - * - * @param reqDTO 校验请求 - */ - void validateSmsCode(@Valid SmsCodeValidateReqDTO reqDTO); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApi.java deleted file mode 100644 index 21f34c8..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApi.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.api.sms; - -import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; - -import jakarta.validation.Valid; - -/** - * 短信发送 API 接口 - * - * @author 芋道源码 - */ -public interface SmsSendApi { - - /** - * 发送单条短信给 Admin 用户 - * - * 在 mobile 为空时,使用 userId 加载对应 Admin 的手机号 - * - * @param reqDTO 发送请求 - * @return 发送日志编号 - */ - Long sendSingleSmsToAdmin(@Valid SmsSendSingleToUserReqDTO reqDTO); - - /** - * 发送单条短信给 Member 用户 - * - * 在 mobile 为空时,使用 userId 加载对应 Member 的手机号 - * - * @param reqDTO 发送请求 - * @return 发送日志编号 - */ - Long sendSingleSmsToMember(@Valid SmsSendSingleToUserReqDTO reqDTO); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeSendReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeSendReqDTO.java deleted file mode 100644 index 31a253f..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeSendReqDTO.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.system.api.sms.dto.code; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.framework.common.validation.Mobile; -import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -/** - * 短信验证码的发送 Request DTO - * - * @author 芋道源码 - */ -@Data -public class SmsCodeSendReqDTO { - - /** - * 手机号 - */ - @Mobile - @NotEmpty(message = "手机号不能为空") - private String mobile; - /** - * 发送场景 - */ - @NotNull(message = "发送场景不能为空") - @InEnum(SmsSceneEnum.class) - private Integer scene; - /** - * 发送 IP - */ - @NotEmpty(message = "发送 IP 不能为空") - private String createIp; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeUseReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeUseReqDTO.java deleted file mode 100644 index c700fa4..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeUseReqDTO.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.system.api.sms.dto.code; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.framework.common.validation.Mobile; -import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -/** - * 短信验证码的使用 Request DTO - * - * @author 芋道源码 - */ -@Data -public class SmsCodeUseReqDTO { - - /** - * 手机号 - */ - @Mobile - @NotEmpty(message = "手机号不能为空") - private String mobile; - /** - * 发送场景 - */ - @NotNull(message = "发送场景不能为空") - @InEnum(SmsSceneEnum.class) - private Integer scene; - /** - * 验证码 - */ - @NotEmpty(message = "验证码") - private String code; - /** - * 使用 IP - */ - @NotEmpty(message = "使用 IP 不能为空") - private String usedIp; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeValidateReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeValidateReqDTO.java deleted file mode 100644 index b388e6b..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/code/SmsCodeValidateReqDTO.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.system.api.sms.dto.code; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.framework.common.validation.Mobile; -import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -/** - * 短信验证码的校验 Request DTO - * - * @author 芋道源码 - */ -@Data -public class SmsCodeValidateReqDTO { - - /** - * 手机号 - */ - @Mobile - @NotEmpty(message = "手机号不能为空") - private String mobile; - /** - * 发送场景 - */ - @NotNull(message = "发送场景不能为空") - @InEnum(SmsSceneEnum.class) - private Integer scene; - /** - * 验证码 - */ - @NotEmpty(message = "验证码") - private String code; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java deleted file mode 100644 index d566a48..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/sms/dto/send/SmsSendSingleToUserReqDTO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.system.api.sms.dto.send; - -import cn.iocoder.yudao.framework.common.validation.Mobile; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import java.util.Map; - -/** - * 短信发送给 Admin 或者 Member 用户 - * - * @author 芋道源码 - */ -@Data -public class SmsSendSingleToUserReqDTO { - - /** - * 用户编号 - */ - private Long userId; - /** - * 手机号 - */ - @Mobile - private String mobile; - /** - * 短信模板编号 - */ - @NotEmpty(message = "短信模板编号不能为空") - private String templateCode; - /** - * 短信模板参数 - */ - private Map templateParams; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java deleted file mode 100644 index 7306310..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApi.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social; - -import cn.iocoder.yudao.module.system.api.social.dto.*; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import jakarta.validation.Valid; - -import java.util.List; - -/** - * 社交应用的 API 接口 - * - * @author 芋道源码 - */ -public interface SocialClientApi { - - /** - * 获得社交平台的授权 URL - * - * @param socialType 社交平台的类型 {@link SocialTypeEnum} - * @param userType 用户类型 - * @param redirectUri 重定向 URL - * @return 社交平台的授权 URL - */ - String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri); - - /** - * 创建微信公众号 JS SDK 初始化所需的签名 - * - * @param userType 用户类型 - * @param url 访问的 URL 地址 - * @return 签名 - */ - SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url); - - //======================= 微信小程序独有 ======================= - - /** - * 获得微信小程序的手机信息 - * - * @param userType 用户类型 - * @param phoneCode 手机授权码 - * @return 手机信息 - */ - SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode); - - /** - * 获得小程序二维码 - * - * @param reqVO 请求信息 - * @return 小程序二维码 - */ - byte[] getWxaQrcode(@Valid SocialWxQrcodeReqDTO reqVO); - - /** - * 获得微信小程订阅模板 - * - * @return 小程序订阅消息模版 - */ - List getWxaSubscribeTemplateList(Integer userType); - - /** - * 发送微信小程序订阅消息 - * - * @param reqDTO 请求 - */ - void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO); - - /** - * 上传订单发货到微信小程序 - * - * @param userType 用户类型 - * @param reqDTO 请求 - */ - void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO); - - /** - * 通知订单收货到微信小程序 - * - * @param userType 用户类型 - * @param reqDTO 请求 - */ - void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java deleted file mode 100644 index 6102c4e..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApi.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social; - -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; -import jakarta.validation.Valid; - -/** - * 社交用户的 API 接口 - * - * @author 芋道源码 - */ -public interface SocialUserApi { - - /** - * 绑定社交用户 - * - * @param reqDTO 绑定信息 - * @return 社交用户 openid - */ - String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO); - - /** - * 取消绑定社交用户 - * - * @param reqDTO 解绑 - */ - void unbindSocialUser(@Valid SocialUserUnbindReqDTO reqDTO); - - /** - * 获得社交用户,基于 userId - * - * @param userType 用户类型 - * @param userId 用户编号 - * @param socialType 社交平台的类型 - * @return 社交用户 - */ - SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType); - - /** - * 获得社交用户 - * - * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 - * - * @param userType 用户类型 - * @param socialType 社交平台的类型 - * @param code 授权码 - * @param state state - * @return 社交用户 - */ - SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java deleted file mode 100644 index 0ad3952..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserBindReqDTO.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -/** - * 取消绑定社交用户 Request DTO - * - * @author 芋道源码 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class SocialUserBindReqDTO { - - /** - * 用户编号 - */ - @NotNull(message = "用户编号不能为空") - private Long userId; - /** - * 用户类型 - */ - @InEnum(UserTypeEnum.class) - @NotNull(message = "用户类型不能为空") - private Integer userType; - - /** - * 社交平台的类型 - */ - @InEnum(SocialTypeEnum.class) - @NotNull(message = "社交平台的类型不能为空") - private Integer socialType; - /** - * 授权码 - */ - @NotEmpty(message = "授权码不能为空") - private String code; - /** - * state - */ - @NotNull(message = "state 不能为空") - private String state; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java deleted file mode 100644 index 29fef01..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserRespDTO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 社交用户 Response DTO - * - * @author 芋道源码 - */ -@Data -@NoArgsConstructor -@AllArgsConstructor -public class SocialUserRespDTO { - - /** - * 社交用户的 openid - */ - private String openid; - /** - * 社交用户的昵称 - */ - private String nickname; - /** - * 社交用户的头像 - */ - private String avatar; - - /** - * 关联的用户编号 - */ - private Long userId; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserUnbindReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserUnbindReqDTO.java deleted file mode 100644 index 7e559d8..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialUserUnbindReqDTO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import lombok.AllArgsConstructor; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.NoArgsConstructor; - -/** - * 社交绑定 Request DTO,使用 code 授权码 - * - * @author 芋道源码 - */ -@Data -@AllArgsConstructor -@NoArgsConstructor -public class SocialUserUnbindReqDTO { - - /** - * 用户编号 - */ - @NotNull(message = "用户编号不能为空") - private Long userId; - /** - * 用户类型 - */ - @InEnum(UserTypeEnum.class) - @NotNull(message = "用户类型不能为空") - private Integer userType; - - /** - * 社交平台的类型 - */ - @InEnum(SocialTypeEnum.class) - @NotNull(message = "社交平台的类型不能为空") - private Integer socialType; - - /** - * 社交平台的 openid - */ - @NotEmpty(message = "社交平台的 openid 不能为空") - private String openid; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxJsapiSignatureRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxJsapiSignatureRespDTO.java deleted file mode 100644 index 7d332e9..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxJsapiSignatureRespDTO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import lombok.Data; - -/** - * 微信公众号 JSAPI 签名 Response DTO - * - * @author 芋道源码 - */ -@Data -public class SocialWxJsapiSignatureRespDTO { - - /** - * 微信公众号的 appId - */ - private String appId; - /** - * 匿名串 - */ - private String nonceStr; - /** - * 时间戳 - */ - private Long timestamp; - /** - * URL - */ - private String url; - /** - * 签名 - */ - private String signature; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxPhoneNumberInfoRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxPhoneNumberInfoRespDTO.java deleted file mode 100644 index 9d404b3..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxPhoneNumberInfoRespDTO.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import lombok.Data; - -/** - * 微信小程序的手机信息 Response DTO - * - * @author 芋道源码 - */ -@Data -public class SocialWxPhoneNumberInfoRespDTO { - - /** - * 用户绑定的手机号(国外手机号会有区号) - */ - private String phoneNumber; - - /** - * 没有区号的手机号 - */ - private String purePhoneNumber; - /** - * 区号 - */ - private String countryCode; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxQrcodeReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxQrcodeReqDTO.java deleted file mode 100644 index 6f4b96f..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxQrcodeReqDTO.java +++ /dev/null @@ -1,66 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -/** - * 获取小程序码 Request DTO - * - * @author HUIHUI - * @see 获取不限制的小程序码 - */ -@Data -public class SocialWxQrcodeReqDTO { - - /** - * 页面路径不能携带参数(参数请放在scene字段里) - */ - public static final String SCENE = ""; - /** - * 二维码宽度 - */ - public static final Integer WIDTH = 430; - /** - * 自动配置线条颜色,如果颜色依然是黑色,则说明不建议配置主色调 - */ - public static final Boolean AUTO_COLOR = true; - /** - * 检查 page 是否存在 - */ - public static final Boolean CHECK_PATH = true; - /** - * 是否需要透明底色 - * - * hyaline 为 true 时,生成透明底色的小程序码 - */ - public static final Boolean HYALINE = true; - - /** - * 场景 - */ - @NotEmpty(message = "场景不能为空") - private String scene; - /** - * 页面路径 - */ - @NotEmpty(message = "页面路径不能为空") - private String path; - /** - * 二维码宽度 - */ - private Integer width; - - /** - * 是否需要透明底色 - */ - private Boolean autoColor; - /** - * 是否检查 page 是否存在 - */ - private Boolean checkPath; - /** - * 是否需要透明底色 - */ - private Boolean hyaline; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java deleted file mode 100644 index d2bf62c..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderNotifyConfirmReceiveReqDTO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 小程序订单上传购物详情 - * - * @see 上传购物详情 - * @author 芋道源码 - */ -@Data -public class SocialWxaOrderNotifyConfirmReceiveReqDTO { - - /** - * 原支付交易对应的微信订单号 - */ - @NotEmpty(message = "原支付交易对应的微信订单号不能为空") - private String transactionId; - - /** - * 快递签收时间 - */ - @NotNull(message = "快递签收时间不能为空") - private LocalDateTime receivedTime; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java deleted file mode 100644 index 555da62..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaOrderUploadShippingInfoReqDTO.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -/** - * 小程序订单上传购物详情 - * - * @see 上传购物详情 - * @author 芋道源码 - */ -@Data -public class SocialWxaOrderUploadShippingInfoReqDTO { - - /** - * 物流模式 - 实体物流配送采用快递公司进行实体物流配送形式 - */ - public static final Integer LOGISTICS_TYPE_EXPRESS = 1; - /** - * 物流模式 - 虚拟商品,虚拟商品,例如话费充值,点卡等,无实体配送形式 - */ - public static final Integer LOGISTICS_TYPE_VIRTUAL = 3; - /** - * 物流模式 - 用户自提 - */ - public static final Integer LOGISTICS_TYPE_PICK_UP = 4; - - /** - * 支付者,支付者信息(openid) - */ - @NotEmpty(message = "支付者,支付者信息(openid)不能为空") - private String openid; - - /** - * 原支付交易对应的微信订单号 - */ - @NotEmpty(message = "原支付交易对应的微信订单号不能为空") - private String transactionId; - - /** - * 物流模式 - */ - @NotNull(message = "物流模式不能为空") - private Integer logisticsType; - /** - * 物流发货单号 - */ - private String logisticsNo; - /** - * 物流公司编号 - * - * @see 物流查询插件简介 - */ - private String expressCompany; - /** - * 商品信息 - */ - @NotEmpty(message = "商品信息不能为空") - private String itemDesc; - /** - * 收件人手机号 - */ - @NotEmpty(message = "收件人手机号") - private String receiverContact; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeMessageSendReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeMessageSendReqDTO.java deleted file mode 100644 index 2677a34..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeMessageSendReqDTO.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.util.HashMap; -import java.util.Map; - -/** - * 微信小程序订阅消息发送 Request DTO - * - * @author HUIHUI - */ -@Data -public class SocialWxaSubscribeMessageSendReqDTO { - - /** - * 用户编号 - * - * 关联 MemberUserDO 的 id 编号 - * 关联 AdminUserDO 的 id 编号 - */ - @NotNull(message = "用户编号不能为空") - private Long userId; - /** - * 用户类型 - * - * 关联 {@link UserTypeEnum} - */ - @NotNull(message = "用户类型不能为空") - private Integer userType; - - /** - * 消息模版标题 - */ - @NotEmpty(message = "消息模版标题不能为空") - private String templateTitle; - - /** - * 点击模板卡片后的跳转页面,仅限本小程序内的页面 - * - * 支持带参数,(示例 index?foo=bar )。该字段不填则模板无跳转。 - */ - private String page; - - /** - * 模板内容的参数 - */ - private Map messages; - - public SocialWxaSubscribeMessageSendReqDTO addMessage(String key, String value) { - if (messages == null) { - messages = new HashMap<>(); - } - messages.put(key, value); - return this; - } - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeTemplateRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeTemplateRespDTO.java deleted file mode 100644 index ae14cc5..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/social/dto/SocialWxaSubscribeTemplateRespDTO.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social.dto; - -import lombok.Data; - - -/** - * 小程序订阅消息模版 Response DTO - * - * @author HUIHUI - */ -@Data -public class SocialWxaSubscribeTemplateRespDTO { - - /** - * 模版编号 - */ - private String id; - - /** - * 模版标题 - */ - private String title; - - /** - * 模版内容 - */ - private String content; - - /** - * 模板内容示例 - */ - private String example; - - /** - * 模版类型 - * - * 2:为一次性订阅 - * 3:为长期订阅 - */ - private Integer type; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java deleted file mode 100644 index 507fb4b..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java +++ /dev/null @@ -1,89 +0,0 @@ -package cn.iocoder.yudao.module.system.api.user; - -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * Admin 用户 API 接口 - * - * @author 芋道源码 - */ -public interface AdminUserApi { - - /** - * 通过用户 ID 查询用户 - * - * @param id 用户ID - * @return 用户对象信息 - */ - AdminUserRespDTO getUser(Long id); - - /** - * 通过用户 ID 查询用户下属 - * - * @param id 用户编号 - * @return 用户下属用户列表 - */ - List getUserListBySubordinate(Long id); - - /** - * 通过用户 ID 查询用户们 - * - * @param ids 用户 ID 们 - * @return 用户对象信息 - */ - List getUserList(Collection ids); - - /** - * 获得指定部门的用户数组 - * - * @param deptIds 部门数组 - * @return 用户数组 - */ - List getUserListByDeptIds(Collection deptIds); - - /** - * 获得指定岗位的用户数组 - * - * @param postIds 岗位数组 - * @return 用户数组 - */ - List getUserListByPostIds(Collection postIds); - - /** - * 获得用户 Map - * - * @param ids 用户编号数组 - * @return 用户 Map - */ - default Map getUserMap(Collection ids) { - List users = getUserList(ids); - return CollectionUtils.convertMap(users, AdminUserRespDTO::getId); - } - - /** - * 校验用户是否有效。如下情况,视为无效: - * 1. 用户编号不存在 - * 2. 用户被禁用 - * - * @param id 用户编号 - */ - default void validateUser(Long id) { - validateUserList(Collections.singleton(id)); - } - - /** - * 校验用户们是否有效。如下情况,视为无效: - * 1. 用户编号不存在 - * 2. 用户被禁用 - * - * @param ids 用户编号数组 - */ - void validateUserList(Collection ids); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java deleted file mode 100644 index d86f3e5..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/dto/AdminUserRespDTO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.system.api.user.dto; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import lombok.Data; - -import java.util.Set; - -/** - * Admin 用户 Response DTO - * - * @author 芋道源码 - */ -@Data -public class AdminUserRespDTO { - - /** - * 用户ID - */ - private Long id; - /** - * 用户昵称 - */ - private String nickname; - /** - * 帐号状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - - /** - * 部门ID - */ - private Long deptId; - /** - * 岗位编号数组 - */ - private Set postIds; - /** - * 手机号码 - */ - private String mobile; - /** - * 用户头像 - */ - private String avatar; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java deleted file mode 100644 index d7592c3..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/DictTypeConstants.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.enums; - -/** - * System 字典类型的枚举类 - * - * @author 芋道源码 - */ -public interface DictTypeConstants { - - String USER_TYPE = "user_type"; // 用户类型 - String COMMON_STATUS = "common_status"; // 系统状态 - - // ========== SYSTEM 模块 ========== - - String USER_SEX = "system_user_sex"; // 用户性别 - String DATA_SCOPE = "system_data_scope"; // 数据范围 - - String LOGIN_TYPE = "system_login_type"; // 登录日志的类型 - String LOGIN_RESULT = "system_login_result"; // 登录结果 - - String SMS_CHANNEL_CODE = "system_sms_channel_code"; // 短信渠道编码 - String SMS_TEMPLATE_TYPE = "system_sms_template_type"; // 短信模板类型 - String SMS_SEND_STATUS = "system_sms_send_status"; // 短信发送状态 - String SMS_RECEIVE_STATUS = "system_sms_receive_status"; // 短信接收状态 - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java deleted file mode 100644 index c89a5dc..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ /dev/null @@ -1,171 +0,0 @@ -package cn.iocoder.yudao.module.system.enums; - -import cn.iocoder.yudao.framework.common.exception.ErrorCode; - -/** - * System 错误码枚举类 - * - * system 系统,使用 1-002-000-000 段 - */ -public interface ErrorCodeConstants { - - // ========== AUTH 模块 1-002-000-000 ========== - ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1_002_000_000, "登录失败,账号密码不正确"); - ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1_002_000_001, "登录失败,账号被禁用"); - ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_004, "验证码不正确,原因:{}"); - ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1_002_000_005, "未绑定账号,需要进行绑定"); - ErrorCode AUTH_MOBILE_NOT_EXISTS = new ErrorCode(1_002_000_007, "手机号不存在"); - ErrorCode AUTH_REGISTER_CAPTCHA_CODE_ERROR = new ErrorCode(1_002_000_008, "验证码不正确,原因:{}"); - - // ========== 菜单模块 1-002-001-000 ========== - ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1_002_001_000, "已经存在该名字的菜单"); - ErrorCode MENU_PARENT_NOT_EXISTS = new ErrorCode(1_002_001_001, "父菜单不存在"); - ErrorCode MENU_PARENT_ERROR = new ErrorCode(1_002_001_002, "不能设置自己为父菜单"); - ErrorCode MENU_NOT_EXISTS = new ErrorCode(1_002_001_003, "菜单不存在"); - ErrorCode MENU_EXISTS_CHILDREN = new ErrorCode(1_002_001_004, "存在子菜单,无法删除"); - ErrorCode MENU_PARENT_NOT_DIR_OR_MENU = new ErrorCode(1_002_001_005, "父菜单的类型必须是目录或者菜单"); - ErrorCode MENU_COMPONENT_NAME_DUPLICATE = new ErrorCode(1_002_001_006, "已经存在该组件名的菜单"); - - // ========== 角色模块 1-002-002-000 ========== - ErrorCode ROLE_NOT_EXISTS = new ErrorCode(1_002_002_000, "角色不存在"); - ErrorCode ROLE_NAME_DUPLICATE = new ErrorCode(1_002_002_001, "已经存在名为【{}】的角色"); - ErrorCode ROLE_CODE_DUPLICATE = new ErrorCode(1_002_002_002, "已经存在标识为【{}】的角色"); - ErrorCode ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE = new ErrorCode(1_002_002_003, "不能操作类型为系统内置的角色"); - ErrorCode ROLE_IS_DISABLE = new ErrorCode(1_002_002_004, "名字为【{}】的角色已被禁用"); - ErrorCode ROLE_ADMIN_CODE_ERROR = new ErrorCode(1_002_002_005, "标识【{}】不能使用"); - - // ========== 用户模块 1-002-003-000 ========== - ErrorCode USER_USERNAME_EXISTS = new ErrorCode(1_002_003_000, "用户账号已经存在"); - ErrorCode USER_MOBILE_EXISTS = new ErrorCode(1_002_003_001, "手机号已经存在"); - ErrorCode USER_EMAIL_EXISTS = new ErrorCode(1_002_003_002, "邮箱已经存在"); - ErrorCode USER_NOT_EXISTS = new ErrorCode(1_002_003_003, "用户不存在"); - ErrorCode USER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_002_003_004, "导入用户数据不能为空!"); - ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1_002_003_005, "用户密码校验失败"); - ErrorCode USER_IS_DISABLE = new ErrorCode(1_002_003_006, "名字为【{}】的用户已被禁用"); - ErrorCode USER_COUNT_MAX = new ErrorCode(1_002_003_008, "创建用户失败,原因:超过租户最大租户配额({})!"); - ErrorCode USER_IMPORT_INIT_PASSWORD = new ErrorCode(1_002_003_009, "初始密码不能为空"); - ErrorCode USER_MOBILE_NOT_EXISTS = new ErrorCode(1_002_003_010, "该手机号尚未注册"); - ErrorCode USER_REGISTER_DISABLED = new ErrorCode(1_002_003_011, "注册功能已关闭"); - - // ========== 部门模块 1-002-004-000 ========== - ErrorCode DEPT_NAME_DUPLICATE = new ErrorCode(1_002_004_000, "已经存在该名字的部门"); - ErrorCode DEPT_PARENT_NOT_EXITS = new ErrorCode(1_002_004_001,"父级部门不存在"); - ErrorCode DEPT_NOT_FOUND = new ErrorCode(1_002_004_002, "当前部门不存在"); - ErrorCode DEPT_EXITS_CHILDREN = new ErrorCode(1_002_004_003, "存在子部门,无法删除"); - ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门"); - ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择"); - ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门"); - - // ========== 岗位模块 1-002-005-000 ========== - ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在"); - ErrorCode POST_NOT_ENABLE = new ErrorCode(1_002_005_001, "岗位({}) 不处于开启状态,不允许选择"); - ErrorCode POST_NAME_DUPLICATE = new ErrorCode(1_002_005_002, "已经存在该名字的岗位"); - ErrorCode POST_CODE_DUPLICATE = new ErrorCode(1_002_005_003, "已经存在该标识的岗位"); - - // ========== 字典类型 1-002-006-000 ========== - ErrorCode DICT_TYPE_NOT_EXISTS = new ErrorCode(1_002_006_001, "当前字典类型不存在"); - ErrorCode DICT_TYPE_NOT_ENABLE = new ErrorCode(1_002_006_002, "字典类型不处于开启状态,不允许选择"); - ErrorCode DICT_TYPE_NAME_DUPLICATE = new ErrorCode(1_002_006_003, "已经存在该名字的字典类型"); - ErrorCode DICT_TYPE_TYPE_DUPLICATE = new ErrorCode(1_002_006_004, "已经存在该类型的字典类型"); - ErrorCode DICT_TYPE_HAS_CHILDREN = new ErrorCode(1_002_006_005, "无法删除,该字典类型还有字典数据"); - - // ========== 字典数据 1-002-007-000 ========== - ErrorCode DICT_DATA_NOT_EXISTS = new ErrorCode(1_002_007_001, "当前字典数据不存在"); - ErrorCode DICT_DATA_NOT_ENABLE = new ErrorCode(1_002_007_002, "字典数据({})不处于开启状态,不允许选择"); - ErrorCode DICT_DATA_VALUE_DUPLICATE = new ErrorCode(1_002_007_003, "已经存在该值的字典数据"); - - // ========== 通知公告 1-002-008-000 ========== - ErrorCode NOTICE_NOT_FOUND = new ErrorCode(1_002_008_001, "当前通知公告不存在"); - - // ========== 短信渠道 1-002-011-000 ========== - ErrorCode SMS_CHANNEL_NOT_EXISTS = new ErrorCode(1_002_011_000, "短信渠道不存在"); - ErrorCode SMS_CHANNEL_DISABLE = new ErrorCode(1_002_011_001, "短信渠道不处于开启状态,不允许选择"); - ErrorCode SMS_CHANNEL_HAS_CHILDREN = new ErrorCode(1_002_011_002, "无法删除,该短信渠道还有短信模板"); - - // ========== 短信模板 1-002-012-000 ========== - ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在"); - ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板"); - ErrorCode SMS_TEMPLATE_API_ERROR = new ErrorCode(1_002_012_002, "短信 API 模板调用失败,原因是:{}"); - ErrorCode SMS_TEMPLATE_API_AUDIT_CHECKING = new ErrorCode(1_002_012_003, "短信 API 模版无法使用,原因:审批中"); - ErrorCode SMS_TEMPLATE_API_AUDIT_FAIL = new ErrorCode(1_002_012_004, "短信 API 模版无法使用,原因:审批不通过,{}"); - ErrorCode SMS_TEMPLATE_API_NOT_FOUND = new ErrorCode(1_002_012_005, "短信 API 模版无法使用,原因:模版不存在"); - - // ========== 短信发送 1-002-013-000 ========== - ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在"); - ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_013_001, "模板参数({})缺失"); - ErrorCode SMS_SEND_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_013_002, "短信模板不存在"); - - // ========== 短信验证码 1-002-014-000 ========== - ErrorCode SMS_CODE_NOT_FOUND = new ErrorCode(1_002_014_000, "验证码不存在"); - ErrorCode SMS_CODE_EXPIRED = new ErrorCode(1_002_014_001, "验证码已过期"); - ErrorCode SMS_CODE_USED = new ErrorCode(1_002_014_002, "验证码已使用"); - ErrorCode SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY = new ErrorCode(1_002_014_004, "超过每日短信发送数量"); - ErrorCode SMS_CODE_SEND_TOO_FAST = new ErrorCode(1_002_014_005, "短信发送过于频繁"); - - // ========== 租户信息 1-002-015-000 ========== - ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1_002_015_000, "租户不存在"); - ErrorCode TENANT_DISABLE = new ErrorCode(1_002_015_001, "名字为【{}】的租户已被禁用"); - ErrorCode TENANT_EXPIRE = new ErrorCode(1_002_015_002, "名字为【{}】的租户已过期"); - ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1_002_015_003, "系统租户不能进行修改、删除等操作!"); - ErrorCode TENANT_NAME_DUPLICATE = new ErrorCode(1_002_015_004, "名字为【{}】的租户已存在"); - ErrorCode TENANT_WEBSITE_DUPLICATE = new ErrorCode(1_002_015_005, "域名为【{}】的租户已存在"); - - // ========== 租户套餐 1-002-016-000 ========== - ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1_002_016_000, "租户套餐不存在"); - ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1_002_016_001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除"); - ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1_002_016_002, "名字为【{}】的租户套餐已被禁用"); - ErrorCode TENANT_PACKAGE_NAME_DUPLICATE = new ErrorCode(1_002_016_003, "已经存在该名字的租户套餐"); - - // ========== 社交用户 1-002-018-000 ========== - ErrorCode SOCIAL_USER_AUTH_FAILURE = new ErrorCode(1_002_018_000, "社交授权失败,原因是:{}"); - ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1_002_018_001, "社交授权失败,找不到对应的用户"); - - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR = new ErrorCode(1_002_018_200, "获得手机号失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR = new ErrorCode(1_002_018_201, "获得小程序码失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR = new ErrorCode(1_002_018_202, "获得小程序订阅消息模版失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR = new ErrorCode(1_002_018_203, "发送小程序订阅消息失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR = new ErrorCode(1_002_018_204, "上传微信小程序发货信息失败"); - ErrorCode SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR = new ErrorCode(1_002_018_205, "上传微信小程序订单收货信息失败"); - ErrorCode SOCIAL_CLIENT_NOT_EXISTS = new ErrorCode(1_002_018_210, "社交客户端不存在"); - ErrorCode SOCIAL_CLIENT_UNIQUE = new ErrorCode(1_002_018_211, "社交客户端已存在配置"); - - // ========== OAuth2 客户端 1-002-020-000 ========= - ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1_002_020_000, "OAuth2 客户端不存在"); - ErrorCode OAUTH2_CLIENT_EXISTS = new ErrorCode(1_002_020_001, "OAuth2 客户端编号已存在"); - ErrorCode OAUTH2_CLIENT_DISABLE = new ErrorCode(1_002_020_002, "OAuth2 客户端已禁用"); - ErrorCode OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS = new ErrorCode(1_002_020_003, "不支持该授权类型"); - ErrorCode OAUTH2_CLIENT_SCOPE_OVER = new ErrorCode(1_002_020_004, "授权范围过大"); - ErrorCode OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH = new ErrorCode(1_002_020_005, "无效 redirect_uri: {}"); - ErrorCode OAUTH2_CLIENT_CLIENT_SECRET_ERROR = new ErrorCode(1_002_020_006, "无效 client_secret: {}"); - - // ========== OAuth2 授权 1-002-021-000 ========= - ErrorCode OAUTH2_GRANT_CLIENT_ID_MISMATCH = new ErrorCode(1_002_021_000, "client_id 不匹配"); - ErrorCode OAUTH2_GRANT_REDIRECT_URI_MISMATCH = new ErrorCode(1_002_021_001, "redirect_uri 不匹配"); - ErrorCode OAUTH2_GRANT_STATE_MISMATCH = new ErrorCode(1_002_021_002, "state 不匹配"); - - // ========== OAuth2 授权 1-002-022-000 ========= - ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1_002_022_000, "code 不存在"); - ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1_002_022_001, "code 已过期"); - - // ========== 邮箱账号 1-002-023-000 ========== - ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1_002_023_000, "邮箱账号不存在"); - ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1_002_023_001, "无法删除,该邮箱账号还有邮件模板"); - - // ========== 邮件模版 1-002-024-000 ========== - ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_024_000, "邮件模版不存在"); - ErrorCode MAIL_TEMPLATE_CODE_EXISTS = new ErrorCode(1_002_024_001, "邮件模版 code({}) 已存在"); - - // ========== 邮件发送 1-002-025-000 ========== - ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_025_000, "模板参数({})缺失"); - ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1_002_025_001, "邮箱不存在"); - - // ========== 站内信模版 1-002-026-000 ========== - ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_026_000, "站内信模版不存在"); - ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_026_001, "已经存在编码为【{}】的站内信模板"); - - // ========== 站内信模版 1-002-027-000 ========== - - // ========== 站内信发送 1-002-028-000 ========== - ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失"); - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java deleted file mode 100644 index 4ee3840..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/LogRecordConstants.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.enums; - -/** - * System 操作日志枚举 - * 目的:统一管理,也减少 Service 里各种“复杂”字符串 - * - * @author 芋道源码 - */ -public interface LogRecordConstants { - - // ======================= SYSTEM_USER 用户 ======================= - - String SYSTEM_USER_TYPE = "SYSTEM 用户"; - String SYSTEM_USER_CREATE_SUB_TYPE = "创建用户"; - String SYSTEM_USER_CREATE_SUCCESS = "创建了用户【{{#user.nickname}}】"; - String SYSTEM_USER_UPDATE_SUB_TYPE = "更新用户"; - String SYSTEM_USER_UPDATE_SUCCESS = "更新了用户【{{#user.nickname}}】: {_DIFF{#updateReqVO}}"; - String SYSTEM_USER_DELETE_SUB_TYPE = "删除用户"; - String SYSTEM_USER_DELETE_SUCCESS = "删除了用户【{{#user.nickname}}】"; - String SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE = "重置用户密码"; - String SYSTEM_USER_UPDATE_PASSWORD_SUCCESS = "将用户【{{#user.nickname}}】的密码从【{{#user.password}}】重置为【{{#newPassword}}】"; - - // ======================= SYSTEM_ROLE 角色 ======================= - - String SYSTEM_ROLE_TYPE = "SYSTEM 角色"; - String SYSTEM_ROLE_CREATE_SUB_TYPE = "创建角色"; - String SYSTEM_ROLE_CREATE_SUCCESS = "创建了角色【{{#role.name}}】"; - String SYSTEM_ROLE_UPDATE_SUB_TYPE = "更新角色"; - String SYSTEM_ROLE_UPDATE_SUCCESS = "更新了角色【{{#role.name}}】: {_DIFF{#updateReqVO}}"; - String SYSTEM_ROLE_DELETE_SUB_TYPE = "删除角色"; - String SYSTEM_ROLE_DELETE_SUCCESS = "删除了角色【{{#role.name}}】"; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java deleted file mode 100644 index c1e222b..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/common/SexEnum.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.common; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 性别的枚举值 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum SexEnum { - - /** 男 */ - MALE(1), - /** 女 */ - FEMALE(2), - /* 未知 */ - UNKNOWN(0); - - /** - * 性别 - */ - private final Integer sex; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java deleted file mode 100644 index 4c51f91..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.logger; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 登录日志的类型枚举 - */ -@Getter -@AllArgsConstructor -public enum LoginLogTypeEnum { - - LOGIN_USERNAME(100), // 使用账号登录 - LOGIN_SOCIAL(101), // 使用社交登录 - LOGIN_MOBILE(103), // 使用手机登陆 - LOGIN_SMS(104), // 使用短信登陆 - - LOGOUT_SELF(200), // 自己主动登出 - LOGOUT_DELETE(202), // 强制退出 - ; - - /** - * 日志类型 - */ - private final Integer type; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginResultEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginResultEnum.java deleted file mode 100644 index afb92e4..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginResultEnum.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.logger; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 登录结果的枚举类 - */ -@Getter -@AllArgsConstructor -public enum LoginResultEnum { - - SUCCESS(0), // 成功 - BAD_CREDENTIALS(10), // 账号或密码不正确 - USER_DISABLED(20), // 用户被禁用 - CAPTCHA_NOT_FOUND(30), // 图片验证码不存在 - CAPTCHA_CODE_ERROR(31), // 图片验证码不正确 - - ; - - /** - * 结果 - */ - private final Integer result; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java deleted file mode 100644 index 7cb16ed..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.mail; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 邮件的发送状态枚举 - * - * @author wangjingyi - * @since 2022/4/10 13:39 - */ -@Getter -@AllArgsConstructor -public enum MailSendStatusEnum { - - INIT(0), // 初始化 - SUCCESS(10), // 发送成功 - FAILURE(20), // 发送失败 - IGNORE(30), // 忽略,即不发送 - ; - - private final int status; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/notice/NoticeTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/notice/NoticeTypeEnum.java deleted file mode 100644 index 9a2a3c4..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/notice/NoticeTypeEnum.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.notice; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 通知类型 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum NoticeTypeEnum { - - NOTICE(1), - ANNOUNCEMENT(2); - - /** - * 类型 - */ - private final Integer type; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/notify/NotifyTemplateTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/notify/NotifyTemplateTypeEnum.java deleted file mode 100644 index dccfb19..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/notify/NotifyTemplateTypeEnum.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.notify; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 通知模板类型枚举 - * - * @author HUIHUI - */ -@Getter -@AllArgsConstructor -public enum NotifyTemplateTypeEnum { - - /** - * 系统消息 - */ - SYSTEM_MESSAGE(2), - /** - * 通知消息 - */ - NOTIFICATION_MESSAGE(1); - - private final Integer type; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2GrantTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2GrantTypeEnum.java deleted file mode 100644 index 0b09fe5..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/oauth2/OAuth2GrantTypeEnum.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.oauth2; - -import cn.hutool.core.util.ArrayUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * OAuth2 授权类型(模式)的枚举 - * - * @author 芋道源码 - */ -@AllArgsConstructor -@Getter -public enum OAuth2GrantTypeEnum { - - PASSWORD("password"), // 密码模式 - AUTHORIZATION_CODE("authorization_code"), // 授权码模式 - IMPLICIT("implicit"), // 简化模式 - CLIENT_CREDENTIALS("client_credentials"), // 客户端模式 - REFRESH_TOKEN("refresh_token"), // 刷新模式 - ; - - private final String grantType; - - public static OAuth2GrantTypeEnum getByGrantType(String grantType) { - return ArrayUtil.firstMatch(o -> o.getGrantType().equals(grantType), values()); - } - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java deleted file mode 100644 index 3440a17..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/DataScopeEnum.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.permission; - -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 数据范围枚举类 - * - * 用于实现数据级别的权限 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum DataScopeEnum implements ArrayValuable { - - ALL(1), // 全部数据权限 - - DEPT_CUSTOM(2), // 指定部门数据权限 - DEPT_ONLY(3), // 部门数据权限 - DEPT_AND_CHILD(4), // 部门及以下数据权限 - - SELF(5); // 仅本人数据权限 - - /** - * 范围 - */ - private final Integer scope; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(DataScopeEnum::getScope).toArray(Integer[]::new); - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java deleted file mode 100644 index 575a33c..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/MenuTypeEnum.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.permission; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 菜单类型枚举类 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum MenuTypeEnum { - - DIR(1), // 目录 - MENU(2), // 菜单 - BUTTON(3) // 按钮 - ; - - /** - * 类型 - */ - private final Integer type; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleCodeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleCodeEnum.java deleted file mode 100644 index cdf9a00..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleCodeEnum.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.permission; - -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 角色标识枚举 - */ -@Getter -@AllArgsConstructor -public enum RoleCodeEnum { - - SUPER_ADMIN("super_admin", "超级管理员"), - TENANT_ADMIN("tenant_admin", "租户管理员"), - CRM_ADMIN("crm_admin", "CRM 管理员"); // CRM 系统专用 - ; - - /** - * 角色编码 - */ - private final String code; - /** - * 名字 - */ - private final String name; - - public static boolean isSuperAdmin(String code) { - return ObjectUtils.equalsAny(code, SUPER_ADMIN.getCode()); - } - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleTypeEnum.java deleted file mode 100644 index 1607b20..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/permission/RoleTypeEnum.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.permission; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public enum RoleTypeEnum { - - /** - * 内置角色 - */ - SYSTEM(1), - /** - * 自定义角色 - */ - CUSTOM(2); - - private final Integer type; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsReceiveStatusEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsReceiveStatusEnum.java deleted file mode 100644 index 581ee6f..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsReceiveStatusEnum.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.sms; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 短信的接收状态枚举 - * - * @author 芋道源码 - * @date 2021/2/1 13:39 - */ -@Getter -@AllArgsConstructor -public enum SmsReceiveStatusEnum { - - INIT(0), // 初始化 - SUCCESS(10), // 接收成功 - FAILURE(20), // 接收失败 - ; - - private final int status; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java deleted file mode 100644 index b8114ef..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSceneEnum.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.sms; - -import cn.hutool.core.util.ArrayUtil; -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 用户短信验证码发送场景的枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum SmsSceneEnum implements ArrayValuable { - - MEMBER_LOGIN(1, "user-sms-login", "会员用户 - 手机号登陆"), - MEMBER_UPDATE_MOBILE(2, "user-update-mobile", "会员用户 - 修改手机"), - MEMBER_UPDATE_PASSWORD(3, "user-update-password", "会员用户 - 修改密码"), - MEMBER_RESET_PASSWORD(4, "user-reset-password", "会员用户 - 忘记密码"), - - ADMIN_MEMBER_LOGIN(21, "admin-sms-login", "后台用户 - 手机号登录"), - ADMIN_MEMBER_REGISTER(22, "admin-sms-register", "后台用户 - 手机号注册"), - ADMIN_MEMBER_RESET_PASSWORD(23, "admin-reset-password", "后台用户 - 忘记密码"); - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(SmsSceneEnum::getScene).toArray(Integer[]::new); - - /** - * 验证场景的编号 - */ - private final Integer scene; - /** - * 模版编码 - */ - private final String templateCode; - /** - * 描述 - */ - private final String description; - - @Override - public Integer[] array() { - return ARRAYS; - } - - public static SmsSceneEnum getCodeByScene(Integer scene) { - return ArrayUtil.firstMatch(sceneEnum -> sceneEnum.getScene().equals(scene), - values()); - } - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSendStatusEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSendStatusEnum.java deleted file mode 100644 index 307c9f6..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsSendStatusEnum.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.sms; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 短信的发送状态枚举 - * - * @author zzf - * @date 2021/2/1 13:39 - */ -@Getter -@AllArgsConstructor -public enum SmsSendStatusEnum { - - INIT(0), // 初始化 - SUCCESS(10), // 发送成功 - FAILURE(20), // 发送失败 - IGNORE(30), // 忽略,即不发送 - ; - - private final int status; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsTemplateTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsTemplateTypeEnum.java deleted file mode 100644 index d24b07a..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/sms/SmsTemplateTypeEnum.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.sms; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 短信的模板类型枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum SmsTemplateTypeEnum { - - VERIFICATION_CODE(1), // 验证码 - NOTICE(2), // 通知 - PROMOTION(3), // 营销 - ; - - /** - * 类型 - */ - private final int type; - -} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java deleted file mode 100644 index e6c4dc3..0000000 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/social/SocialTypeEnum.java +++ /dev/null @@ -1,78 +0,0 @@ -package cn.iocoder.yudao.module.system.enums.social; - -import cn.hutool.core.util.ArrayUtil; -import cn.iocoder.yudao.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 社交平台的类型枚举 - * - * @author 芋道源码 - */ -@Getter -@AllArgsConstructor -public enum SocialTypeEnum implements ArrayValuable { - - /** - * Gitee - * - * @see 接入文档 - */ - GITEE(10, "GITEE"), - /** - * 钉钉 - * - * @see 接入文档 - */ - DINGTALK(20, "DINGTALK"), - - /** - * 企业微信 - * - * @see 接入文档 - */ - WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"), - /** - * 微信公众平台 - 移动端 H5 - * - * @see 接入文档 - */ - WECHAT_MP(31, "WECHAT_MP"), - /** - * 微信开放平台 - 网站应用 PC 端扫码授权登录 - * - * @see 接入文档 - */ - WECHAT_OPEN(32, "WECHAT_OPEN"), - /** - * 微信小程序 - * - * @see 接入文档 - */ - WECHAT_MINI_PROGRAM(34, "WECHAT_MINI_PROGRAM"), - ; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(SocialTypeEnum::getType).toArray(Integer[]::new); - - /** - * 类型 - */ - private final Integer type; - /** - * 类型的标识 - */ - private final String source; - - @Override - public Integer[] array() { - return ARRAYS; - } - - public static SocialTypeEnum valueOfType(Integer type) { - return ArrayUtil.firstMatch(o -> o.getType().equals(type), values()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/pom.xml b/yudao-module-system/yudao-module-system-biz/pom.xml deleted file mode 100644 index 16c7f0b..0000000 --- a/yudao-module-system/yudao-module-system-biz/pom.xml +++ /dev/null @@ -1,129 +0,0 @@ - - - - cn.iocoder.boot - yudao-module-system - ${revision} - - 4.0.0 - yudao-module-system-biz - jar - - ${project.artifactId} - - system 模块下,我们放通用业务,支撑上层的核心业务。 - 例如说:用户、部门、权限、数据字典等等 - - - - - cn.iocoder.boot - yudao-module-system-api - ${revision} - - - cn.iocoder.boot - yudao-module-infra-api - ${revision} - - - - - cn.iocoder.boot - yudao-spring-boot-starter-biz-data-permission - - - cn.iocoder.boot - yudao-spring-boot-starter-biz-tenant - - - cn.iocoder.boot - yudao-spring-boot-starter-biz-ip - - - - - cn.iocoder.boot - yudao-spring-boot-starter-security - - - - org.springframework.boot - spring-boot-starter-validation - - - - - cn.iocoder.boot - yudao-spring-boot-starter-mybatis - - - - cn.iocoder.boot - yudao-spring-boot-starter-redis - - - - - cn.iocoder.boot - yudao-spring-boot-starter-job - - - - - cn.iocoder.boot - yudao-spring-boot-starter-mq - - - - - cn.iocoder.boot - yudao-spring-boot-starter-test - test - - - - - cn.iocoder.boot - yudao-spring-boot-starter-excel - - - - org.springframework.boot - spring-boot-starter-mail - - - - - me.zhyd.oauth - JustAuth - - - com.xkcoding.justauth - justauth-spring-boot-starter - - - - com.github.binarywang - wx-java-mp-spring-boot-starter - - - com.github.binarywang - wx-java-miniapp-spring-boot-starter - - - - com.anji-plus - captcha-spring-boot-starter - - - - org.dromara.hutool - hutool-extra - - - - - diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java deleted file mode 100644 index 6eb55ef..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.system.api.dept; - -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.util.Collection; -import java.util.List; - -/** - * 部门 API 实现类 - * - * @author 芋道源码 - */ -@Service -public class DeptApiImpl implements DeptApi { - - @Resource - private DeptService deptService; - - @Override - public DeptRespDTO getDept(Long id) { - DeptDO dept = deptService.getDept(id); - return BeanUtils.toBean(dept, DeptRespDTO.class); - } - - @Override - public List getDeptList(Collection ids) { - List depts = deptService.getDeptList(ids); - return BeanUtils.toBean(depts, DeptRespDTO.class); - } - - @Override - public void validateDeptList(Collection ids) { - deptService.validateDeptList(ids); - } - - @Override - public List getChildDeptList(Long id) { - List childDeptList = deptService.getChildDeptList(id); - return BeanUtils.toBean(childDeptList, DeptRespDTO.class); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java deleted file mode 100644 index 708ea0b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/PostApiImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.api.dept; - -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import cn.iocoder.yudao.module.system.service.dept.PostService; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.util.Collection; -import java.util.List; - -/** - * 岗位 API 实现类 - * - * @author 芋道源码 - */ -@Service -public class PostApiImpl implements PostApi { - - @Resource - private PostService postService; - - @Override - public void validPostList(Collection ids) { - postService.validatePostList(ids); - } - - @Override - public List getPostList(Collection ids) { - List list = postService.getPostList(ids); - return BeanUtils.toBean(list, PostRespDTO.class); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java deleted file mode 100644 index 8f46358..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dict/DictDataApiImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.api.dict; - -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.biz.system.dict.dto.DictDataRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; -import cn.iocoder.yudao.module.system.service.dict.DictDataService; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; - -import java.util.Collection; -import java.util.List; - -/** - * 字典数据 API 实现类 - * - * @author 芋道源码 - */ -@Service -public class DictDataApiImpl implements DictDataApi { - - @Resource - private DictDataService dictDataService; - - @Override - public void validateDictDataList(String dictType, Collection values) { - dictDataService.validateDictDataList(dictType, values); - } - - @Override - public List getDictDataList(String dictType) { - List list = dictDataService.getDictDataListByDictType(dictType); - return BeanUtils.toBean(list, DictDataRespDTO.class); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApiImpl.java deleted file mode 100644 index 3d8dc92..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/LoginLogApiImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.system.api.logger; - -import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; -import cn.iocoder.yudao.module.system.service.logger.LoginLogService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; - -/** - * 登录日志的 API 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class LoginLogApiImpl implements LoginLogApi { - - @Resource - private LoginLogService loginLogService; - - @Override - public void createLoginLog(LoginLogCreateReqDTO reqDTO) { - loginLogService.createLoginLog(reqDTO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java deleted file mode 100644 index 61fdf9f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/logger/OperateLogApiImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.api.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; -import cn.iocoder.yudao.module.system.service.logger.OperateLogService; -import com.fhs.core.trans.anno.TransMethodResult; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -/** - * 操作日志 API 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class OperateLogApiImpl implements OperateLogApi { - - @Resource - private OperateLogService operateLogService; - - @Override - public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { - operateLogService.createOperateLog(createReqDTO); - } - - @Override - @TransMethodResult - public PageResult getOperateLogPage(OperateLogPageReqDTO pageReqDTO) { - PageResult operateLogPage = operateLogService.getOperateLogPage(pageReqDTO); - return BeanUtils.toBean(operateLogPage, OperateLogRespDTO.class); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java deleted file mode 100644 index 0a8910e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.api.mail; - -import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleToUserReqDTO; -import cn.iocoder.yudao.module.system.service.mail.MailSendService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; - -/** - * 邮件发送 API 实现类 - * - * @author wangjingyi - */ -@Service -@Validated -public class MailSendApiImpl implements MailSendApi { - - @Resource - private MailSendService mailSendService; - - @Override - public Long sendSingleMailToAdmin(MailSendSingleToUserReqDTO reqDTO) { - return mailSendService.sendSingleMailToAdmin(reqDTO.getMail(), reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); - } - - @Override - public Long sendSingleMailToMember(MailSendSingleToUserReqDTO reqDTO) { - return mailSendService.sendSingleMailToMember(reqDTO.getMail(), reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApiImpl.java deleted file mode 100644 index 3fee347..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApiImpl.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.system.api.notify; - -import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; -import cn.iocoder.yudao.module.system.service.notify.NotifySendService; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; - -/** - * 站内信发送 API 实现类 - * - * @author xrcoder - */ -@Service -public class NotifyMessageSendApiImpl implements NotifyMessageSendApi { - - @Resource - private NotifySendService notifySendService; - - @Override - public Long sendSingleMessageToAdmin(NotifySendSingleToUserReqDTO reqDTO) { - return notifySendService.sendSingleNotifyToAdmin(reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); - } - - @Override - public Long sendSingleMessageToMember(NotifySendSingleToUserReqDTO reqDTO) { - return notifySendService.sendSingleNotifyToMember(reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java deleted file mode 100644 index 94fed14..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/oauth2/OAuth2TokenApiImpl.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.system.api.oauth2; - -import cn.iocoder.yudao.framework.common.biz.system.oauth2.OAuth2TokenCommonApi; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCheckRespDTO; -import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenCreateReqDTO; -import cn.iocoder.yudao.framework.common.biz.system.oauth2.dto.OAuth2AccessTokenRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; - -/** - * OAuth2.0 Token API 实现类 - * - * @author 芋道源码 - */ -@Service -public class OAuth2TokenApiImpl implements OAuth2TokenCommonApi { - - @Resource - private OAuth2TokenService oauth2TokenService; - - @Override - public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) { - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken( - reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId(), reqDTO.getScopes()); - return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class); - } - - @Override - public OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken) { - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.checkAccessToken(accessToken); - return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenCheckRespDTO.class); - } - - @Override - public OAuth2AccessTokenRespDTO removeAccessToken(String accessToken) { - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(accessToken); - return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class); - } - - @Override - public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, String clientId) { - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId); - return BeanUtils.toBean(accessTokenDO, OAuth2AccessTokenRespDTO.class); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java deleted file mode 100644 index 273669d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/PermissionApiImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.system.api.permission; - -import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.util.Collection; -import java.util.Set; - -/** - * 权限 API 实现类 - * - * @author 芋道源码 - */ -@Service -public class PermissionApiImpl implements PermissionApi { - - @Resource - private PermissionService permissionService; - - @Override - public Set getUserRoleIdListByRoleIds(Collection roleIds) { - return permissionService.getUserRoleIdListByRoleId(roleIds); - } - - @Override - public boolean hasAnyPermissions(Long userId, String... permissions) { - return permissionService.hasAnyPermissions(userId, permissions); - } - - @Override - public boolean hasAnyRoles(Long userId, String... roles) { - return permissionService.hasAnyRoles(userId, roles); - } - - @Override - public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) { - return permissionService.getDeptDataPermission(userId); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApiImpl.java deleted file mode 100644 index 6b75606..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApiImpl.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.system.api.permission; - -import cn.iocoder.yudao.module.system.service.permission.RoleService; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.util.Collection; - -/** - * 角色 API 实现类 - * - * @author 芋道源码 - */ -@Service -public class RoleApiImpl implements RoleApi { - - @Resource - private RoleService roleService; - - @Override - public void validRoleList(Collection ids) { - roleService.validateRoleList(ids); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApiImpl.java deleted file mode 100644 index 79dae8b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsCodeApiImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.api.sms; - -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import cn.iocoder.yudao.module.system.service.sms.SmsCodeService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; - -/** - * 短信验证码 API 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class SmsCodeApiImpl implements SmsCodeApi { - - @Resource - private SmsCodeService smsCodeService; - - @Override - public void sendSmsCode(SmsCodeSendReqDTO reqDTO) { - smsCodeService.sendSmsCode(reqDTO); - } - - @Override - public void useSmsCode(SmsCodeUseReqDTO reqDTO) { - smsCodeService.useSmsCode(reqDTO); - } - - @Override - public void validateSmsCode(SmsCodeValidateReqDTO reqDTO) { - smsCodeService.validateSmsCode(reqDTO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApiImpl.java deleted file mode 100644 index 09c5b65..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/sms/SmsSendApiImpl.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.api.sms; - -import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; -import cn.iocoder.yudao.module.system.service.sms.SmsSendService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; - -/** - * 短信发送 API 接口 - * - * @author 芋道源码 - */ -@Service -@Validated -public class SmsSendApiImpl implements SmsSendApi { - - @Resource - private SmsSendService smsSendService; - - @Override - public Long sendSingleSmsToAdmin(SmsSendSingleToUserReqDTO reqDTO) { - return smsSendService.sendSingleSmsToAdmin(reqDTO.getMobile(), reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); - } - - @Override - public Long sendSingleSmsToMember(SmsSendSingleToUserReqDTO reqDTO) { - return smsSendService.sendSingleSmsToMember(reqDTO.getMobile(), reqDTO.getUserId(), - reqDTO.getTemplateCode(), reqDTO.getTemplateParams()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java deleted file mode 100644 index d2eb00d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialClientApiImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social; - -import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.api.social.dto.*; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import cn.iocoder.yudao.module.system.service.social.SocialClientService; -import cn.iocoder.yudao.module.system.service.social.SocialUserService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.List; - -import static cn.hutool.core.collection.CollUtil.findOne; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 社交应用的 API 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class SocialClientApiImpl implements SocialClientApi { - - @Resource - private SocialClientService socialClientService; - @Resource - private SocialUserService socialUserService; - - @Override - public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) { - return socialClientService.getAuthorizeUrl(socialType, userType, redirectUri); - } - - @Override - public SocialWxJsapiSignatureRespDTO createWxMpJsapiSignature(Integer userType, String url) { - WxJsapiSignature signature = socialClientService.createWxMpJsapiSignature(userType, url); - return BeanUtils.toBean(signature, SocialWxJsapiSignatureRespDTO.class); - } - - //======================= 微信小程序独有 ======================= - - @Override - public SocialWxPhoneNumberInfoRespDTO getWxMaPhoneNumberInfo(Integer userType, String phoneCode) { - WxMaPhoneNumberInfo info = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode); - return BeanUtils.toBean(info, SocialWxPhoneNumberInfoRespDTO.class); - } - - @Override - public byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO) { - return socialClientService.getWxaQrcode(reqVO); - } - - @Override - public List getWxaSubscribeTemplateList(Integer userType) { - List list = socialClientService.getSubscribeTemplateList(userType); - return convertList(list, item -> BeanUtils.toBean(item, SocialWxaSubscribeTemplateRespDTO.class).setId(item.getPriTmplId())); - } - - @Override - public void sendWxaSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO) { - // 1.1 获得订阅模版列表 - List templateList = socialClientService.getSubscribeTemplateList(reqDTO.getUserType()); - if (CollUtil.isEmpty(templateList)) { - log.warn("[sendSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO); - return; - } - // 1.2 获得需要使用的模版 - TemplateInfo template = findOne(templateList, item -> - ObjUtil.equal(item.getTitle(), reqDTO.getTemplateTitle())); - if (template == null) { - log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:没有找到订阅模板]", reqDTO); - return; - } - - // 2. 获得社交用户 - SocialUserRespDTO socialUser = socialUserService.getSocialUserByUserId(reqDTO.getUserType(), reqDTO.getUserId(), - SocialTypeEnum.WECHAT_MINI_PROGRAM.getType()); - if (StrUtil.isBlankIfStr(socialUser.getOpenid())) { - log.warn("[sendWxaSubscribeMessage][reqDTO({}) 发送订阅消息失败,原因:会员 openid 缺失]", reqDTO); - return; - } - - // 3. 发送订阅消息 - socialClientService.sendSubscribeMessage(reqDTO, template.getPriTmplId(), socialUser.getOpenid()); - } - - @Override - public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) { - socialClientService.uploadWxaOrderShippingInfo(userType, reqDTO); - } - - @Override - public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) { - socialClientService.notifyWxaOrderConfirmReceive(userType, reqDTO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java deleted file mode 100644 index a8c30a0..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/social/SocialUserApiImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.system.api.social; - -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; -import cn.iocoder.yudao.module.system.service.social.SocialUserService; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; - -/** - * 社交用户的 API 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class SocialUserApiImpl implements SocialUserApi { - - @Resource - private SocialUserService socialUserService; - - @Override - public String bindSocialUser(SocialUserBindReqDTO reqDTO) { - return socialUserService.bindSocialUser(reqDTO); - } - - @Override - public void unbindSocialUser(SocialUserUnbindReqDTO reqDTO) { - socialUserService.unbindSocialUser(reqDTO.getUserId(), reqDTO.getUserType(), - reqDTO.getSocialType(), reqDTO.getOpenid()); - } - - @Override - public SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType) { - return socialUserService.getSocialUserByUserId(userType, userId, socialType); - } - - @Override - public SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state) { - return socialUserService.getSocialUserByCode(userType, socialType, code, state); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/tenant/TenantApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/tenant/TenantApiImpl.java deleted file mode 100644 index 73367c0..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/tenant/TenantApiImpl.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.system.api.tenant; - -import cn.iocoder.yudao.framework.common.biz.system.tenant.TenantCommonApi; -import cn.iocoder.yudao.module.system.service.tenant.TenantService; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.util.List; - -/** - * 多租户的 API 实现类 - * - * @author 芋道源码 - */ -@Service -public class TenantApiImpl implements TenantCommonApi { - - @Resource - private TenantService tenantService; - - @Override - public List getTenantIdList() { - return tenantService.getTenantIdList(); - } - - @Override - public void validateTenant(Long id) { - tenantService.validTenant(id); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java deleted file mode 100644 index 4f7b4e4..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -package cn.iocoder.yudao.module.system.api.user; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils; -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; - -/** - * Admin 用户 API 实现类 - * - * @author 芋道源码 - */ -@Service -public class AdminUserApiImpl implements AdminUserApi { - - @Resource - private AdminUserService userService; - @Resource - private DeptService deptService; - - @Override - public AdminUserRespDTO getUser(Long id) { - AdminUserDO user = userService.getUser(id); - return BeanUtils.toBean(user, AdminUserRespDTO.class); - } - - @Override - public List getUserListBySubordinate(Long id) { - // 1.1 获取用户负责的部门 - List depts = deptService.getDeptListByLeaderUserId(id); - if (CollUtil.isEmpty(depts)) { - return Collections.emptyList(); - } - // 1.2 获取所有子部门 - Set deptIds = convertSet(depts, DeptDO::getId); - List childDeptList = deptService.getChildDeptList(deptIds); - if (CollUtil.isNotEmpty(childDeptList)) { - deptIds.addAll(convertSet(childDeptList, DeptDO::getId)); - } - - // 2. 获取部门对应的用户信息 - List users = userService.getUserListByDeptIds(deptIds); - users.removeIf(item -> ObjUtil.equal(item.getId(), id)); // 排除自己 - return BeanUtils.toBean(users, AdminUserRespDTO.class); - } - - @Override - public List getUserList(Collection ids) { - return DataPermissionUtils.executeIgnore(() -> { // 禁用数据权限。原因是,一般基于指定 id 的 API 查询,都是数据拼接为主 - List users = userService.getUserList(ids); - return BeanUtils.toBean(users, AdminUserRespDTO.class); - }); - } - - @Override - public List getUserListByDeptIds(Collection deptIds) { - List users = userService.getUserListByDeptIds(deptIds); - return BeanUtils.toBean(users, AdminUserRespDTO.class); - } - - @Override - public List getUserListByPostIds(Collection postIds) { - List users = userService.getUserListByPostIds(postIds); - return BeanUtils.toBean(users, AdminUserRespDTO.class); - } - - @Override - public void validateUserList(Collection ids) { - userService.validateUserList(ids); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http deleted file mode 100644 index f42dfcd..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http +++ /dev/null @@ -1,33 +0,0 @@ -### 请求 /login 接口 => 成功 -POST {{baseUrl}}/system/auth/login -Content-Type: application/json -tenant-id: {{adminTenantId}} -tag: Yunai.local - -{ - "username": "admin", - "password": "admin123", - "uuid": "3acd87a09a4f48fb9118333780e94883", - "code": "1024" -} - -### 请求 /login 接口 => 成功(无验证码) -POST {{baseUrl}}/system/auth/login -Content-Type: application/json -tenant-id: {{adminTenantId}} - -{ - "username": "admin", - "password": "admin123" -} - -### 请求 /get-permission-info 接口 => 成功 -GET {{baseUrl}}/system/auth/get-permission-info -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - -### 请求 /list-menus 接口 => 成功 -GET {{baseUrl}}/system/list-menus -Authorization: Bearer {{token}} -#Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a -tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java deleted file mode 100644 index d926947..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ /dev/null @@ -1,172 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.security.config.SecurityProperties; -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; -import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; -import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; -import cn.iocoder.yudao.module.system.service.permission.MenuService; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import cn.iocoder.yudao.module.system.service.permission.RoleService; -import cn.iocoder.yudao.module.system.service.social.SocialClientService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -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.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.annotation.security.PermitAll; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 认证") -@RestController -@RequestMapping("/system/auth") -@Validated -@Slf4j -public class AuthController { - - @Resource - private AdminAuthService authService; - @Resource - private AdminUserService userService; - @Resource - private RoleService roleService; - @Resource - private MenuService menuService; - @Resource - private PermissionService permissionService; - @Resource - private SocialClientService socialClientService; - - @Resource - private SecurityProperties securityProperties; - - @PostMapping("/login") - @PermitAll - @Operation(summary = "使用账号密码登录") - public CommonResult login(@RequestBody @Valid AuthLoginReqVO reqVO) { - return success(authService.login(reqVO)); - } - - @PostMapping("/logout") - @PermitAll - @Operation(summary = "登出系统") - public CommonResult logout(HttpServletRequest request) { - String token = SecurityFrameworkUtils.obtainAuthorization(request, - securityProperties.getTokenHeader(), securityProperties.getTokenParameter()); - if (StrUtil.isNotBlank(token)) { - authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); - } - return success(true); - } - - @PostMapping("/refresh-token") - @PermitAll - @Operation(summary = "刷新令牌") - @Parameter(name = "refreshToken", description = "刷新令牌", required = true) - public CommonResult refreshToken(@RequestParam("refreshToken") String refreshToken) { - return success(authService.refreshToken(refreshToken)); - } - - @GetMapping("/get-permission-info") - @Operation(summary = "获取登录用户的权限信息") - public CommonResult getPermissionInfo() { - // 1.1 获得用户信息 - AdminUserDO user = userService.getUser(getLoginUserId()); - if (user == null) { - return success(null); - } - - // 1.2 获得角色列表 - Set roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId()); - if (CollUtil.isEmpty(roleIds)) { - return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList())); - } - List roles = roleService.getRoleList(roleIds); - roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色 - - // 1.3 获得菜单列表 - Set menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId)); - List menuList = menuService.getMenuList(menuIds); - menuList = menuService.filterDisableMenus(menuList); - - // 2. 拼接结果返回 - return success(AuthConvert.INSTANCE.convert(user, roles, menuList)); - } - - @PostMapping("/register") - @PermitAll - @Operation(summary = "注册用户") - public CommonResult register(@RequestBody @Valid AuthRegisterReqVO registerReqVO) { - return success(authService.register(registerReqVO)); - } - - // ========== 短信登录相关 ========== - - @PostMapping("/sms-login") - @PermitAll - @Operation(summary = "使用短信验证码登录") - public CommonResult smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) { - return success(authService.smsLogin(reqVO)); - } - - @PostMapping("/send-sms-code") - @PermitAll - @Operation(summary = "发送手机验证码") - public CommonResult sendLoginSmsCode(@RequestBody @Valid AuthSmsSendReqVO reqVO) { - authService.sendSmsCode(reqVO); - return success(true); - } - - @PostMapping("/reset-password") - @PermitAll - @Operation(summary = "重置密码") - public CommonResult resetPassword(@RequestBody @Valid AuthResetPasswordReqVO reqVO) { - authService.resetPassword(reqVO); - return success(true); - } - - // ========== 社交登录相关 ========== - - @GetMapping("/social-auth-redirect") - @PermitAll - @Operation(summary = "社交授权的跳转") - @Parameters({ - @Parameter(name = "type", description = "社交类型", required = true), - @Parameter(name = "redirectUri", description = "回调路径") - }) - public CommonResult socialLogin(@RequestParam("type") Integer type, - @RequestParam("redirectUri") String redirectUri) { - return success(socialClientService.getAuthorizeUrl( - type, UserTypeEnum.ADMIN.getValue(), redirectUri)); - } - - @PostMapping("/social-login") - @PermitAll - @Operation(summary = "社交快捷登录,使用 code 授权码", description = "适合未登录的用户,但是社交账号已绑定用户") - public CommonResult socialQuickLogin(@RequestBody @Valid AuthSocialLoginReqVO reqVO) { - return success(authService.socialLogin(reqVO)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java deleted file mode 100644 index a29660e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginReqVO.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.hibernate.validator.constraints.Length; - -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.Pattern; - -@Schema(description = "管理后台 - 账号密码登录 Request VO,如果登录并绑定社交用户,需要传递 social 开头的参数") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AuthLoginReqVO extends CaptchaVerificationReqVO { - - @Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma") - @NotEmpty(message = "登录账号不能为空") - @Length(min = 4, max = 16, message = "账号长度为 4-16 位") - @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母") - private String username; - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "buzhidao") - @NotEmpty(message = "密码不能为空") - @Length(min = 4, max = 16, message = "密码长度为 4-16 位") - private String password; - - // ========== 绑定社交登录时,需要传递如下参数 ========== - - @Schema(description = "社交平台的类型,参见 SocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(SocialTypeEnum.class) - private Integer socialType; - - @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String socialCode; - - @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") - private String socialState; - - @AssertTrue(message = "授权码不能为空") - public boolean isSocialCodeValid() { - return socialType == null || StrUtil.isNotEmpty(socialCode); - } - - @AssertTrue(message = "授权 state 不能为空") - public boolean isSocialState() { - return socialType == null || StrUtil.isNotEmpty(socialState); - } - -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginRespVO.java deleted file mode 100644 index 1720d07..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthLoginRespVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 登录 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AuthLoginRespVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long userId; - - @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "happy") - private String accessToken; - - @Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice") - private String refreshToken; - - @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime expiresTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthMenuRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthMenuRespVO.java deleted file mode 100644 index d310bde..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthMenuRespVO.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Schema(description = "管理后台 - 登录用户的菜单信息 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AuthMenuRespVO { - - @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private Long id; - - @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long parentId; - - @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String name; - - @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") - private String path; - - @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") - private String component; - - @Schema(description = "组件名", example = "SystemUser") - private String componentName; - - @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") - private String icon; - - @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - private Boolean visible; - - @Schema(description = "是否缓存", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - private Boolean keepAlive; - - @Schema(description = "是否总是显示", example = "false") - private Boolean alwaysShow; - - /** - * 子路由 - */ - private List children; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java deleted file mode 100644 index a86ceda..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthPermissionInfoRespVO.java +++ /dev/null @@ -1,102 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; -import java.util.Set; - -@Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AuthPermissionInfoRespVO { - - @Schema(description = "用户信息", requiredMode = Schema.RequiredMode.REQUIRED) - private UserVO user; - - @Schema(description = "角色标识数组", requiredMode = Schema.RequiredMode.REQUIRED) - private Set roles; - - @Schema(description = "操作权限数组", requiredMode = Schema.RequiredMode.REQUIRED) - private Set permissions; - - @Schema(description = "菜单树", requiredMode = Schema.RequiredMode.REQUIRED) - private List menus; - - @Schema(description = "用户信息 VO") - @Data - @NoArgsConstructor - @AllArgsConstructor - @Builder - public static class UserVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") - private String nickname; - - @Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.jpg") - private String avatar; - - @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - private Long deptId; - - @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - private String username; - - @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") - private String email; - - } - - @Schema(description = "管理后台 - 登录用户的菜单信息 Response VO") - @Data - @NoArgsConstructor - @AllArgsConstructor - @Builder - public static class MenuVO { - - @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private Long id; - - @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long parentId; - - @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String name; - - @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") - private String path; - - @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") - private String component; - - @Schema(description = "组件名", example = "SystemUser") - private String componentName; - - @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") - private String icon; - - @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - private Boolean visible; - - @Schema(description = "是否缓存", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - private Boolean keepAlive; - - @Schema(description = "是否总是显示", example = "false") - private Boolean alwaysShow; - - /** - * 子路由 - */ - private List children; - - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java deleted file mode 100644 index 97edc75..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthRegisterReqVO.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.Length; - -import jakarta.validation.constraints.*; - -@Schema(description = "管理后台 - Register Request VO") -@Data -public class AuthRegisterReqVO extends CaptchaVerificationReqVO { - - @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @NotBlank(message = "用户账号不能为空") - @Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成") - @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符") - private String username; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - @NotBlank(message = "用户昵称不能为空") - @Size(max = 30, message = "用户昵称长度不能超过 30 个字符") - private String nickname; - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - @NotEmpty(message = "密码不能为空") - @Length(min = 4, max = 16, message = "密码长度为 4-16 位") - private String password; -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java deleted file mode 100644 index 16337cc..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthResetPasswordReqVO.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import cn.iocoder.yudao.framework.common.validation.Mobile; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.hibernate.validator.constraints.Length; - -@Schema(description = "管理后台 - 短信重置账号密码 Request VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AuthResetPasswordReqVO { - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1234") - @NotEmpty(message = "密码不能为空") - @Length(min = 4, max = 16, message = "密码长度为 4-16 位") - private String password; - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13312341234") - @NotEmpty(message = "手机号不能为空") - @Mobile - private String mobile; - - @Schema(description = "手机短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - @NotEmpty(message = "手机手机短信验证码不能为空") - private String code; -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsLoginReqVO.java deleted file mode 100644 index e5730d2..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsLoginReqVO.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import cn.iocoder.yudao.framework.common.validation.Mobile; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import jakarta.validation.constraints.NotEmpty; - -@Schema(description = "管理后台 - 短信验证码的登录 Request VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AuthSmsLoginReqVO { - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma") - @NotEmpty(message = "手机号不能为空") - @Mobile - private String mobile; - - @Schema(description = "短信验证码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "验证码不能为空") - private String code; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java deleted file mode 100644 index 4efd98f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSmsSendReqVO.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.framework.common.validation.Mobile; -import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 发送手机验证码 Request VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AuthSmsSendReqVO extends CaptchaVerificationReqVO { - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma") - @NotEmpty(message = "手机号不能为空") - @Mobile - private String mobile; - - @Schema(description = "短信场景", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "发送场景不能为空") - @InEnum(SmsSceneEnum.class) - private Integer scene; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialLoginReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialLoginReqVO.java deleted file mode 100644 index da22470..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/AuthSocialLoginReqVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 社交绑定登录 Request VO,使用 code 授权码 + 账号密码") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class AuthSocialLoginReqVO { - - @Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(SocialTypeEnum.class) - @NotNull(message = "社交平台的类型不能为空") - private Integer type; - - @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "授权码不能为空") - private String code; - - @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") - @NotEmpty(message = "state 不能为空") - private String state; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java deleted file mode 100644 index b080cb5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/CaptchaVerificationReqVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.auth.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "管理后台 - 验证码 Request VO") -@Data -public class CaptchaVerificationReqVO { - - // ========== 图片验证码相关 ========== - @Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED, - example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==") - @NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class) - private String captchaVerification; - - /** - * 开启验证码的 Group - */ - public interface CodeEnableGroup { - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java deleted file mode 100644 index f4e5541..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/captcha/CaptchaController.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.captcha; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import com.anji.captcha.model.common.ResponseModel; -import com.anji.captcha.model.vo.CaptchaVO; -import com.anji.captcha.service.CaptchaService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.annotation.security.PermitAll; -import jakarta.servlet.http.HttpServletRequest; -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; - -@Tag(name = "管理后台 - 验证码") -@RestController("adminCaptchaController") -@RequestMapping("/system/captcha") -public class CaptchaController { - - @Resource - private CaptchaService captchaService; - - @PostMapping({"/get"}) - @Operation(summary = "获得验证码") - @PermitAll - @TenantIgnore - public ResponseModel get(@RequestBody CaptchaVO data, HttpServletRequest request) { - assert request.getRemoteHost() != null; - data.setBrowserInfo(getRemoteId(request)); - return captchaService.get(data); - } - - @PostMapping("/check") - @Operation(summary = "校验验证码") - @PermitAll - @TenantIgnore - public ResponseModel check(@RequestBody CaptchaVO data, HttpServletRequest request) { - data.setBrowserInfo(getRemoteId(request)); - return captchaService.check(data); - } - - public static String getRemoteId(HttpServletRequest request) { - String ip = ServletUtils.getClientIP(request); - String ua = request.getHeader("user-agent"); - if (StrUtil.isNotBlank(ip)) { - return ip + ua; - } - return request.getRemoteAddr() + ua; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java deleted file mode 100644 index 7873d00..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptRespVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSimpleRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 部门") -@RestController -@RequestMapping("/system/dept") -@Validated -public class DeptController { - - @Resource - private DeptService deptService; - - @PostMapping("create") - @Operation(summary = "创建部门") - @PreAuthorize("@ss.hasPermission('system:dept:create')") - public CommonResult createDept(@Valid @RequestBody DeptSaveReqVO createReqVO) { - Long deptId = deptService.createDept(createReqVO); - return success(deptId); - } - - @PutMapping("update") - @Operation(summary = "更新部门") - @PreAuthorize("@ss.hasPermission('system:dept:update')") - public CommonResult updateDept(@Valid @RequestBody DeptSaveReqVO updateReqVO) { - deptService.updateDept(updateReqVO); - return success(true); - } - - @DeleteMapping("delete") - @Operation(summary = "删除部门") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:dept:delete')") - public CommonResult deleteDept(@RequestParam("id") Long id) { - deptService.deleteDept(id); - return success(true); - } - - @GetMapping("/list") - @Operation(summary = "获取部门列表") - @PreAuthorize("@ss.hasPermission('system:dept:query')") - public CommonResult> getDeptList(DeptListReqVO reqVO) { - List list = deptService.getDeptList(reqVO); - return success(BeanUtils.toBean(list, DeptRespVO.class)); - } - - @GetMapping(value = {"/list-all-simple", "/simple-list"}) - @Operation(summary = "获取部门精简信息列表", description = "只包含被开启的部门,主要用于前端的下拉选项") - public CommonResult> getSimpleDeptList() { - List list = deptService.getDeptList( - new DeptListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus())); - return success(BeanUtils.toBean(list, DeptSimpleRespVO.class)); - } - - @GetMapping("/get") - @Operation(summary = "获得部门信息") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:dept:query')") - public CommonResult getDept(@RequestParam("id") Long id) { - DeptDO dept = deptService.getDept(id); - return success(BeanUtils.toBean(dept, DeptRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java deleted file mode 100644 index 897cea2..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/PostController.java +++ /dev/null @@ -1,106 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostRespVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSimpleRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import cn.iocoder.yudao.module.system.service.dept.PostService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.io.IOException; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 岗位") -@RestController -@RequestMapping("/system/post") -@Validated -public class PostController { - - @Resource - private PostService postService; - - @PostMapping("/create") - @Operation(summary = "创建岗位") - @PreAuthorize("@ss.hasPermission('system:post:create')") - public CommonResult createPost(@Valid @RequestBody PostSaveReqVO createReqVO) { - Long postId = postService.createPost(createReqVO); - return success(postId); - } - - @PutMapping("/update") - @Operation(summary = "修改岗位") - @PreAuthorize("@ss.hasPermission('system:post:update')") - public CommonResult updatePost(@Valid @RequestBody PostSaveReqVO updateReqVO) { - postService.updatePost(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除岗位") - @PreAuthorize("@ss.hasPermission('system:post:delete')") - public CommonResult deletePost(@RequestParam("id") Long id) { - postService.deletePost(id); - return success(true); - } - - @GetMapping(value = "/get") - @Operation(summary = "获得岗位信息") - @Parameter(name = "id", description = "岗位编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:post:query')") - public CommonResult getPost(@RequestParam("id") Long id) { - PostDO post = postService.getPost(id); - return success(BeanUtils.toBean(post, PostRespVO.class)); - } - - @GetMapping(value = {"/list-all-simple", "simple-list"}) - @Operation(summary = "获取岗位全列表", description = "只包含被开启的岗位,主要用于前端的下拉选项") - public CommonResult> getSimplePostList() { - // 获得岗位列表,只要开启状态的 - List list = postService.getPostList(null, Collections.singleton(CommonStatusEnum.ENABLE.getStatus())); - // 排序后,返回给前端 - list.sort(Comparator.comparing(PostDO::getSort)); - return success(BeanUtils.toBean(list, PostSimpleRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得岗位分页列表") - @PreAuthorize("@ss.hasPermission('system:post:query')") - public CommonResult> getPostPage(@Validated PostPageReqVO pageReqVO) { - PageResult pageResult = postService.getPostPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, PostRespVO.class)); - } - - @GetMapping("/export") - @Operation(summary = "岗位管理") - @PreAuthorize("@ss.hasPermission('system:post:export')") - @ApiAccessLog(operateType = EXPORT) - public void export(HttpServletResponse response, @Validated PostPageReqVO reqVO) throws IOException { - reqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = postService.getPostPage(reqVO).getList(); - // 输出 - ExcelUtils.write(response, "岗位数据.xls", "岗位列表", PostRespVO.class, - BeanUtils.toBean(list, PostRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptListReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptListReqVO.java deleted file mode 100644 index 131686f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptListReqVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 部门列表 Request VO") -@Data -public class DeptListReqVO { - - @Schema(description = "部门名称,模糊匹配", example = "芋道") - private String name; - - @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java deleted file mode 100644 index 83e6ed0..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 部门信息 Response VO") -@Data -public class DeptRespVO { - - @Schema(description = "部门编号", example = "1024") - private Long id; - - @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String name; - - @Schema(description = "父部门 ID", example = "1024") - private Long parentId; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Integer sort; - - @Schema(description = "负责人的用户编号", example = "2048") - private Long leaderUserId; - - @Schema(description = "联系电话", example = "15601691000") - private String phone; - - @Schema(description = "邮箱", example = "yudao@iocoder.cn") - private String email; - - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java deleted file mode 100644 index 4d25b7c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; - -@Schema(description = "管理后台 - 部门创建/修改 Request VO") -@Data -public class DeptSaveReqVO { - - @Schema(description = "部门编号", example = "1024") - private Long id; - - @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - @NotBlank(message = "部门名称不能为空") - @Size(max = 30, message = "部门名称长度不能超过 30 个字符") - private String name; - - @Schema(description = "父部门 ID", example = "1024") - private Long parentId; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "显示顺序不能为空") - private Integer sort; - - @Schema(description = "负责人的用户编号", example = "2048") - private Long leaderUserId; - - @Schema(description = "联系电话", example = "15601691000") - @Size(max = 11, message = "联系电话长度不能超过11个字符") - private String phone; - - @Schema(description = "邮箱", example = "yudao@iocoder.cn") - @Email(message = "邮箱格式不正确") - @Size(max = 50, message = "邮箱长度不能超过 50 个字符") - private String email; - - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java deleted file mode 100644 index 31c15d5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Schema(description = "管理后台 - 部门精简信息 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class DeptSimpleRespVO { - - @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String name; - - @Schema(description = "父部门 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long parentId; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostPageReqVO.java deleted file mode 100644 index 78f306e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostPageReqVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Schema(description = "管理后台 - 岗位分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -public class PostPageReqVO extends PageParam { - - @Schema(description = "岗位编码,模糊匹配", example = "yudao") - private String code; - - @Schema(description = "岗位名称,模糊匹配", example = "芋道") - private String name; - - @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java deleted file mode 100644 index dde6f95..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostRespVO.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 岗位信息 Response VO") -@Data -@ExcelIgnoreUnannotated -public class PostRespVO { - - @Schema(description = "岗位序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("岗位序号") - private Long id; - - @Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆") - @ExcelProperty("岗位名称") - private String name; - - @Schema(description = "岗位编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @ExcelProperty("岗位编码") - private String code; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("岗位排序") - private Integer sort; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - - @Schema(description = "备注", example = "快乐的备注") - private String remark; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java deleted file mode 100644 index 5ac2be3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSaveReqVO.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; - -@Schema(description = "管理后台 - 岗位创建/修改 Request VO") -@Data -public class PostSaveReqVO { - - @Schema(description = "岗位编号", example = "1024") - private Long id; - - @Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆") - @NotBlank(message = "岗位名称不能为空") - @Size(max = 50, message = "岗位名称长度不能超过 50 个字符") - private String name; - - @Schema(description = "岗位编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @NotBlank(message = "岗位编码不能为空") - @Size(max = 64, message = "岗位编码长度不能超过64个字符") - private String code; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "显示顺序不能为空") - private Integer sort; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "备注", example = "快乐的备注") - private String remark; - -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java deleted file mode 100644 index 72852a1..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/post/PostSimpleRespVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dept.vo.post; - -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 岗位信息的精简 Response VO") -@Data -public class PostSimpleRespVO { - - @Schema(description = "岗位序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("岗位序号") - private Long id; - - @Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "小土豆") - @ExcelProperty("岗位名称") - private String name; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java deleted file mode 100644 index c05de99..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictDataController.java +++ /dev/null @@ -1,104 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataRespVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSimpleRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; -import cn.iocoder.yudao.module.system.service.dict.DictDataService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.io.IOException; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 字典数据") -@RestController -@RequestMapping("/system/dict-data") -@Validated -public class DictDataController { - - @Resource - private DictDataService dictDataService; - - @PostMapping("/create") - @Operation(summary = "新增字典数据") - @PreAuthorize("@ss.hasPermission('system:dict:create')") - public CommonResult createDictData(@Valid @RequestBody DictDataSaveReqVO createReqVO) { - Long dictDataId = dictDataService.createDictData(createReqVO); - return success(dictDataId); - } - - @PutMapping("/update") - @Operation(summary = "修改字典数据") - @PreAuthorize("@ss.hasPermission('system:dict:update')") - public CommonResult updateDictData(@Valid @RequestBody DictDataSaveReqVO updateReqVO) { - dictDataService.updateDictData(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除字典数据") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:dict:delete')") - public CommonResult deleteDictData(Long id) { - dictDataService.deleteDictData(id); - return success(true); - } - - @GetMapping(value = {"/list-all-simple", "simple-list"}) - @Operation(summary = "获得全部字典数据列表", description = "一般用于管理后台缓存字典数据在本地") - // 无需添加权限认证,因为前端全局都需要 - public CommonResult> getSimpleDictDataList() { - List list = dictDataService.getDictDataList( - CommonStatusEnum.ENABLE.getStatus(), null); - return success(BeanUtils.toBean(list, DictDataSimpleRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "/获得字典类型的分页列表") - @PreAuthorize("@ss.hasPermission('system:dict:query')") - public CommonResult> getDictTypePage(@Valid DictDataPageReqVO pageReqVO) { - PageResult pageResult = dictDataService.getDictDataPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, DictDataRespVO.class)); - } - - @GetMapping(value = "/get") - @Operation(summary = "/查询字典数据详细") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:dict:query')") - public CommonResult getDictData(@RequestParam("id") Long id) { - DictDataDO dictData = dictDataService.getDictData(id); - return success(BeanUtils.toBean(dictData, DictDataRespVO.class)); - } - - @GetMapping("/export") - @Operation(summary = "导出字典数据") - @PreAuthorize("@ss.hasPermission('system:dict:export')") - @ApiAccessLog(operateType = EXPORT) - public void export(HttpServletResponse response, @Valid DictDataPageReqVO exportReqVO) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = dictDataService.getDictDataPage(exportReqVO).getList(); - // 输出 - ExcelUtils.write(response, "字典数据.xls", "数据", DictDataRespVO.class, - BeanUtils.toBean(list, DictDataRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java deleted file mode 100644 index c40980c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/DictTypeController.java +++ /dev/null @@ -1,102 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeRespVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSimpleRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; -import cn.iocoder.yudao.module.system.service.dict.DictTypeService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.io.IOException; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 字典类型") -@RestController -@RequestMapping("/system/dict-type") -@Validated -public class DictTypeController { - - @Resource - private DictTypeService dictTypeService; - - @PostMapping("/create") - @Operation(summary = "创建字典类型") - @PreAuthorize("@ss.hasPermission('system:dict:create')") - public CommonResult createDictType(@Valid @RequestBody DictTypeSaveReqVO createReqVO) { - Long dictTypeId = dictTypeService.createDictType(createReqVO); - return success(dictTypeId); - } - - @PutMapping("/update") - @Operation(summary = "修改字典类型") - @PreAuthorize("@ss.hasPermission('system:dict:update')") - public CommonResult updateDictType(@Valid @RequestBody DictTypeSaveReqVO updateReqVO) { - dictTypeService.updateDictType(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除字典类型") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:dict:delete')") - public CommonResult deleteDictType(Long id) { - dictTypeService.deleteDictType(id); - return success(true); - } - - @GetMapping("/page") - @Operation(summary = "获得字典类型的分页列表") - @PreAuthorize("@ss.hasPermission('system:dict:query')") - public CommonResult> pageDictTypes(@Valid DictTypePageReqVO pageReqVO) { - PageResult pageResult = dictTypeService.getDictTypePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, DictTypeRespVO.class)); - } - - @Operation(summary = "/查询字典类型详细") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @GetMapping(value = "/get") - @PreAuthorize("@ss.hasPermission('system:dict:query')") - public CommonResult getDictType(@RequestParam("id") Long id) { - DictTypeDO dictType = dictTypeService.getDictType(id); - return success(BeanUtils.toBean(dictType, DictTypeRespVO.class)); - } - - @GetMapping(value = {"/list-all-simple", "simple-list"}) - @Operation(summary = "获得全部字典类型列表", description = "包括开启 + 禁用的字典类型,主要用于前端的下拉选项") - // 无需添加权限认证,因为前端全局都需要 - public CommonResult> getSimpleDictTypeList() { - List list = dictTypeService.getDictTypeList(); - return success(BeanUtils.toBean(list, DictTypeSimpleRespVO.class)); - } - - @Operation(summary = "导出数据类型") - @GetMapping("/export") - @PreAuthorize("@ss.hasPermission('system:dict:query')") - @ApiAccessLog(operateType = EXPORT) - public void export(HttpServletResponse response, @Valid DictTypePageReqVO exportReqVO) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = dictTypeService.getDictTypePage(exportReqVO).getList(); - // 导出 - ExcelUtils.write(response, "字典类型.xls", "数据", DictTypeRespVO.class, - BeanUtils.toBean(list, DictTypeRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataPageReqVO.java deleted file mode 100644 index 90c7f39..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataPageReqVO.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import jakarta.validation.constraints.Size; - -@Schema(description = "管理后台 - 字典类型分页列表 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -public class DictDataPageReqVO extends PageParam { - - @Schema(description = "字典标签", example = "芋道") - @Size(max = 100, message = "字典标签长度不能超过100个字符") - private String label; - - @Schema(description = "字典类型,模糊匹配", example = "sys_common_sex") - @Size(max = 100, message = "字典类型类型长度不能超过100个字符") - private String dictType; - - @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") - @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java deleted file mode 100644 index 8857a70..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataRespVO.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 字典数据信息 Response VO") -@Data -@ExcelIgnoreUnannotated -public class DictDataRespVO { - - @Schema(description = "字典数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("字典编码") - private Long id; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("字典排序") - private Integer sort; - - @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - @ExcelProperty("字典标签") - private String label; - - @Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder") - @ExcelProperty("字典键值") - private String value; - - @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") - @ExcelProperty("字典类型") - private String dictType; - - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - - @Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default") - private String colorType; - - @Schema(description = "css 样式", example = "btn-visible") - private String cssClass; - - @Schema(description = "备注", example = "我是一个角色") - private String remark; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java deleted file mode 100644 index ca71572..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSaveReqVO.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; - -@Schema(description = "管理后台 - 字典数据创建/修改 Request VO") -@Data -public class DictDataSaveReqVO { - - @Schema(description = "字典数据编号", example = "1024") - private Long id; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "显示顺序不能为空") - private Integer sort; - - @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - @NotBlank(message = "字典标签不能为空") - @Size(max = 100, message = "字典标签长度不能超过100个字符") - private String label; - - @Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder") - @NotBlank(message = "字典键值不能为空") - @Size(max = 100, message = "字典键值长度不能超过100个字符") - private String value; - - @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") - @NotBlank(message = "字典类型不能为空") - @Size(max = 100, message = "字典类型长度不能超过100个字符") - private String dictType; - - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") - private Integer status; - - @Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default") - private String colorType; - - @Schema(description = "css 样式", example = "btn-visible") - private String cssClass; - - @Schema(description = "备注", example = "我是一个角色") - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java deleted file mode 100644 index d1497c3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/data/DictDataSimpleRespVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict.vo.data; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 数据字典精简 Response VO") -@Data -public class DictDataSimpleRespVO { - - @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "gender") - private String dictType; - - @Schema(description = "字典键值", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private String value; - - @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "男") - private String label; - - @Schema(description = "颜色类型,default、primary、success、info、warning、danger", example = "default") - private String colorType; - - @Schema(description = "css 样式", example = "btn-visible") - private String cssClass; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypePageReqVO.java deleted file mode 100644 index 2650dd8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypePageReqVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict.vo.type; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.springframework.format.annotation.DateTimeFormat; - -import jakarta.validation.constraints.Size; -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 字典类型分页列表 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -public class DictTypePageReqVO extends PageParam { - - @Schema(description = "字典类型名称,模糊匹配", example = "芋道") - private String name; - - @Schema(description = "字典类型,模糊匹配", example = "sys_common_sex") - @Size(max = 100, message = "字典类型类型长度不能超过100个字符") - private String type; - - @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "创建时间") - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java deleted file mode 100644 index 6ddd47b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeRespVO.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict.vo.type; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 字典类型信息 Response VO") -@Data -@ExcelIgnoreUnannotated -public class DictTypeRespVO { - - @Schema(description = "字典类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("字典主键") - private Long id; - - @Schema(description = "字典名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "性别") - @ExcelProperty("字典名称") - private String name; - - @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") - @ExcelProperty("字典类型") - private String type; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - - @Schema(description = "备注", example = "快乐的备注") - private String remark; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSaveReqVO.java deleted file mode 100644 index 48a1494..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSaveReqVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict.vo.type; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; - -@Schema(description = "管理后台 - 字典类型创建/修改 Request VO") -@Data -public class DictTypeSaveReqVO { - - @Schema(description = "字典类型编号", example = "1024") - private Long id; - - @Schema(description = "字典名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "性别") - @NotBlank(message = "字典名称不能为空") - @Size(max = 100, message = "字典类型名称长度不能超过100个字符") - private String name; - - @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") - @NotNull(message = "字典类型不能为空") - @Size(max = 100, message = "字典类型类型长度不能超过 100 个字符") - private String type; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - private Integer status; - - @Schema(description = "备注", example = "快乐的备注") - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSimpleRespVO.java deleted file mode 100644 index f3e717d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dict/vo/type/DictTypeSimpleRespVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.dict.vo.type; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 字典类型精简信息 Response VO") -@Data -public class DictTypeSimpleRespVO { - - @Schema(description = "字典类型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "字典类型名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String name; - - @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") - private String type; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http deleted file mode 100644 index 1416561..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.http +++ /dev/null @@ -1,5 +0,0 @@ -### 获得地区树 -GET {{baseUrl}}/system/area/tree -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java deleted file mode 100644 index b2f95d6..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/AreaController.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.ip; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.ip.core.Area; -import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; -import cn.iocoder.yudao.framework.ip.core.utils.IPUtils; -import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeRespVO; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 地区") -@RestController -@RequestMapping("/system/area") -@Validated -public class AreaController { - - @GetMapping("/tree") - @Operation(summary = "获得地区树") - public CommonResult> getAreaTree() { - Area area = AreaUtils.getArea(Area.ID_CHINA); - Assert.notNull(area, "获取不到中国"); - return success(BeanUtils.toBean(area.getChildren(), AreaNodeRespVO.class)); - } - - @GetMapping("/get-by-ip") - @Operation(summary = "获得 IP 对应的地区名") - @Parameter(name = "ip", description = "IP", required = true) - public CommonResult getAreaByIp(@RequestParam("ip") String ip) { - // 获得城市 - Area area = IPUtils.getArea(ip); - if (area == null) { - return success("未知"); - } - // 格式化返回 - return success(AreaUtils.format(area.getId())); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/vo/AreaNodeRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/vo/AreaNodeRespVO.java deleted file mode 100644 index 1360ec6..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/ip/vo/AreaNodeRespVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.ip.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; - -@Schema(description = "管理后台 - 地区节点 Response VO") -@Data -public class AreaNodeRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "110000") - private Integer id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "北京") - private String name; - - /** - * 子节点 - */ - private List children; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java deleted file mode 100644 index f2ff3a4..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/LoginLogController.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.logger; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; -import cn.iocoder.yudao.module.system.service.logger.LoginLogService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.io.IOException; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 登录日志") -@RestController -@RequestMapping("/system/login-log") -@Validated -public class LoginLogController { - - @Resource - private LoginLogService loginLogService; - - @GetMapping("/page") - @Operation(summary = "获得登录日志分页列表") - @PreAuthorize("@ss.hasPermission('system:login-log:query')") - public CommonResult> getLoginLogPage(@Valid LoginLogPageReqVO pageReqVO) { - PageResult pageResult = loginLogService.getLoginLogPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, LoginLogRespVO.class)); - } - - @GetMapping("/export") - @Operation(summary = "导出登录日志 Excel") - @PreAuthorize("@ss.hasPermission('system:login-log:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportLoginLog(HttpServletResponse response, @Valid LoginLogPageReqVO exportReqVO) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = loginLogService.getLoginLogPage(exportReqVO).getList(); - // 输出 - ExcelUtils.write(response, "登录日志.xls", "数据列表", LoginLogRespVO.class, - BeanUtils.toBean(list, LoginLogRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http deleted file mode 100644 index be3102e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.http +++ /dev/null @@ -1,4 +0,0 @@ -### 请求 /system/operate-log/page 接口 => 成功 -GET {{baseUrl}}/system/operate-log/page -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java deleted file mode 100644 index 57b9b61..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/OperateLogController.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.logger; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.translate.core.TranslateUtils; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; -import cn.iocoder.yudao.module.system.service.logger.OperateLogService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.io.IOException; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 操作日志") -@RestController -@RequestMapping("/system/operate-log") -@Validated -public class OperateLogController { - - @Resource - private OperateLogService operateLogService; - - @GetMapping("/page") - @Operation(summary = "查看操作日志分页列表") - @PreAuthorize("@ss.hasPermission('system:operate-log:query')") - public CommonResult> pageOperateLog(@Valid OperateLogPageReqVO pageReqVO) { - PageResult pageResult = operateLogService.getOperateLogPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, OperateLogRespVO.class)); - } - - @Operation(summary = "导出操作日志") - @GetMapping("/export") - @PreAuthorize("@ss.hasPermission('system:operate-log:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportOperateLog(HttpServletResponse response, @Valid OperateLogPageReqVO exportReqVO) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = operateLogService.getOperateLogPage(exportReqVO).getList(); - ExcelUtils.write(response, "操作日志.xls", "数据列表", OperateLogRespVO.class, - TranslateUtils.translate(BeanUtils.toBean(list, OperateLogRespVO.class))); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogPageReqVO.java deleted file mode 100644 index 40e12a8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogPageReqVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 登录日志分页列表 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -public class LoginLogPageReqVO extends PageParam { - - @Schema(description = "用户 IP,模拟匹配", example = "127.0.0.1") - private String userIp; - - @Schema(description = "用户账号,模拟匹配", example = "芋道") - private String username; - - @Schema(description = "操作状态", example = "true") - private Boolean status; - - @Schema(description = "登录时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java deleted file mode 100644 index 7b9cd16..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/loginlog/LoginLogRespVO.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 登录日志 Response VO") -@Data -@ExcelIgnoreUnannotated -public class LoginLogRespVO { - - @Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("日志主键") - private Long id; - - @Schema(description = "日志类型,参见 LoginLogTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "日志类型", converter = DictConvert.class) - @DictFormat(DictTypeConstants.LOGIN_TYPE) - private Integer logType; - - @Schema(description = "用户编号", example = "666") - private Long userId; - - @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer userType; - - @Schema(description = "链路追踪编号", example = "89aca178-a370-411c-ae02-3f0d672be4ab") - private String traceId; - - @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @ExcelProperty("用户账号") - private String username; - - @Schema(description = "登录结果,参见 LoginResultEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "登录结果", converter = DictConvert.class) - @DictFormat(DictTypeConstants.LOGIN_RESULT) - private Integer result; - - @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") - @ExcelProperty("登录 IP") - private String userIp; - - @Schema(description = "浏览器 UserAgent", example = "Mozilla/5.0") - @ExcelProperty("浏览器 UA") - private String userAgent; - - @Schema(description = "登录时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("登录时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java deleted file mode 100644 index 1061f6d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogPageReqVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 操作日志分页列表 Request VO") -@Data -public class OperateLogPageReqVO extends PageParam { - - @Schema(description = "用户编号", example = "芋道") - private Long userId; - - @Schema(description = "操作模块业务编号", example = "1") - private Long bizId; - - @Schema(description = "操作模块,模拟匹配", example = "订单") - private String type; - - @Schema(description = "操作名,模拟匹配", example = "创建订单") - private String subType; - - @Schema(description = "操作明细,模拟匹配", example = "修改编号为 1 的用户信息") - private String action; - - @Schema(description = "开始时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java deleted file mode 100644 index e017ba7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/logger/vo/operatelog/OperateLogRespVO.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog; - -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import com.fhs.core.trans.anno.Trans; -import com.fhs.core.trans.constant.TransType; -import com.fhs.core.trans.vo.VO; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 操作日志 Response VO") -@Data -@ExcelIgnoreUnannotated -public class OperateLogRespVO implements VO { - - @Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("日志编号") - private Long id; - - @Schema(description = "链路追踪编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "89aca178-a370-411c-ae02-3f0d672be4ab") - private String traceId; - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @Trans(type = TransType.SIMPLE, target = AdminUserDO.class, fields = "nickname", ref = "userName") - private Long userId; - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - @ExcelProperty("操作人") - private String userName; - - @Schema(description = "操作模块类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "订单") - @ExcelProperty("操作模块类型") - private String type; - - @Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建订单") - @ExcelProperty("操作名") - private String subType; - - @Schema(description = "操作模块业务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("操作模块业务编号") - private Long bizId; - - @Schema(description = "操作明细", example = "修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。") - private String action; - - @Schema(description = "拓展字段", example = "{'orderId': 1}") - private String extra; - - @Schema(description = "请求方法名", requiredMode = Schema.RequiredMode.REQUIRED, example = "GET") - @NotEmpty(message = "请求方法名不能为空") - private String requestMethod; - - @Schema(description = "请求地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "/xxx/yyy") - private String requestUrl; - - @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") - private String userIp; - - @Schema(description = "浏览器 UserAgent", requiredMode = Schema.RequiredMode.REQUIRED, example = "Mozilla/5.0") - private String userAgent; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailAccountController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailAccountController.java deleted file mode 100644 index 4cd7c40..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailAccountController.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail; - - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountRespVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSimpleRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; -import cn.iocoder.yudao.module.system.service.mail.MailAccountService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 邮箱账号") -@RestController -@RequestMapping("/system/mail-account") -public class MailAccountController { - - @Resource - private MailAccountService mailAccountService; - - @PostMapping("/create") - @Operation(summary = "创建邮箱账号") - @PreAuthorize("@ss.hasPermission('system:mail-account:create')") - public CommonResult createMailAccount(@Valid @RequestBody MailAccountSaveReqVO createReqVO) { - return success(mailAccountService.createMailAccount(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "修改邮箱账号") - @PreAuthorize("@ss.hasPermission('system:mail-account:update')") - public CommonResult updateMailAccount(@Valid @RequestBody MailAccountSaveReqVO updateReqVO) { - mailAccountService.updateMailAccount(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除邮箱账号") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:mail-account:delete')") - public CommonResult deleteMailAccount(@RequestParam Long id) { - mailAccountService.deleteMailAccount(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得邮箱账号") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:mail-account:query')") - public CommonResult getMailAccount(@RequestParam("id") Long id) { - MailAccountDO account = mailAccountService.getMailAccount(id); - return success(BeanUtils.toBean(account, MailAccountRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得邮箱账号分页") - @PreAuthorize("@ss.hasPermission('system:mail-account:query')") - public CommonResult> getMailAccountPage(@Valid MailAccountPageReqVO pageReqVO) { - PageResult pageResult = mailAccountService.getMailAccountPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, MailAccountRespVO.class)); - } - - @GetMapping({"/list-all-simple", "simple-list"}) - @Operation(summary = "获得邮箱账号精简列表") - public CommonResult> getSimpleMailAccountList() { - List list = mailAccountService.getMailAccountList(); - return success(BeanUtils.toBean(list, MailAccountSimpleRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailLogController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailLogController.java deleted file mode 100644 index 1a78d43..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailLogController.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; -import cn.iocoder.yudao.module.system.service.mail.MailLogService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 邮件日志") -@RestController -@RequestMapping("/system/mail-log") -public class MailLogController { - - @Resource - private MailLogService mailLogService; - - @GetMapping("/page") - @Operation(summary = "获得邮箱日志分页") - @PreAuthorize("@ss.hasPermission('system:mail-log:query')") - public CommonResult> getMailLogPage(@Valid MailLogPageReqVO pageVO) { - PageResult pageResult = mailLogService.getMailLogPage(pageVO); - return success(BeanUtils.toBean(pageResult, MailLogRespVO.class)); - } - - @GetMapping("/get") - @Operation(summary = "获得邮箱日志") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:mail-log:query')") - public CommonResult getMailTemplate(@RequestParam("id") Long id) { - MailLogDO log = mailLogService.getMailLog(id); - return success(BeanUtils.toBean(log, MailLogRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http deleted file mode 100644 index 9ad2ed2..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http +++ /dev/null @@ -1,14 +0,0 @@ -### 请求 /system/mail-template/send-mail 接口 => 成功 -POST {{baseUrl}}/system/mail-template/send-mail -Authorization: Bearer {{token}} -Content-Type: application/json -tenant-id: {{adminTenantId}} - -{ - "templateCode": "test_01", - "mail": "7685413@qq.com", - "templateParams": { - "key01": "value01", - "key02": "value02" - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java deleted file mode 100644 index 22e2658..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java +++ /dev/null @@ -1,89 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.*; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; -import cn.iocoder.yudao.module.system.service.mail.MailSendService; -import cn.iocoder.yudao.module.system.service.mail.MailTemplateService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 邮件模版") -@RestController -@RequestMapping("/system/mail-template") -public class MailTemplateController { - - @Resource - private MailTemplateService mailTempleService; - @Resource - private MailSendService mailSendService; - - @PostMapping("/create") - @Operation(summary = "创建邮件模版") - @PreAuthorize("@ss.hasPermission('system:mail-template:create')") - public CommonResult createMailTemplate(@Valid @RequestBody MailTemplateSaveReqVO createReqVO){ - return success(mailTempleService.createMailTemplate(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "修改邮件模版") - @PreAuthorize("@ss.hasPermission('system:mail-template:update')") - public CommonResult updateMailTemplate(@Valid @RequestBody MailTemplateSaveReqVO updateReqVO){ - mailTempleService.updateMailTemplate(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除邮件模版") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:mail-template:delete')") - public CommonResult deleteMailTemplate(@RequestParam("id") Long id) { - mailTempleService.deleteMailTemplate(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得邮件模版") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:mail-template:query')") - public CommonResult getMailTemplate(@RequestParam("id") Long id) { - MailTemplateDO template = mailTempleService.getMailTemplate(id); - return success(BeanUtils.toBean(template, MailTemplateRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得邮件模版分页") - @PreAuthorize("@ss.hasPermission('system:mail-template:query')") - public CommonResult> getMailTemplatePage(@Valid MailTemplatePageReqVO pageReqVO) { - PageResult pageResult = mailTempleService.getMailTemplatePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, MailTemplateRespVO.class)); - } - - @GetMapping({"/list-all-simple", "simple-list"}) - @Operation(summary = "获得邮件模版精简列表") - public CommonResult> getSimpleTemplateList() { - List list = mailTempleService.getMailTemplateList(); - return success(BeanUtils.toBean(list, MailTemplateSimpleRespVO.class)); - } - - @PostMapping("/send-mail") - @Operation(summary = "发送短信") - @PreAuthorize("@ss.hasPermission('system:mail-template:send-mail')") - public CommonResult sendMail(@Valid @RequestBody MailTemplateSendReqVO sendReqVO) { - return success(mailSendService.sendSingleMailToAdmin(sendReqVO.getMail(), getLoginUserId(), - sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountPageReqVO.java deleted file mode 100644 index 9de195d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountPageReqVO.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -@Schema(description = "管理后台 - 邮箱账号分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class MailAccountPageReqVO extends PageParam { - - @Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma@123.com") - private String mail; - - @Schema(description = "用户名" , requiredMode = Schema.RequiredMode.REQUIRED , example = "yudao") - private String username; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountRespVO.java deleted file mode 100644 index 15232b2..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountRespVO.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 邮箱账号 Response VO") -@Data -public class MailAccountRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma@123.com") - private String mail; - - @Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - private String username; - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - private String password; - - @Schema(description = "SMTP 服务器域名", requiredMode = Schema.RequiredMode.REQUIRED, example = "www.iocoder.cn") - private String host; - - @Schema(description = "SMTP 服务器端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") - private Integer port; - - @Schema(description = "是否开启 ssl", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - private Boolean sslEnable; - - @Schema(description = "是否开启 starttls", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - private Boolean starttlsEnable; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSaveReqVO.java deleted file mode 100644 index 5bac2c9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSaveReqVO.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 邮箱账号创建/修改 Request VO") -@Data -public class MailAccountSaveReqVO { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudaoyuanma@123.com") - @NotNull(message = "邮箱不能为空") - @Email(message = "必须是 Email 格式") - private String mail; - - @Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @NotNull(message = "用户名不能为空") - private String username; - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - @NotNull(message = "密码必填") - private String password; - - @Schema(description = "SMTP 服务器域名", requiredMode = Schema.RequiredMode.REQUIRED, example = "www.iocoder.cn") - @NotNull(message = "SMTP 服务器域名不能为空") - private String host; - - @Schema(description = "SMTP 服务器端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "80") - @NotNull(message = "SMTP 服务器端口不能为空") - private Integer port; - - @Schema(description = "是否开启 ssl", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否开启 ssl 必填") - private Boolean sslEnable; - - @Schema(description = "是否开启 starttls", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否开启 starttls 必填") - private Boolean starttlsEnable; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSimpleRespVO.java deleted file mode 100644 index 0600633..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSimpleRespVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 邮箱账号的精简 Response VO") -@Data -public class MailAccountSimpleRespVO { - - @Schema(description = "邮箱编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "768541388@qq.com") - private String mail; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogPageReqVO.java deleted file mode 100644 index 5b3ad72..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogPageReqVO.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.log; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 邮箱日志分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class MailLogPageReqVO extends PageParam { - - @Schema(description = "用户编号", example = "30883") - private Long userId; - - @Schema(description = "用户类型,参见 UserTypeEnum 枚举", example = "2") - private Integer userType; - - @Schema(description = "接收邮箱地址,模糊匹配", example = "76854@qq.com") - private String toMail; - - @Schema(description = "邮箱账号编号", example = "18107") - private Long accountId; - - @Schema(description = "模板编号", example = "5678") - private Long templateId; - - @Schema(description = "发送状态,参见 MailSendStatusEnum 枚举", example = "1") - private Integer sendStatus; - - @Schema(description = "发送时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] sendTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java deleted file mode 100755 index 49edfff..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.log; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 邮件日志 Response VO") -@Data -public class MailLogRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31020") - private Long id; - - @Schema(description = "用户编号", example = "30883") - private Long userId; - - @Schema(description = "用户类型,参见 UserTypeEnum 枚举", example = "2") - private Byte userType; - - @Schema(description = "接收邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "76854@qq.com") - private String toMail; - - @Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18107") - private Long accountId; - - @Schema(description = "发送邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "85757@qq.com") - private String fromMail; - - @Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5678") - private Long templateId; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") - private String templateCode; - - @Schema(description = "模版发送人名称", example = "李四") - private String templateNickname; - - @Schema(description = "邮件标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试标题") - private String templateTitle; - - @Schema(description = "邮件内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试内容") - private String templateContent; - - @Schema(description = "邮件参数", requiredMode = Schema.RequiredMode.REQUIRED) - private Map templateParams; - - @Schema(description = "发送状态,参见 MailSendStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Byte sendStatus; - - @Schema(description = "发送时间") - private LocalDateTime sendTime; - - @Schema(description = "发送返回的消息 ID", example = "28568") - private String sendMessageId; - - @Schema(description = "发送异常") - private String sendException; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java deleted file mode 100644 index 2a9afd7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 邮件模版分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class MailTemplatePageReqVO extends PageParam { - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", example = "1") - private Integer status; - - @Schema(description = "标识,模糊匹配", example = "code_1024") - private String code; - - @Schema(description = "名称,模糊匹配", example = "芋头") - private String name; - - @Schema(description = "账号编号", example = "2048") - private Long accountId; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java deleted file mode 100644 index 58a9b61..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - 邮件末班 Response VO") -@Data -public class MailTemplateRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试名字") - private String name; - - @Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") - private String code; - - @Schema(description = "发送的邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long accountId; - - @Schema(description = "发送人名称", example = "芋头") - private String nickname; - - @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "注册成功") - private String title; - - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,注册成功啦") - private String content; - - @Schema(description = "参数数组", example = "name,code") - private List params; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "备注", example = "奥特曼") - private String remark; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSaveReqVO.java deleted file mode 100644 index fd6a60a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSaveReqVO.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 邮件模版创建/修改 Request VO") -@Data -public class MailTemplateSaveReqVO { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试名字") - @NotNull(message = "名称不能为空") - private String name; - - @Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "test") - @NotNull(message = "模版编号不能为空") - private String code; - - @Schema(description = "发送的邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "发送的邮箱账号编号不能为空") - private Long accountId; - - @Schema(description = "发送人名称", example = "芋头") - private String nickname; - - @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "注册成功") - @NotEmpty(message = "标题不能为空") - private String title; - - @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,注册成功啦") - @NotEmpty(message = "内容不能为空") - private String content; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - private Integer status; - - @Schema(description = "备注", example = "奥特曼") - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSendReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSendReqVO.java deleted file mode 100644 index b76b7ff..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSendReqVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import java.util.Map; - -@Schema(description = "管理后台 - 邮件发送 Req VO") -@Data -public class MailTemplateSendReqVO { - - @Schema(description = "接收邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "7685413@qq.com") - @NotEmpty(message = "接收邮箱不能为空") - private String mail; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") - @NotNull(message = "模板编码不能为空") - private String templateCode; - - @Schema(description = "模板参数") - private Map templateParams; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSimpleRespVO.java deleted file mode 100644 index 29833ff..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSimpleRespVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 邮件模版的精简 Response VO") -@Data -public class MailTemplateSimpleRespVO { - - @Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "模版名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "哒哒哒") - private String name; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/NoticeController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/NoticeController.java deleted file mode 100644 index 95fc92d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/NoticeController.java +++ /dev/null @@ -1,92 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notice; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.infra.api.websocket.WebSocketSenderApi; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeRespVO; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; -import cn.iocoder.yudao.module.system.service.notice.NoticeService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 通知公告") -@RestController -@RequestMapping("/system/notice") -@Validated -public class NoticeController { - - @Resource - private NoticeService noticeService; - - @Resource - private WebSocketSenderApi webSocketSenderApi; - - @PostMapping("/create") - @Operation(summary = "创建通知公告") - @PreAuthorize("@ss.hasPermission('system:notice:create')") - public CommonResult createNotice(@Valid @RequestBody NoticeSaveReqVO createReqVO) { - Long noticeId = noticeService.createNotice(createReqVO); - return success(noticeId); - } - - @PutMapping("/update") - @Operation(summary = "修改通知公告") - @PreAuthorize("@ss.hasPermission('system:notice:update')") - public CommonResult updateNotice(@Valid @RequestBody NoticeSaveReqVO updateReqVO) { - noticeService.updateNotice(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除通知公告") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:notice:delete')") - public CommonResult deleteNotice(@RequestParam("id") Long id) { - noticeService.deleteNotice(id); - return success(true); - } - - @GetMapping("/page") - @Operation(summary = "获取通知公告列表") - @PreAuthorize("@ss.hasPermission('system:notice:query')") - public CommonResult> getNoticePage(@Validated NoticePageReqVO pageReqVO) { - PageResult pageResult = noticeService.getNoticePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, NoticeRespVO.class)); - } - - @GetMapping("/get") - @Operation(summary = "获得通知公告") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:notice:query')") - public CommonResult getNotice(@RequestParam("id") Long id) { - NoticeDO notice = noticeService.getNotice(id); - return success(BeanUtils.toBean(notice, NoticeRespVO.class)); - } - - @PostMapping("/push") - @Operation(summary = "推送通知公告", description = "只发送给 websocket 连接在线的用户") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:notice:update')") - public CommonResult push(@RequestParam("id") Long id) { - NoticeDO notice = noticeService.getNotice(id); - Assert.notNull(notice, "公告不能为空"); - // 通过 websocket 推送给在线的用户 - webSocketSenderApi.sendObject(UserTypeEnum.ADMIN.getValue(), "notice-push", notice); - return success(true); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticePageReqVO.java deleted file mode 100644 index e398042..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticePageReqVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notice.vo; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Schema(description = "管理后台 - 通知公告分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -public class NoticePageReqVO extends PageParam { - - @Schema(description = "通知公告名称,模糊匹配", example = "芋道") - private String title; - - @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeRespVO.java deleted file mode 100644 index 543f588..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeRespVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notice.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 通知公告信息 Response VO") -@Data -public class NoticeRespVO { - - @Schema(description = "通知公告序号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主") - private String title; - - @Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主") - private Integer type; - - @Schema(description = "公告内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "半生编码") - private String content; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java deleted file mode 100644 index 78794ed..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notice/vo/NoticeSaveReqVO.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notice.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; - -@Schema(description = "管理后台 - 通知公告创建/修改 Request VO") -@Data -public class NoticeSaveReqVO { - - @Schema(description = "岗位公告编号", example = "1024") - private Long id; - - @Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主") - @NotBlank(message = "公告标题不能为空") - @Size(max = 50, message = "公告标题不能超过50个字符") - private String title; - - @Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "小博主") - @NotNull(message = "公告类型不能为空") - private Integer type; - - @Schema(description = "公告内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "半生编码") - private String content; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java deleted file mode 100644 index 752ad28..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java +++ /dev/null @@ -1,98 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notify; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; -import cn.iocoder.yudao.module.system.service.notify.NotifyMessageService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 我的站内信") -@RestController -@RequestMapping("/system/notify-message") -@Validated -public class NotifyMessageController { - - @Resource - private NotifyMessageService notifyMessageService; - - // ========== 管理所有的站内信 ========== - - @GetMapping("/get") - @Operation(summary = "获得站内信") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:notify-message:query')") - public CommonResult getNotifyMessage(@RequestParam("id") Long id) { - NotifyMessageDO message = notifyMessageService.getNotifyMessage(id); - return success(BeanUtils.toBean(message, NotifyMessageRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得站内信分页") - @PreAuthorize("@ss.hasPermission('system:notify-message:query')") - public CommonResult> getNotifyMessagePage(@Valid NotifyMessagePageReqVO pageVO) { - PageResult pageResult = notifyMessageService.getNotifyMessagePage(pageVO); - return success(BeanUtils.toBean(pageResult, NotifyMessageRespVO.class)); - } - - // ========== 查看自己的站内信 ========== - - @GetMapping("/my-page") - @Operation(summary = "获得我的站内信分页") - public CommonResult> getMyMyNotifyMessagePage(@Valid NotifyMessageMyPageReqVO pageVO) { - PageResult pageResult = notifyMessageService.getMyMyNotifyMessagePage(pageVO, - getLoginUserId(), UserTypeEnum.ADMIN.getValue()); - return success(BeanUtils.toBean(pageResult, NotifyMessageRespVO.class)); - } - - @PutMapping("/update-read") - @Operation(summary = "标记站内信为已读") - @Parameter(name = "ids", description = "编号列表", required = true, example = "1024,2048") - public CommonResult updateNotifyMessageRead(@RequestParam("ids") List ids) { - notifyMessageService.updateNotifyMessageRead(ids, getLoginUserId(), UserTypeEnum.ADMIN.getValue()); - return success(Boolean.TRUE); - } - - @PutMapping("/update-all-read") - @Operation(summary = "标记所有站内信为已读") - public CommonResult updateAllNotifyMessageRead() { - notifyMessageService.updateAllNotifyMessageRead(getLoginUserId(), UserTypeEnum.ADMIN.getValue()); - return success(Boolean.TRUE); - } - - @GetMapping("/get-unread-list") - @Operation(summary = "获取当前用户的最新站内信列表,默认 10 条") - @Parameter(name = "size", description = "10") - public CommonResult> getUnreadNotifyMessageList( - @RequestParam(name = "size", defaultValue = "10") Integer size) { - List list = notifyMessageService.getUnreadNotifyMessageList( - getLoginUserId(), UserTypeEnum.ADMIN.getValue(), size); - return success(BeanUtils.toBean(list, NotifyMessageRespVO.class)); - } - - @GetMapping("/get-unread-count") - @Operation(summary = "获得当前用户的未读站内信数量") - @ApiAccessLog(enable = false) // 由于前端会不断轮询该接口,记录日志没有意义 - public CommonResult getUnreadNotifyMessageCount() { - return success(notifyMessageService.getUnreadNotifyMessageCount( - getLoginUserId(), UserTypeEnum.ADMIN.getValue())); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java deleted file mode 100644 index 57c35db..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java +++ /dev/null @@ -1,88 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notify; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.*; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; -import cn.iocoder.yudao.module.system.service.notify.NotifySendService; -import cn.iocoder.yudao.module.system.service.notify.NotifyTemplateService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 站内信模版") -@RestController -@RequestMapping("/system/notify-template") -@Validated -public class NotifyTemplateController { - - @Resource - private NotifyTemplateService notifyTemplateService; - - @Resource - private NotifySendService notifySendService; - - @PostMapping("/create") - @Operation(summary = "创建站内信模版") - @PreAuthorize("@ss.hasPermission('system:notify-template:create')") - public CommonResult createNotifyTemplate(@Valid @RequestBody NotifyTemplateSaveReqVO createReqVO) { - return success(notifyTemplateService.createNotifyTemplate(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新站内信模版") - @PreAuthorize("@ss.hasPermission('system:notify-template:update')") - public CommonResult updateNotifyTemplate(@Valid @RequestBody NotifyTemplateSaveReqVO updateReqVO) { - notifyTemplateService.updateNotifyTemplate(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除站内信模版") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:notify-template:delete')") - public CommonResult deleteNotifyTemplate(@RequestParam("id") Long id) { - notifyTemplateService.deleteNotifyTemplate(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得站内信模版") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:notify-template:query')") - public CommonResult getNotifyTemplate(@RequestParam("id") Long id) { - NotifyTemplateDO template = notifyTemplateService.getNotifyTemplate(id); - return success(BeanUtils.toBean(template, NotifyTemplateRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得站内信模版分页") - @PreAuthorize("@ss.hasPermission('system:notify-template:query')") - public CommonResult> getNotifyTemplatePage(@Valid NotifyTemplatePageReqVO pageVO) { - PageResult pageResult = notifyTemplateService.getNotifyTemplatePage(pageVO); - return success(BeanUtils.toBean(pageResult, NotifyTemplateRespVO.class)); - } - - @PostMapping("/send-notify") - @Operation(summary = "发送站内信") - @PreAuthorize("@ss.hasPermission('system:notify-template:send-notify')") - public CommonResult sendNotify(@Valid @RequestBody NotifyTemplateSendReqVO sendReqVO) { - if (UserTypeEnum.MEMBER.getValue().equals(sendReqVO.getUserType())) { - return success(notifySendService.sendSingleNotifyToMember(sendReqVO.getUserId(), - sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); - } else { - return success(notifySendService.sendSingleNotifyToAdmin(sendReqVO.getUserId(), - sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); - } - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageMyPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageMyPageReqVO.java deleted file mode 100644 index 213d6e1..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageMyPageReqVO.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 站内信分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class NotifyMessageMyPageReqVO extends PageParam { - - @Schema(description = "是否已读", example = "true") - private Boolean readStatus; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessagePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessagePageReqVO.java deleted file mode 100644 index 4e3aea5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessagePageReqVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 站内信分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class NotifyMessagePageReqVO extends PageParam { - - @Schema(description = "用户编号", example = "25025") - private Long userId; - - @Schema(description = "用户类型", example = "1") - private Integer userType; - - @Schema(description = "模板编码", example = "test_01") - private String templateCode; - - @Schema(description = "模版类型", example = "2") - private Integer templateType; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageRespVO.java deleted file mode 100644 index 774ed2a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageRespVO.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.Map; - -@Schema(description = "管理后台 - 站内信 Response VO") -@Data -public class NotifyMessageRespVO { - - @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25025") - private Long userId; - - @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Byte userType; - - @Schema(description = "模版编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13013") - private Long templateId; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") - private String templateCode; - - @Schema(description = "模版发送人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - private String templateNickname; - - @Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试内容") - private String templateContent; - - @Schema(description = "模版类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer templateType; - - @Schema(description = "模版参数", requiredMode = Schema.RequiredMode.REQUIRED) - private Map templateParams; - - @Schema(description = "是否已读", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - private Boolean readStatus; - - @Schema(description = "阅读时间") - private LocalDateTime readTime; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplatePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplatePageReqVO.java deleted file mode 100644 index 0557297..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplatePageReqVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 站内信模版分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class NotifyTemplatePageReqVO extends PageParam { - - @Schema(description = "模版编码", example = "test_01") - private String code; - - @Schema(description = "模版名称", example = "我是名称") - private String name; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举类", example = "1") - private Integer status; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateRespVO.java deleted file mode 100644 index 8d362b8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateRespVO.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - 站内信模版 Response VO") -@Data -public class NotifyTemplateRespVO { - - @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试模版") - private String name; - - @Schema(description = "模版编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "SEND_TEST") - private String code; - - @Schema(description = "模版类型,对应 system_notify_template_type 字典", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer type; - - @Schema(description = "发送人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") - private String nickname; - - @Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是模版内容") - private String content; - - @Schema(description = "参数数组", example = "name,code") - private List params; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "备注", example = "我是备注") - private String remark; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSaveReqVO.java deleted file mode 100644 index 364c125..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSaveReqVO.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 站内信模版创建/修改 Request VO") -@Data -public class NotifyTemplateSaveReqVO { - - @Schema(description = "ID", example = "1024") - private Long id; - - @Schema(description = "模版名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试模版") - @NotEmpty(message = "模版名称不能为空") - private String name; - - @Schema(description = "模版编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "SEND_TEST") - @NotNull(message = "模版编码不能为空") - private String code; - - @Schema(description = "模版类型,对应 system_notify_template_type 字典", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "模版类型不能为空") - private Integer type; - - @Schema(description = "发送人名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") - @NotEmpty(message = "发送人名称不能为空") - private String nickname; - - @Schema(description = "模版内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是模版内容") - @NotEmpty(message = "模版内容不能为空") - private String content; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") - private Integer status; - - @Schema(description = "备注", example = "我是备注") - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSendReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSendReqVO.java deleted file mode 100644 index e7fc1a7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSendReqVO.java +++ /dev/null @@ -1,29 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import java.util.Map; - -@Schema(description = "管理后台 - 站内信模板的发送 Request VO") -@Data -public class NotifyTemplateSendReqVO { - - @Schema(description = "用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "01") - @NotNull(message = "用户id不能为空") - private Long userId; - - @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "用户类型不能为空") - private Integer userType; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "01") - @NotEmpty(message = "模板编码不能为空") - private String templateCode; - - @Schema(description = "模板参数") - private Map templateParams; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http deleted file mode 100644 index f8d7b89..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.http +++ /dev/null @@ -1,23 +0,0 @@ -### 请求 /login 接口 => 成功 -POST {{baseUrl}}/system/oauth2-client/create -Content-Type: application/json -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - -{ - "id": "1", - "secret": "admin123", - "name": "芋道源码", - "logo": "https://www.iocoder.cn/images/favicon.ico", - "description": "我是描述", - "status": 0, - "accessTokenValiditySeconds": 180, - "refreshTokenValiditySeconds": 8640, - "redirectUris": ["https://www.iocoder.cn"], - "autoApprove": true, - "authorizedGrantTypes": ["password"], - "scopes": ["user_info"], - "authorities": ["system:user:query"], - "resource_ids": ["1024"], - "additionalInformation": "{}" -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java deleted file mode 100755 index d3f4aed..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2ClientController.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientRespVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - OAuth2 客户端") -@RestController -@RequestMapping("/system/oauth2-client") -@Validated -public class OAuth2ClientController { - - @Resource - private OAuth2ClientService oAuth2ClientService; - - @PostMapping("/create") - @Operation(summary = "创建 OAuth2 客户端") - @PreAuthorize("@ss.hasPermission('system:oauth2-client:create')") - public CommonResult createOAuth2Client(@Valid @RequestBody OAuth2ClientSaveReqVO createReqVO) { - return success(oAuth2ClientService.createOAuth2Client(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新 OAuth2 客户端") - @PreAuthorize("@ss.hasPermission('system:oauth2-client:update')") - public CommonResult updateOAuth2Client(@Valid @RequestBody OAuth2ClientSaveReqVO updateReqVO) { - oAuth2ClientService.updateOAuth2Client(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除 OAuth2 客户端") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:oauth2-client:delete')") - public CommonResult deleteOAuth2Client(@RequestParam("id") Long id) { - oAuth2ClientService.deleteOAuth2Client(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得 OAuth2 客户端") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:oauth2-client:query')") - public CommonResult getOAuth2Client(@RequestParam("id") Long id) { - OAuth2ClientDO client = oAuth2ClientService.getOAuth2Client(id); - return success(BeanUtils.toBean(client, OAuth2ClientRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得 OAuth2 客户端分页") - @PreAuthorize("@ss.hasPermission('system:oauth2-client:query')") - public CommonResult> getOAuth2ClientPage(@Valid OAuth2ClientPageReqVO pageVO) { - PageResult pageResult = oAuth2ClientService.getOAuth2ClientPage(pageVO); - return success(BeanUtils.toBean(pageResult, OAuth2ClientRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http deleted file mode 100644 index f770ed9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.http +++ /dev/null @@ -1,54 +0,0 @@ -### 请求 /system/oauth2/authorize 接口 => 成功 -GET {{baseUrl}}/system/oauth2/authorize?clientId=default -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - -### 请求 /system/oauth2/authorize + token 接口 => 成功 -POST {{baseUrl}}/system/oauth2/authorize -Content-Type: application/x-www-form-urlencoded -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - -response_type=token&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=true - -### 请求 /system/oauth2/authorize + code 接口 => 成功 -POST {{baseUrl}}/system/oauth2/authorize -Content-Type: application/x-www-form-urlencoded -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - -response_type=code&client_id=default&scope={"user.read": true}&redirect_uri=https://www.iocoder.cn&auto_approve=false - -### 请求 /system/oauth2/token + code 接口 => 成功 -POST {{baseUrl}}/system/oauth2/token -Content-Type: application/x-www-form-urlencoded -Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenantId}} - -grant_type=authorization_code&redirect_uri=https://www.iocoder.cn&code=189956c07a174588a97157eabef2f93a - -### 请求 /system/oauth2/token + password 接口 => 成功 -POST {{baseUrl}}/system/oauth2/token -Content-Type: application/x-www-form-urlencoded -Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenantId}} - -grant_type=password&username=admin&password=admin123&scope=user.read - -### 请求 /system/oauth2/token + refresh_token 接口 => 成功 -POST {{baseUrl}}/system/oauth2/token -Content-Type: application/x-www-form-urlencoded -Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenantId}} - -grant_type=refresh_token&refresh_token=00895465d6994f72a9d926ceeed0f588 - -### 请求 /system/oauth2/token + DELETE 接口 => 成功 -DELETE {{baseUrl}}/system/oauth2/token?token=ca8a188f464441d6949c51493a2b7596 -Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenantId}} - -### 请求 /system/oauth2/check-token 接口 => 成功 -POST {{baseUrl}}/system/oauth2/check-token?token=620d307c5b4148df8a98dd6c6c547106 -Authorization: Basic ZGVmYXVsdDphZG1pbjEyMw== -tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java deleted file mode 100644 index fd37be8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenController.java +++ /dev/null @@ -1,297 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; -import cn.iocoder.yudao.module.system.convert.oauth2.OAuth2OpenConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2GrantTypeEnum; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; -import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; -import io.swagger.v3.oas.annotations.Operation; -import lombok.extern.slf4j.Slf4j; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.annotation.security.PermitAll; -import jakarta.servlet.http.HttpServletRequest; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -/** - * 提供给外部应用调用为主 - * - * 一般来说,管理后台的 /system-api/* 是不直接提供给外部应用使用,主要是外部应用能够访问的数据与接口是有限的,而管理后台的 RBAC 无法很好的控制。 - * 参考大量的开放平台,都是独立的一套 OpenAPI,对应到【本系统】就是在 Controller 下新建 open 包,实现 /open-api/* 接口,然后通过 scope 进行控制。 - * 另外,一个公司如果有多个管理后台,它们 client_id 产生的 access token 相互之间是无法互通的,即无法访问它们系统的 API 接口,直到两个 client_id 产生信任授权。 - * - * 考虑到【本系统】暂时不想做的过于复杂,默认只有获取到 access token 之后,可以访问【本系统】管理后台的 /system-api/* 所有接口,除非手动添加 scope 控制。 - * scope 的使用示例,可见 {@link OAuth2UserController} 类 - * - * @author 芋道源码 - */ -@Tag(name = "管理后台 - OAuth2.0 授权") -@RestController -@RequestMapping("/system/oauth2") -@Validated -@Slf4j -public class OAuth2OpenController { - - @Resource - private OAuth2GrantService oauth2GrantService; - @Resource - private OAuth2ClientService oauth2ClientService; - @Resource - private OAuth2ApproveService oauth2ApproveService; - @Resource - private OAuth2TokenService oauth2TokenService; - - /** - * 对应 Spring Security OAuth 的 TokenEndpoint 类的 postAccessToken 方法 - * - * 授权码 authorization_code 模式时:code + redirectUri + state 参数 - * 密码 password 模式时:username + password + scope 参数 - * 刷新 refresh_token 模式时:refreshToken 参数 - * 客户端 client_credentials 模式:scope 参数 - * 简化 implicit 模式时:不支持 - * - * 注意,默认需要传递 client_id + client_secret 参数 - */ - @PostMapping("/token") - @PermitAll - @Operation(summary = "获得访问令牌", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【获取】调用") - @Parameters({ - @Parameter(name = "grant_type", required = true, description = "授权类型", example = "code"), - @Parameter(name = "code", description = "授权范围", example = "userinfo.read"), - @Parameter(name = "redirect_uri", description = "重定向 URI", example = "https://www.iocoder.cn"), - @Parameter(name = "state", description = "状态", example = "1"), - @Parameter(name = "username", example = "tudou"), - @Parameter(name = "password", example = "cai"), // 多个使用空格分隔 - @Parameter(name = "scope", example = "user_info"), - @Parameter(name = "refresh_token", example = "123424233"), - }) - public CommonResult postAccessToken(HttpServletRequest request, - @RequestParam("grant_type") String grantType, - @RequestParam(value = "code", required = false) String code, // 授权码模式 - @RequestParam(value = "redirect_uri", required = false) String redirectUri, // 授权码模式 - @RequestParam(value = "state", required = false) String state, // 授权码模式 - @RequestParam(value = "username", required = false) String username, // 密码模式 - @RequestParam(value = "password", required = false) String password, // 密码模式 - @RequestParam(value = "scope", required = false) String scope, // 密码模式 - @RequestParam(value = "refresh_token", required = false) String refreshToken) { // 刷新模式 - List scopes = OAuth2Utils.buildScopes(scope); - // 1.1 校验授权类型 - OAuth2GrantTypeEnum grantTypeEnum = OAuth2GrantTypeEnum.getByGrantType(grantType); - if (grantTypeEnum == null) { - throw exception0(BAD_REQUEST.getCode(), StrUtil.format("未知授权类型({})", grantType)); - } - if (grantTypeEnum == OAuth2GrantTypeEnum.IMPLICIT) { - throw exception0(BAD_REQUEST.getCode(), "Token 接口不支持 implicit 授权模式"); - } - - // 1.2 校验客户端 - String[] clientIdAndSecret = obtainBasicAuthorization(request); - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], - grantType, scopes, redirectUri); - - // 2. 根据授权模式,获取访问令牌 - OAuth2AccessTokenDO accessTokenDO; - switch (grantTypeEnum) { - case AUTHORIZATION_CODE: - accessTokenDO = oauth2GrantService.grantAuthorizationCodeForAccessToken(client.getClientId(), code, redirectUri, state); - break; - case PASSWORD: - accessTokenDO = oauth2GrantService.grantPassword(username, password, client.getClientId(), scopes); - break; - case CLIENT_CREDENTIALS: - accessTokenDO = oauth2GrantService.grantClientCredentials(client.getClientId(), scopes); - break; - case REFRESH_TOKEN: - accessTokenDO = oauth2GrantService.grantRefreshToken(refreshToken, client.getClientId()); - break; - default: - throw new IllegalArgumentException("未知授权类型:" + grantType); - } - Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 - return success(OAuth2OpenConvert.INSTANCE.convert(accessTokenDO)); - } - - @DeleteMapping("/token") - @PermitAll - @Operation(summary = "删除访问令牌") - @Parameter(name = "token", required = true, description = "访问令牌", example = "biu") - public CommonResult revokeToken(HttpServletRequest request, - @RequestParam("token") String token) { - // 校验客户端 - String[] clientIdAndSecret = obtainBasicAuthorization(request); - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], - null, null, null); - - // 删除访问令牌 - return success(oauth2GrantService.revokeToken(client.getClientId(), token)); - } - - /** - * 对应 Spring Security OAuth 的 CheckTokenEndpoint 类的 checkToken 方法 - */ - @PostMapping("/check-token") - @PermitAll - @Operation(summary = "校验访问令牌") - @Parameter(name = "token", required = true, description = "访问令牌", example = "biu") - public CommonResult checkToken(HttpServletRequest request, - @RequestParam("token") String token) { - // 校验客户端 - String[] clientIdAndSecret = obtainBasicAuthorization(request); - oauth2ClientService.validOAuthClientFromCache(clientIdAndSecret[0], clientIdAndSecret[1], - null, null, null); - - // 校验令牌 - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.checkAccessToken(token); - Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 - return success(OAuth2OpenConvert.INSTANCE.convert2(accessTokenDO)); - } - - /** - * 对应 Spring Security OAuth 的 AuthorizationEndpoint 类的 authorize 方法 - */ - @GetMapping("/authorize") - @Operation(summary = "获得授权信息", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【获取】调用") - @Parameter(name = "clientId", required = true, description = "客户端编号", example = "tudou") - public CommonResult authorize(@RequestParam("clientId") String clientId) { - // 0. 校验用户已经登录。通过 Spring Security 实现 - - // 1. 获得 Client 客户端的信息 - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId); - // 2. 获得用户已经授权的信息 - List approves = oauth2ApproveService.getApproveList(getLoginUserId(), getUserType(), clientId); - // 拼接返回 - return success(OAuth2OpenConvert.INSTANCE.convert(client, approves)); - } - - /** - * 对应 Spring Security OAuth 的 AuthorizationEndpoint 类的 approveOrDeny 方法 - * - * 场景一:【自动授权 autoApprove = true】 - * 刚进入 sso.vue 界面,调用该接口,用户历史已经给该应用做过对应的授权,或者 OAuth2Client 支持该 scope 的自动授权 - * 场景二:【手动授权 autoApprove = false】 - * 在 sso.vue 界面,用户选择好 scope 授权范围,调用该接口,进行授权。此时,approved 为 true 或者 false - * - * 因为前后端分离,Axios 无法很好的处理 302 重定向,所以和 Spring Security OAuth 略有不同,返回结果是重定向的 URL,剩余交给前端处理 - */ - @PostMapping("/authorize") - @Operation(summary = "申请授权", description = "适合 code 授权码模式,或者 implicit 简化模式;在 sso.vue 单点登录界面被【提交】调用") - @Parameters({ - @Parameter(name = "response_type", required = true, description = "响应类型", example = "code"), - @Parameter(name = "client_id", required = true, description = "客户端编号", example = "tudou"), - @Parameter(name = "scope", description = "授权范围", example = "userinfo.read"), // 使用 Map 格式,Spring MVC 暂时不支持这么接收参数 - @Parameter(name = "redirect_uri", required = true, description = "重定向 URI", example = "https://www.iocoder.cn"), - @Parameter(name = "auto_approve", required = true, description = "用户是否接受", example = "true"), - @Parameter(name = "state", example = "1") - }) - public CommonResult approveOrDeny(@RequestParam("response_type") String responseType, - @RequestParam("client_id") String clientId, - @RequestParam(value = "scope", required = false) String scope, - @RequestParam("redirect_uri") String redirectUri, - @RequestParam(value = "auto_approve") Boolean autoApprove, - @RequestParam(value = "state", required = false) String state) { - @SuppressWarnings("unchecked") - Map scopes = JsonUtils.parseObject(scope, Map.class); - scopes = ObjectUtil.defaultIfNull(scopes, Collections.emptyMap()); - // 0. 校验用户已经登录。通过 Spring Security 实现 - - // 1.1 校验 responseType 是否满足 code 或者 token 值 - OAuth2GrantTypeEnum grantTypeEnum = getGrantTypeEnum(responseType); - // 1.2 校验 redirectUri 重定向域名是否合法 + 校验 scope 是否在 Client 授权范围内 - OAuth2ClientDO client = oauth2ClientService.validOAuthClientFromCache(clientId, null, - grantTypeEnum.getGrantType(), scopes.keySet(), redirectUri); - - // 2.1 假设 approved 为 null,说明是场景一 - if (Boolean.TRUE.equals(autoApprove)) { - // 如果无法自动授权通过,则返回空 url,前端不进行跳转 - if (!oauth2ApproveService.checkForPreApproval(getLoginUserId(), getUserType(), clientId, scopes.keySet())) { - return success(null); - } - } else { // 2.2 假设 approved 非 null,说明是场景二 - // 如果计算后不通过,则跳转一个错误链接 - if (!oauth2ApproveService.updateAfterApproval(getLoginUserId(), getUserType(), clientId, scopes)) { - return success(OAuth2Utils.buildUnsuccessfulRedirect(redirectUri, responseType, state, - "access_denied", "User denied access")); - } - } - - // 3.1 如果是 code 授权码模式,则发放 code 授权码,并重定向 - List approveScopes = convertList(scopes.entrySet(), Map.Entry::getKey, Map.Entry::getValue); - if (grantTypeEnum == OAuth2GrantTypeEnum.AUTHORIZATION_CODE) { - return success(getAuthorizationCodeRedirect(getLoginUserId(), client, approveScopes, redirectUri, state)); - } - // 3.2 如果是 token 则是 implicit 简化模式,则发送 accessToken 访问令牌,并重定向 - return success(getImplicitGrantRedirect(getLoginUserId(), client, approveScopes, redirectUri, state)); - } - - private static OAuth2GrantTypeEnum getGrantTypeEnum(String responseType) { - if (StrUtil.equals(responseType, "code")) { - return OAuth2GrantTypeEnum.AUTHORIZATION_CODE; - } - if (StrUtil.equalsAny(responseType, "token")) { - return OAuth2GrantTypeEnum.IMPLICIT; - } - throw exception0(BAD_REQUEST.getCode(), "response_type 参数值只允许 code 和 token"); - } - - private String getImplicitGrantRedirect(Long userId, OAuth2ClientDO client, - List scopes, String redirectUri, String state) { - // 1. 创建 access token 访问令牌 - OAuth2AccessTokenDO accessTokenDO = oauth2GrantService.grantImplicit(userId, getUserType(), client.getClientId(), scopes); - Assert.notNull(accessTokenDO, "访问令牌不能为空"); // 防御性检查 - // 2. 拼接重定向的 URL - // noinspection unchecked - return OAuth2Utils.buildImplicitRedirectUri(redirectUri, accessTokenDO.getAccessToken(), state, accessTokenDO.getExpiresTime(), - scopes, JsonUtils.parseObject(client.getAdditionalInformation(), Map.class)); - } - - private String getAuthorizationCodeRedirect(Long userId, OAuth2ClientDO client, - List scopes, String redirectUri, String state) { - // 1. 创建 code 授权码 - String authorizationCode = oauth2GrantService.grantAuthorizationCodeForCode(userId, getUserType(), client.getClientId(), scopes, - redirectUri, state); - // 2. 拼接重定向的 URL - return OAuth2Utils.buildAuthorizationCodeRedirectUri(redirectUri, authorizationCode, state); - } - - private Integer getUserType() { - return UserTypeEnum.ADMIN.getValue(); - } - - private String[] obtainBasicAuthorization(HttpServletRequest request) { - String[] clientIdAndSecret = HttpUtils.obtainBasicAuthorization(request); - if (ArrayUtil.isEmpty(clientIdAndSecret) || clientIdAndSecret.length != 2) { - throw exception0(BAD_REQUEST.getCode(), "client_id 或 client_secret 未正确传递"); - } - return clientIdAndSecret; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java deleted file mode 100644 index cafcb23..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2TokenController.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; -import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - OAuth2.0 令牌") -@RestController -@RequestMapping("/system/oauth2-token") -public class OAuth2TokenController { - - @Resource - private OAuth2TokenService oauth2TokenService; - @Resource - private AdminAuthService authService; - - @GetMapping("/page") - @Operation(summary = "获得访问令牌分页", description = "只返回有效期内的") - @PreAuthorize("@ss.hasPermission('system:oauth2-token:page')") - public CommonResult> getAccessTokenPage(@Valid OAuth2AccessTokenPageReqVO reqVO) { - PageResult pageResult = oauth2TokenService.getAccessTokenPage(reqVO); - return success(BeanUtils.toBean(pageResult, OAuth2AccessTokenRespVO.class)); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除访问令牌") - @Parameter(name = "accessToken", description = "访问令牌", required = true, example = "tudou") - @PreAuthorize("@ss.hasPermission('system:oauth2-token:delete')") - public CommonResult deleteAccessToken(@RequestParam("accessToken") String accessToken) { - authService.logout(accessToken, LoginLogTypeEnum.LOGOUT_DELETE.getType()); - return success(true); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http deleted file mode 100644 index 9e76c7c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.http +++ /dev/null @@ -1,14 +0,0 @@ -### 请求 /system/oauth2/user/get 接口 => 成功 -GET {{baseUrl}}/system/oauth2/user/get -Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d -tenant-id: {{adminTenantId}} - -### 请求 /system/oauth2/user/update 接口 => 成功 -PUT {{baseUrl}}/system/oauth2/user/update -Content-Type: application/json -Authorization: Bearer 47f9c74ec11041f193b777ebb95c3b0d -tenant-id: {{adminTenantId}} - -{ - "nickname": "芋道源码" -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.java deleted file mode 100644 index 5b3942d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2UserController.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.user.OAuth2UserInfoRespVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.user.OAuth2UserUpdateReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import cn.iocoder.yudao.module.system.service.dept.PostService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -/** - * 提供给外部应用调用为主 - * - * 1. 在 getUserInfo 方法上,添加 @PreAuthorize("@ss.hasScope('user.read')") 注解,声明需要满足 scope = user.read - * 2. 在 updateUserInfo 方法上,添加 @PreAuthorize("@ss.hasScope('user.write')") 注解,声明需要满足 scope = user.write - * - * @author 芋道源码 - */ -@Tag(name = "管理后台 - OAuth2.0 用户") -@RestController -@RequestMapping("/system/oauth2/user") -@Validated -@Slf4j -public class OAuth2UserController { - - @Resource - private AdminUserService userService; - @Resource - private DeptService deptService; - @Resource - private PostService postService; - - @GetMapping("/get") - @Operation(summary = "获得用户基本信息") - @PreAuthorize("@ss.hasScope('user.read')") // - public CommonResult getUserInfo() { - // 获得用户基本信息 - AdminUserDO user = userService.getUser(getLoginUserId()); - OAuth2UserInfoRespVO resp = BeanUtils.toBean(user, OAuth2UserInfoRespVO.class); - // 获得部门信息 - if (user.getDeptId() != null) { - DeptDO dept = deptService.getDept(user.getDeptId()); - resp.setDept(BeanUtils.toBean(dept, OAuth2UserInfoRespVO.Dept.class)); - } - // 获得岗位信息 - if (CollUtil.isNotEmpty(user.getPostIds())) { - List posts = postService.getPostList(user.getPostIds()); - resp.setPosts(BeanUtils.toBean(posts, OAuth2UserInfoRespVO.Post.class)); - } - return success(resp); - } - - @PutMapping("/update") - @Operation(summary = "更新用户基本信息") - @PreAuthorize("@ss.hasScope('user.write')") - public CommonResult updateUserInfo(@Valid @RequestBody OAuth2UserUpdateReqVO reqVO) { - // 这里将 UserProfileUpdateReqVO =》UserProfileUpdateReqVO 对象,实现接口的复用。 - // 主要是,AdminUserService 没有自己的 BO 对象,所以复用只能这么做 - userService.updateUserProfile(getLoginUserId(), BeanUtils.toBean(reqVO, UserProfileUpdateReqVO.class)); - return success(true); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java deleted file mode 100755 index f10f15a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientPageReqVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import cn.iocoder.yudao.framework.common.pojo.PageParam; - -@Schema(description = "管理后台 - OAuth2 客户端分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class OAuth2ClientPageReqVO extends PageParam { - - @Schema(description = "应用名,模糊匹配", example = "土豆") - private String name; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", example = "1") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java deleted file mode 100755 index acd26c7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientRespVO.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - OAuth2 客户端 Response VO") -@Data -public class OAuth2ClientRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") - private String clientId; - - @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "fan") - private String secret; - - @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") - private String name; - - @Schema(description = "应用图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png") - private String logo; - - @Schema(description = "应用描述", example = "我是一个应用") - private String description; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "访问令牌的有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "8640") - private Integer accessTokenValiditySeconds; - - @Schema(description = "刷新令牌的有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "8640000") - private Integer refreshTokenValiditySeconds; - - @Schema(description = "可重定向的 URI 地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") - private List redirectUris; - - @Schema(description = "授权类型,参见 OAuth2GrantTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "password") - private List authorizedGrantTypes; - - @Schema(description = "授权范围", example = "user_info") - private List scopes; - - @Schema(description = "自动通过的授权范围", example = "user_info") - private List autoApproveScopes; - - @Schema(description = "权限", example = "system:user:query") - private List authorities; - - @Schema(description = "资源", example = "1024") - private List resourceIds; - - @Schema(description = "附加信息", example = "{yunai: true}") - private String additionalInformation; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientSaveReqVO.java deleted file mode 100755 index 184d81d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/client/OAuth2ClientSaveReqVO.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; -import org.hibernate.validator.constraints.URL; - -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import java.util.List; - -@Schema(description = "管理后台 - OAuth2 客户端创建/修改 Request VO") -@Data -public class OAuth2ClientSaveReqVO { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") - @NotNull(message = "客户端编号不能为空") - private String clientId; - - @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "fan") - @NotNull(message = "客户端密钥不能为空") - private String secret; - - @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") - @NotNull(message = "应用名不能为空") - private String name; - - @Schema(description = "应用图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png") - @NotNull(message = "应用图标不能为空") - @URL(message = "应用图标的地址不正确") - private String logo; - - @Schema(description = "应用描述", example = "我是一个应用") - private String description; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - private Integer status; - - @Schema(description = "访问令牌的有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "8640") - @NotNull(message = "访问令牌的有效期不能为空") - private Integer accessTokenValiditySeconds; - - @Schema(description = "刷新令牌的有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "8640000") - @NotNull(message = "刷新令牌的有效期不能为空") - private Integer refreshTokenValiditySeconds; - - @Schema(description = "可重定向的 URI 地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn") - @NotNull(message = "可重定向的 URI 地址不能为空") - private List<@NotEmpty(message = "重定向的 URI 不能为空") @URL(message = "重定向的 URI 格式不正确") String> redirectUris; - - @Schema(description = "授权类型,参见 OAuth2GrantTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "password") - @NotNull(message = "授权类型不能为空") - private List authorizedGrantTypes; - - @Schema(description = "授权范围", example = "user_info") - private List scopes; - - @Schema(description = "自动通过的授权范围", example = "user_info") - private List autoApproveScopes; - - @Schema(description = "权限", example = "system:user:query") - private List authorities; - - @Schema(description = "资源", example = "1024") - private List resourceIds; - - @Schema(description = "附加信息", example = "{yunai: true}") - private String additionalInformation; - - @AssertTrue(message = "附加信息必须是 JSON 格式") - public boolean isAdditionalInformationJson() { - return StrUtil.isEmpty(additionalInformation) || JsonUtils.isJson(additionalInformation); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java deleted file mode 100644 index 1cdc6cb..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAccessTokenRespVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Schema(description = "管理后台 - 【开放接口】访问令牌 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2OpenAccessTokenRespVO { - - @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") - @JsonProperty("access_token") - private String accessToken; - - @Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice") - @JsonProperty("refresh_token") - private String refreshToken; - - @Schema(description = "令牌类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "bearer") - @JsonProperty("token_type") - private String tokenType; - - @Schema(description = "过期时间,单位:秒", requiredMode = Schema.RequiredMode.REQUIRED, example = "42430") - @JsonProperty("expires_in") - private Long expiresIn; - - @Schema(description = "授权范围,如果多个授权范围,使用空格分隔", example = "user_info") - private String scope; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java deleted file mode 100644 index 2aa3d77..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenAuthorizeInfoRespVO.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Schema(description = "管理后台 - 授权页的信息 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2OpenAuthorizeInfoRespVO { - - /** - * 客户端 - */ - private Client client; - - @Schema(description = "scope 的选中信息,使用 List 保证有序性,Key 是 scope,Value 为是否选中", requiredMode = Schema.RequiredMode.REQUIRED) - private List> scopes; - - @Data - @NoArgsConstructor - @AllArgsConstructor - public static class Client { - - @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "土豆") - private String name; - - @Schema(description = "应用图标", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.png") - private String logo; - - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java deleted file mode 100644 index cbee613..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/open/OAuth2OpenCheckTokenRespVO.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open; - -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Schema(description = "管理后台 - 【开放接口】校验令牌 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2OpenCheckTokenRespVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") - @JsonProperty("user_id") - private Long userId; - @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @JsonProperty("user_type") - private Integer userType; - @Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @JsonProperty("tenant_id") - private Long tenantId; - - @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "car") - @JsonProperty("client_id") - private String clientId; - @Schema(description = "授权范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "user_info") - private List scopes; - - @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") - @JsonProperty("access_token") - private String accessToken; - - @Schema(description = "过期时间,时间戳 / 1000,即单位:秒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1593092157") - private Long exp; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenPageReqVO.java deleted file mode 100644 index 0f932ee..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenPageReqVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Schema(description = "管理后台 - 访问令牌分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -public class OAuth2AccessTokenPageReqVO extends PageParam { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") - private Long userId; - - @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer userType; - - @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private String clientId; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenRespVO.java deleted file mode 100644 index 362a460..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/token/OAuth2AccessTokenRespVO.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 访问令牌 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2AccessTokenRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "访问令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "tudou") - private String accessToken; - - @Schema(description = "刷新令牌", requiredMode = Schema.RequiredMode.REQUIRED, example = "nice") - private String refreshToken; - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") - private Long userId; - - @Schema(description = "用户类型,参见 UserTypeEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer userType; - - @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private String clientId; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime expiresTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserInfoRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserInfoRespVO.java deleted file mode 100644 index 69a905d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserInfoRespVO.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.user; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -@Schema(description = "管理后台 - OAuth2 获得用户基本信息 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2UserInfoRespVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - private String username; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String nickname; - - @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") - private String email; - @Schema(description = "手机号码", example = "15601691300") - private String mobile; - - @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") - private Integer sex; - - @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") - private String avatar; - - /** - * 所在部门 - */ - private Dept dept; - - /** - * 所属岗位数组 - */ - private List posts; - - @Schema(description = "部门") - @Data - public static class Dept { - - @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部") - private String name; - - } - - @Schema(description = "岗位") - @Data - public static class Post { - - @Schema(description = "岗位编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "岗位名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "开发") - private String name; - - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserUpdateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserUpdateReqVO.java deleted file mode 100644 index e51ab20..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/vo/user/OAuth2UserUpdateReqVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.user; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.hibernate.validator.constraints.Length; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.Size; - -@Schema(description = "管理后台 - OAuth2 更新用户基本信息 Request VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2UserUpdateReqVO { - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - @Size(max = 30, message = "用户昵称长度不能超过 30 个字符") - private String nickname; - - @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") - @Email(message = "邮箱格式不正确") - @Size(max = 50, message = "邮箱长度不能超过 50 个字符") - private String email; - - @Schema(description = "手机号码", example = "15601691300") - @Length(min = 11, max = 11, message = "手机号长度必须 11 位") - private String mobile; - - @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") - private Integer sex; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.http deleted file mode 100644 index fbaff1e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.http +++ /dev/null @@ -1,4 +0,0 @@ -### 请求 /menu/list 接口 => 成功 -GET {{baseUrl}}/system/menu/list -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java deleted file mode 100644 index 824fe41..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/MenuController.java +++ /dev/null @@ -1,88 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuRespVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSimpleRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import cn.iocoder.yudao.module.system.service.permission.MenuService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.Comparator; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 菜单") -@RestController -@RequestMapping("/system/menu") -@Validated -public class MenuController { - - @Resource - private MenuService menuService; - - @PostMapping("/create") - @Operation(summary = "创建菜单") - @PreAuthorize("@ss.hasPermission('system:menu:create')") - public CommonResult createMenu(@Valid @RequestBody MenuSaveVO createReqVO) { - Long menuId = menuService.createMenu(createReqVO); - return success(menuId); - } - - @PutMapping("/update") - @Operation(summary = "修改菜单") - @PreAuthorize("@ss.hasPermission('system:menu:update')") - public CommonResult updateMenu(@Valid @RequestBody MenuSaveVO updateReqVO) { - menuService.updateMenu(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除菜单") - @Parameter(name = "id", description = "菜单编号", required= true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:menu:delete')") - public CommonResult deleteMenu(@RequestParam("id") Long id) { - menuService.deleteMenu(id); - return success(true); - } - - @GetMapping("/list") - @Operation(summary = "获取菜单列表", description = "用于【菜单管理】界面") - @PreAuthorize("@ss.hasPermission('system:menu:query')") - public CommonResult> getMenuList(MenuListReqVO reqVO) { - List list = menuService.getMenuList(reqVO); - list.sort(Comparator.comparing(MenuDO::getSort)); - return success(BeanUtils.toBean(list, MenuRespVO.class)); - } - - @GetMapping({"/list-all-simple", "simple-list"}) - @Operation(summary = "获取菜单精简信息列表", - description = "只包含被开启的菜单,用于【角色分配菜单】功能的选项。在多租户的场景下,会只返回租户所在套餐有的菜单") - public CommonResult> getSimpleMenuList() { - List list = menuService.getMenuListByTenant( - new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus())); - list = menuService.filterDisableMenus(list); - list.sort(Comparator.comparing(MenuDO::getSort)); - return success(BeanUtils.toBean(list, MenuSimpleRespVO.class)); - } - - @GetMapping("/get") - @Operation(summary = "获取菜单信息") - @PreAuthorize("@ss.hasPermission('system:menu:query')") - public CommonResult getMenu(Long id) { - MenuDO menu = menuService.getMenu(id); - return success(BeanUtils.toBean(menu, MenuRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java deleted file mode 100644 index a7b7325..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleDataScopeReqVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission.PermissionAssignRoleMenuReqVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission.PermissionAssignUserRoleReqVO; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import cn.iocoder.yudao.module.system.service.tenant.TenantService; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -/** - * 权限 Controller,提供赋予用户、角色的权限的 API 接口 - * - * @author 芋道源码 - */ -@Tag(name = "管理后台 - 权限") -@RestController -@RequestMapping("/system/permission") -public class PermissionController { - - @Resource - private PermissionService permissionService; - @Resource - private TenantService tenantService; - - @Operation(summary = "获得角色拥有的菜单编号") - @Parameter(name = "roleId", description = "角色编号", required = true) - @GetMapping("/list-role-menus") - @PreAuthorize("@ss.hasPermission('system:permission:assign-role-menu')") - public CommonResult> getRoleMenuList(Long roleId) { - return success(permissionService.getRoleMenuListByRoleId(roleId)); - } - - @PostMapping("/assign-role-menu") - @Operation(summary = "赋予角色菜单") - @PreAuthorize("@ss.hasPermission('system:permission:assign-role-menu')") - public CommonResult assignRoleMenu(@Validated @RequestBody PermissionAssignRoleMenuReqVO reqVO) { - // 开启多租户的情况下,需要过滤掉未开通的菜单 - tenantService.handleTenantMenu(menuIds -> reqVO.getMenuIds().removeIf(menuId -> !CollUtil.contains(menuIds, menuId))); - - // 执行菜单的分配 - permissionService.assignRoleMenu(reqVO.getRoleId(), reqVO.getMenuIds()); - return success(true); - } - - @PostMapping("/assign-role-data-scope") - @Operation(summary = "赋予角色数据权限") - @PreAuthorize("@ss.hasPermission('system:permission:assign-role-data-scope')") - public CommonResult assignRoleDataScope(@Valid @RequestBody PermissionAssignRoleDataScopeReqVO reqVO) { - permissionService.assignRoleDataScope(reqVO.getRoleId(), reqVO.getDataScope(), reqVO.getDataScopeDeptIds()); - return success(true); - } - - @Operation(summary = "获得管理员拥有的角色编号列表") - @Parameter(name = "userId", description = "用户编号", required = true) - @GetMapping("/list-user-roles") - @PreAuthorize("@ss.hasPermission('system:permission:assign-user-role')") - public CommonResult> listAdminRoles(@RequestParam("userId") Long userId) { - return success(permissionService.getUserRoleIdListByUserId(userId)); - } - - @Operation(summary = "赋予用户角色") - @PostMapping("/assign-user-role") - @PreAuthorize("@ss.hasPermission('system:permission:assign-user-role')") - public CommonResult assignUserRole(@Validated @RequestBody PermissionAssignUserRoleReqVO reqVO) { - permissionService.assignUserRole(reqVO.getUserId(), reqVO.getRoleIds()); - return success(true); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http deleted file mode 100644 index 375180a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.http +++ /dev/null @@ -1,42 +0,0 @@ -### /role/create 成功 -POST {{baseUrl}}/system/role/create -Authorization: Bearer {{token}} -Content-Type: application/json -tenant-id: {{adminTenantId}} - -{ - "name": "测试角色", - "code": "test", - "sort": 0 -} - -### /role/update 成功 -POST {{baseUrl}}/system/role/update -Authorization: Bearer {{token}} -Content-Type: application/json -tenant-id: {{adminTenantId}} - -{ - "id": 100, - "name": "测试角色", - "code": "test", - "sort": 10 -} -### /resource/delete 成功 -POST {{baseUrl}}/system/role/delete -Content-Type: application/x-www-form-urlencoded -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - -roleId=14 - -### /role/get 成功 -GET {{baseUrl}}/system/role/get?id=100 -Content-Type: application/x-www-form-urlencoded -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - -### /role/page 成功 -GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10 -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java deleted file mode 100644 index cf22687..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java +++ /dev/null @@ -1,100 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.*; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.service.permission.RoleService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.io.IOException; -import java.util.Comparator; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static java.util.Collections.singleton; - -@Tag(name = "管理后台 - 角色") -@RestController -@RequestMapping("/system/role") -@Validated -public class RoleController { - - @Resource - private RoleService roleService; - - @PostMapping("/create") - @Operation(summary = "创建角色") - @PreAuthorize("@ss.hasPermission('system:role:create')") - public CommonResult createRole(@Valid @RequestBody RoleSaveReqVO createReqVO) { - return success(roleService.createRole(createReqVO, null)); - } - - @PutMapping("/update") - @Operation(summary = "修改角色") - @PreAuthorize("@ss.hasPermission('system:role:update')") - public CommonResult updateRole(@Valid @RequestBody RoleSaveReqVO updateReqVO) { - roleService.updateRole(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除角色") - @Parameter(name = "id", description = "角色编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:role:delete')") - public CommonResult deleteRole(@RequestParam("id") Long id) { - roleService.deleteRole(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得角色信息") - @PreAuthorize("@ss.hasPermission('system:role:query')") - public CommonResult getRole(@RequestParam("id") Long id) { - RoleDO role = roleService.getRole(id); - return success(BeanUtils.toBean(role, RoleRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得角色分页") - @PreAuthorize("@ss.hasPermission('system:role:query')") - public CommonResult> getRolePage(RolePageReqVO pageReqVO) { - PageResult pageResult = roleService.getRolePage(pageReqVO); - return success(BeanUtils.toBean(pageResult, RoleRespVO.class)); - } - - @GetMapping({"/list-all-simple", "/simple-list"}) - @Operation(summary = "获取角色精简信息列表", description = "只包含被开启的角色,主要用于前端的下拉选项") - public CommonResult> getSimpleRoleList() { - List list = roleService.getRoleListByStatus(singleton(CommonStatusEnum.ENABLE.getStatus())); - list.sort(Comparator.comparing(RoleDO::getSort)); - return success(BeanUtils.toBean(list, RoleRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出角色 Excel") - @ApiAccessLog(operateType = EXPORT) - @PreAuthorize("@ss.hasPermission('system:role:export')") - public void export(HttpServletResponse response, @Validated RolePageReqVO exportReqVO) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = roleService.getRolePage(exportReqVO).getList(); - // 输出 - ExcelUtils.write(response, "角色数据.xls", "数据", RoleRespVO.class, - BeanUtils.toBean(list, RoleRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuListReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuListReqVO.java deleted file mode 100644 index b3b7f2d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuListReqVO.java +++ /dev/null @@ -1,16 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 菜单列表 Request VO") -@Data -public class MenuListReqVO { - - @Schema(description = "菜单名称,模糊匹配", example = "芋道") - private String name; - - @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuRespVO.java deleted file mode 100644 index 8dff186..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuRespVO.java +++ /dev/null @@ -1,69 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 菜单信息 Response VO") -@Data -public class MenuRespVO { - - @Schema(description = "菜单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - @NotBlank(message = "菜单名称不能为空") - @Size(max = 50, message = "菜单名称长度不能超过50个字符") - private String name; - - @Schema(description = "权限标识,仅菜单类型为按钮时,才需要传递", example = "sys:menu:add") - @Size(max = 100) - private String permission; - - @Schema(description = "类型,参见 MenuTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "菜单类型不能为空") - private Integer type; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "显示顺序不能为空") - private Integer sort; - - @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "父菜单 ID 不能为空") - private Long parentId; - - @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") - @Size(max = 200, message = "路由地址不能超过200个字符") - private String path; - - @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") - private String icon; - - @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") - @Size(max = 200, message = "组件路径不能超过255个字符") - private String component; - - @Schema(description = "组件名", example = "SystemUser") - private String componentName; - - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - private Integer status; - - @Schema(description = "是否可见", example = "false") - private Boolean visible; - - @Schema(description = "是否缓存", example = "false") - private Boolean keepAlive; - - @Schema(description = "是否总是显示", example = "false") - private Boolean alwaysShow; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSaveVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSaveVO.java deleted file mode 100644 index 9f96f19..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSaveVO.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; - -@Schema(description = "管理后台 - 菜单创建/修改 Request VO") -@Data -public class MenuSaveVO { - - @Schema(description = "菜单编号", example = "1024") - private Long id; - - @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - @NotBlank(message = "菜单名称不能为空") - @Size(max = 50, message = "菜单名称长度不能超过50个字符") - private String name; - - @Schema(description = "权限标识,仅菜单类型为按钮时,才需要传递", example = "sys:menu:add") - @Size(max = 100) - private String permission; - - @Schema(description = "类型,参见 MenuTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "菜单类型不能为空") - private Integer type; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "显示顺序不能为空") - private Integer sort; - - @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "父菜单 ID 不能为空") - private Long parentId; - - @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post") - @Size(max = 200, message = "路由地址不能超过200个字符") - private String path; - - @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list") - private String icon; - - @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index") - @Size(max = 200, message = "组件路径不能超过255个字符") - private String component; - - @Schema(description = "组件名", example = "SystemUser") - private String componentName; - - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - private Integer status; - - @Schema(description = "是否可见", example = "false") - private Boolean visible; - - @Schema(description = "是否缓存", example = "false") - private Boolean keepAlive; - - @Schema(description = "是否总是显示", example = "false") - private Boolean alwaysShow; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSimpleRespVO.java deleted file mode 100644 index bb94601..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/menu/MenuSimpleRespVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 菜单精简信息 Response VO") -@Data -public class MenuSimpleRespVO { - - @Schema(description = "菜单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String name; - - @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long parentId; - - @Schema(description = "类型,参见 MenuTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer type; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java deleted file mode 100644 index b889a04..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleDataScopeReqVO.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotNull; -import java.util.Collections; -import java.util.Set; - -@Schema(description = "管理后台 - 赋予角色数据权限 Request VO") -@Data -public class PermissionAssignRoleDataScopeReqVO { - - @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "角色编号不能为空") - private Long roleId; - - @Schema(description = "数据范围,参见 DataScopeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "数据范围不能为空") - @InEnum(value = DataScopeEnum.class, message = "数据范围必须是 {value}") - private Integer dataScope; - - @Schema(description = "部门编号列表,只有范围类型为 DEPT_CUSTOM 时,该字段才需要", example = "1,3,5") - private Set dataScopeDeptIds = Collections.emptySet(); // 兜底 - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleMenuReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleMenuReqVO.java deleted file mode 100644 index cd04c5b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignRoleMenuReqVO.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotNull; -import java.util.Collections; -import java.util.Set; - -@Schema(description = "管理后台 - 赋予角色菜单 Request VO") -@Data -public class PermissionAssignRoleMenuReqVO { - - @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "角色编号不能为空") - private Long roleId; - - @Schema(description = "菜单编号列表", example = "1,3,5") - private Set menuIds = Collections.emptySet(); // 兜底 - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignUserRoleReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignUserRoleReqVO.java deleted file mode 100644 index bc97dcb..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/permission/PermissionAssignUserRoleReqVO.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotNull; -import java.util.Collections; -import java.util.Set; - -@Schema(description = "管理后台 - 赋予用户角色 Request VO") -@Data -public class PermissionAssignUserRoleReqVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "用户编号不能为空") - private Long userId; - - @Schema(description = "角色编号列表", example = "1,3,5") - private Set roleIds = Collections.emptySet(); // 兜底 - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RolePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RolePageReqVO.java deleted file mode 100644 index c871274..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RolePageReqVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 角色分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -public class RolePageReqVO extends PageParam { - - @Schema(description = "角色名称,模糊匹配", example = "芋道") - private String name; - - @Schema(description = "角色标识,模糊匹配", example = "yudao") - private String code; - - @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") - private Integer status; - - @Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java deleted file mode 100644 index 89f80c6..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleRespVO.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.Set; - -@Schema(description = "管理后台 - 角色信息 Response VO") -@Data -@ExcelIgnoreUnannotated -public class RoleRespVO { - - @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("角色序号") - private Long id; - - @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "管理员") - @ExcelProperty("角色名称") - private String name; - - @Schema(description = "角色标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "admin") - @NotBlank(message = "角色标志不能为空") - @ExcelProperty("角色标志") - private String code; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("角色排序") - private Integer sort; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "角色状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - - @Schema(description = "角色类型,参见 RoleTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer type; - - @Schema(description = "备注", example = "我是一个角色") - private String remark; - - @Schema(description = "数据范围,参见 DataScopeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "数据范围", converter = DictConvert.class) - @DictFormat(DictTypeConstants.DATA_SCOPE) - private Integer dataScope; - - @Schema(description = "数据范围(指定部门数组)", example = "1") - private Set dataScopeDeptIds; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java deleted file mode 100644 index 2d273f3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import com.mzt.logapi.starter.annotation.DiffLogField; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.Data; - -@Schema(description = "管理后台 - 角色创建/更新 Request VO") -@Data -public class RoleSaveReqVO { - - @Schema(description = "角色编号", example = "1") - private Long id; - - @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "管理员") - @NotBlank(message = "角色名称不能为空") - @Size(max = 30, message = "角色名称长度不能超过 30 个字符") - @DiffLogField(name = "角色名称") - private String name; - - @NotBlank(message = "角色标志不能为空") - @Size(max = 100, message = "角色标志长度不能超过 100 个字符") - @Schema(description = "角色标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ADMIN") - @DiffLogField(name = "角色标志") - private String code; - - @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "显示顺序不能为空") - @DiffLogField(name = "显示顺序") - private Integer sort; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @DiffLogField(name = "状态") - @NotNull(message = "状态不能为空") - @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") - private Integer status; - - @Schema(description = "备注", example = "我是一个角色") - @Size(max = 500, message = "备注长度不能超过 500 个字符") - @DiffLogField(name = "备注") - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSimpleRespVO.java deleted file mode 100644 index 1e9a9df..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSimpleRespVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.permission.vo.role; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Schema(description = "管理后台 - 角色精简信息 Response VO") -@Data -public class RoleSimpleRespVO { - - @Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "角色名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String name; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java deleted file mode 100644 index 7cfc3ea..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java +++ /dev/null @@ -1,65 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; -import cn.iocoder.yudao.module.system.service.sms.SmsSendService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.annotation.security.PermitAll; -import jakarta.servlet.http.HttpServletRequest; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 短信回调") -@RestController -@RequestMapping("/system/sms/callback") -public class SmsCallbackController { - - @Resource - private SmsSendService smsSendService; - - @PostMapping("/aliyun") - @PermitAll - @TenantIgnore - @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/document_detail/120998.html 文档") - public CommonResult receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable { - String text = ServletUtils.getBody(request); - smsSendService.receiveSmsStatus(SmsChannelEnum.ALIYUN.getCode(), text); - return success(true); - } - - @PostMapping("/tencent") - @PermitAll - @TenantIgnore - @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/52077 文档") - public CommonResult receiveTencentSmsStatus(HttpServletRequest request) throws Throwable { - String text = ServletUtils.getBody(request); - smsSendService.receiveSmsStatus(SmsChannelEnum.TENCENT.getCode(), text); - return success(true); - } - - - @PostMapping("/huawei") - @PermitAll - @TenantIgnore - @Operation(summary = "华为云短信的回调", description = "参见 https://support.huaweicloud.com/api-msgsms/sms_05_0003.html 文档") - public CommonResult receiveHuaweiSmsStatus(@RequestBody String requestBody) throws Throwable { - smsSendService.receiveSmsStatus(SmsChannelEnum.HUAWEI.getCode(), requestBody); - return success(true); - } - - @PostMapping("/qiniu") - @PermitAll - @TenantIgnore - @Operation(summary = "七牛云短信的回调", description = "参见 https://developer.qiniu.com/sms/5910/message-push 文档") - public CommonResult receiveQiniuSmsStatus(@RequestBody String requestBody) throws Throwable { - smsSendService.receiveSmsStatus(SmsChannelEnum.QINIU.getCode(), requestBody); - return success(true); - } - -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsChannelController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsChannelController.java deleted file mode 100644 index cb26c7f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsChannelController.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelRespVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSimpleRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; -import cn.iocoder.yudao.module.system.service.sms.SmsChannelService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.Comparator; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 短信渠道") -@RestController -@RequestMapping("system/sms-channel") -public class SmsChannelController { - - @Resource - private SmsChannelService smsChannelService; - - @PostMapping("/create") - @Operation(summary = "创建短信渠道") - @PreAuthorize("@ss.hasPermission('system:sms-channel:create')") - public CommonResult createSmsChannel(@Valid @RequestBody SmsChannelSaveReqVO createReqVO) { - return success(smsChannelService.createSmsChannel(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新短信渠道") - @PreAuthorize("@ss.hasPermission('system:sms-channel:update')") - public CommonResult updateSmsChannel(@Valid @RequestBody SmsChannelSaveReqVO updateReqVO) { - smsChannelService.updateSmsChannel(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除短信渠道") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:sms-channel:delete')") - public CommonResult deleteSmsChannel(@RequestParam("id") Long id) { - smsChannelService.deleteSmsChannel(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得短信渠道") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:sms-channel:query')") - public CommonResult getSmsChannel(@RequestParam("id") Long id) { - SmsChannelDO channel = smsChannelService.getSmsChannel(id); - return success(BeanUtils.toBean(channel, SmsChannelRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得短信渠道分页") - @PreAuthorize("@ss.hasPermission('system:sms-channel:query')") - public CommonResult> getSmsChannelPage(@Valid SmsChannelPageReqVO pageVO) { - PageResult pageResult = smsChannelService.getSmsChannelPage(pageVO); - return success(BeanUtils.toBean(pageResult, SmsChannelRespVO.class)); - } - - @GetMapping({"/list-all-simple", "/simple-list"}) - @Operation(summary = "获得短信渠道精简列表", description = "包含被禁用的短信渠道") - public CommonResult> getSimpleSmsChannelList() { - List list = smsChannelService.getSmsChannelList(); - list.sort(Comparator.comparing(SmsChannelDO::getId)); - return success(BeanUtils.toBean(list, SmsChannelSimpleRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java deleted file mode 100644 index 4646e78..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsLogController.java +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; -import cn.iocoder.yudao.module.system.service.sms.SmsLogService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.io.IOException; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 短信日志") -@RestController -@RequestMapping("/system/sms-log") -@Validated -public class SmsLogController { - - @Resource - private SmsLogService smsLogService; - - @GetMapping("/page") - @Operation(summary = "获得短信日志分页") - @PreAuthorize("@ss.hasPermission('system:sms-log:query')") - public CommonResult> getSmsLogPage(@Valid SmsLogPageReqVO pageReqVO) { - PageResult pageResult = smsLogService.getSmsLogPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, SmsLogRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出短信日志 Excel") - @PreAuthorize("@ss.hasPermission('system:sms-log:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportSmsLogExcel(@Valid SmsLogPageReqVO exportReqVO, - HttpServletResponse response) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = smsLogService.getSmsLogPage(exportReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "短信日志.xls", "数据", SmsLogRespVO.class, - BeanUtils.toBean(list, SmsLogRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http deleted file mode 100644 index 3b975c3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.http +++ /dev/null @@ -1,14 +0,0 @@ -### 请求 /system/sms-template/send-sms 接口 => 成功 -POST {{baseUrl}}/system/sms-template/send-sms -Authorization: Bearer {{token}} -Content-Type: application/json -tenant-id: {{adminTenantId}} - -{ - "templateCode": "test_01", - "mobile": "15601691390", - "templateParams": { - "operation": "value01", - "code": "value02" - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java deleted file mode 100644 index 80c6082..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java +++ /dev/null @@ -1,100 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.*; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; -import cn.iocoder.yudao.module.system.service.sms.SmsTemplateService; -import cn.iocoder.yudao.module.system.service.sms.SmsSendService; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import java.io.IOException; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 短信模板") -@RestController -@RequestMapping("/system/sms-template") -public class SmsTemplateController { - - @Resource - private SmsTemplateService smsTemplateService; - @Resource - private SmsSendService smsSendService; - - @PostMapping("/create") - @Operation(summary = "创建短信模板") - @PreAuthorize("@ss.hasPermission('system:sms-template:create')") - public CommonResult createSmsTemplate(@Valid @RequestBody SmsTemplateSaveReqVO createReqVO) { - return success(smsTemplateService.createSmsTemplate(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新短信模板") - @PreAuthorize("@ss.hasPermission('system:sms-template:update')") - public CommonResult updateSmsTemplate(@Valid @RequestBody SmsTemplateSaveReqVO updateReqVO) { - smsTemplateService.updateSmsTemplate(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除短信模板") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:sms-template:delete')") - public CommonResult deleteSmsTemplate(@RequestParam("id") Long id) { - smsTemplateService.deleteSmsTemplate(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得短信模板") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:sms-template:query')") - public CommonResult getSmsTemplate(@RequestParam("id") Long id) { - SmsTemplateDO template = smsTemplateService.getSmsTemplate(id); - return success(BeanUtils.toBean(template, SmsTemplateRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得短信模板分页") - @PreAuthorize("@ss.hasPermission('system:sms-template:query')") - public CommonResult> getSmsTemplatePage(@Valid SmsTemplatePageReqVO pageVO) { - PageResult pageResult = smsTemplateService.getSmsTemplatePage(pageVO); - return success(BeanUtils.toBean(pageResult, SmsTemplateRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出短信模板 Excel") - @PreAuthorize("@ss.hasPermission('system:sms-template:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportSmsTemplateExcel(@Valid SmsTemplatePageReqVO exportReqVO, - HttpServletResponse response) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = smsTemplateService.getSmsTemplatePage(exportReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "短信模板.xls", "数据", SmsTemplateRespVO.class, - BeanUtils.toBean(list, SmsTemplateRespVO.class)); - } - - @PostMapping("/send-sms") - @Operation(summary = "发送短信") - @PreAuthorize("@ss.hasPermission('system:sms-template:send-sms')") - public CommonResult sendSms(@Valid @RequestBody SmsTemplateSendReqVO sendReqVO) { - return success(smsSendService.sendSingleSmsToAdmin(sendReqVO.getMobile(), null, - sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java deleted file mode 100644 index 2ec9b2f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelPageReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 短信渠道分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SmsChannelPageReqVO extends PageParam { - - @Schema(description = "任务状态", example = "1") - private Integer status; - - @Schema(description = "短信签名,模糊匹配", example = "芋道源码") - private String signature; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "创建时间") - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java deleted file mode 100644 index 091dae8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelRespVO.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import jakarta.validation.constraints.NotNull; -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 短信渠道 Response VO") -@Data -public class SmsChannelRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") - @NotNull(message = "短信签名不能为空") - private String signature; - - @Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN") - private String code; - - @Schema(description = "启用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "启用状态不能为空") - private Integer status; - - @Schema(description = "备注", example = "好吃!") - private String remark; - - @Schema(description = "短信 API 的账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @NotNull(message = "短信 API 的账号不能为空") - private String apiKey; - - @Schema(description = "短信 API 的密钥", example = "yuanma") - private String apiSecret; - - @Schema(description = "短信发送回调 URL", example = "https://www.iocoder.cn") - @URL(message = "回调 URL 格式不正确") - private String callbackUrl; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java deleted file mode 100644 index ade624e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSaveReqVO.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 短信渠道创建/修改 Request VO") -@Data -public class SmsChannelSaveReqVO { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") - @NotNull(message = "短信签名不能为空") - private String signature; - - @Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN") - @NotNull(message = "渠道编码不能为空") - private String code; - - @Schema(description = "启用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "启用状态不能为空") - private Integer status; - - @Schema(description = "备注", example = "好吃!") - private String remark; - - @Schema(description = "短信 API 的账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @NotNull(message = "短信 API 的账号不能为空") - private String apiKey; - - @Schema(description = "短信 API 的密钥", example = "yuanma") - private String apiSecret; - - @Schema(description = "短信发送回调 URL", example = "http://www.iocoder.cn") - @URL(message = "回调 URL 格式不正确") - private String callbackUrl; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java deleted file mode 100644 index 350297c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/channel/SmsChannelSimpleRespVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 短信渠道精简 Response VO") -@Data -public class SmsChannelSimpleRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "短信签名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") - private String signature; - - @Schema(description = "渠道编码,参见 SmsChannelEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "YUN_PIAN") - private String code; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogPageReqVO.java deleted file mode 100644 index fbf9dcf..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogPageReqVO.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.log; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 短信日志分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SmsLogPageReqVO extends PageParam { - - @Schema(description = "短信渠道编号", example = "10") - private Long channelId; - - @Schema(description = "模板编号", example = "20") - private Long templateId; - - @Schema(description = "手机号", example = "15601691300") - private String mobile; - - @Schema(description = "发送状态,参见 SmsSendStatusEnum 枚举类", example = "1") - private Integer sendStatus; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "发送时间") - private LocalDateTime[] sendTime; - - @Schema(description = "接收状态,参见 SmsReceiveStatusEnum 枚举类", example = "0") - private Integer receiveStatus; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "接收时间") - private LocalDateTime[] receiveTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java deleted file mode 100644 index 77409ed..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java +++ /dev/null @@ -1,116 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.log; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.framework.excel.core.convert.JsonConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.Map; - -@Schema(description = "管理后台 - 短信日志 Response VO") -@Data -@ExcelIgnoreUnannotated -public class SmsLogRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "短信渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @ExcelProperty("短信渠道编号") - private Long channelId; - - @Schema(description = "短信渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ALIYUN") - @ExcelProperty("短信渠道编码") - private String channelCode; - - @Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20") - @ExcelProperty("模板编号") - private Long templateId; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test-01") - @ExcelProperty("模板编码") - private String templateCode; - - @Schema(description = "短信类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "短信类型", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_TEMPLATE_TYPE) - private Integer templateType; - - @Schema(description = "短信内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你的验证码是 1024") - @ExcelProperty("短信内容") - private String templateContent; - - @Schema(description = "短信参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "name,code") - @ExcelProperty(value = "短信参数", converter = JsonConvert.class) - private Map templateParams; - - @Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "SMS_207945135") - @ExcelProperty("短信 API 的模板编号") - private String apiTemplateId; - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") - @ExcelProperty("手机号") - private String mobile; - - @Schema(description = "用户编号", example = "10") - @ExcelProperty("用户编号") - private Long userId; - - @Schema(description = "用户类型", example = "1") - @ExcelProperty(value = "用户类型", converter = DictConvert.class) - @DictFormat(DictTypeConstants.USER_TYPE) - private Integer userType; - - @Schema(description = "发送状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "发送状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_SEND_STATUS) - private Integer sendStatus; - - @Schema(description = "发送时间") - @ExcelProperty("发送时间") - private LocalDateTime sendTime; - - @Schema(description = "短信 API 发送结果的编码", example = "SUCCESS") - @ExcelProperty("短信 API 发送结果的编码") - private String apiSendCode; - - @Schema(description = "短信 API 发送失败的提示", example = "成功") - @ExcelProperty("短信 API 发送失败的提示") - private String apiSendMsg; - - @Schema(description = "短信 API 发送返回的唯一请求 ID", example = "3837C6D3-B96F-428C-BBB2-86135D4B5B99") - @ExcelProperty("短信 API 发送返回的唯一请求 ID") - private String apiRequestId; - - @Schema(description = "短信 API 发送返回的序号", example = "62923244790") - @ExcelProperty("短信 API 发送返回的序号") - private String apiSerialNo; - - @Schema(description = "接收状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - @ExcelProperty(value = "接收状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_RECEIVE_STATUS) - private Integer receiveStatus; - - @Schema(description = "接收时间") - @ExcelProperty("接收时间") - private LocalDateTime receiveTime; - - @Schema(description = "API 接收结果的编码", example = "DELIVRD") - @ExcelProperty("API 接收结果的编码") - private String apiReceiveCode; - - @Schema(description = "API 接收结果的说明", example = "用户接收成功") - @ExcelProperty("API 接收结果的说明") - private String apiReceiveMsg; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplatePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplatePageReqVO.java deleted file mode 100644 index acf99e3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplatePageReqVO.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.template; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 短信模板分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SmsTemplatePageReqVO extends PageParam { - - @Schema(description = "短信签名", example = "1") - private Integer type; - - @Schema(description = "开启状态", example = "1") - private Integer status; - - @Schema(description = "模板编码,模糊匹配", example = "test_01") - private String code; - - @Schema(description = "模板内容,模糊匹配", example = "你好,{name}。你长的太{like}啦!") - private String content; - - @Schema(description = "短信 API 的模板编号,模糊匹配", example = "4383920") - private String apiTemplateId; - - @Schema(description = "短信渠道编号", example = "10") - private Long channelId; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "创建时间") - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java deleted file mode 100644 index 6b8aeca..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateRespVO.java +++ /dev/null @@ -1,69 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.template; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - 短信模板 Response VO") -@Data -@ExcelIgnoreUnannotated -public class SmsTemplateRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "短信类型,参见 SmsTemplateTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "短信签名", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_TEMPLATE_TYPE) - private Integer type; - - @Schema(description = "开启状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "开启状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") - @ExcelProperty("模板编码") - private String code; - - @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @ExcelProperty("模板名称") - private String name; - - @Schema(description = "模板内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,{name}。你长的太{like}啦!") - @ExcelProperty("模板内容") - private String content; - - @Schema(description = "参数数组", example = "name,code") - private List params; - - @Schema(description = "备注", example = "哈哈哈") - @ExcelProperty("备注") - private String remark; - - @Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4383920") - @ExcelProperty("短信 API 的模板编号") - private String apiTemplateId; - - @Schema(description = "短信渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @ExcelProperty("短信渠道编号") - private Long channelId; - - @Schema(description = "短信渠道编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "ALIYUN") - @ExcelProperty(value = "短信渠道编码", converter = DictConvert.class) - @DictFormat(DictTypeConstants.SMS_CHANNEL_CODE) - private String channelCode; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java deleted file mode 100644 index 649061f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSaveReqVO.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 短信模板创建/修改 Request VO") -@Data -public class SmsTemplateSaveReqVO { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "短信类型,参见 SmsTemplateTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "短信类型不能为空") - private Integer type; - - @Schema(description = "开启状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "开启状态不能为空") - private Integer status; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") - @NotNull(message = "模板编码不能为空") - private String code; - - @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @NotNull(message = "模板名称不能为空") - private String name; - - @Schema(description = "模板内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,{name}。你长的太{like}啦!") - @NotNull(message = "模板内容不能为空") - private String content; - - @Schema(description = "备注", example = "哈哈哈") - private String remark; - - @Schema(description = "短信 API 的模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4383920") - @NotNull(message = "短信 API 的模板编号不能为空") - private String apiTemplateId; - - @Schema(description = "短信渠道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @NotNull(message = "短信渠道编号不能为空") - private Long channelId; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSendReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSendReqVO.java deleted file mode 100644 index 12a36fd..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSendReqVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.sms.vo.template; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotNull; -import java.util.Map; - -@Schema(description = "管理后台 - 短信模板的发送 Request VO") -@Data -public class SmsTemplateSendReqVO { - - @Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300") - @NotNull(message = "手机号不能为空") - private String mobile; - - @Schema(description = "模板编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "test_01") - @NotNull(message = "模板编码不能为空") - private String templateCode; - - @Schema(description = "模板参数") - private Map templateParams; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http deleted file mode 100644 index 9909b51..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.http +++ /dev/null @@ -1,20 +0,0 @@ -### 请求 /system/social-client/send-subscribe-message 接口 => 发送测试订阅消息 -POST {{baseUrl}}/system/social-client/send-subscribe-message -Authorization: Bearer {{token}} -Content-Type: application/json -#Authorization: Bearer test100 -tenant-id: {{adminTenantId}} - -{ - "userId": 247, - "userType": 1, - "socialType": 34, - "templateTitle": "充值成功通知", - "page": "", - "messages": { - "character_string1":"5616122165165", - "amount2":"1000.00", - "time3":"2024-01-01 10:10:10", - "phrase4": "充值成功" - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.java deleted file mode 100644 index e91a3d8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialClientController.java +++ /dev/null @@ -1,83 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.socail; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.api.social.SocialClientApi; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientRespVO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; -import cn.iocoder.yudao.module.system.service.social.SocialClientService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 社交客户端") -@RestController -@RequestMapping("/system/social-client") -@Validated -public class SocialClientController { - - @Resource - private SocialClientService socialClientService; - @Resource - private SocialClientApi socialClientApi; - - @PostMapping("/create") - @Operation(summary = "创建社交客户端") - @PreAuthorize("@ss.hasPermission('system:social-client:create')") - public CommonResult createSocialClient(@Valid @RequestBody SocialClientSaveReqVO createReqVO) { - return success(socialClientService.createSocialClient(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新社交客户端") - @PreAuthorize("@ss.hasPermission('system:social-client:update')") - public CommonResult updateSocialClient(@Valid @RequestBody SocialClientSaveReqVO updateReqVO) { - socialClientService.updateSocialClient(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除社交客户端") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:social-client:delete')") - public CommonResult deleteSocialClient(@RequestParam("id") Long id) { - socialClientService.deleteSocialClient(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得社交客户端") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:social-client:query')") - public CommonResult getSocialClient(@RequestParam("id") Long id) { - SocialClientDO client = socialClientService.getSocialClient(id); - return success(BeanUtils.toBean(client, SocialClientRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得社交客户端分页") - @PreAuthorize("@ss.hasPermission('system:social-client:query')") - public CommonResult> getSocialClientPage(@Valid SocialClientPageReqVO pageVO) { - PageResult pageResult = socialClientService.getSocialClientPage(pageVO); - return success(BeanUtils.toBean(pageResult, SocialClientRespVO.class)); - } - - @PostMapping("/send-subscribe-message") - @Operation(summary = "发送订阅消息") // 用于测试 - @PreAuthorize("@ss.hasPermission('system:social-client:query')") - public void sendSubscribeMessage(@RequestBody SocialWxaSubscribeMessageSendReqDTO reqDTO) { - socialClientApi.sendWxaSubscribeMessage(reqDTO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialUserController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialUserController.java deleted file mode 100644 index 643e465..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/SocialUserController.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.socail; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserRespVO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserUnbindReqVO; -import cn.iocoder.yudao.module.system.convert.social.SocialUserConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; -import cn.iocoder.yudao.module.system.service.social.SocialUserService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 社交用户") -@RestController -@RequestMapping("/system/social-user") -@Validated -public class SocialUserController { - - @Resource - private SocialUserService socialUserService; - - @PostMapping("/bind") - @Operation(summary = "社交绑定,使用 code 授权码") - public CommonResult socialBind(@RequestBody @Valid SocialUserBindReqVO reqVO) { - socialUserService.bindSocialUser(SocialUserConvert.INSTANCE.convert( - getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO)); - return CommonResult.success(true); - } - - @DeleteMapping("/unbind") - @Operation(summary = "取消社交绑定") - public CommonResult socialUnbind(@RequestBody SocialUserUnbindReqVO reqVO) { - socialUserService.unbindSocialUser(getLoginUserId(), UserTypeEnum.ADMIN.getValue(), reqVO.getType(), reqVO.getOpenid()); - return CommonResult.success(true); - } - - @GetMapping("/get-bind-list") - @Operation(summary = "获得绑定社交用户列表") - public CommonResult> getBindSocialUserList() { - List list = socialUserService.getSocialUserList(getLoginUserId(), UserTypeEnum.ADMIN.getValue()); - return success(convertList(list, socialUser -> new SocialUserRespVO() // 返回精简信息 - .setId(socialUser.getId()).setType(socialUser.getType()).setOpenid(socialUser.getOpenid()) - .setNickname(socialUser.getNickname()).setAvatar(socialUser.getNickname()))); - } - - // ==================== 社交用户 CRUD ==================== - - @GetMapping("/get") - @Operation(summary = "获得社交用户") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:social-user:query')") - public CommonResult getSocialUser(@RequestParam("id") Long id) { - SocialUserDO socialUser = socialUserService.getSocialUser(id); - return success(BeanUtils.toBean(socialUser, SocialUserRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得社交用户分页") - @PreAuthorize("@ss.hasPermission('system:social-user:query')") - public CommonResult> getSocialUserPage(@Valid SocialUserPageReqVO pageVO) { - PageResult pageResult = socialUserService.getSocialUserPage(pageVO); - return success(BeanUtils.toBean(pageResult, SocialUserRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java deleted file mode 100644 index a225530..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientPageReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.socail.vo.client; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -@Schema(description = "管理后台 - 社交客户端分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SocialClientPageReqVO extends PageParam { - - @Schema(description = "应用名", example = "yudao商城") - private String name; - - @Schema(description = "社交平台的类型", example = "31") - private Integer socialType; - - @Schema(description = "用户类型", example = "2") - private Integer userType; - - @Schema(description = "客户端编号", example = "145442115") - private String clientId; - - @Schema(description = "状态", example = "1") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java deleted file mode 100644 index 900f363..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientRespVO.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.socail.vo.client; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 社交客户端 Response VO") -@Data -public class SocialClientRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "27162") - private Long id; - - @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao商城") - private String name; - - @Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "31") - private Integer socialType; - - @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer userType; - - @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "wwd411c69a39ad2e54") - private String clientId; - - @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "peter") - private String clientSecret; - - @Schema(description = "授权方的网页应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000045") - private String agentId; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java deleted file mode 100644 index 9549b82..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/client/SocialClientSaveReqVO.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.socail.vo.client; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotNull; -import java.util.Objects; - -@Schema(description = "管理后台 - 社交客户端创建/修改 Request VO") -@Data -public class SocialClientSaveReqVO { - - @Schema(description = "编号", example = "27162") - private Long id; - - @Schema(description = "应用名", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao商城") - @NotNull(message = "应用名不能为空") - private String name; - - @Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "31") - @NotNull(message = "社交平台的类型不能为空") - @InEnum(SocialTypeEnum.class) - private Integer socialType; - - @Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - @NotNull(message = "用户类型不能为空") - @InEnum(UserTypeEnum.class) - private Integer userType; - - @Schema(description = "客户端编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "wwd411c69a39ad2e54") - @NotNull(message = "客户端编号不能为空") - private String clientId; - - @Schema(description = "客户端密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "peter") - @NotNull(message = "客户端密钥不能为空") - private String clientSecret; - - @Schema(description = "授权方的网页应用编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2000045") - private String agentId; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @AssertTrue(message = "agentId 不能为空") - @JsonIgnore - public boolean isAgentIdValid() { - // 如果是企业微信,必须填写 agentId 属性 - return !Objects.equals(socialType, SocialTypeEnum.WECHAT_ENTERPRISE.getType()) - || !StrUtil.isEmpty(agentId); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java deleted file mode 100644 index 5ed0042..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserBindReqVO.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user; - -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 社交绑定 Request VO,使用 code 授权码") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class SocialUserBindReqVO { - - @Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(SocialTypeEnum.class) - @NotNull(message = "社交平台的类型不能为空") - private Integer type; - - @Schema(description = "授权码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "授权码不能为空") - private String code; - - @Schema(description = "state", requiredMode = Schema.RequiredMode.REQUIRED, example = "9b2ffbc1-7425-4155-9894-9d5c08541d62") - @NotEmpty(message = "state 不能为空") - private String state; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java deleted file mode 100644 index 3297db4..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserPageReqVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 社交用户分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class SocialUserPageReqVO extends PageParam { - - @Schema(description = "社交平台的类型", example = "30") - private Integer type; - - @Schema(description = "用户昵称", example = "李四") - private String nickname; - - @Schema(description = "社交 openid", example = "oz-Jdt0kd_jdhUxJHQdBJMlOFN7w") - private String openid; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java deleted file mode 100644 index fd37703..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserRespVO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 社交用户 Response VO") -@Data -public class SocialUserRespVO { - - @Schema(description = "主键(自增策略)", requiredMode = Schema.RequiredMode.REQUIRED, example = "14569") - private Long id; - - @Schema(description = "社交平台的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "30") - private Integer type; - - @Schema(description = "社交 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") - private String openid; - - @Schema(description = "社交 token", requiredMode = Schema.RequiredMode.REQUIRED, example = "666") - private String token; - - @Schema(description = "原始 Token 数据,一般是 JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - private String rawTokenInfo; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - private String nickname; - - @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") - private String avatar; - - @Schema(description = "原始用户数据,一般是 JSON 格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - private String rawUserInfo; - - @Schema(description = "最后一次的认证 code", requiredMode = Schema.RequiredMode.REQUIRED, example = "666666") - private String code; - - @Schema(description = "最后一次的认证 state", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - private String state; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime updateTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java deleted file mode 100644 index e5b1961..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/socail/vo/user/SocialUserUnbindReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.socail.vo.user; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 取消社交绑定 Request VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -@Builder -public class SocialUserUnbindReqVO { - - @Schema(description = "社交平台的类型,参见 UserSocialTypeEnum 枚举值", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(SocialTypeEnum.class) - @NotNull(message = "社交平台的类型不能为空") - private Integer type; - - @Schema(description = "社交用户的 openid", requiredMode = Schema.RequiredMode.REQUIRED, example = "IPRmJ0wvBptiPIlGEZiPewGwiEiE") - @NotEmpty(message = "社交用户的 openid 不能为空") - private String openid; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http deleted file mode 100644 index 38aa594..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http +++ /dev/null @@ -1,21 +0,0 @@ -### 获取租户编号 /admin-api/system/get-id-by-name -GET {{baseUrl}}/system/tenant/get-id-by-name?name=芋道源码 - -### 创建租户 /admin-api/system/tenant/create -POST {{baseUrl}}/system/tenant/create -Content-Type: application/json -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} - -{ - "name": "芋道", - "contactName": "芋艿", - "contactMobile": "15601691300", - "status": 0, - "domain": "https://www.iocoder.cn", - "packageId": 110, - "expireTime": 1699545600000, - "accountCount": 20, - "username": "admin", - "password": "123321" -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java deleted file mode 100644 index 8b4f5c7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.java +++ /dev/null @@ -1,127 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant; - -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; -import cn.iocoder.yudao.module.system.service.tenant.TenantService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.annotation.security.PermitAll; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.*; - -import java.io.IOException; -import java.util.List; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - 租户") -@RestController -@RequestMapping("/system/tenant") -public class TenantController { - - @Resource - private TenantService tenantService; - - @GetMapping("/get-id-by-name") - @PermitAll - @TenantIgnore - @Operation(summary = "使用租户名,获得租户编号", description = "登录界面,根据用户的租户名,获得租户编号") - @Parameter(name = "name", description = "租户名", required = true, example = "1024") - public CommonResult getTenantIdByName(@RequestParam("name") String name) { - TenantDO tenant = tenantService.getTenantByName(name); - return success(tenant != null ? tenant.getId() : null); - } - - @GetMapping({ "simple-list" }) - @PermitAll - @TenantIgnore - @Operation(summary = "获取租户精简信息列表", description = "只包含被开启的租户,用于【首页】功能的选择租户选项") - public CommonResult> getTenantSimpleList() { - List list = tenantService.getTenantListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(convertList(list, tenantDO -> - new TenantRespVO().setId(tenantDO.getId()).setName(tenantDO.getName()))); - } - - @GetMapping("/get-by-website") - @PermitAll - @TenantIgnore - @Operation(summary = "使用域名,获得租户信息", description = "登录界面,根据用户的域名,获得租户信息") - @Parameter(name = "website", description = "域名", required = true, example = "www.iocoder.cn") - public CommonResult getTenantByWebsite(@RequestParam("website") String website) { - TenantDO tenant = tenantService.getTenantByWebsite(website); - if (tenant == null || CommonStatusEnum.isDisable(tenant.getStatus())) { - return success(null); - } - return success(new TenantRespVO().setId(tenant.getId()).setName(tenant.getName())); - } - - @PostMapping("/create") - @Operation(summary = "创建租户") - @PreAuthorize("@ss.hasPermission('system:tenant:create')") - public CommonResult createTenant(@Valid @RequestBody TenantSaveReqVO createReqVO) { - return success(tenantService.createTenant(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新租户") - @PreAuthorize("@ss.hasPermission('system:tenant:update')") - public CommonResult updateTenant(@Valid @RequestBody TenantSaveReqVO updateReqVO) { - tenantService.updateTenant(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除租户") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:tenant:delete')") - public CommonResult deleteTenant(@RequestParam("id") Long id) { - tenantService.deleteTenant(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得租户") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:tenant:query')") - public CommonResult getTenant(@RequestParam("id") Long id) { - TenantDO tenant = tenantService.getTenant(id); - return success(BeanUtils.toBean(tenant, TenantRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得租户分页") - @PreAuthorize("@ss.hasPermission('system:tenant:query')") - public CommonResult> getTenantPage(@Valid TenantPageReqVO pageVO) { - PageResult pageResult = tenantService.getTenantPage(pageVO); - return success(BeanUtils.toBean(pageResult, TenantRespVO.class)); - } - - @GetMapping("/export-excel") - @Operation(summary = "导出租户 Excel") - @PreAuthorize("@ss.hasPermission('system:tenant:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportTenantExcel(@Valid TenantPageReqVO exportReqVO, HttpServletResponse response) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = tenantService.getTenantPage(exportReqVO).getList(); - // 导出 Excel - ExcelUtils.write(response, "租户.xls", "数据", TenantRespVO.class, - BeanUtils.toBean(list, TenantRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantPackageController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantPackageController.java deleted file mode 100644 index ad9ab3d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantPackageController.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.*; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; -import cn.iocoder.yudao.module.system.service.tenant.TenantPackageService; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Operation; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - 租户套餐") -@RestController -@RequestMapping("/system/tenant-package") -@Validated -public class TenantPackageController { - - @Resource - private TenantPackageService tenantPackageService; - - @PostMapping("/create") - @Operation(summary = "创建租户套餐") - @PreAuthorize("@ss.hasPermission('system:tenant-package:create')") - public CommonResult createTenantPackage(@Valid @RequestBody TenantPackageSaveReqVO createReqVO) { - return success(tenantPackageService.createTenantPackage(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新租户套餐") - @PreAuthorize("@ss.hasPermission('system:tenant-package:update')") - public CommonResult updateTenantPackage(@Valid @RequestBody TenantPackageSaveReqVO updateReqVO) { - tenantPackageService.updateTenantPackage(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除租户套餐") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('system:tenant-package:delete')") - public CommonResult deleteTenantPackage(@RequestParam("id") Long id) { - tenantPackageService.deleteTenantPackage(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得租户套餐") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:tenant-package:query')") - public CommonResult getTenantPackage(@RequestParam("id") Long id) { - TenantPackageDO tenantPackage = tenantPackageService.getTenantPackage(id); - return success(BeanUtils.toBean(tenantPackage, TenantPackageRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得租户套餐分页") - @PreAuthorize("@ss.hasPermission('system:tenant-package:query')") - public CommonResult> getTenantPackagePage(@Valid TenantPackagePageReqVO pageVO) { - PageResult pageResult = tenantPackageService.getTenantPackagePage(pageVO); - return success(BeanUtils.toBean(pageResult, TenantPackageRespVO.class)); - } - - @GetMapping({"/get-simple-list", "simple-list"}) - @Operation(summary = "获取租户套餐精简信息列表", description = "只包含被开启的租户套餐,主要用于前端的下拉选项") - public CommonResult> getTenantPackageList() { - List list = tenantPackageService.getTenantPackageListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(BeanUtils.toBean(list, TenantPackageSimpleRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackagePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackagePageReqVO.java deleted file mode 100755 index 525a5da..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackagePageReqVO.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 租户套餐分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class TenantPackagePageReqVO extends PageParam { - - @Schema(description = "套餐名", example = "VIP") - private String name; - - @Schema(description = "状态", example = "1") - private Integer status; - - @Schema(description = "备注", example = "好") - private String remark; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "创建时间") - private LocalDateTime[] createTime; -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageRespVO.java deleted file mode 100755 index 16ffd81..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageRespVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.Set; - -@Schema(description = "管理后台 - 租户套餐 Response VO") -@Data -public class TenantPackageRespVO { - - @Schema(description = "套餐编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "套餐名", requiredMode = Schema.RequiredMode.REQUIRED, example = "VIP") - private String name; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "备注", example = "好") - private String remark; - - @Schema(description = "关联的菜单编号", requiredMode = Schema.RequiredMode.REQUIRED) - private Set menuIds; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSaveReqVO.java deleted file mode 100755 index 748283b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSaveReqVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import java.util.Set; - -@Schema(description = "管理后台 - 租户套餐创建/修改 Request VO") -@Data -public class TenantPackageSaveReqVO { - - @Schema(description = "套餐编号", example = "1024") - private Long id; - - @Schema(description = "套餐名", requiredMode = Schema.RequiredMode.REQUIRED, example = "VIP") - @NotEmpty(message = "套餐名不能为空") - private String name; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}") - private Integer status; - - @Schema(description = "备注", example = "好") - private String remark; - - @Schema(description = "关联的菜单编号", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "关联的菜单编号不能为空") - private Set menuIds; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSimpleRespVO.java deleted file mode 100644 index 5611731..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSimpleRespVO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 租户套餐精简 Response VO") -@Data -public class TenantPackageSimpleRespVO { - - @Schema(description = "套餐编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "套餐编号不能为空") - private Long id; - - @Schema(description = "套餐名", requiredMode = Schema.RequiredMode.REQUIRED, example = "VIP") - @NotNull(message = "套餐名不能为空") - private String name; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java deleted file mode 100755 index 512a4a7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 租户分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class TenantPageReqVO extends PageParam { - - @Schema(description = "租户名", example = "芋道") - private String name; - - @Schema(description = "联系人", example = "芋艿") - private String contactName; - - @Schema(description = "联系手机", example = "15601691300") - private String contactMobile; - - @Schema(description = "租户状态(0正常 1停用)", example = "1") - private Integer status; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "创建时间") - private LocalDateTime[] createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java deleted file mode 100755 index 5a444b5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 租户 Response VO") -@Data -@ExcelIgnoreUnannotated -public class TenantRespVO { - - @Schema(description = "租户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @ExcelProperty("租户编号") - private Long id; - - @Schema(description = "租户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - @ExcelProperty("租户名") - private String name; - - @Schema(description = "联系人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - @ExcelProperty("联系人") - private String contactName; - - @Schema(description = "联系手机", example = "15601691300") - @ExcelProperty("联系手机") - private String contactMobile; - - @Schema(description = "租户状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - - @Schema(description = "绑定域名", example = "https://www.iocoder.cn") - private String website; - - @Schema(description = "租户套餐编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long packageId; - - @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime expireTime; - - @Schema(description = "账号数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Integer accountCount; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - @ExcelProperty("创建时间") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java deleted file mode 100644 index 175afa3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantSaveReqVO.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; - -import cn.hutool.core.util.ObjectUtil; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.Length; - -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Size; -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 租户创建/修改 Request VO") -@Data -public class TenantSaveReqVO { - - @Schema(description = "租户编号", example = "1024") - private Long id; - - @Schema(description = "租户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - @NotNull(message = "租户名不能为空") - private String name; - - @Schema(description = "联系人", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - @NotNull(message = "联系人不能为空") - private String contactName; - - @Schema(description = "联系手机", example = "15601691300") - private String contactMobile; - - @Schema(description = "租户状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "租户状态") - private Integer status; - - @Schema(description = "绑定域名", example = "https://www.iocoder.cn") - private String website; - - @Schema(description = "租户套餐编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "租户套餐编号不能为空") - private Long packageId; - - @Schema(description = "过期时间", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "过期时间不能为空") - private LocalDateTime expireTime; - - @Schema(description = "账号数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "账号数量不能为空") - private Integer accountCount; - - // ========== 仅【创建】时,需要传递的字段 ========== - - @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成") - @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符") - private String username; - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - @Length(min = 4, max = 16, message = "密码长度为 4-16 位") - private String password; - - @AssertTrue(message = "用户账号、密码不能为空") - @JsonIgnore - public boolean isUsernameValid() { - return id != null // 修改时,不需要传递 - || (ObjectUtil.isAllNotEmpty(username, password)); // 新增时,必须都传递 username、password - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http deleted file mode 100644 index 0ca6915..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.http +++ /dev/null @@ -1,11 +0,0 @@ -### 请求 /system/user/page 接口 => 没有权限 -GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10 -Authorization: Bearer {{token}} -#Authorization: Bearer test100 -tenant-id: {{adminTenantId}} - -### 请求 /system/user/page 接口(测试访问别的租户) -GET {{baseUrl}}/system/user/page?pageNo=1&pageSize=10 -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} -visit-tenant-id: 122 \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java deleted file mode 100644 index 1015bc0..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java +++ /dev/null @@ -1,172 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*; -import cn.iocoder.yudao.module.system.convert.user.UserConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.enums.common.SexEnum; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -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.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - 用户") -@RestController -@RequestMapping("/system/user") -@Validated -public class UserController { - - @Resource - private AdminUserService userService; - @Resource - private DeptService deptService; - - @PostMapping("/create") - @Operation(summary = "新增用户") - @PreAuthorize("@ss.hasPermission('system:user:create')") - public CommonResult createUser(@Valid @RequestBody UserSaveReqVO reqVO) { - Long id = userService.createUser(reqVO); - return success(id); - } - - @PutMapping("update") - @Operation(summary = "修改用户") - @PreAuthorize("@ss.hasPermission('system:user:update')") - public CommonResult updateUser(@Valid @RequestBody UserSaveReqVO reqVO) { - userService.updateUser(reqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除用户") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:user:delete')") - public CommonResult deleteUser(@RequestParam("id") Long id) { - userService.deleteUser(id); - return success(true); - } - - @PutMapping("/update-password") - @Operation(summary = "重置用户密码") - @PreAuthorize("@ss.hasPermission('system:user:update-password')") - public CommonResult updateUserPassword(@Valid @RequestBody UserUpdatePasswordReqVO reqVO) { - userService.updateUserPassword(reqVO.getId(), reqVO.getPassword()); - return success(true); - } - - @PutMapping("/update-status") - @Operation(summary = "修改用户状态") - @PreAuthorize("@ss.hasPermission('system:user:update')") - public CommonResult updateUserStatus(@Valid @RequestBody UserUpdateStatusReqVO reqVO) { - userService.updateUserStatus(reqVO.getId(), reqVO.getStatus()); - return success(true); - } - - @GetMapping("/page") - @Operation(summary = "获得用户分页列表") - @PreAuthorize("@ss.hasPermission('system:user:query')") - public CommonResult> getUserPage(@Valid UserPageReqVO pageReqVO) { - // 获得用户分页列表 - PageResult pageResult = userService.getUserPage(pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(new PageResult<>(pageResult.getTotal())); - } - // 拼接数据 - Map deptMap = deptService.getDeptMap( - convertList(pageResult.getList(), AdminUserDO::getDeptId)); - return success(new PageResult<>(UserConvert.INSTANCE.convertList(pageResult.getList(), deptMap), - pageResult.getTotal())); - } - - @GetMapping({"/list-all-simple", "/simple-list"}) - @Operation(summary = "获取用户精简信息列表", description = "只包含被开启的用户,主要用于前端的下拉选项") - public CommonResult> getSimpleUserList() { - List list = userService.getUserListByStatus(CommonStatusEnum.ENABLE.getStatus()); - // 拼接数据 - Map deptMap = deptService.getDeptMap( - convertList(list, AdminUserDO::getDeptId)); - return success(UserConvert.INSTANCE.convertSimpleList(list, deptMap)); - } - - @GetMapping("/get") - @Operation(summary = "获得用户详情") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('system:user:query')") - public CommonResult getUser(@RequestParam("id") Long id) { - AdminUserDO user = userService.getUser(id); - if (user == null) { - return success(null); - } - // 拼接数据 - DeptDO dept = deptService.getDept(user.getDeptId()); - return success(UserConvert.INSTANCE.convert(user, dept)); - } - - @GetMapping("/export") - @Operation(summary = "导出用户") - @PreAuthorize("@ss.hasPermission('system:user:export')") - @ApiAccessLog(operateType = EXPORT) - public void exportUserList(@Validated UserPageReqVO exportReqVO, - HttpServletResponse response) throws IOException { - exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); - List list = userService.getUserPage(exportReqVO).getList(); - // 输出 Excel - Map deptMap = deptService.getDeptMap( - convertList(list, AdminUserDO::getDeptId)); - ExcelUtils.write(response, "用户数据.xls", "数据", UserRespVO.class, - UserConvert.INSTANCE.convertList(list, deptMap)); - } - - @GetMapping("/get-import-template") - @Operation(summary = "获得导入用户模板") - public void importTemplate(HttpServletResponse response) throws IOException { - // 手动创建导出 demo - List list = Arrays.asList( - UserImportExcelVO.builder().username("yunai").deptId(1L).email("yunai@iocoder.cn").mobile("15601691300") - .nickname("芋道").status(CommonStatusEnum.ENABLE.getStatus()).sex(SexEnum.MALE.getSex()).build(), - UserImportExcelVO.builder().username("yuanma").deptId(2L).email("yuanma@iocoder.cn").mobile("15601701300") - .nickname("源码").status(CommonStatusEnum.DISABLE.getStatus()).sex(SexEnum.FEMALE.getSex()).build() - ); - // 输出 - ExcelUtils.write(response, "用户导入模板.xls", "用户列表", UserImportExcelVO.class, list); - } - - @PostMapping("/import") - @Operation(summary = "导入用户") - @Parameters({ - @Parameter(name = "file", description = "Excel 文件", required = true), - @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true") - }) - @PreAuthorize("@ss.hasPermission('system:user:import')") - public CommonResult importExcel(@RequestParam("file") MultipartFile file, - @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { - List list = ExcelUtils.read(file, UserImportExcelVO.class); - return success(userService.importUserList(list, updateSupport)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http deleted file mode 100644 index c94c2ad..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.http +++ /dev/null @@ -1,4 +0,0 @@ -### 请求 /system/user/profile/get 接口 => 没有权限 -GET {{baseUrl}}/system/user/profile/get -Authorization: Bearer {{token}} -tenant-id: {{adminTenantId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java deleted file mode 100644 index d4391e5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserProfileController.java +++ /dev/null @@ -1,79 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileRespVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; -import cn.iocoder.yudao.module.system.convert.user.UserConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import cn.iocoder.yudao.module.system.service.dept.PostService; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import cn.iocoder.yudao.module.system.service.permission.RoleService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 用户个人中心") -@RestController -@RequestMapping("/system/user/profile") -@Validated -@Slf4j -public class UserProfileController { - - @Resource - private AdminUserService userService; - @Resource - private DeptService deptService; - @Resource - private PostService postService; - @Resource - private PermissionService permissionService; - @Resource - private RoleService roleService; - - @GetMapping("/get") - @Operation(summary = "获得登录用户信息") - @DataPermission(enable = false) // 关闭数据权限,避免只查看自己时,查询不到部门。 - public CommonResult getUserProfile() { - // 获得用户基本信息 - AdminUserDO user = userService.getUser(getLoginUserId()); - // 获得用户角色 - List userRoles = roleService.getRoleListFromCache(permissionService.getUserRoleIdListByUserId(user.getId())); - // 获得部门信息 - DeptDO dept = user.getDeptId() != null ? deptService.getDept(user.getDeptId()) : null; - // 获得岗位信息 - List posts = CollUtil.isNotEmpty(user.getPostIds()) ? postService.getPostList(user.getPostIds()) : null; - return success(UserConvert.INSTANCE.convert(user, userRoles, dept, posts)); - } - - @PutMapping("/update") - @Operation(summary = "修改用户个人信息") - public CommonResult updateUserProfile(@Valid @RequestBody UserProfileUpdateReqVO reqVO) { - userService.updateUserProfile(getLoginUserId(), reqVO); - return success(true); - } - - @PutMapping("/update-password") - @Operation(summary = "修改用户个人密码") - public CommonResult updateUserProfilePassword(@Valid @RequestBody UserProfileUpdatePasswordReqVO reqVO) { - userService.updateUserPassword(getLoginUserId(), reqVO); - return success(true); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileRespVO.java deleted file mode 100644 index dc9d72e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileRespVO.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.profile; - -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSimpleRespVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSimpleRespVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSimpleRespVO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Data -@Schema(description = "管理后台 - 用户个人中心信息 Response VO") -public class UserProfileRespVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - private String username; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - private String nickname; - - @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") - private String email; - - @Schema(description = "手机号码", example = "15601691300") - private String mobile; - - @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") - private Integer sex; - - @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") - private String avatar; - - @Schema(description = "最后登录 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "192.168.1.1") - private String loginIp; - - @Schema(description = "最后登录时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - private LocalDateTime loginDate; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - private LocalDateTime createTime; - - /** - * 所属角色 - */ - private List roles; - /** - * 所在部门 - */ - private DeptSimpleRespVO dept; - /** - * 所属岗位数组 - */ - private List posts; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java deleted file mode 100644 index cfe37f6..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdatePasswordReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.profile; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.Length; - -import jakarta.validation.constraints.NotEmpty; - -@Schema(description = "管理后台 - 用户个人中心更新密码 Request VO") -@Data -public class UserProfileUpdatePasswordReqVO { - - @Schema(description = "旧密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - @NotEmpty(message = "旧密码不能为空") - @Length(min = 4, max = 16, message = "密码长度为 4-16 位") - private String oldPassword; - - @Schema(description = "新密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "654321") - @NotEmpty(message = "新密码不能为空") - @Length(min = 4, max = 16, message = "密码长度为 4-16 位") - private String newPassword; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java deleted file mode 100644 index 150fbb9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/profile/UserProfileUpdateReqVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.profile; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.Length; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.Size; -import org.hibernate.validator.constraints.URL; - - -@Schema(description = "管理后台 - 用户个人信息更新 Request VO") -@Data -public class UserProfileUpdateReqVO { - - @Schema(description = "用户昵称", example = "芋艿") - @Size(max = 30, message = "用户昵称长度不能超过 30 个字符") - private String nickname; - - @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") - @Email(message = "邮箱格式不正确") - @Size(max = 50, message = "邮箱长度不能超过 50 个字符") - private String email; - - @Schema(description = "手机号码", example = "15601691300") - @Length(min = 11, max = 11, message = "手机号长度必须 11 位") - private String mobile; - - @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") - private Integer sex; - - @Schema(description = "角色头像", example = "https://www.iocoder.cn/1.png") - @URL(message = "头像地址格式不正确") - private String avatar; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java deleted file mode 100644 index a360f1a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportExcelVO.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.Accessors; - -/** - * 用户 Excel 导入 VO - */ -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -@Accessors(chain = false) // 设置 chain = false,避免用户导入有问题 -public class UserImportExcelVO { - - @ExcelProperty("登录名称") - private String username; - - @ExcelProperty("用户名称") - private String nickname; - - @ExcelProperty("部门编号") - private Long deptId; - - @ExcelProperty("用户邮箱") - private String email; - - @ExcelProperty("手机号码") - private String mobile; - - @ExcelProperty(value = "用户性别", converter = DictConvert.class) - @DictFormat(DictTypeConstants.USER_SEX) - private Integer sex; - - @ExcelProperty(value = "账号状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportRespVO.java deleted file mode 100644 index 746c5af..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserImportRespVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Builder; -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Schema(description = "管理后台 - 用户导入 Response VO") -@Data -@Builder -public class UserImportRespVO { - - @Schema(description = "创建成功的用户名数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List createUsernames; - - @Schema(description = "更新成功的用户名数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List updateUsernames; - - @Schema(description = "导入失败的用户集合,key 为用户名,value 为失败原因", requiredMode = Schema.RequiredMode.REQUIRED) - private Map failureUsernames; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java deleted file mode 100644 index 7dadb51..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserPageReqVO.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 用户分页 Request VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -@EqualsAndHashCode(callSuper = true) -public class UserPageReqVO extends PageParam { - - @Schema(description = "用户账号,模糊匹配", example = "yudao") - private String username; - - @Schema(description = "手机号码,模糊匹配", example = "yudao") - private String mobile; - - @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1") - private Integer status; - - @Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - - @Schema(description = "部门编号,同时筛选子部门", example = "1024") - private Long deptId; - - @Schema(description = "角色编号", example = "1024") - private Long roleId; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java deleted file mode 100644 index 2837318..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; - -import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; -import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.Set; - -@Schema(description = "管理后台 - 用户信息 Response VO") -@Data -@ExcelIgnoreUnannotated -public class UserRespVO{ - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty("用户编号") - private Long id; - - @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @ExcelProperty("用户名称") - private String username; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - @ExcelProperty("用户昵称") - private String nickname; - - @Schema(description = "备注", example = "我是一个用户") - private String remark; - - @Schema(description = "部门ID", example = "我是一个用户") - private Long deptId; - @Schema(description = "部门名称", example = "IT 部") - @ExcelProperty("部门名称") - private String deptName; - - @Schema(description = "岗位编号数组", example = "1") - private Set postIds; - - @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") - @ExcelProperty("用户邮箱") - private String email; - - @Schema(description = "手机号码", example = "15601691300") - @ExcelProperty("手机号码") - private String mobile; - - @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") - @ExcelProperty(value = "用户性别", converter = DictConvert.class) - @DictFormat(DictTypeConstants.USER_SEX) - private Integer sex; - - @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") - private String avatar; - - @Schema(description = "状态,参见 CommonStatusEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @ExcelProperty(value = "帐号状态", converter = DictConvert.class) - @DictFormat(DictTypeConstants.COMMON_STATUS) - private Integer status; - - @Schema(description = "最后登录 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "192.168.1.1") - @ExcelProperty("最后登录IP") - private String loginIp; - - @Schema(description = "最后登录时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - @ExcelProperty("最后登录时间") - private LocalDateTime loginDate; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") - private LocalDateTime createTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java deleted file mode 100644 index 482d140..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSaveReqVO.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; - -import cn.hutool.core.util.ObjectUtil; -import cn.iocoder.yudao.framework.common.validation.Mobile; -import cn.iocoder.yudao.module.system.framework.operatelog.core.DeptParseFunction; -import cn.iocoder.yudao.module.system.framework.operatelog.core.PostParseFunction; -import cn.iocoder.yudao.module.system.framework.operatelog.core.SexParseFunction; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.mzt.logapi.starter.annotation.DiffLogField; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.*; -import lombok.Data; -import org.hibernate.validator.constraints.Length; - -import java.util.Set; - -@Schema(description = "管理后台 - 用户创建/修改 Request VO") -@Data -public class UserSaveReqVO { - - @Schema(description = "用户编号", example = "1024") - private Long id; - - @Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "yudao") - @NotBlank(message = "用户账号不能为空") - @Pattern(regexp = "^[a-zA-Z0-9]+$", message = "用户账号由 数字、字母 组成") - @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符") - @DiffLogField(name = "用户账号") - private String username; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") - @Size(max = 30, message = "用户昵称长度不能超过30个字符") - @DiffLogField(name = "用户昵称") - private String nickname; - - @Schema(description = "备注", example = "我是一个用户") - @DiffLogField(name = "备注") - private String remark; - - @Schema(description = "部门编号", example = "我是一个用户") - @DiffLogField(name = "部门", function = DeptParseFunction.NAME) - private Long deptId; - - @Schema(description = "岗位编号数组", example = "1") - @DiffLogField(name = "岗位", function = PostParseFunction.NAME) - private Set postIds; - - @Schema(description = "用户邮箱", example = "yudao@iocoder.cn") - @Email(message = "邮箱格式不正确") - @Size(max = 50, message = "邮箱长度不能超过 50 个字符") - @DiffLogField(name = "用户邮箱") - private String email; - - @Schema(description = "手机号码", example = "15601691300") - @Mobile - @DiffLogField(name = "手机号码") - private String mobile; - - @Schema(description = "用户性别,参见 SexEnum 枚举类", example = "1") - @DiffLogField(name = "用户性别", function = SexParseFunction.NAME) - private Integer sex; - - @Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png") - @DiffLogField(name = "用户头像") - private String avatar; - - // ========== 仅【创建】时,需要传递的字段 ========== - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - @Length(min = 4, max = 16, message = "密码长度为 4-16 位") - private String password; - - @AssertTrue(message = "密码不能为空") - @JsonIgnore - public boolean isPasswordValid() { - return id != null // 修改时,不需要传递 - || (ObjectUtil.isAllNotEmpty(password)); // 新增时,必须都传递 password - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java deleted file mode 100644 index 6dac286..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Schema(description = "管理后台 - 用户精简信息 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class UserSimpleRespVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String nickname; - - @Schema(description = "部门ID", example = "我是一个用户") - private Long deptId; - @Schema(description = "部门名称", example = "IT 部") - private String deptName; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java deleted file mode 100644 index 065d0d9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdatePasswordReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.hibernate.validator.constraints.Length; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 用户更新密码 Request VO") -@Data -public class UserUpdatePasswordReqVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "用户编号不能为空") - private Long id; - - @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED, example = "123456") - @NotEmpty(message = "密码不能为空") - @Length(min = 4, max = 16, message = "密码长度为 4-16 位") - private String password; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java deleted file mode 100644 index ae03d18..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserUpdateStatusReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import jakarta.validation.constraints.NotNull; - -@Schema(description = "管理后台 - 用户更新状态 Request VO") -@Data -public class UserUpdateStatusReqVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "角色编号不能为空") - private Long id; - - @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/AppDictDataController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/AppDictDataController.java deleted file mode 100644 index 05e0003..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/AppDictDataController.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.app.dict; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.app.dict.vo.AppDictDataRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; -import cn.iocoder.yudao.module.system.service.dict.DictDataService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.security.PermitAll; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import jakarta.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "用户 App - 字典数据") -@RestController -@RequestMapping("/system/dict-data") -@Validated -public class AppDictDataController { - - @Resource - private DictDataService dictDataService; - - @GetMapping("/type") - @Operation(summary = "根据字典类型查询字典数据信息") - @Parameter(name = "type", description = "字典类型", required = true, example = "common_status") - @PermitAll - public CommonResult> getDictDataListByType(@RequestParam("type") String type) { - List list = dictDataService.getDictDataList( - CommonStatusEnum.ENABLE.getStatus(), type); - return success(BeanUtils.toBean(list, AppDictDataRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/vo/AppDictDataRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/vo/AppDictDataRespVO.java deleted file mode 100644 index 5e3bc1c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/dict/vo/AppDictDataRespVO.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.app.dict.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Schema(description = "用户 App - 字典数据信息 Response VO") -@Data -@NoArgsConstructor -@AllArgsConstructor -public class AppDictDataRespVO { - - @Schema(description = "字典数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") - private String label; - - @Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder") - private String value; - - @Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex") - private String dictType; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/AppAreaController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/AppAreaController.java deleted file mode 100644 index b07add4..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/AppAreaController.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.app.ip; - -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.ip.core.Area; -import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; -import cn.iocoder.yudao.module.system.controller.app.ip.vo.AppAreaNodeRespVO; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.security.PermitAll; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "用户 App - 地区") -@RestController -@RequestMapping("/system/area") -@Validated -public class AppAreaController { - - @GetMapping("/tree") - @Operation(summary = "获得地区树") - @PermitAll - public CommonResult> getAreaTree() { - Area area = AreaUtils.getArea(Area.ID_CHINA); - Assert.notNull(area, "获取不到中国"); - return success(BeanUtils.toBean(area.getChildren(), AppAreaNodeRespVO.class)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/vo/AppAreaNodeRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/vo/AppAreaNodeRespVO.java deleted file mode 100644 index f6ca4a8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/ip/vo/AppAreaNodeRespVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.app.ip.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; - -@Schema(description = "用户 App - 地区节点 Response VO") -@Data -public class AppAreaNodeRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "110000") - private Integer id; - - @Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "北京") - private String name; - - /** - * 子节点 - */ - private List children; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/package-info.java deleted file mode 100644 index 659a909..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 提供 RESTful API 给前端: - * 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目 - * 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 - */ -package cn.iocoder.yudao.module.system.controller; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java deleted file mode 100644 index 18463e7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java +++ /dev/null @@ -1,88 +0,0 @@ -package cn.iocoder.yudao.module.system.convert.auth; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; -import org.slf4j.LoggerFactory; - -import java.util.*; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; -import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; - -@Mapper -public interface AuthConvert { - - AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); - - AuthLoginRespVO convert(OAuth2AccessTokenDO bean); - - default AuthPermissionInfoRespVO convert(AdminUserDO user, List roleList, List menuList) { - return AuthPermissionInfoRespVO.builder() - .user(BeanUtils.toBean(user, AuthPermissionInfoRespVO.UserVO.class)) - .roles(convertSet(roleList, RoleDO::getCode)) - // 权限标识信息 - .permissions(convertSet(menuList, MenuDO::getPermission)) - // 菜单树 - .menus(buildMenuTree(menuList)) - .build(); - } - - AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu); - - /** - * 将菜单列表,构建成菜单树 - * - * @param menuList 菜单列表 - * @return 菜单树 - */ - default List buildMenuTree(List menuList) { - if (CollUtil.isEmpty(menuList)) { - return Collections.emptyList(); - } - // 移除按钮 - menuList.removeIf(menu -> menu.getType().equals(MenuTypeEnum.BUTTON.getType())); - // 排序,保证菜单的有序性 - menuList.sort(Comparator.comparing(MenuDO::getSort)); - - // 构建菜单树 - // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。 - Map treeNodeMap = new LinkedHashMap<>(); - menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu))); - // 处理父子关系 - treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> { - // 获得父节点 - AuthPermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId()); - if (parentNode == null) { - LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]", - childNode.getId(), childNode.getParentId()); - return; - } - // 将自己添加到父节点中 - if (parentNode.getChildren() == null) { - parentNode.setChildren(new ArrayList<>()); - } - parentNode.getChildren().add(childNode); - }); - // 获得到所有的根节点 - return filterList(treeNodeMap.values(), node -> ID_ROOT.equals(node.getParentId())); - } - - SocialUserBindReqDTO convert(Long userId, Integer userType, AuthSocialLoginReqVO reqVO); - - SmsCodeSendReqDTO convert(AuthSmsSendReqVO reqVO); - - SmsCodeUseReqDTO convert(AuthSmsLoginReqVO reqVO, Integer scene, String usedIp); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java deleted file mode 100644 index 24c9537..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/oauth2/OAuth2OpenConvert.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.module.system.convert.oauth2; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.util.oauth2.OAuth2Utils; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -@Mapper -public interface OAuth2OpenConvert { - - OAuth2OpenConvert INSTANCE = Mappers.getMapper(OAuth2OpenConvert.class); - - default OAuth2OpenAccessTokenRespVO convert(OAuth2AccessTokenDO bean) { - OAuth2OpenAccessTokenRespVO respVO = BeanUtils.toBean(bean, OAuth2OpenAccessTokenRespVO.class); - respVO.setTokenType(SecurityFrameworkUtils.AUTHORIZATION_BEARER.toLowerCase()); - respVO.setExpiresIn(OAuth2Utils.getExpiresIn(bean.getExpiresTime())); - respVO.setScope(OAuth2Utils.buildScopeStr(bean.getScopes())); - return respVO; - } - - default OAuth2OpenCheckTokenRespVO convert2(OAuth2AccessTokenDO bean) { - OAuth2OpenCheckTokenRespVO respVO = BeanUtils.toBean(bean, OAuth2OpenCheckTokenRespVO.class); - respVO.setExp(LocalDateTimeUtil.toEpochMilli(bean.getExpiresTime()) / 1000L); - respVO.setUserType(UserTypeEnum.ADMIN.getValue()); - return respVO; - } - - default OAuth2OpenAuthorizeInfoRespVO convert(OAuth2ClientDO client, List approves) { - // 构建 scopes - List> scopes = new ArrayList<>(client.getScopes().size()); - Map approveMap = CollectionUtils.convertMap(approves, OAuth2ApproveDO::getScope); - client.getScopes().forEach(scope -> { - OAuth2ApproveDO approve = approveMap.get(scope); - scopes.add(new KeyValue<>(scope, approve != null ? approve.getApproved() : false)); - }); - // 拼接返回 - return new OAuth2OpenAuthorizeInfoRespVO( - new OAuth2OpenAuthorizeInfoRespVO.Client(client.getName(), client.getLogo()), scopes); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/package-info.java deleted file mode 100644 index b1ce8e3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 提供 POJO 类的实体转换 - * - * 目前使用 MapStruct 框架 - */ -package cn.iocoder.yudao.module.system.convert; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/social/SocialUserConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/social/SocialUserConvert.java deleted file mode 100644 index 9e679a2..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/social/SocialUserConvert.java +++ /dev/null @@ -1,17 +0,0 @@ -package cn.iocoder.yudao.module.system.convert.social; - -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserBindReqVO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.factory.Mappers; - -@Mapper -public interface SocialUserConvert { - - SocialUserConvert INSTANCE = Mappers.getMapper(SocialUserConvert.class); - - @Mapping(source = "reqVO.type", target = "socialType") - SocialUserBindReqDTO convert(Long userId, Integer userType, SocialUserBindReqVO reqVO); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/tenant/TenantConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/tenant/TenantConvert.java deleted file mode 100755 index 669954d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/tenant/TenantConvert.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.convert.tenant; - -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -/** - * 租户 Convert - * - * @author 芋道源码 - */ -@Mapper -public interface TenantConvert { - - TenantConvert INSTANCE = Mappers.getMapper(TenantConvert.class); - - default UserSaveReqVO convert02(TenantSaveReqVO bean) { - UserSaveReqVO reqVO = new UserSaveReqVO(); - reqVO.setUsername(bean.getUsername()); - reqVO.setPassword(bean.getPassword()); - reqVO.setNickname(bean.getContactName()).setMobile(bean.getContactMobile()); - return reqVO; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/user/UserConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/user/UserConvert.java deleted file mode 100644 index 20e5fbb..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/user/UserConvert.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.module.system.convert.user; - -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSimpleRespVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSimpleRespVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSimpleRespVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileRespVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserRespVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSimpleRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.List; -import java.util.Map; - -@Mapper -public interface UserConvert { - - UserConvert INSTANCE = Mappers.getMapper(UserConvert.class); - - default List convertList(List list, Map deptMap) { - return CollectionUtils.convertList(list, user -> convert(user, deptMap.get(user.getDeptId()))); - } - - default UserRespVO convert(AdminUserDO user, DeptDO dept) { - UserRespVO userVO = BeanUtils.toBean(user, UserRespVO.class); - if (dept != null) { - userVO.setDeptName(dept.getName()); - } - return userVO; - } - - default List convertSimpleList(List list, Map deptMap) { - return CollectionUtils.convertList(list, user -> { - UserSimpleRespVO userVO = BeanUtils.toBean(user, UserSimpleRespVO.class); - MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> userVO.setDeptName(dept.getName())); - return userVO; - }); - } - - default UserProfileRespVO convert(AdminUserDO user, List userRoles, - DeptDO dept, List posts) { - UserProfileRespVO userVO = BeanUtils.toBean(user, UserProfileRespVO.class); - userVO.setRoles(BeanUtils.toBean(userRoles, RoleSimpleRespVO.class)); - userVO.setDept(BeanUtils.toBean(dept, DeptSimpleRespVO.class)); - userVO.setPosts(BeanUtils.toBean(posts, PostSimpleRespVO.class)); - return userVO; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java deleted file mode 100644 index a59fa8b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java +++ /dev/null @@ -1,66 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.dept; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 部门表 - * - * @author ruoyi - * @author 芋道源码 - */ -@TableName("system_dept") -@KeySequence("system_dept_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class DeptDO extends TenantBaseDO { - - public static final Long PARENT_ID_ROOT = 0L; - - /** - * 部门ID - */ - @TableId - private Long id; - /** - * 部门名称 - */ - private String name; - /** - * 父部门ID - * - * 关联 {@link #id} - */ - private Long parentId; - /** - * 显示顺序 - */ - private Integer sort; - /** - * 负责人 - * - * 关联 {@link AdminUserDO#getId()} - */ - private Long leaderUserId; - /** - * 联系电话 - */ - private String phone; - /** - * 邮箱 - */ - private String email; - /** - * 部门状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/PostDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/PostDO.java deleted file mode 100644 index 3c97a9c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/PostDO.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.dept; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 岗位表 - * - * @author ruoyi - */ -@TableName("system_post") -@KeySequence("system_post_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class PostDO extends BaseDO { - - /** - * 岗位序号 - */ - @TableId - private Long id; - /** - * 岗位名称 - */ - private String name; - /** - * 岗位编码 - */ - private String code; - /** - * 岗位排序 - */ - private Integer sort; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 备注 - */ - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/UserPostDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/UserPostDO.java deleted file mode 100644 index b2a4f52..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/UserPostDO.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.dept; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 用户和岗位关联 - * - * @author ruoyi - */ -@TableName("system_user_post") -@KeySequence("system_user_post_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class UserPostDO extends BaseDO { - - /** - * 自增主键 - */ - @TableId - private Long id; - /** - * 用户 ID - * - * 关联 {@link AdminUserDO#getId()} - */ - private Long userId; - /** - * 角色 ID - * - * 关联 {@link PostDO#getId()} - */ - private Long postId; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictDataDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictDataDO.java deleted file mode 100644 index 05aba92..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictDataDO.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.dict; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import com.baomidou.mybatisplus.annotation.*; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 字典数据表 - * - * @author ruoyi - */ -@TableName("system_dict_data") -@KeySequence("system_dict_data_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@TenantIgnore -public class DictDataDO extends BaseDO { - - /** - * 字典数据编号 - */ - @TableId - private Long id; - /** - * 字典排序 - */ - private Integer sort; - /** - * 字典标签 - */ - private String label; - /** - * 字典值 - */ - private String value; - /** - * 字典类型 - * - * 冗余 {@link DictDataDO#getDictType()} - */ - private String dictType; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 颜色类型 - * - * 对应到 element-ui 为 default、primary、success、info、warning、danger - */ - private String colorType; - /** - * css 样式 - */ - @TableField(updateStrategy = FieldStrategy.ALWAYS) - private String cssClass; - /** - * 备注 - */ - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictTypeDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictTypeDO.java deleted file mode 100644 index 39d78f0..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dict/DictTypeDO.java +++ /dev/null @@ -1,58 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.dict; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -import java.time.LocalDateTime; - -/** - * 字典类型表 - * - * @author ruoyi - */ -@TableName("system_dict_type") -@KeySequence("system_dict_type_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -@TenantIgnore -public class DictTypeDO extends BaseDO { - - /** - * 字典主键 - */ - @TableId - private Long id; - /** - * 字典名称 - */ - private String name; - /** - * 字典类型 - */ - private String type; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 备注 - */ - private String remark; - - /** - * 删除时间 - */ - private LocalDateTime deletedTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/LoginLogDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/LoginLogDO.java deleted file mode 100644 index d2fbcb9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/LoginLogDO.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.logger; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; -import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -/** - * 登录日志表 - * - * 注意,包括登录和登出两种行为 - * - * @author 芋道源码 - */ -@TableName("system_login_log") -@KeySequence("system_login_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class LoginLogDO extends BaseDO { - - /** - * 日志主键 - */ - private Long id; - /** - * 日志类型 - * - * 枚举 {@link LoginLogTypeEnum} - */ - private Integer logType; - /** - * 链路追踪编号 - */ - private String traceId; - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 用户账号 - * - * 冗余,因为账号可以变更 - */ - private String username; - /** - * 登录结果 - * - * 枚举 {@link LoginResultEnum} - */ - private Integer result; - /** - * 用户 IP - */ - private String userIp; - /** - * 浏览器 UA - */ - private String userAgent; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java deleted file mode 100644 index 3c7d171..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/logger/OperateLogDO.java +++ /dev/null @@ -1,85 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.logger; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -/** - * 操作日志表 - * - * @author 芋道源码 - */ -@TableName(value = "system_operate_log", autoResultMap = true) -@KeySequence("system_operate_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -public class OperateLogDO extends BaseDO { - - /** - * 日志主键 - */ - @TableId - private Long id; - /** - * 链路追踪编号 - * - * 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。 - */ - private String traceId; - /** - * 用户编号 - * - * 关联 MemberUserDO 的 id 属性,或者 AdminUserDO 的 id 属性 - */ - private Long userId; - /** - * 用户类型 - * - * 关联 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 操作模块类型 - */ - private String type; - /** - * 操作名 - */ - private String subType; - /** - * 操作模块业务编号 - */ - private Long bizId; - /** - * 日志内容,记录整个操作的明细 - * - * 例如说,修改编号为 1 的用户信息,将性别从男改成女,将姓名从芋道改成源码。 - */ - private String action; - /** - * 拓展字段,有些复杂的业务,需要记录一些字段 ( JSON 格式 ) - * - * 例如说,记录订单编号,{ orderId: "1"} - */ - private String extra; - - /** - * 请求方法名 - */ - private String requestMethod; - /** - * 请求地址 - */ - private String requestUrl; - /** - * 用户 IP - */ - private String userIp; - /** - * 浏览器 UA - */ - private String userAgent; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailAccountDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailAccountDO.java deleted file mode 100644 index 3d9ec69..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailAccountDO.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.mail; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 邮箱账号 DO - * - * 用途:配置发送邮箱的账号 - * - * @author wangjingyi - * @since 2022-03-21 - */ -@TableName(value = "system_mail_account", autoResultMap = true) -@KeySequence("system_mail_account_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@TenantIgnore -public class MailAccountDO extends BaseDO { - - /** - * 主键 - */ - @TableId - private Long id; - /** - * 邮箱 - */ - private String mail; - - /** - * 用户名 - */ - private String username; - /** - * 密码 - */ - private String password; - /** - * SMTP 服务器域名 - */ - private String host; - /** - * SMTP 服务器端口 - */ - private Integer port; - /** - * 是否开启 SSL - */ - private Boolean sslEnable; - /** - * 是否开启 STARTTLS - */ - private Boolean starttlsEnable; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java deleted file mode 100644 index 2a0da51..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java +++ /dev/null @@ -1,125 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.mail; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.*; - -import java.io.Serializable; -import java.time.LocalDateTime; -import java.util.Map; - -/** - * 邮箱日志 DO - * 记录每一次邮件的发送 - * - * @author wangjingyi - * @since 2022-03-21 - */ -@TableName(value = "system_mail_log", autoResultMap = true) -@KeySequence("system_mail_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@AllArgsConstructor -@NoArgsConstructor -@TenantIgnore -public class MailLogDO extends BaseDO implements Serializable { - - /** - * 日志编号,自增 - */ - private Long id; - - /** - * 用户编码 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 接收邮箱地址 - */ - private String toMail; - - /** - * 邮箱账号编号 - * - * 关联 {@link MailAccountDO#getId()} - */ - private Long accountId; - /** - * 发送邮箱地址 - * - * 冗余 {@link MailAccountDO#getMail()} - */ - private String fromMail; - - // ========= 模板相关字段 ========= - /** - * 模版编号 - * - * 关联 {@link MailTemplateDO#getId()} - */ - private Long templateId; - /** - * 模版编码 - * - * 冗余 {@link MailTemplateDO#getCode()} - */ - private String templateCode; - /** - * 模版发送人名称 - * - * 冗余 {@link MailTemplateDO#getNickname()} - */ - private String templateNickname; - /** - * 模版标题 - */ - private String templateTitle; - /** - * 模版内容 - * - * 基于 {@link MailTemplateDO#getContent()} 格式化后的内容 - */ - private String templateContent; - /** - * 模版参数 - * - * 基于 {@link MailTemplateDO#getParams()} 输入后的参数 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Map templateParams; - - // ========= 发送相关字段 ========= - /** - * 发送状态 - * - * 枚举 {@link MailSendStatusEnum} - */ - private Integer sendStatus; - /** - * 发送时间 - */ - private LocalDateTime sendTime; - /** - * 发送返回的消息 ID - */ - private String sendMessageId; - /** - * 发送异常 - */ - private String sendException; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java deleted file mode 100644 index df026c1..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.mail; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.util.List; - -/** - * 邮件模版 DO - * - * @author wangjingyi - * @since 2022-03-21 - */ -@TableName(value = "system_mail_template", autoResultMap = true) -@KeySequence("system_mail_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@TenantIgnore -public class MailTemplateDO extends BaseDO { - - /** - * 主键 - */ - private Long id; - /** - * 模版名称 - */ - private String name; - /** - * 模版编号 - */ - private String code; - /** - * 发送的邮箱账号编号 - * - * 关联 {@link MailAccountDO#getId()} - */ - private Long accountId; - - /** - * 发送人名称 - */ - private String nickname; - /** - * 标题 - */ - private String title; - /** - * 内容 - */ - private String content; - /** - * 参数数组(自动根据内容生成) - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List params; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 备注 - */ - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notice/NoticeDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notice/NoticeDO.java deleted file mode 100644 index e7149d1..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notice/NoticeDO.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.notice; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.system.enums.notice.NoticeTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 通知公告表 - * - * @author ruoyi - */ -@TableName("system_notice") -@KeySequence("system_notice_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class NoticeDO extends BaseDO { - - /** - * 公告ID - */ - private Long id; - /** - * 公告标题 - */ - private String title; - /** - * 公告类型 - * - * 枚举 {@link NoticeTypeEnum} - */ - private Integer type; - /** - * 公告内容 - */ - private String content; - /** - * 公告状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyMessageDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyMessageDO.java deleted file mode 100644 index e73badf..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyMessageDO.java +++ /dev/null @@ -1,101 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.notify; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.*; - -import java.time.LocalDateTime; -import java.util.Date; -import java.util.Map; - -/** - * 站内信 DO - * - * @author xrcoder - */ -@TableName(value = "system_notify_message", autoResultMap = true) -@KeySequence("system_notify_message_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class NotifyMessageDO extends BaseDO { - - /** - * 站内信编号,自增 - */ - @TableId - private Long id; - /** - * 用户编号 - * - * 关联 MemberUserDO 的 id 字段、或者 AdminUserDO 的 id 字段 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - - // ========= 模板相关字段 ========= - - /** - * 模版编号 - * - * 关联 {@link NotifyTemplateDO#getId()} - */ - private Long templateId; - /** - * 模版编码 - * - * 关联 {@link NotifyTemplateDO#getCode()} - */ - private String templateCode; - /** - * 模版类型 - * - * 冗余 {@link NotifyTemplateDO#getType()} - */ - private Integer templateType; - /** - * 模版发送人名称 - * - * 冗余 {@link NotifyTemplateDO#getNickname()} - */ - private String templateNickname; - /** - * 模版内容 - * - * 基于 {@link NotifyTemplateDO#getContent()} 格式化后的内容 - */ - private String templateContent; - /** - * 模版参数 - * - * 基于 {@link NotifyTemplateDO#getParams()} 输入后的参数 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Map templateParams; - - // ========= 读取相关字段 ========= - - /** - * 是否已读 - */ - private Boolean readStatus; - /** - * 阅读时间 - */ - private LocalDateTime readTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyTemplateDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyTemplateDO.java deleted file mode 100644 index be4d185..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyTemplateDO.java +++ /dev/null @@ -1,74 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.notify; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.*; - -import java.util.List; - -/** - * 站内信模版 DO - * - * @author xrcoder - */ -@TableName(value = "system_notify_template", autoResultMap = true) -@KeySequence("system_notify_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -@TenantIgnore -public class NotifyTemplateDO extends BaseDO { - - /** - * ID - */ - @TableId - private Long id; - /** - * 模版名称 - */ - private String name; - /** - * 模版编码 - */ - private String code; - /** - * 模版类型 - * - * 对应 system_notify_template_type 字典 - */ - private Integer type; - /** - * 发送人名称 - */ - private String nickname; - /** - * 模版内容 - */ - private String content; - /** - * 参数数组 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List params; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 备注 - */ - private String remark; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2AccessTokenDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2AccessTokenDO.java deleted file mode 100644 index ef235ed..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2AccessTokenDO.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -/** - * OAuth2 访问令牌 DO - * - * 如下字段,暂时未使用,暂时不支持: - * user_name、authentication(用户信息) - * - * @author 芋道源码 - */ -@TableName(value = "system_oauth2_access_token", autoResultMap = true) -@KeySequence("system_oauth2_access_token_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class OAuth2AccessTokenDO extends TenantBaseDO { - - /** - * 编号,数据库递增 - */ - @TableId - private Long id; - /** - * 访问令牌 - */ - private String accessToken; - /** - * 刷新令牌 - */ - private String refreshToken; - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 用户信息 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Map userInfo; - /** - * 客户端编号 - * - * 关联 {@link OAuth2ClientDO#getId()} - */ - private String clientId; - /** - * 授权范围 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List scopes; - /** - * 过期时间 - */ - private LocalDateTime expiresTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java deleted file mode 100644 index e07b0a0..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ApproveDO.java +++ /dev/null @@ -1,63 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.time.LocalDateTime; - -/** - * OAuth2 批准 DO - * - * 用户在 sso.vue 界面时,记录接受的 scope 列表 - * - * @author 芋道源码 - */ -@TableName(value = "system_oauth2_approve", autoResultMap = true) -@KeySequence("system_oauth2_approve_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class OAuth2ApproveDO extends BaseDO { - - /** - * 编号,数据库自增 - */ - @TableId - private Long id; - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 客户端编号 - * - * 关联 {@link OAuth2ClientDO#getId()} - */ - private String clientId; - /** - * 授权范围 - */ - private String scope; - /** - * 是否接受 - * - * true - 接受 - * false - 拒绝 - */ - private Boolean approved; - /** - * 过期时间 - */ - private LocalDateTime expiresTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ClientDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ClientDO.java deleted file mode 100644 index 3851cc8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2ClientDO.java +++ /dev/null @@ -1,109 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2GrantTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.util.List; - -/** - * OAuth2 客户端 DO - * - * @author 芋道源码 - */ -@TableName(value = "system_oauth2_client", autoResultMap = true) -@KeySequence("system_oauth2_client_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@TenantIgnore -public class OAuth2ClientDO extends BaseDO { - - /** - * 编号,数据库自增 - * - * 由于 SQL Server 在存储 String 主键有点问题,所以暂时使用 Long 类型 - */ - @TableId - private Long id; - /** - * 客户端编号 - */ - private String clientId; - /** - * 客户端密钥 - */ - private String secret; - /** - * 应用名 - */ - private String name; - /** - * 应用图标 - */ - private String logo; - /** - * 应用描述 - */ - private String description; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 访问令牌的有效期 - */ - private Integer accessTokenValiditySeconds; - /** - * 刷新令牌的有效期 - */ - private Integer refreshTokenValiditySeconds; - /** - * 可重定向的 URI 地址 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List redirectUris; - /** - * 授权类型(模式) - * - * 枚举 {@link OAuth2GrantTypeEnum} - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List authorizedGrantTypes; - /** - * 授权范围 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List scopes; - /** - * 自动授权的 Scope - * - * code 授权时,如果 scope 在这个范围内,则自动通过 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List autoApproveScopes; - /** - * 权限 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List authorities; - /** - * 资源 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List resourceIds; - /** - * 附加信息,JSON 格式 - */ - private String additionalInformation; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2CodeDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2CodeDO.java deleted file mode 100644 index fad9c16..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2CodeDO.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.time.LocalDateTime; -import java.util.List; - -/** - * OAuth2 授权码 DO - * - * @author 芋道源码 - */ -@TableName(value = "system_oauth2_code", autoResultMap = true) -@KeySequence("system_oauth2_code_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class OAuth2CodeDO extends BaseDO { - - /** - * 编号,数据库递增 - */ - private Long id; - /** - * 授权码 - */ - private String code; - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 客户端编号 - * - * 关联 {@link OAuth2ClientDO#getClientId()} - */ - private String clientId; - /** - * 授权范围 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List scopes; - /** - * 重定向地址 - */ - private String redirectUri; - /** - * 状态 - */ - private String state; - /** - * 过期时间 - */ - private LocalDateTime expiresTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2RefreshTokenDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2RefreshTokenDO.java deleted file mode 100644 index 99d153e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/oauth2/OAuth2RefreshTokenDO.java +++ /dev/null @@ -1,63 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.oauth2; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; - -import java.time.LocalDateTime; -import java.util.List; - -/** - * OAuth2 刷新令牌 - * - * @author 芋道源码 - */ -@TableName(value = "system_oauth2_refresh_token", autoResultMap = true) -// 由于 Oracle 的 SEQ 的名字长度有限制,所以就先用 system_oauth2_access_token_seq 吧,反正也没啥问题 -@KeySequence("system_oauth2_access_token_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Accessors(chain = true) -public class OAuth2RefreshTokenDO extends TenantBaseDO { - - /** - * 编号,数据库字典 - */ - private Long id; - /** - * 刷新令牌 - */ - private String refreshToken; - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 客户端编号 - * - * 关联 {@link OAuth2ClientDO#getId()} - */ - private String clientId; - /** - * 授权范围 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List scopes; - /** - * 过期时间 - */ - private LocalDateTime expiresTime; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/MenuDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/MenuDO.java deleted file mode 100644 index 8922b61..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/MenuDO.java +++ /dev/null @@ -1,109 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.permission; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 菜单 DO - * - * @author ruoyi - */ -@TableName("system_menu") -@KeySequence("system_menu_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@TenantIgnore -public class MenuDO extends BaseDO { - - /** - * 菜单编号 - 根节点 - */ - public static final Long ID_ROOT = 0L; - - /** - * 菜单编号 - */ - @TableId - private Long id; - /** - * 菜单名称 - */ - private String name; - /** - * 权限标识 - * - * 一般格式为:${系统}:${模块}:${操作} - * 例如说:system:admin:add,即 system 服务的添加管理员。 - * - * 当我们把该 MenuDO 赋予给角色后,意味着该角色有该资源: - * - 对于后端,配合 @PreAuthorize 注解,配置 API 接口需要该权限,从而对 API 接口进行权限控制。 - * - 对于前端,配合前端标签,配置按钮是否展示,避免用户没有该权限时,结果可以看到该操作。 - */ - private String permission; - /** - * 菜单类型 - * - * 枚举 {@link MenuTypeEnum} - */ - private Integer type; - /** - * 显示顺序 - */ - private Integer sort; - /** - * 父菜单ID - */ - private Long parentId; - /** - * 路由地址 - * - * 如果 path 为 http(s) 时,则它是外链 - */ - private String path; - /** - * 菜单图标 - */ - private String icon; - /** - * 组件路径 - */ - private String component; - /** - * 组件名 - */ - private String componentName; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 是否可见 - * - * 只有菜单、目录使用 - * 当设置为 true 时,该菜单不会展示在侧边栏,但是路由还是存在。例如说,一些独立的编辑页面 /edit/1024 等等 - */ - private Boolean visible; - /** - * 是否缓存 - * - * 只有菜单、目录使用,否使用 Vue 路由的 keep-alive 特性 - * 注意:如果开启缓存,则必须填写 {@link #componentName} 属性,否则无法缓存 - */ - private Boolean keepAlive; - /** - * 是否总是显示 - * - * 如果为 false 时,当该菜单只有一个子菜单时,不展示自己,直接展示子菜单 - */ - private Boolean alwaysShow; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java deleted file mode 100644 index 482a4b5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java +++ /dev/null @@ -1,78 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.permission; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; -import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.util.Set; - -/** - * 角色 DO - * - * @author ruoyi - */ -@TableName(value = "system_role", autoResultMap = true) -@KeySequence("system_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class RoleDO extends TenantBaseDO { - - /** - * 角色ID - */ - @TableId - private Long id; - /** - * 角色名称 - */ - private String name; - /** - * 角色标识 - * - * 枚举 - */ - private String code; - /** - * 角色排序 - */ - private Integer sort; - /** - * 角色状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 角色类型 - * - * 枚举 {@link RoleTypeEnum} - */ - private Integer type; - /** - * 备注 - */ - private String remark; - - /** - * 数据范围 - * - * 枚举 {@link DataScopeEnum} - */ - private Integer dataScope; - /** - * 数据范围(指定部门数组) - * - * 适用于 {@link #dataScope} 的值为 {@link DataScopeEnum#DEPT_CUSTOM} 时 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Set dataScopeDeptIds; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleMenuDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleMenuDO.java deleted file mode 100644 index 4978b0e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleMenuDO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.permission; - -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 角色和菜单关联 - * - * @author ruoyi - */ -@TableName("system_role_menu") -@KeySequence("system_role_menu_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class RoleMenuDO extends TenantBaseDO { - - /** - * 自增主键 - */ - @TableId - private Long id; - /** - * 角色ID - */ - private Long roleId; - /** - * 菜单ID - */ - private Long menuId; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java deleted file mode 100644 index 0101840..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/UserRoleDO.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.permission; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 用户和角色关联 - * - * @author ruoyi - */ -@TableName("system_user_role") -@KeySequence("system_user_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class UserRoleDO extends BaseDO { - - /** - * 自增主键 - */ - @TableId - private Long id; - /** - * 用户 ID - */ - private Long userId; - /** - * 角色 ID - */ - private Long roleId; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsChannelDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsChannelDO.java deleted file mode 100644 index c31aa0a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsChannelDO.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.sms; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -/** - * 短信渠道 DO - * - * @author zzf - * @since 2021-01-25 - */ -@TableName(value = "system_sms_channel", autoResultMap = true) -@KeySequence("system_sms_channel_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@TenantIgnore -public class SmsChannelDO extends BaseDO { - - /** - * 渠道编号 - */ - private Long id; - /** - * 短信签名 - */ - private String signature; - /** - * 渠道编码 - * - * 枚举 {@link SmsChannelEnum} - */ - private String code; - /** - * 启用状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 备注 - */ - private String remark; - /** - * 短信 API 的账号 - */ - private String apiKey; - /** - * 短信 API 的密钥 - */ - private String apiSecret; - /** - * 短信发送回调 URL - */ - private String callbackUrl; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsCodeDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsCodeDO.java deleted file mode 100644 index 63c5481..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsCodeDO.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.sms; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -import java.time.LocalDateTime; - -/** - * 手机验证码 DO - * - * idx_mobile 索引:基于 {@link #mobile} 字段 - * - * @author 芋道源码 - */ -@TableName("system_sms_code") -@KeySequence("system_sms_code_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -@TenantIgnore -public class SmsCodeDO extends BaseDO { - - /** - * 编号 - */ - private Long id; - /** - * 手机号 - */ - private String mobile; - /** - * 验证码 - */ - private String code; - /** - * 发送场景 - * - * 枚举 {@link SmsCodeDO} - */ - private Integer scene; - /** - * 创建 IP - */ - private String createIp; - /** - * 今日发送的第几条 - */ - private Integer todayIndex; - /** - * 是否使用 - */ - private Boolean used; - /** - * 使用时间 - */ - private LocalDateTime usedTime; - /** - * 使用 IP - */ - private String usedIp; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java deleted file mode 100644 index 073ebff..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java +++ /dev/null @@ -1,163 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.sms; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum; -import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.*; - -import java.time.LocalDateTime; -import java.util.Map; - -/** - * 短信日志 DO - * - * @author zzf - * @since 2021-01-25 - */ -@TableName(value = "system_sms_log", autoResultMap = true) -@KeySequence("system_sms_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@AllArgsConstructor -@NoArgsConstructor -@TenantIgnore -public class SmsLogDO extends BaseDO { - - /** - * 自增编号 - */ - private Long id; - - // ========= 渠道相关字段 ========= - - /** - * 短信渠道编号 - * - * 关联 {@link SmsChannelDO#getId()} - */ - private Long channelId; - /** - * 短信渠道编码 - * - * 冗余 {@link SmsChannelDO#getCode()} - */ - private String channelCode; - - // ========= 模板相关字段 ========= - - /** - * 模板编号 - * - * 关联 {@link SmsTemplateDO#getId()} - */ - private Long templateId; - /** - * 模板编码 - * - * 冗余 {@link SmsTemplateDO#getCode()} - */ - private String templateCode; - /** - * 短信类型 - * - * 冗余 {@link SmsTemplateDO#getType()} - */ - private Integer templateType; - /** - * 基于 {@link SmsTemplateDO#getContent()} 格式化后的内容 - */ - private String templateContent; - /** - * 基于 {@link SmsTemplateDO#getParams()} 输入后的参数 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Map templateParams; - /** - * 短信 API 的模板编号 - * - * 冗余 {@link SmsTemplateDO#getApiTemplateId()} - */ - private String apiTemplateId; - - // ========= 手机相关字段 ========= - - /** - * 手机号 - */ - private String mobile; - /** - * 用户编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - - // ========= 发送相关字段 ========= - - /** - * 发送状态 - * - * 枚举 {@link SmsSendStatusEnum} - */ - private Integer sendStatus; - /** - * 发送时间 - */ - private LocalDateTime sendTime; - /** - * 短信 API 发送结果的编码 - * - * 由于第三方的错误码可能是字符串,所以使用 String 类型 - */ - private String apiSendCode; - /** - * 短信 API 发送失败的提示 - */ - private String apiSendMsg; - /** - * 短信 API 发送返回的唯一请求 ID - * - * 用于和短信 API 进行定位于排错 - */ - private String apiRequestId; - /** - * 短信 API 发送返回的序号 - * - * 用于和短信 API 平台的发送记录关联 - */ - private String apiSerialNo; - - // ========= 接收相关字段 ========= - - /** - * 接收状态 - * - * 枚举 {@link SmsReceiveStatusEnum} - */ - private Integer receiveStatus; - /** - * 接收时间 - */ - private LocalDateTime receiveTime; - /** - * 短信 API 接收结果的编码 - */ - private String apiReceiveCode; - /** - * 短信 API 接收结果的提示 - */ - private String apiReceiveMsg; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsTemplateDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsTemplateDO.java deleted file mode 100644 index 450a0b9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsTemplateDO.java +++ /dev/null @@ -1,93 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.sms; - -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -import java.util.List; - -/** - * 短信模板 DO - * - * @author zzf - * @since 2021-01-25 - */ -@TableName(value = "system_sms_template", autoResultMap = true) -@KeySequence("system_sms_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@TenantIgnore -public class SmsTemplateDO extends BaseDO { - - /** - * 自增编号 - */ - private Long id; - - // ========= 模板相关字段 ========= - - /** - * 短信类型 - * - * 枚举 {@link SmsTemplateTypeEnum} - */ - private Integer type; - /** - * 启用状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 模板编码,保证唯一 - */ - private String code; - /** - * 模板名称 - */ - private String name; - /** - * 模板内容 - * - * 内容的参数,使用 {} 包括,例如说 {name} - */ - private String content; - /** - * 参数数组(自动根据内容生成) - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List params; - /** - * 备注 - */ - private String remark; - /** - * 短信 API 的模板编号 - */ - private String apiTemplateId; - - // ========= 渠道相关字段 ========= - - /** - * 短信渠道编号 - * - * 关联 {@link SmsChannelDO#getId()} - */ - private Long channelId; - /** - * 短信渠道编码 - * - * 冗余 {@link SmsChannelDO#getCode()} - */ - private String channelCode; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialClientDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialClientDO.java deleted file mode 100644 index 5449106..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialClientDO.java +++ /dev/null @@ -1,76 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.social; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; -import me.zhyd.oauth.config.AuthConfig; - -/** - * 社交客户端 DO - * - * 对应 {@link AuthConfig} 配置,满足不同租户,有自己的客户端配置,实现社交(三方)登录 - * - * @author 芋道源码 - */ -@TableName(value = "system_social_client", autoResultMap = true) -@KeySequence("system_social_client_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SocialClientDO extends TenantBaseDO { - - /** - * 编号,自增 - */ - @TableId - private Long id; - /** - * 应用名 - */ - private String name; - /** - * 社交类型 - * - * 枚举 {@link SocialTypeEnum} - */ - private Integer socialType; - /** - * 用户类型 - * - * 目的:不同用户类型,对应不同的小程序,需要自己的配置 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - - /** - * 客户端 id - */ - private String clientId; - /** - * 客户端 Secret - */ - private String clientSecret; - - /** - * 代理编号 - * - * 目前只有部分“社交类型”在使用: - * 1. 企业微信:对应授权方的网页应用 ID - */ - private String agentId; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java deleted file mode 100644 index 0f4e41f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserBindDO.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.social; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -/** - * 社交用户的绑定 - * 即 {@link SocialUserDO} 与 UserDO 的关联表 - * - * @author 芋道源码 - */ -@TableName(value = "system_social_user_bind", autoResultMap = true) -@KeySequence("system_social_user_bind_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SocialUserBindDO extends BaseDO { - - /** - * 编号 - */ - @TableId - private Long id; - /** - * 关联的用户编号 - * - * 关联 UserDO 的编号 - */ - private Long userId; - /** - * 用户类型 - * - * 枚举 {@link UserTypeEnum} - */ - private Integer userType; - - /** - * 社交平台的用户编号 - * - * 关联 {@link SocialUserDO#getId()} - */ - private Long socialUserId; - /** - * 社交平台的类型 - * - * 冗余 {@link SocialUserDO#getType()} - */ - private Integer socialType; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java deleted file mode 100644 index f3adf03..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/social/SocialUserDO.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.social; - -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -/** - * 社交(三方)用户 - * - * @author weir - */ -@TableName(value = "system_social_user", autoResultMap = true) -@KeySequence("system_social_user_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SocialUserDO extends BaseDO { - - /** - * 自增主键 - */ - @TableId - private Long id; - /** - * 社交平台的类型 - * - * 枚举 {@link SocialTypeEnum} - */ - private Integer type; - - /** - * 社交 openid - */ - private String openid; - /** - * 社交 token - */ - private String token; - /** - * 原始 Token 数据,一般是 JSON 格式 - */ - private String rawTokenInfo; - - /** - * 用户昵称 - */ - private String nickname; - /** - * 用户头像 - */ - private String avatar; - /** - * 原始用户数据,一般是 JSON 格式 - */ - private String rawUserInfo; - - /** - * 最后一次的认证 code - */ - private String code; - /** - * 最后一次的认证 state - */ - private String state; - -} - - diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantDO.java deleted file mode 100644 index 6e60f61..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantDO.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.tenant; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.*; - -import java.time.LocalDateTime; - -/** - * 租户 DO - * - * @author 芋道源码 - */ -@TableName(value = "system_tenant", autoResultMap = true) -@KeySequence("system_tenant_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@AllArgsConstructor -@NoArgsConstructor -@TenantIgnore -public class TenantDO extends BaseDO { - - /** - * 套餐编号 - 系统 - */ - public static final Long PACKAGE_ID_SYSTEM = 0L; - - /** - * 租户编号,自增 - */ - private Long id; - /** - * 租户名,唯一 - */ - private String name; - /** - * 联系人的用户编号 - * - * 关联 {@link AdminUserDO#getId()} - */ - private Long contactUserId; - /** - * 联系人 - */ - private String contactName; - /** - * 联系手机 - */ - private String contactMobile; - /** - * 租户状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 绑定域名 - */ - private String website; - /** - * 租户套餐编号 - * - * 关联 {@link TenantPackageDO#getId()} - * 特殊逻辑:系统内置租户,不使用套餐,暂时使用 {@link #PACKAGE_ID_SYSTEM} 标识 - */ - private Long packageId; - /** - * 过期时间 - */ - private LocalDateTime expireTime; - /** - * 账号数量 - */ - private Integer accountCount; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantPackageDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantPackageDO.java deleted file mode 100644 index 159a838..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/tenant/TenantPackageDO.java +++ /dev/null @@ -1,54 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.tenant; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.*; - -import java.util.Set; - -/** - * 租户套餐 DO - * - * @author 芋道源码 - */ -@TableName(value = "system_tenant_package", autoResultMap = true) -@KeySequence("system_tenant_package_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@AllArgsConstructor -@NoArgsConstructor -@TenantIgnore -public class TenantPackageDO extends BaseDO { - - /** - * 套餐编号,自增 - */ - private Long id; - /** - * 套餐名,唯一 - */ - private String name; - /** - * 租户套餐状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 备注 - */ - private String remark; - /** - * 关联的菜单编号 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Set menuIds; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java deleted file mode 100644 index 2f07a30..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java +++ /dev/null @@ -1,96 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.dataobject.user; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; -import cn.iocoder.yudao.module.system.enums.common.SexEnum; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import lombok.*; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; - -import java.time.LocalDateTime; -import java.util.Set; - -/** - * 管理后台的用户 DO - * - * @author 芋道源码 - */ -@TableName(value = "system_users", autoResultMap = true) // 由于 SQL Server 的 system_user 是关键字,所以使用 system_users -@KeySequence("system_users_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class AdminUserDO extends TenantBaseDO { - - /** - * 用户ID - */ - @TableId - private Long id; - /** - * 用户账号 - */ - private String username; - /** - * 加密后的密码 - * - * 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐 - */ - private String password; - /** - * 用户昵称 - */ - private String nickname; - /** - * 备注 - */ - private String remark; - /** - * 部门 ID - */ - private Long deptId; - /** - * 岗位编号数组 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Set postIds; - /** - * 用户邮箱 - */ - private String email; - /** - * 手机号码 - */ - private String mobile; - /** - * 用户性别 - * - * 枚举类 {@link SexEnum} - */ - private Integer sex; - /** - * 用户头像 - */ - private String avatar; - /** - * 帐号状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 最后登录IP - */ - private String loginIp; - /** - * 最后登录时间 - */ - private LocalDateTime loginDate; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java deleted file mode 100644 index a09fcf7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.dept; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface DeptMapper extends BaseMapperX { - - default List selectList(DeptListReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .likeIfPresent(DeptDO::getName, reqVO.getName()) - .eqIfPresent(DeptDO::getStatus, reqVO.getStatus())); - } - - default DeptDO selectByParentIdAndName(Long parentId, String name) { - return selectOne(DeptDO::getParentId, parentId, DeptDO::getName, name); - } - - default Long selectCountByParentId(Long parentId) { - return selectCount(DeptDO::getParentId, parentId); - } - - default List selectListByParentId(Collection parentIds) { - return selectList(DeptDO::getParentId, parentIds); - } - - default List selectListByLeaderUserId(Long id) { - return selectList(DeptDO::getLeaderUserId, id); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/PostMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/PostMapper.java deleted file mode 100644 index e05c38c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/PostMapper.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.dept; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface PostMapper extends BaseMapperX { - - default List selectList(Collection ids, Collection statuses) { - return selectList(new LambdaQueryWrapperX() - .inIfPresent(PostDO::getId, ids) - .inIfPresent(PostDO::getStatus, statuses)); - } - - default PageResult selectPage(PostPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(PostDO::getCode, reqVO.getCode()) - .likeIfPresent(PostDO::getName, reqVO.getName()) - .eqIfPresent(PostDO::getStatus, reqVO.getStatus()) - .orderByDesc(PostDO::getId)); - } - - default PostDO selectByName(String name) { - return selectOne(PostDO::getName, name); - } - - default PostDO selectByCode(String code) { - return selectOne(PostDO::getCode, code); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/UserPostMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/UserPostMapper.java deleted file mode 100644 index addc67d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/UserPostMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.dept; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface UserPostMapper extends BaseMapperX { - - default List selectListByUserId(Long userId) { - return selectList(UserPostDO::getUserId, userId); - } - - default void deleteByUserIdAndPostId(Long userId, Collection postIds) { - delete(new LambdaQueryWrapperX() - .eq(UserPostDO::getUserId, userId) - .in(UserPostDO::getPostId, postIds)); - } - - default List selectListByPostIds(Collection postIds) { - return selectList(UserPostDO::getPostId, postIds); - } - - default void deleteByUserId(Long userId) { - delete(Wrappers.lambdaUpdate(UserPostDO.class).eq(UserPostDO::getUserId, userId)); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java deleted file mode 100644 index 87a05c6..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictDataMapper.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.dict; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -@Mapper -public interface DictDataMapper extends BaseMapperX { - - default DictDataDO selectByDictTypeAndValue(String dictType, String value) { - return selectOne(DictDataDO::getDictType, dictType, DictDataDO::getValue, value); - } - - default DictDataDO selectByDictTypeAndLabel(String dictType, String label) { - return selectOne(DictDataDO::getDictType, dictType, DictDataDO::getLabel, label); - } - - default List selectByDictTypeAndValues(String dictType, Collection values) { - return selectList(new LambdaQueryWrapper().eq(DictDataDO::getDictType, dictType) - .in(DictDataDO::getValue, values)); - } - - default long selectCountByDictType(String dictType) { - return selectCount(DictDataDO::getDictType, dictType); - } - - default PageResult selectPage(DictDataPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(DictDataDO::getLabel, reqVO.getLabel()) - .eqIfPresent(DictDataDO::getDictType, reqVO.getDictType()) - .eqIfPresent(DictDataDO::getStatus, reqVO.getStatus()) - .orderByDesc(Arrays.asList(DictDataDO::getDictType, DictDataDO::getSort))); - } - - default List selectListByStatusAndDictType(Integer status, String dictType) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(DictDataDO::getStatus, status) - .eqIfPresent(DictDataDO::getDictType, dictType)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictTypeMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictTypeMapper.java deleted file mode 100644 index 52393a9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dict/DictTypeMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.dict; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Update; - -import java.time.LocalDateTime; - -@Mapper -public interface DictTypeMapper extends BaseMapperX { - - default PageResult selectPage(DictTypePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(DictTypeDO::getName, reqVO.getName()) - .likeIfPresent(DictTypeDO::getType, reqVO.getType()) - .eqIfPresent(DictTypeDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(DictTypeDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(DictTypeDO::getId)); - } - - default DictTypeDO selectByType(String type) { - return selectOne(DictTypeDO::getType, type); - } - - default DictTypeDO selectByName(String name) { - return selectOne(DictTypeDO::getName, name); - } - - @Update("UPDATE system_dict_type SET deleted = 1, deleted_time = #{deletedTime} WHERE id = #{id}") - void updateToDelete(@Param("id") Long id, @Param("deletedTime") LocalDateTime deletedTime); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/LoginLogMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/LoginLogMapper.java deleted file mode 100644 index 0cb47eb..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/LoginLogMapper.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; -import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface LoginLogMapper extends BaseMapperX { - - default PageResult selectPage(LoginLogPageReqVO reqVO) { - LambdaQueryWrapperX query = new LambdaQueryWrapperX() - .likeIfPresent(LoginLogDO::getUserIp, reqVO.getUserIp()) - .likeIfPresent(LoginLogDO::getUsername, reqVO.getUsername()) - .betweenIfPresent(LoginLogDO::getCreateTime, reqVO.getCreateTime()); - if (Boolean.TRUE.equals(reqVO.getStatus())) { - query.eq(LoginLogDO::getResult, LoginResultEnum.SUCCESS.getResult()); - } else if (Boolean.FALSE.equals(reqVO.getStatus())) { - query.gt(LoginLogDO::getResult, LoginResultEnum.SUCCESS.getResult()); - } - query.orderByDesc(LoginLogDO::getId); // 降序 - return selectPage(reqVO, query); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java deleted file mode 100644 index 14e308b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/logger/OperateLogMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface OperateLogMapper extends BaseMapperX { - - default PageResult selectPage(OperateLogPageReqVO pageReqDTO) { - return selectPage(pageReqDTO, new LambdaQueryWrapperX() - .eqIfPresent(OperateLogDO::getUserId, pageReqDTO.getUserId()) - .eqIfPresent(OperateLogDO::getBizId, pageReqDTO.getBizId()) - .likeIfPresent(OperateLogDO::getType, pageReqDTO.getType()) - .likeIfPresent(OperateLogDO::getSubType, pageReqDTO.getSubType()) - .likeIfPresent(OperateLogDO::getAction, pageReqDTO.getAction()) - .betweenIfPresent(OperateLogDO::getCreateTime, pageReqDTO.getCreateTime()) - .orderByDesc(OperateLogDO::getId)); - } - - default PageResult selectPage(OperateLogPageReqDTO pageReqDTO) { - return selectPage(pageReqDTO, new LambdaQueryWrapperX() - .eqIfPresent(OperateLogDO::getType, pageReqDTO.getType()) - .eqIfPresent(OperateLogDO::getBizId, pageReqDTO.getBizId()) - .eqIfPresent(OperateLogDO::getUserId, pageReqDTO.getUserId()) - .orderByDesc(OperateLogDO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java deleted file mode 100644 index f9a9d7d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.mail; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface MailAccountMapper extends BaseMapperX { - - default PageResult selectPage(MailAccountPageReqVO pageReqVO) { - return selectPage(pageReqVO, new LambdaQueryWrapperX() - .likeIfPresent(MailAccountDO::getMail, pageReqVO.getMail()) - .likeIfPresent(MailAccountDO::getUsername , pageReqVO.getUsername()) - .orderByDesc(MailAccountDO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java deleted file mode 100644 index 6b147cf..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.mail; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface MailLogMapper extends BaseMapperX { - - default PageResult selectPage(MailLogPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(MailLogDO::getUserId, reqVO.getUserId()) - .eqIfPresent(MailLogDO::getUserType, reqVO.getUserType()) - .likeIfPresent(MailLogDO::getToMail, reqVO.getToMail()) - .eqIfPresent(MailLogDO::getAccountId, reqVO.getAccountId()) - .eqIfPresent(MailLogDO::getTemplateId, reqVO.getTemplateId()) - .eqIfPresent(MailLogDO::getSendStatus, reqVO.getSendStatus()) - .betweenIfPresent(MailLogDO::getSendTime, reqVO.getSendTime()) - .orderByDesc(MailLogDO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java deleted file mode 100644 index 1730e58..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.mail; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface MailTemplateMapper extends BaseMapperX { - - default PageResult selectPage(MailTemplatePageReqVO pageReqVO){ - return selectPage(pageReqVO , new LambdaQueryWrapperX() - .eqIfPresent(MailTemplateDO::getStatus, pageReqVO.getStatus()) - .likeIfPresent(MailTemplateDO::getCode, pageReqVO.getCode()) - .likeIfPresent(MailTemplateDO::getName, pageReqVO.getName()) - .eqIfPresent(MailTemplateDO::getAccountId, pageReqVO.getAccountId()) - .betweenIfPresent(MailTemplateDO::getCreateTime, pageReqVO.getCreateTime()) - .orderByDesc(MailTemplateDO::getId)); - } - - default Long selectCountByAccountId(Long accountId) { - return selectCount(MailTemplateDO::getAccountId, accountId); - } - - default MailTemplateDO selectByCode(String code) { - return selectOne(MailTemplateDO::getCode, code); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notice/NoticeMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notice/NoticeMapper.java deleted file mode 100644 index 67ff8db..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notice/NoticeMapper.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.notice; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface NoticeMapper extends BaseMapperX { - - default PageResult selectPage(NoticePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(NoticeDO::getTitle, reqVO.getTitle()) - .eqIfPresent(NoticeDO::getStatus, reqVO.getStatus()) - .orderByDesc(NoticeDO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java deleted file mode 100644 index e9ce6d7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.notify; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; -import org.apache.ibatis.annotations.Mapper; - -import java.time.LocalDateTime; -import java.util.Collection; -import java.util.List; - -@Mapper -public interface NotifyMessageMapper extends BaseMapperX { - - default PageResult selectPage(NotifyMessagePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(NotifyMessageDO::getUserId, reqVO.getUserId()) - .eqIfPresent(NotifyMessageDO::getUserType, reqVO.getUserType()) - .likeIfPresent(NotifyMessageDO::getTemplateCode, reqVO.getTemplateCode()) - .eqIfPresent(NotifyMessageDO::getTemplateType, reqVO.getTemplateType()) - .betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(NotifyMessageDO::getId)); - } - - default PageResult selectPage(NotifyMessageMyPageReqVO reqVO, Long userId, Integer userType) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(NotifyMessageDO::getReadStatus, reqVO.getReadStatus()) - .betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime()) - .eq(NotifyMessageDO::getUserId, userId) - .eq(NotifyMessageDO::getUserType, userType) - .orderByDesc(NotifyMessageDO::getId)); - } - - default int updateListRead(Collection ids, Long userId, Integer userType) { - return update(new NotifyMessageDO().setReadStatus(true).setReadTime(LocalDateTime.now()), - new LambdaQueryWrapperX() - .in(NotifyMessageDO::getId, ids) - .eq(NotifyMessageDO::getUserId, userId) - .eq(NotifyMessageDO::getUserType, userType) - .eq(NotifyMessageDO::getReadStatus, false)); - } - - default int updateListRead(Long userId, Integer userType) { - return update(new NotifyMessageDO().setReadStatus(true).setReadTime(LocalDateTime.now()), - new LambdaQueryWrapperX() - .eq(NotifyMessageDO::getUserId, userId) - .eq(NotifyMessageDO::getUserType, userType) - .eq(NotifyMessageDO::getReadStatus, false)); - } - - default List selectUnreadListByUserIdAndUserType(Long userId, Integer userType, Integer size) { - return selectList(new QueryWrapperX() // 由于要使用 limitN 语句,所以只能用 QueryWrapperX - .eq("user_id", userId) - .eq("user_type", userType) - .eq("read_status", false) - .orderByDesc("id").limitN(size)); - } - - default Long selectUnreadCountByUserIdAndUserType(Long userId, Integer userType) { - return selectCount(new LambdaQueryWrapperX() - .eq(NotifyMessageDO::getReadStatus, false) - .eq(NotifyMessageDO::getUserId, userId) - .eq(NotifyMessageDO::getUserType, userType)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java deleted file mode 100644 index 1fcb8ee..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.notify; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface NotifyTemplateMapper extends BaseMapperX { - - default NotifyTemplateDO selectByCode(String code) { - return selectOne(NotifyTemplateDO::getCode, code); - } - - default PageResult selectPage(NotifyTemplatePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(NotifyTemplateDO::getCode, reqVO.getCode()) - .likeIfPresent(NotifyTemplateDO::getName, reqVO.getName()) - .eqIfPresent(NotifyTemplateDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(NotifyTemplateDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(NotifyTemplateDO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java deleted file mode 100644 index 81ca13f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2AccessTokenMapper.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.oauth2; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import org.apache.ibatis.annotations.Mapper; - -import java.time.LocalDateTime; -import java.util.List; - -@Mapper -public interface OAuth2AccessTokenMapper extends BaseMapperX { - - @TenantIgnore // 获取 token 的时候,需要忽略租户编号。原因是:一些场景下,可能不会传递 tenant-id 请求头,例如说文件上传、积木报表等等 - default OAuth2AccessTokenDO selectByAccessToken(String accessToken) { - return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken); - } - - default List selectListByRefreshToken(String refreshToken) { - return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken); - } - - default PageResult selectPage(OAuth2AccessTokenPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(OAuth2AccessTokenDO::getUserId, reqVO.getUserId()) - .eqIfPresent(OAuth2AccessTokenDO::getUserType, reqVO.getUserType()) - .likeIfPresent(OAuth2AccessTokenDO::getClientId, reqVO.getClientId()) - .gt(OAuth2AccessTokenDO::getExpiresTime, LocalDateTime.now()) - .orderByDesc(OAuth2AccessTokenDO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java deleted file mode 100644 index 61a976f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ApproveMapper.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.oauth2; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface OAuth2ApproveMapper extends BaseMapperX { - - default int update(OAuth2ApproveDO updateObj) { - return update(updateObj, new LambdaQueryWrapperX() - .eq(OAuth2ApproveDO::getUserId, updateObj.getUserId()) - .eq(OAuth2ApproveDO::getUserType, updateObj.getUserType()) - .eq(OAuth2ApproveDO::getClientId, updateObj.getClientId()) - .eq(OAuth2ApproveDO::getScope, updateObj.getScope())); - } - - default List selectListByUserIdAndUserTypeAndClientId(Long userId, Integer userType, String clientId) { - return selectList(new LambdaQueryWrapperX() - .eq(OAuth2ApproveDO::getUserId, userId) - .eq(OAuth2ApproveDO::getUserType, userType) - .eq(OAuth2ApproveDO::getClientId, clientId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java deleted file mode 100755 index e6012b0..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.oauth2; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import org.apache.ibatis.annotations.Mapper; - - -/** - * OAuth2 客户端 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface OAuth2ClientMapper extends BaseMapperX { - - default PageResult selectPage(OAuth2ClientPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(OAuth2ClientDO::getName, reqVO.getName()) - .eqIfPresent(OAuth2ClientDO::getStatus, reqVO.getStatus()) - .orderByDesc(OAuth2ClientDO::getId)); - } - - default OAuth2ClientDO selectByClientId(String clientId) { - return selectOne(OAuth2ClientDO::getClientId, clientId); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java deleted file mode 100644 index c85581c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2CodeMapper.java +++ /dev/null @@ -1,14 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.oauth2; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface OAuth2CodeMapper extends BaseMapperX { - - default OAuth2CodeDO selectByCode(String code) { - return selectOne(OAuth2CodeDO::getCode, code); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java deleted file mode 100644 index bf91457..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2RefreshTokenMapper.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.oauth2; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface OAuth2RefreshTokenMapper extends BaseMapperX { - - default int deleteByRefreshToken(String refreshToken) { - return delete(new LambdaQueryWrapperX() - .eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken)); - } - - @TenantIgnore // 获取 token 的时候,需要忽略租户编号。原因是:一些场景下,可能不会传递 tenant-id 请求头,例如说文件上传、积木报表等等 - default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) { - return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/package-info.java deleted file mode 100644 index 6ad304f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/package-info.java +++ /dev/null @@ -1,9 +0,0 @@ -/** - * DAL = Data Access Layer 数据访问层 - * 1. data object:数据对象 - * 2. redis:Redis 的 CRUD 操作 - * 3. mysql:MySQL 的 CRUD 操作 - * - * 其中,MySQL 的表以 system_ 作为前缀 - */ -package cn.iocoder.yudao.module.system.dal.mysql; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java deleted file mode 100644 index 31eb117..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.permission; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface MenuMapper extends BaseMapperX { - - default MenuDO selectByParentIdAndName(Long parentId, String name) { - return selectOne(MenuDO::getParentId, parentId, MenuDO::getName, name); - } - - default Long selectCountByParentId(Long parentId) { - return selectCount(MenuDO::getParentId, parentId); - } - - default List selectList(MenuListReqVO reqVO) { - return selectList(new LambdaQueryWrapperX() - .likeIfPresent(MenuDO::getName, reqVO.getName()) - .eqIfPresent(MenuDO::getStatus, reqVO.getStatus())); - } - - default List selectListByPermission(String permission) { - return selectList(MenuDO::getPermission, permission); - } - - default MenuDO selectByComponentName(String componentName) { - return selectOne(MenuDO::getComponentName, componentName); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java deleted file mode 100644 index 2e4da2b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.permission; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import org.apache.ibatis.annotations.Mapper; -import org.springframework.lang.Nullable; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface RoleMapper extends BaseMapperX { - - default PageResult selectPage(RolePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(RoleDO::getName, reqVO.getName()) - .likeIfPresent(RoleDO::getCode, reqVO.getCode()) - .eqIfPresent(RoleDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(BaseDO::getCreateTime, reqVO.getCreateTime()) - .orderByAsc(RoleDO::getSort)); - } - - default RoleDO selectByName(String name) { - return selectOne(RoleDO::getName, name); - } - - default RoleDO selectByCode(String code) { - return selectOne(RoleDO::getCode, code); - } - - default List selectListByStatus(@Nullable Collection statuses) { - return selectList(RoleDO::getStatus, statuses); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java deleted file mode 100644 index 02cb6b8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.permission; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface RoleMenuMapper extends BaseMapperX { - - default List selectListByRoleId(Long roleId) { - return selectList(RoleMenuDO::getRoleId, roleId); - } - - default List selectListByRoleId(Collection roleIds) { - return selectList(RoleMenuDO::getRoleId, roleIds); - } - - default List selectListByMenuId(Long menuId) { - return selectList(RoleMenuDO::getMenuId, menuId); - } - - default void deleteListByRoleIdAndMenuIds(Long roleId, Collection menuIds) { - delete(new LambdaQueryWrapper() - .eq(RoleMenuDO::getRoleId, roleId) - .in(RoleMenuDO::getMenuId, menuIds)); - } - - default void deleteListByMenuId(Long menuId) { - delete(new LambdaQueryWrapper().eq(RoleMenuDO::getMenuId, menuId)); - } - - default void deleteListByRoleId(Long roleId) { - delete(new LambdaQueryWrapper().eq(RoleMenuDO::getRoleId, roleId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/UserRoleMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/UserRoleMapper.java deleted file mode 100644 index 12ff88a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/UserRoleMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.permission; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface UserRoleMapper extends BaseMapperX { - - default List selectListByUserId(Long userId) { - return selectList(UserRoleDO::getUserId, userId); - } - - default void deleteListByUserIdAndRoleIdIds(Long userId, Collection roleIds) { - delete(new LambdaQueryWrapper() - .eq(UserRoleDO::getUserId, userId) - .in(UserRoleDO::getRoleId, roleIds)); - } - - default void deleteListByUserId(Long userId) { - delete(new LambdaQueryWrapper().eq(UserRoleDO::getUserId, userId)); - } - - default void deleteListByRoleId(Long roleId) { - delete(new LambdaQueryWrapper().eq(UserRoleDO::getRoleId, roleId)); - } - - default List selectListByRoleIds(Collection roleIds) { - return selectList(UserRoleDO::getRoleId, roleIds); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java deleted file mode 100644 index 1f356ed..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.sms; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SmsChannelMapper extends BaseMapperX { - - default PageResult selectPage(SmsChannelPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(SmsChannelDO::getSignature, reqVO.getSignature()) - .eqIfPresent(SmsChannelDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(SmsChannelDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(SmsChannelDO::getId)); - } - - default SmsChannelDO selectByCode(String code) { - return selectOne(SmsChannelDO::getCode, code); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java deleted file mode 100644 index 599c1ab..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.sms; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SmsCodeMapper extends BaseMapperX { - - /** - * 获得手机号的最后一个手机验证码 - * - * @param mobile 手机号 - * @param scene 发送场景,选填 - * @param code 验证码 选填 - * @return 手机验证码 - */ - default SmsCodeDO selectLastByMobile(String mobile, String code, Integer scene) { - return selectOne(new QueryWrapperX() - .eq("mobile", mobile) - .eqIfPresent("scene", scene) - .eqIfPresent("code", code) - .orderByDesc("id") - .limitN(1)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsLogMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsLogMapper.java deleted file mode 100644 index f238871..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsLogMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.sms; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SmsLogMapper extends BaseMapperX { - - default PageResult selectPage(SmsLogPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(SmsLogDO::getChannelId, reqVO.getChannelId()) - .eqIfPresent(SmsLogDO::getTemplateId, reqVO.getTemplateId()) - .likeIfPresent(SmsLogDO::getMobile, reqVO.getMobile()) - .eqIfPresent(SmsLogDO::getSendStatus, reqVO.getSendStatus()) - .betweenIfPresent(SmsLogDO::getSendTime, reqVO.getSendTime()) - .eqIfPresent(SmsLogDO::getReceiveStatus, reqVO.getReceiveStatus()) - .betweenIfPresent(SmsLogDO::getReceiveTime, reqVO.getReceiveTime()) - .orderByDesc(SmsLogDO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsTemplateMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsTemplateMapper.java deleted file mode 100644 index a9a1ebb..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsTemplateMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.sms; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SmsTemplateMapper extends BaseMapperX { - - default SmsTemplateDO selectByCode(String code) { - return selectOne(SmsTemplateDO::getCode, code); - } - - default PageResult selectPage(SmsTemplatePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(SmsTemplateDO::getType, reqVO.getType()) - .eqIfPresent(SmsTemplateDO::getStatus, reqVO.getStatus()) - .likeIfPresent(SmsTemplateDO::getCode, reqVO.getCode()) - .likeIfPresent(SmsTemplateDO::getContent, reqVO.getContent()) - .likeIfPresent(SmsTemplateDO::getApiTemplateId, reqVO.getApiTemplateId()) - .eqIfPresent(SmsTemplateDO::getChannelId, reqVO.getChannelId()) - .betweenIfPresent(SmsTemplateDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(SmsTemplateDO::getId)); - } - - default Long selectCountByChannelId(Long channelId) { - return selectCount(SmsTemplateDO::getChannelId, channelId); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialClientMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialClientMapper.java deleted file mode 100644 index 4ebe226..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialClientMapper.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.social; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SocialClientMapper extends BaseMapperX { - - default SocialClientDO selectBySocialTypeAndUserType(Integer socialType, Integer userType) { - return selectOne(SocialClientDO::getSocialType, socialType, - SocialClientDO::getUserType, userType); - } - - default PageResult selectPage(SocialClientPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(SocialClientDO::getName, reqVO.getName()) - .eqIfPresent(SocialClientDO::getSocialType, reqVO.getSocialType()) - .eqIfPresent(SocialClientDO::getUserType, reqVO.getUserType()) - .likeIfPresent(SocialClientDO::getClientId, reqVO.getClientId()) - .eqIfPresent(SocialClientDO::getStatus, reqVO.getStatus()) - .orderByDesc(SocialClientDO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java deleted file mode 100644 index d9957f7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserBindMapper.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.social; - -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface SocialUserBindMapper extends BaseMapperX { - - default void deleteByUserTypeAndUserIdAndSocialType(Integer userType, Long userId, Integer socialType) { - delete(new LambdaQueryWrapperX() - .eq(SocialUserBindDO::getUserType, userType) - .eq(SocialUserBindDO::getUserId, userId) - .eq(SocialUserBindDO::getSocialType, socialType)); - } - - default void deleteByUserTypeAndSocialUserId(Integer userType, Long socialUserId) { - delete(new LambdaQueryWrapperX() - .eq(SocialUserBindDO::getUserType, userType) - .eq(SocialUserBindDO::getSocialUserId, socialUserId)); - } - - default SocialUserBindDO selectByUserTypeAndSocialUserId(Integer userType, Long socialUserId) { - return selectOne(SocialUserBindDO::getUserType, userType, - SocialUserBindDO::getSocialUserId, socialUserId); - } - - default List selectListByUserIdAndUserType(Long userId, Integer userType) { - return selectList(new LambdaQueryWrapperX() - .eq(SocialUserBindDO::getUserId, userId) - .eq(SocialUserBindDO::getUserType, userType)); - } - - default SocialUserBindDO selectByUserIdAndUserTypeAndSocialType(Long userId, Integer userType, Integer socialType) { - return selectOne(new LambdaQueryWrapperX() - .eq(SocialUserBindDO::getUserId, userId) - .eq(SocialUserBindDO::getUserType, userType) - .eq(SocialUserBindDO::getSocialType, socialType)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java deleted file mode 100644 index a90e6ac..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/social/SocialUserMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.social; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; -import org.apache.ibatis.annotations.Mapper; - -@Mapper -public interface SocialUserMapper extends BaseMapperX { - - default SocialUserDO selectByTypeAndCodeAnState(Integer type, String code, String state) { - return selectOne(SocialUserDO::getType, type, - SocialUserDO::getCode, code, - SocialUserDO::getState, state); - } - - default SocialUserDO selectByTypeAndOpenid(Integer type, String openid) { - return selectFirstOne(SocialUserDO::getType, type, - SocialUserDO::getOpenid, openid); - } - - default PageResult selectPage(SocialUserPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(SocialUserDO::getType, reqVO.getType()) - .likeIfPresent(SocialUserDO::getNickname, reqVO.getNickname()) - .likeIfPresent(SocialUserDO::getOpenid, reqVO.getOpenid()) - .betweenIfPresent(SocialUserDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(SocialUserDO::getId)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java deleted file mode 100755 index 8ddf060..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.tenant; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * 租户 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface TenantMapper extends BaseMapperX { - - default PageResult selectPage(TenantPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(TenantDO::getName, reqVO.getName()) - .likeIfPresent(TenantDO::getContactName, reqVO.getContactName()) - .likeIfPresent(TenantDO::getContactMobile, reqVO.getContactMobile()) - .eqIfPresent(TenantDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(TenantDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(TenantDO::getId)); - } - - default TenantDO selectByName(String name) { - return selectOne(TenantDO::getName, name); - } - - default TenantDO selectByWebsite(String website) { - return selectOne(TenantDO::getWebsite, website); - } - - default Long selectCountByPackageId(Long packageId) { - return selectCount(TenantDO::getPackageId, packageId); - } - - default List selectListByPackageId(Long packageId) { - return selectList(TenantDO::getPackageId, packageId); - } - - default List selectListByStatus(Integer status) { - return selectList(TenantDO::getStatus, status); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantPackageMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantPackageMapper.java deleted file mode 100755 index 2918617..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantPackageMapper.java +++ /dev/null @@ -1,36 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.tenant; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * 租户套餐 Mapper - * - * @author 芋道源码 - */ -@Mapper -public interface TenantPackageMapper extends BaseMapperX { - - default PageResult selectPage(TenantPackagePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(TenantPackageDO::getName, reqVO.getName()) - .eqIfPresent(TenantPackageDO::getStatus, reqVO.getStatus()) - .likeIfPresent(TenantPackageDO::getRemark, reqVO.getRemark()) - .betweenIfPresent(TenantPackageDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(TenantPackageDO::getId)); - } - - default List selectListByStatus(Integer status) { - return selectList(TenantPackageDO::getStatus, status); - } - - default TenantPackageDO selectByName(String name) { - return selectOne(TenantPackageDO::getName, name); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java deleted file mode 100644 index c0c9be8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java +++ /dev/null @@ -1,51 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.mysql.user; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface AdminUserMapper extends BaseMapperX { - - default AdminUserDO selectByUsername(String username) { - return selectOne(AdminUserDO::getUsername, username); - } - - default AdminUserDO selectByEmail(String email) { - return selectOne(AdminUserDO::getEmail, email); - } - - default AdminUserDO selectByMobile(String mobile) { - return selectOne(AdminUserDO::getMobile, mobile); - } - - default PageResult selectPage(UserPageReqVO reqVO, Collection deptIds, Collection userIds) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(AdminUserDO::getUsername, reqVO.getUsername()) - .likeIfPresent(AdminUserDO::getMobile, reqVO.getMobile()) - .eqIfPresent(AdminUserDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(AdminUserDO::getCreateTime, reqVO.getCreateTime()) - .inIfPresent(AdminUserDO::getDeptId, deptIds) - .inIfPresent(AdminUserDO::getId, userIds) - .orderByDesc(AdminUserDO::getId)); - } - - default List selectListByNickname(String nickname) { - return selectList(new LambdaQueryWrapperX().like(AdminUserDO::getNickname, nickname)); - } - - default List selectListByStatus(Integer status) { - return selectList(AdminUserDO::getStatus, status); - } - - default List selectListByDeptIds(Collection deptIds) { - return selectList(AdminUserDO::getDeptId, deptIds); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java deleted file mode 100644 index 1cd5830..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java +++ /dev/null @@ -1,110 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.redis; - -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; - -/** - * System Redis Key 枚举类 - * - * @author 芋道源码 - */ -public interface RedisKeyConstants { - - /** - * 指定部门的所有子部门编号数组的缓存 - *

- * KEY 格式:dept_children_ids:{id} - * VALUE 数据类型:String 子部门编号集合 - */ - String DEPT_CHILDREN_ID_LIST = "dept_children_ids"; - - /** - * 角色的缓存 - *

- * KEY 格式:role:{id} - * VALUE 数据类型:String 角色信息 - */ - String ROLE = "role"; - - /** - * 用户拥有的角色编号的缓存 - *

- * KEY 格式:user_role_ids:{userId} - * VALUE 数据类型:String 角色编号集合 - */ - String USER_ROLE_ID_LIST = "user_role_ids"; - - /** - * 拥有指定菜单的角色编号的缓存 - *

- * KEY 格式:user_role_ids:{menuId} - * VALUE 数据类型:String 角色编号集合 - */ - String MENU_ROLE_ID_LIST = "menu_role_ids"; - - /** - * 拥有权限对应的菜单编号数组的缓存 - *

- * KEY 格式:permission_menu_ids:{permission} - * VALUE 数据类型:String 菜单编号数组 - */ - String PERMISSION_MENU_ID_LIST = "permission_menu_ids"; - - /** - * OAuth2 客户端的缓存 - *

- * KEY 格式:oauth_client:{id} - * VALUE 数据类型:String 客户端信息 - */ - String OAUTH_CLIENT = "oauth_client"; - - /** - * 访问令牌的缓存 - *

- * KEY 格式:oauth2_access_token:{token} - * VALUE 数据类型:String 访问令牌信息 {@link OAuth2AccessTokenDO} - *

- * 由于动态过期时间,使用 RedisTemplate 操作 - */ - String OAUTH2_ACCESS_TOKEN = "oauth2_access_token:%s"; - - /** - * 站内信模版的缓存 - *

- * KEY 格式:notify_template:{code} - * VALUE 数据格式:String 模版信息 - */ - String NOTIFY_TEMPLATE = "notify_template"; - - /** - * 邮件账号的缓存 - *

- * KEY 格式:mail_account:{id} - * VALUE 数据格式:String 账号信息 - */ - String MAIL_ACCOUNT = "mail_account"; - - /** - * 邮件模版的缓存 - *

- * KEY 格式:mail_template:{code} - * VALUE 数据格式:String 模版信息 - */ - String MAIL_TEMPLATE = "mail_template"; - - /** - * 短信模版的缓存 - *

- * KEY 格式:sms_template:{id} - * VALUE 数据格式:String 模版信息 - */ - String SMS_TEMPLATE = "sms_template"; - - /** - * 小程序订阅模版的缓存 - * - * KEY 格式:wxa_subscribe_template:{userType} - * VALUE 数据格式 String, 模版信息 - */ - String WXA_SUBSCRIBE_TEMPLATE = "wxa_subscribe_template"; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java deleted file mode 100644 index 81501b4..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/oauth2/OAuth2AccessTokenRedisDAO.java +++ /dev/null @@ -1,59 +0,0 @@ -package cn.iocoder.yudao.module.system.dal.redis.oauth2; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Repository; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN; - -/** - * {@link OAuth2AccessTokenDO} 的 RedisDAO - * - * @author 芋道源码 - */ -@Repository -public class OAuth2AccessTokenRedisDAO { - - @Resource - private StringRedisTemplate stringRedisTemplate; - - public OAuth2AccessTokenDO get(String accessToken) { - String redisKey = formatKey(accessToken); - return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class); - } - - public void set(OAuth2AccessTokenDO accessTokenDO) { - String redisKey = formatKey(accessTokenDO.getAccessToken()); - // 清理多余字段,避免缓存 - accessTokenDO.setUpdater(null).setUpdateTime(null).setCreateTime(null).setCreator(null).setDeleted(null); - long time = LocalDateTimeUtil.between(LocalDateTime.now(), accessTokenDO.getExpiresTime(), ChronoUnit.SECONDS); - if (time > 0) { - stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(accessTokenDO), time, TimeUnit.SECONDS); - } - } - - public void delete(String accessToken) { - String redisKey = formatKey(accessToken); - stringRedisTemplate.delete(redisKey); - } - - public void deleteList(Collection accessTokens) { - List redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey); - stringRedisTemplate.delete(redisKeys); - } - - private static String formatKey(String accessToken) { - return String.format(OAUTH2_ACCESS_TOKEN, accessToken); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/YudaoCaptchaConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/YudaoCaptchaConfiguration.java deleted file mode 100644 index 5db497e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/config/YudaoCaptchaConfiguration.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.captcha.config; - -import cn.iocoder.yudao.module.system.framework.captcha.core.RedisCaptchaServiceImpl; -import com.anji.captcha.config.AjCaptchaAutoConfiguration; -import com.anji.captcha.properties.AjCaptchaProperties; -import com.anji.captcha.service.CaptchaCacheService; -import com.anji.captcha.service.impl.CaptchaServiceFactory; -import org.springframework.boot.autoconfigure.ImportAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.data.redis.core.StringRedisTemplate; - -/** - * 验证码的配置类 - * - * @author 芋道源码 - */ -@Configuration(proxyBeanMethods = false) -@ImportAutoConfiguration(AjCaptchaAutoConfiguration.class) // 目的:解决 aj-captcha 针对 SpringBoot 3.X 自动配置不生效的问题 -public class YudaoCaptchaConfiguration { - - @Bean(name = "AjCaptchaCacheService") - @Primary - public CaptchaCacheService captchaCacheService(AjCaptchaProperties config, - StringRedisTemplate stringRedisTemplate) { - CaptchaCacheService captchaCacheService = CaptchaServiceFactory.getCache(config.getCacheType().name()); - if (captchaCacheService instanceof RedisCaptchaServiceImpl) { - ((RedisCaptchaServiceImpl) captchaCacheService).setStringRedisTemplate(stringRedisTemplate); - } - return captchaCacheService; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/RedisCaptchaServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/RedisCaptchaServiceImpl.java deleted file mode 100644 index 2e66a67..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/RedisCaptchaServiceImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.captcha.core; - -import com.anji.captcha.service.CaptchaCacheService; -import lombok.Setter; -import org.springframework.data.redis.core.StringRedisTemplate; - -import java.util.concurrent.TimeUnit; - -/** - * 基于 Redis 实现验证码的存储 - * - * @author 星语 - */ -@Setter -public class RedisCaptchaServiceImpl implements CaptchaCacheService { - - private StringRedisTemplate stringRedisTemplate; - - @Override - public String type() { - return "redis"; - } - - @Override - public void set(String key, String value, long expiresInSeconds) { - stringRedisTemplate.opsForValue().set(key, value, expiresInSeconds, TimeUnit.SECONDS); - } - - @Override - public boolean exists(String key) { - return stringRedisTemplate.hasKey(key); - } - - @Override - public void delete(String key) { - stringRedisTemplate.delete(key); - } - - @Override - public String get(String key) { - return stringRedisTemplate.opsForValue().get(key); - } - - @Override - public Long increment(String key, long val) { - return stringRedisTemplate.opsForValue().increment(key,val); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/package-info.java deleted file mode 100644 index a94be21..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * 验证码拓展 - * - * 基于 aj-captcha 实现滑块验证码,文档:https://ajcaptcha.beliefteam.cn/captcha-doc/ - * - * @author 星语 - */ -package cn.iocoder.yudao.module.system.framework.captcha; \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java deleted file mode 100644 index 136866c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/config/DataPermissionConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.datapermission.config; - -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.framework.datapermission.core.rule.dept.DeptDataPermissionRuleCustomizer; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * system 模块的数据权限 Configuration - * - * @author 芋道源码 - */ -@Configuration(proxyBeanMethods = false) -public class DataPermissionConfiguration { - - @Bean - public DeptDataPermissionRuleCustomizer sysDeptDataPermissionRuleCustomizer() { - return rule -> { - // dept - rule.addDeptColumn(AdminUserDO.class); - rule.addDeptColumn(DeptDO.class, "id"); - // user - rule.addUserColumn(AdminUserDO.class, "id"); - }; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/package-info.java deleted file mode 100644 index a666845..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/datapermission/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * system 模块的数据权限配置 - */ -package cn.iocoder.yudao.module.system.framework.datapermission; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/config/YudaoJustAuthConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/config/YudaoJustAuthConfiguration.java deleted file mode 100644 index 56a24ee..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/config/YudaoJustAuthConfiguration.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.justauth.config; - -import cn.iocoder.yudao.module.system.framework.justauth.core.AuthRequestFactory; -import com.xkcoding.justauth.autoconfigure.JustAuthProperties; -import com.xkcoding.justauth.support.cache.RedisStateCache; -import me.zhyd.oauth.cache.AuthStateCache; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.data.redis.core.RedisTemplate; - -/** - * JustAuth 配置类 TODO 芋艿:等 justauth 1.4.1 版本发布!!! - * - * @author 芋道源码 - */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties({JustAuthProperties.class}) -public class YudaoJustAuthConfiguration { - - @Bean - @ConditionalOnProperty( - prefix = "justauth", - value = {"enabled"}, - havingValue = "true", - matchIfMissing = true - ) - public AuthRequestFactory authRequestFactory(JustAuthProperties properties, AuthStateCache authStateCache) { - return new AuthRequestFactory(properties, authStateCache); - } - - @Bean - public AuthStateCache authStateCache(RedisTemplate justAuthRedisCacheTemplate, - JustAuthProperties justAuthProperties) { - return new RedisStateCache(justAuthRedisCacheTemplate, justAuthProperties.getCache()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java deleted file mode 100644 index 4ae8b78..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/core/AuthRequestFactory.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (c) 2019-2029, xkcoding & Yangkai.Shen & 沈扬凯 (237497819@qq.com & xkcoding.com). - *

- * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0; - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - *

- * http://www.gnu.org/licenses/lgpl.html - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cn.iocoder.yudao.module.system.framework.justauth.core; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.EnumUtil; -import cn.hutool.core.util.ReflectUtil; -import cn.hutool.core.util.StrUtil; -import com.xkcoding.http.config.HttpConfig; -import com.xkcoding.justauth.autoconfigure.ExtendProperties; -import com.xkcoding.justauth.autoconfigure.JustAuthProperties; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import me.zhyd.oauth.cache.AuthStateCache; -import me.zhyd.oauth.config.AuthConfig; -import me.zhyd.oauth.config.AuthDefaultSource; -import me.zhyd.oauth.config.AuthSource; -import me.zhyd.oauth.enums.AuthResponseStatus; -import me.zhyd.oauth.exception.AuthException; -import me.zhyd.oauth.request.*; -import org.springframework.util.CollectionUtils; - -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -// TODO @芋艿:等官方发布 1.4.1!!! -/** - *

- * AuthRequest工厂类 - *

- * - * @author yangkai.shen - * @date Created in 2019-07-22 14:21 - */ -@Slf4j -@RequiredArgsConstructor -public class AuthRequestFactory { - private final JustAuthProperties properties; - private final AuthStateCache authStateCache; - - /** - * 返回当前Oauth列表 - * - * @return Oauth列表 - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - public List oauthList() { - // 默认列表 - List defaultList = new ArrayList<>(properties.getType().keySet()); - // 扩展列表 - List extendList = new ArrayList<>(); - ExtendProperties extend = properties.getExtend(); - if (null != extend) { - Class enumClass = extend.getEnumClass(); - List names = EnumUtil.getNames(enumClass); - // 扩展列表 - extendList = extend.getConfig() - .keySet() - .stream() - .filter(x -> names.contains(x.toUpperCase())) - .map(String::toUpperCase) - .collect(Collectors.toList()); - } - - // 合并 - return (List) CollUtil.addAll(defaultList, extendList); - } - - /** - * 返回AuthRequest对象 - * - * @param source {@link AuthSource} - * @return {@link AuthRequest} - */ - public AuthRequest get(String source) { - if (StrUtil.isBlank(source)) { - throw new AuthException(AuthResponseStatus.NO_AUTH_SOURCE); - } - - // 获取 JustAuth 中已存在的 - AuthRequest authRequest = getDefaultRequest(source); - - // 如果获取不到则尝试取自定义的 - if (authRequest == null) { - authRequest = getExtendRequest(properties.getExtend().getEnumClass(), source); - } - - if (authRequest == null) { - throw new AuthException(AuthResponseStatus.UNSUPPORTED); - } - - return authRequest; - } - - /** - * 获取自定义的 request - * - * @param clazz 枚举类 {@link AuthSource} - * @param source {@link AuthSource} - * @return {@link AuthRequest} - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - private AuthRequest getExtendRequest(Class clazz, String source) { - String upperSource = source.toUpperCase(); - try { - EnumUtil.fromString(clazz, upperSource); - } catch (IllegalArgumentException e) { - // 无自定义匹配 - return null; - } - - Map extendConfig = properties.getExtend().getConfig(); - - // key 转大写 - Map upperConfig = new HashMap<>(6); - extendConfig.forEach((k, v) -> upperConfig.put(k.toUpperCase(), v)); - - ExtendProperties.ExtendRequestConfig extendRequestConfig = upperConfig.get(upperSource); - if (extendRequestConfig != null) { - - // 配置 http config - configureHttpConfig(upperSource, extendRequestConfig, properties.getHttpConfig()); - - Class requestClass = extendRequestConfig.getRequestClass(); - - if (requestClass != null) { - // 反射获取 Request 对象,所以必须实现 2 个参数的构造方法 - return ReflectUtil.newInstance(requestClass, (AuthConfig) extendRequestConfig, authStateCache); - } - } - - return null; - } - - - /** - * 获取默认的 Request - * - * @param source {@link AuthSource} - * @return {@link AuthRequest} - */ - private AuthRequest getDefaultRequest(String source) { - AuthDefaultSource authDefaultSource; - - try { - authDefaultSource = EnumUtil.fromString(AuthDefaultSource.class, source.toUpperCase()); - } catch (IllegalArgumentException e) { - // 无自定义匹配 - return null; - } - - AuthConfig config = properties.getType().get(authDefaultSource.name()); - // 找不到对应关系,直接返回空 - if (config == null) { - return null; - } - - // 配置 http config - configureHttpConfig(authDefaultSource.name(), config, properties.getHttpConfig()); - - switch (authDefaultSource) { - case GITHUB: - return new AuthGithubRequest(config, authStateCache); - case WEIBO: - return new AuthWeiboRequest(config, authStateCache); - case GITEE: - return new AuthGiteeRequest(config, authStateCache); - case DINGTALK: - return new AuthDingTalkRequest(config, authStateCache); - case DINGTALK_V2: - return new AuthDingTalkV2Request(config, authStateCache); - case DINGTALK_ACCOUNT: - return new AuthDingTalkAccountRequest(config, authStateCache); - case BAIDU: - return new AuthBaiduRequest(config, authStateCache); - case CSDN: - return new AuthCsdnRequest(config, authStateCache); - case CODING: - return new AuthCodingRequest(config, authStateCache); - case OSCHINA: - return new AuthOschinaRequest(config, authStateCache); - case ALIPAY: - return new AuthAlipayRequest(config, authStateCache); - case QQ: - return new AuthQqRequest(config, authStateCache); - case WECHAT_OPEN: - return new AuthWeChatOpenRequest(config, authStateCache); - case WECHAT_MP: - return new AuthWeChatMpRequest(config, authStateCache); - case TAOBAO: - return new AuthTaobaoRequest(config, authStateCache); - case GOOGLE: - return new AuthGoogleRequest(config, authStateCache); - case FACEBOOK: - return new AuthFacebookRequest(config, authStateCache); - case DOUYIN: - return new AuthDouyinRequest(config, authStateCache); - case LINKEDIN: - return new AuthLinkedinRequest(config, authStateCache); - case MICROSOFT: - return new AuthMicrosoftRequest(config, authStateCache); - case MICROSOFT_CN: - return new AuthMicrosoftCnRequest(config, authStateCache); - - case MI: - return new AuthMiRequest(config, authStateCache); - case TOUTIAO: - return new AuthToutiaoRequest(config, authStateCache); - case TEAMBITION: - return new AuthTeambitionRequest(config, authStateCache); - case RENREN: - return new AuthRenrenRequest(config, authStateCache); - case PINTEREST: - return new AuthPinterestRequest(config, authStateCache); - case STACK_OVERFLOW: - return new AuthStackOverflowRequest(config, authStateCache); - case HUAWEI: - return new AuthHuaweiRequest(config, authStateCache); - case HUAWEI_V3: - return new AuthHuaweiV3Request(config, authStateCache); - case WECHAT_ENTERPRISE: - return new AuthWeChatEnterpriseQrcodeRequest(config, authStateCache); - case WECHAT_ENTERPRISE_V2: - return new AuthWeChatEnterpriseQrcodeV2Request(config, authStateCache); - case WECHAT_ENTERPRISE_QRCODE_THIRD: - return new AuthWeChatEnterpriseThirdQrcodeRequest(config, authStateCache); - case WECHAT_ENTERPRISE_WEB: - return new AuthWeChatEnterpriseWebRequest(config, authStateCache); - case KUJIALE: - return new AuthKujialeRequest(config, authStateCache); - case GITLAB: - return new AuthGitlabRequest(config, authStateCache); - case MEITUAN: - return new AuthMeituanRequest(config, authStateCache); - case ELEME: - return new AuthElemeRequest(config, authStateCache); - case TWITTER: - return new AuthTwitterRequest(config, authStateCache); - case FEISHU: - return new AuthFeishuRequest(config, authStateCache); - case JD: - return new AuthJdRequest(config, authStateCache); - case ALIYUN: - return new AuthAliyunRequest(config, authStateCache); - case XMLY: - return new AuthXmlyRequest(config, authStateCache); - case AMAZON: - return new AuthAmazonRequest(config, authStateCache); - case SLACK: - return new AuthSlackRequest(config, authStateCache); - case LINE: - return new AuthLineRequest(config, authStateCache); - case OKTA: - return new AuthOktaRequest(config, authStateCache); - case PROGINN: - return new AuthProginnRequest(config,authStateCache); - case AFDIAN: - return new AuthAfDianRequest(config,authStateCache); - case APPLE: - return new AuthAppleRequest(config,authStateCache); - case FIGMA: - return new AuthFigmaRequest(config,authStateCache); - case WECHAT_MINI_PROGRAM: - config.setIgnoreCheckRedirectUri(true); - config.setIgnoreCheckState(true); - return new AuthWechatMiniProgramRequest(config, authStateCache); - case QQ_MINI_PROGRAM: - config.setIgnoreCheckRedirectUri(true); - config.setIgnoreCheckState(true); - return new AuthQQMiniProgramRequest(config, authStateCache); - default: - return null; - } - } - - /** - * 配置 http 相关的配置 - * - * @param authSource {@link AuthSource} - * @param authConfig {@link AuthConfig} - */ - private void configureHttpConfig(String authSource, AuthConfig authConfig, JustAuthProperties.JustAuthHttpConfig httpConfig) { - if (null == httpConfig) { - return; - } - Map proxyConfigMap = httpConfig.getProxy(); - if (CollectionUtils.isEmpty(proxyConfigMap)) { - return; - } - JustAuthProperties.JustAuthProxyConfig proxyConfig = proxyConfigMap.get(authSource); - - if (null == proxyConfig) { - return; - } - - authConfig.setHttpConfig(HttpConfig.builder() - .timeout(httpConfig.getTimeout()) - .proxy(new Proxy(Proxy.Type.valueOf(proxyConfig.getType()), new InetSocketAddress(proxyConfig.getHostname(), proxyConfig.getPort()))) - .build()); - } -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/package-info.java deleted file mode 100644 index e9af3ab..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/justauth/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * justauth 三方登录的拓展 - * - * @author 芋道源码 - */ -package cn.iocoder.yudao.module.system.framework.justauth; \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java deleted file mode 100644 index f071251..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AdminUserParseFunction.java +++ /dev/null @@ -1,51 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.operatelog.core; - -import cn.hutool.core.convert.Convert; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import com.mzt.logapi.service.IParseFunction; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 管理员名字的 {@link IParseFunction} 实现类 - * - * @author HUIHUI - */ -@Slf4j -@Component -public class AdminUserParseFunction implements IParseFunction { - - public static final String NAME = "getAdminUserById"; - - @Resource - private AdminUserService adminUserService; - - @Override - public String functionName() { - return NAME; - } - - @Override - public String apply(Object value) { - if (StrUtil.isEmptyIfStr(value)) { - return ""; - } - - // 获取用户信息 - AdminUserDO user = adminUserService.getUser(Convert.toLong(value)); - if (user == null) { - log.warn("[apply][获取用户{{}}为空", value); - return ""; - } - // 返回格式 芋道源码(13888888888) - String nickname = user.getNickname(); - if (StrUtil.isEmpty(user.getMobile())) { - return nickname; - } - return StrUtil.format("{}({})", nickname, user.getMobile()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java deleted file mode 100644 index f2b9089..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/AreaParseFunction.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.operatelog.core; - -import cn.hutool.core.convert.Convert; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; -import com.mzt.logapi.service.IParseFunction; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 地名的 {@link IParseFunction} 实现类 - * - * @author HUIHUI - */ -@Slf4j -@Component -public class AreaParseFunction implements IParseFunction { - - public static final String NAME = "getArea"; - - @Override - public boolean executeBefore() { - return true; // 先转换值后对比 - } - - @Override - public String functionName() { - return NAME; - } - - @Override - public String apply(Object value) { - if (StrUtil.isEmptyIfStr(value)) { - return ""; - } - return AreaUtils.format(Convert.toInt(value)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/BooleanParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/BooleanParseFunction.java deleted file mode 100644 index dacb1cd..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/BooleanParseFunction.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.operatelog.core; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; -import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; -import com.mzt.logapi.service.IParseFunction; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 是否类型的 {@link IParseFunction} 实现类 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class BooleanParseFunction implements IParseFunction { - - public static final String NAME = "getBoolean"; - - @Override - public boolean executeBefore() { - return true; // 先转换值后对比 - } - - @Override - public String functionName() { - return NAME; - } - - @Override - public String apply(Object value) { - if (StrUtil.isEmptyIfStr(value)) { - return ""; - } - return DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.BOOLEAN_STRING, value.toString()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java deleted file mode 100644 index 125c324..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/DeptParseFunction.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.operatelog.core; - -import cn.hutool.core.convert.Convert; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import com.mzt.logapi.service.IParseFunction; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 部门名字的 {@link IParseFunction} 实现类 - * - * @author HUIHUI - */ -@Slf4j -@Component -public class DeptParseFunction implements IParseFunction { - - public static final String NAME = "getDeptById"; - - @Resource - private DeptService deptService; - - @Override - public String functionName() { - return NAME; - } - - @Override - public String apply(Object value) { - if (StrUtil.isEmptyIfStr(value)) { - return ""; - } - - // 获取部门信息 - DeptDO dept = deptService.getDept(Convert.toLong(value)); - if (dept == null) { - log.warn("[apply][获取部门{{}}为空", value); - return ""; - } - return dept.getName(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java deleted file mode 100644 index e246d12..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/PostParseFunction.java +++ /dev/null @@ -1,46 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.operatelog.core; - -import cn.hutool.core.convert.Convert; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import cn.iocoder.yudao.module.system.service.dept.PostService; -import com.mzt.logapi.service.IParseFunction; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 岗位名字的 {@link IParseFunction} 实现类 - * - * @author HUIHUI - */ -@Slf4j -@Component -public class PostParseFunction implements IParseFunction { - - public static final String NAME = "getPostById"; - - @Resource - private PostService postService; - - @Override - public String functionName() { - return NAME; - } - - @Override - public String apply(Object value) { - if (StrUtil.isEmptyIfStr(value)) { - return ""; - } - - // 获取岗位信息 - PostDO post = postService.getPost(Convert.toLong(value)); - if (post == null) { - log.warn("[apply][获取岗位{{}}为空", value); - return ""; - } - return post.getName(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/SexParseFunction.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/SexParseFunction.java deleted file mode 100644 index 684b388..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/core/SexParseFunction.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.operatelog.core; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils; -import cn.iocoder.yudao.module.system.enums.DictTypeConstants; -import com.mzt.logapi.service.IParseFunction; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * 行业的 {@link IParseFunction} 实现类 - * - * @author HUIHUI - */ -@Component -@Slf4j -public class SexParseFunction implements IParseFunction { - - public static final String NAME = "getSex"; - - @Override - public boolean executeBefore() { - return true; // 先转换值后对比 - } - - @Override - public String functionName() { - return NAME; - } - - @Override - public String apply(Object value) { - if (StrUtil.isEmptyIfStr(value)) { - return ""; - } - return DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.USER_SEX, value.toString()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java deleted file mode 100644 index c7f1248..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/operatelog/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位文件,避免文件夹缩进 - */ -package cn.iocoder.yudao.module.system.framework.operatelog; \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/package-info.java deleted file mode 100644 index 4b84406..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 属于 system 模块的 framework 封装 - * - * @author 芋道源码 - */ -package cn.iocoder.yudao.module.system.framework; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsCodeProperties.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsCodeProperties.java deleted file mode 100644 index 16f15a5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsCodeProperties.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.config; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.validation.annotation.Validated; - -import jakarta.validation.constraints.NotNull; -import java.time.Duration; - -@ConfigurationProperties(prefix = "yudao.sms-code") -@Validated -@Data -public class SmsCodeProperties { - - /** - * 过期时间 - */ - @NotNull(message = "过期时间不能为空") - private Duration expireTimes; - /** - * 短信发送频率 - */ - @NotNull(message = "短信发送频率不能为空") - private Duration sendFrequency; - /** - * 每日发送最大数量 - */ - @NotNull(message = "每日发送最大数量不能为空") - private Integer sendMaximumQuantityPerDay; - /** - * 验证码最小值 - */ - @NotNull(message = "验证码最小值不能为空") - private Integer beginCode; - /** - * 验证码最大值 - */ - @NotNull(message = "验证码最大值不能为空") - private Integer endCode; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsConfiguration.java deleted file mode 100644 index eb34775..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/config/SmsConfiguration.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.config; - -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClientFactory; -import cn.iocoder.yudao.module.system.framework.sms.core.client.impl.SmsClientFactoryImpl; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * 短信配置类,包括短信客户端、短信验证码两部分 - * - * @author 芋道源码 - */ -@Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(SmsCodeProperties.class) -public class SmsConfiguration { - - @Bean - public SmsClientFactory smsClientFactory() { - return new SmsClientFactoryImpl(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClient.java deleted file mode 100644 index 5d7b809..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClient.java +++ /dev/null @@ -1,56 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; - -import java.util.List; - -/** - * 短信客户端,用于对接各短信平台的 SDK,实现短信发送等功能 - * - * @author zzf - * @since 2021/1/25 14:14 - */ -public interface SmsClient { - - /** - * 获得渠道编号 - * - * @return 渠道编号 - */ - Long getId(); - - /** - * 发送消息 - * - * @param logId 日志编号 - * @param mobile 手机号 - * @param apiTemplateId 短信 API 的模板编号 - * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序 - * @return 短信发送结果 - */ - SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId, - List> templateParams) throws Throwable; - - /** - * 解析接收短信的接收结果 - * - * @param text 结果 - * @return 结果内容 - * @throws Throwable 当解析 text 发生异常时,则会抛出异常 - */ - List parseSmsReceiveStatus(String text) throws Throwable; - - /** - * 查询指定的短信模板 - * - * 如果查询失败,则返回 null 空 - * - * @param apiTemplateId 短信 API 的模板编号 - * @return 短信模板 - */ - SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClientFactory.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClientFactory.java deleted file mode 100644 index ad878b7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/SmsClientFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client; - -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; - -/** - * 短信客户端的工厂接口 - * - * @author zzf - * @since 2021/1/28 14:01 - */ -public interface SmsClientFactory { - - /** - * 获得短信 Client - * - * @param channelId 渠道编号 - * @return 短信 Client - */ - SmsClient getSmsClient(Long channelId); - - /** - * 获得短信 Client - * - * @param channelCode 渠道编码 - * @return 短信 Client - */ - SmsClient getSmsClient(String channelCode); - - /** - * 创建短信 Client - * - * @param properties 配置对象 - * @return 短信 Client - */ - SmsClient createOrUpdateSmsClient(SmsChannelProperties properties); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsReceiveRespDTO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsReceiveRespDTO.java deleted file mode 100644 index c90a33e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsReceiveRespDTO.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.dto; - -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * 消息接收 Response DTO - * - * @author 芋道源码 - */ -@Data -public class SmsReceiveRespDTO { - - /** - * 是否接收成功 - */ - private Boolean success; - /** - * API 接收结果的编码 - */ - private String errorCode; - /** - * API 接收结果的说明 - */ - private String errorMsg; - - /** - * 手机号 - */ - private String mobile; - /** - * 用户接收时间 - */ - private LocalDateTime receiveTime; - - /** - * 短信 API 发送返回的序号 - */ - private String serialNo; - /** - * 短信日志编号 - * - * 对应 SysSmsLogDO 的编号 - */ - private Long logId; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsSendRespDTO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsSendRespDTO.java deleted file mode 100644 index 9f3094b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsSendRespDTO.java +++ /dev/null @@ -1,43 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.dto; - -import lombok.Data; - -/** - * 短信发送 Response DTO - * - * @author 芋道源码 - */ -@Data -public class SmsSendRespDTO { - - /** - * 是否成功 - */ - private Boolean success; - - /** - * API 请求编号 - */ - private String apiRequestId; - - // ==================== 成功时字段 ==================== - - /** - * 短信 API 发送返回的序号 - */ - private String serialNo; - - // ==================== 失败时字段 ==================== - - /** - * API 返回错误码 - * - * 由于第三方的错误码可能是字符串,所以使用 String 类型 - */ - private String apiCode; - /** - * API 返回提示 - */ - private String apiMsg; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsTemplateRespDTO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsTemplateRespDTO.java deleted file mode 100644 index 38a43aa..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/dto/SmsTemplateRespDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.dto; - -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import lombok.Data; - -/** - * 短信模板 Response DTO - * - * @author 芋道源码 - */ -@Data -public class SmsTemplateRespDTO { - - /** - * 模板编号 - */ - private String id; - /** - * 短信内容 - */ - private String content; - /** - * 审核状态 - * - * 枚举 {@link SmsTemplateAuditStatusEnum} - */ - private Integer auditStatus; - /** - * 审核未通过的理由 - */ - private String auditReason; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AbstractSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AbstractSmsClient.java deleted file mode 100644 index a1883bf..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AbstractSmsClient.java +++ /dev/null @@ -1,48 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import lombok.extern.slf4j.Slf4j; - -/** - * 短信客户端的抽象类,提供模板方法,减少子类的冗余代码 - * - * @author zzf - * @since 2021/2/1 9:28 - */ -@Slf4j -public abstract class AbstractSmsClient implements SmsClient { - - /** - * 短信渠道配置 - */ - protected volatile SmsChannelProperties properties; - - public AbstractSmsClient(SmsChannelProperties properties) { - this.properties = properties; - } - - /** - * 初始化 - */ - public final void init() { - log.debug("[init][配置({}) 初始化完成]", properties); - } - - public final void refresh(SmsChannelProperties properties) { - // 判断是否更新 - if (properties.equals(this.properties)) { - return; - } - log.info("[refresh][配置({})发生变化,重新初始化]", properties); - this.properties = properties; - // 初始化 - this.init(); - } - - @Override - public Long getId() { - return properties.getId(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java deleted file mode 100644 index 3dd1249..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java +++ /dev/null @@ -1,193 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.hutool.core.date.format.FastDateFormat; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.crypto.digest.DigestUtil; -import cn.hutool.json.JSONArray; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import com.google.common.annotations.VisibleForTesting; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 阿里短信客户端的实现类 - * - * @author zzf - * @since 2021/1/25 14:17 - */ -@Slf4j -public class AliyunSmsClient extends AbstractSmsClient { - - private static final String URL = "https://dysmsapi.aliyuncs.com"; - private static final String HOST = "dysmsapi.aliyuncs.com"; - private static final String VERSION = "2017-05-25"; - - private static final String RESPONSE_CODE_SUCCESS = "OK"; - - public AliyunSmsClient(SmsChannelProperties properties) { - super(properties); - Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); - Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); - } - - @Override - public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, - List> templateParams) throws Throwable { - Assert.notBlank(properties.getSignature(), "短信签名不能为空"); - // 1. 执行请求 - // 参考链接 https://api.aliyun.com/document/Dysmsapi/2017-05-25/SendSms - TreeMap queryParam = new TreeMap<>(); - queryParam.put("PhoneNumbers", mobile); - queryParam.put("SignName", properties.getSignature()); - queryParam.put("TemplateCode", apiTemplateId); - queryParam.put("TemplateParam", JsonUtils.toJsonString(MapUtils.convertMap(templateParams))); - queryParam.put("OutId", sendLogId); - JSONObject response = request("SendSms", queryParam); - - // 2. 解析请求 - return new SmsSendRespDTO() - .setSuccess(Objects.equals(response.getStr("Code"), RESPONSE_CODE_SUCCESS)) - .setSerialNo(response.getStr("BizId")) - .setApiRequestId(response.getStr("RequestId")) - .setApiCode(response.getStr("Code")) - .setApiMsg(response.getStr("Message")); - } - - @Override - public List parseSmsReceiveStatus(String text) { - JSONArray statuses = JSONUtil.parseArray(text); - // 字段参考 https://help.aliyun.com/zh/sms/developer-reference/smsreport-2 - return convertList(statuses, status -> { - JSONObject statusObj = (JSONObject) status; - return new SmsReceiveRespDTO() - .setSuccess(statusObj.getBool("success")) // 是否接收成功 - .setErrorCode(statusObj.getStr("err_code")) // 状态报告编码 - .setErrorMsg(statusObj.getStr("err_msg")) // 状态报告说明 - .setMobile(statusObj.getStr("phone_number")) // 手机号 - .setReceiveTime(statusObj.getLocalDateTime("report_time", null)) // 状态报告时间 - .setSerialNo(statusObj.getStr("biz_id")) // 发送序列号 - .setLogId(statusObj.getLong("out_id")); // 用户序列号 - }); - } - - @Override - public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { - // 1. 执行请求 - // 参考链接 https://api.aliyun.com/document/Dysmsapi/2017-05-25/QuerySmsTemplate - TreeMap queryParam = new TreeMap<>(); - queryParam.put("TemplateCode", apiTemplateId); - JSONObject response = request("QuerySmsTemplate", queryParam); - - // 2.1 请求失败 - String code = response.getStr("Code"); - if (ObjectUtil.notEqual(code, RESPONSE_CODE_SUCCESS)) { - log.error("[getSmsTemplate][模版编号({}) 响应不正确({})]", apiTemplateId, response); - return null; - } - // 2.2 请求成功 - return new SmsTemplateRespDTO() - .setId(response.getStr("TemplateCode")) - .setContent(response.getStr("TemplateContent")) - .setAuditStatus(convertSmsTemplateAuditStatus(response.getInt("TemplateStatus"))) - .setAuditReason(response.getStr("Reason")); - } - - @VisibleForTesting - Integer convertSmsTemplateAuditStatus(Integer templateStatus) { - switch (templateStatus) { - case 0: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); - case 1: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); - case 2: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); - default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); - } - } - - /** - * 请求阿里云短信 - * - * @see V3 版本请求体&签名机制 - * @param apiName 请求的 API 名称 - * @param queryParams 请求参数 - * @return 请求结果 - */ - private JSONObject request(String apiName, TreeMap queryParams) { - // 1. 请求参数 - String queryString = queryParams.entrySet().stream() - .map(entry -> percentCode(entry.getKey()) + "=" + percentCode(String.valueOf(entry.getValue()))) - .collect(Collectors.joining("&")); - - // 2.1 请求 Header - TreeMap headers = new TreeMap<>(); - headers.put("host", HOST); - headers.put("x-acs-version", VERSION); - headers.put("x-acs-action", apiName); - headers.put("x-acs-date", FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("GMT")).format(new Date())); - headers.put("x-acs-signature-nonce", IdUtil.randomUUID()); - - // 2.2 构建签名 Header - StringBuilder canonicalHeaders = new StringBuilder(); // 构造请求头,多个规范化消息头,按照消息头名称(小写)的字符代码顺序以升序排列后拼接在一起 - StringBuilder signedHeadersBuilder = new StringBuilder(); // 已签名消息头列表,多个请求头名称(小写)按首字母升序排列并以英文分号(;)分隔 - headers.entrySet().stream().filter(entry -> entry.getKey().toLowerCase().startsWith("x-acs-") - || entry.getKey().equalsIgnoreCase("host") - || entry.getKey().equalsIgnoreCase("content-type")) - .sorted(Map.Entry.comparingByKey()).forEach(entry -> { - String lowerKey = entry.getKey().toLowerCase(); - canonicalHeaders.append(lowerKey).append(":").append(String.valueOf(entry.getValue()).trim()).append("\n"); - signedHeadersBuilder.append(lowerKey).append(";"); - }); - String signedHeaders = signedHeadersBuilder.substring(0, signedHeadersBuilder.length() - 1); - - // 3. 请求 Body - String requestBody = ""; // 短信 API 为 RPC 接口,query parameters 在 uri 中拼接,因此 request body 如果没有特殊要求,设置为空。 - String hashedRequestBody = DigestUtil.sha256Hex(requestBody); - - // 4. 构建 Authorization 签名 - String canonicalRequest = "POST" + "\n" + "/" + "\n" + queryString + "\n" - + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestBody; - String hashedCanonicalRequest = DigestUtil.sha256Hex(canonicalRequest); - String stringToSign = "ACS3-HMAC-SHA256" + "\n" + hashedCanonicalRequest; - String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign); // 计算签名 - headers.put("Authorization", "ACS3-HMAC-SHA256" + " " + "Credential=" + properties.getApiKey() - + ", " + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature); - - // 5. 发起请求 - String responseBody = HttpUtils.post(URL + "?" + queryString, headers, requestBody); - return JSONUtil.parseObj(responseBody); - } - - /** - * 对指定的字符串进行 URL 编码,并对特定的字符进行替换,以符合URL编码规范 - * - * @param str 需要进行 URL 编码的字符串 - * @return 编码后的字符串 - */ - @SneakyThrows - private static String percentCode(String str) { - Assert.notNull(str, "str 不能为空"); - return HttpUtils.encodeUtf8(str) - .replace("+", "%20") // 加号 "+" 被替换为 "%20" - .replace("*", "%2A") // 星号 "*" 被替换为 "%2A" - .replace("%7E", "~"); // 波浪号 "%7E" 被替换为 "~" - } - -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/DebugDingTalkSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/DebugDingTalkSmsClient.java deleted file mode 100644 index 6d2f2d0..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/DebugDingTalkSmsClient.java +++ /dev/null @@ -1,91 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.hutool.core.codec.Base64; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.digest.DigestUtil; -import cn.hutool.crypto.digest.HmacAlgorithm; -import cn.hutool.http.HttpUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * 基于钉钉 WebHook 实现的调试的短信客户端实现类 - * - * 考虑到省钱,我们使用钉钉 WebHook 模拟发送短信,方便调试。 - * - * @author 芋道源码 - */ -public class DebugDingTalkSmsClient extends AbstractSmsClient { - - public DebugDingTalkSmsClient(SmsChannelProperties properties) { - super(properties); - Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); - Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); - } - - @Override - public SmsSendRespDTO sendSms(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) throws Throwable { - // 构建请求 - String url = buildUrl("robot/send"); - Map params = new HashMap<>(); - params.put("msgtype", "text"); - String content = String.format("【模拟短信】\n手机号:%s\n短信日志编号:%d\n模板参数:%s", - mobile, sendLogId, MapUtils.convertMap(templateParams)); - params.put("text", MapUtil.builder().put("content", content).build()); - // 执行请求 - String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params)); - // 解析结果 - Map responseObj = JsonUtils.parseObject(responseText, Map.class); - String errorCode = MapUtil.getStr(responseObj, "errcode"); - return new SmsSendRespDTO().setSuccess(Objects.equals(errorCode, "0")).setSerialNo(StrUtil.uuid()) - .setApiCode(errorCode).setApiMsg(MapUtil.getStr(responseObj, "errorMsg")); - } - - /** - * 构建请求地址 - * - * 参见 文档 - * - * @param path 请求路径 - * @return 请求地址 - */ - @SuppressWarnings("SameParameterValue") - private String buildUrl(String path) { - // 生成 timestamp - long timestamp = System.currentTimeMillis(); - // 生成 sign - String secret = properties.getApiSecret(); - String stringToSign = timestamp + "\n" + secret; - byte[] signData = DigestUtil.hmac(HmacAlgorithm.HmacSHA256, StrUtil.bytes(secret)).digest(stringToSign); - String sign = Base64.encode(signData); - // 构建最终 URL - return String.format("https://oapi.dingtalk.com/%s?access_token=%s×tamp=%d&sign=%s", - path, properties.getApiKey(), timestamp, sign); - } - - @Override - public List parseSmsReceiveStatus(String text) { - throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调"); - } - - @Override - public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) { - return new SmsTemplateRespDTO().setId(apiTemplateId).setContent("") - .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(""); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java deleted file mode 100644 index 622f8ac..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java +++ /dev/null @@ -1,162 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.date.format.FastDateFormat; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.http.HttpUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import lombok.extern.slf4j.Slf4j; - -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.*; - -import static cn.hutool.crypto.digest.DigestUtil.sha256Hex; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 华为短信客户端的实现类 - * - * @author scholar - * @since 2024/6/02 11:55 - */ -@Slf4j -public class HuaweiSmsClient extends AbstractSmsClient { - - private static final String URL = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1";//APP接入地址+接口访问URI - private static final String HOST = "smsapi.cn-north-4.myhuaweicloud.com:443"; - private static final String SIGNEDHEADERS = "content-type;host;x-sdk-date"; - - private static final String RESPONSE_CODE_SUCCESS = "000000"; - - public HuaweiSmsClient(SmsChannelProperties properties) { - super(properties); - Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); - Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); - validateSender(properties); - } - - /** - * 参数校验华为云的 sender 通道号 - * - * 原因是:验华为云发放短信的时候,需要额外的参数 sender - * - * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。 - * - * @param properties 配置 - */ - private static void validateSender(SmsChannelProperties properties) { - String combineKey = properties.getApiKey(); - Assert.notEmpty(combineKey, "apiKey 不能为空"); - String[] keys = combineKey.trim().split(" "); - Assert.isTrue(keys.length == 2, "华为云短信 apiKey 配置格式错误,请配置 为[accessKeyId sender]"); - } - - private String getAccessKey() { - return StrUtil.subBefore(properties.getApiKey(), " ", true); - } - - private String getSender() { - return StrUtil.subAfter(properties.getApiKey(), " ", true); - } - - @Override - public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, - List> templateParams) throws Throwable { - StringBuilder requestBody = new StringBuilder(); - appendToBody(requestBody, "from=", getSender()); - appendToBody(requestBody, "&to=", mobile); - appendToBody(requestBody, "&templateId=", apiTemplateId); - appendToBody(requestBody, "&templateParas=", JsonUtils.toJsonString( - convertList(templateParams, kv -> String.valueOf(kv.getValue())))); - appendToBody(requestBody, "&statusCallback=", properties.getCallbackUrl()); - appendToBody(requestBody, "&extend=", String.valueOf(sendLogId)); - JSONObject response = request("/sms/batchSendSms/v1/", "POST", requestBody.toString()); - - // 2. 解析请求 - if (!response.containsKey("result")) { // 例如说:密钥不正确 - return new SmsSendRespDTO().setSuccess(false) - .setApiCode(response.getStr("code")) - .setApiMsg(response.getStr("description")); - } - JSONObject sendResult = response.getJSONArray("result").getJSONObject(0); - return new SmsSendRespDTO().setSuccess(RESPONSE_CODE_SUCCESS.equals(response.getStr("code"))) - .setSerialNo(sendResult.getStr("smsMsgId")).setApiCode(sendResult.getStr("status")); - } - - /** - * 请求华为云短信 - * - * @see https://support.huaweicloud.com/api-msgsms/sms_05_0046.html - * @param uri 请求 URI - * @param method 请求 Method - * @param requestBody 请求 Body - * @return 请求结果 - */ - private JSONObject request(String uri, String method, String requestBody) { - // 1.1 请求 Header - TreeMap headers = new TreeMap<>(); - headers.put("Content-Type", "application/x-www-form-urlencoded"); - String sdkDate = FastDateFormat.getInstance("yyyyMMdd'T'HHmmss'Z'", TimeZone.getTimeZone("UTC")).format(new Date()); - headers.put("X-Sdk-Date", sdkDate); - headers.put("host", HOST); - - // 1.2 构建签名 Header - String canonicalQueryString = ""; // 查询参数为空 - String canonicalHeaders = "content-type:application/x-www-form-urlencoded\n" - + "host:"+ HOST +"\n" + "x-sdk-date:" + sdkDate + "\n"; - String canonicalRequest = method + "\n" + uri + "\n" + canonicalQueryString + "\n" - + canonicalHeaders + "\n" + SIGNEDHEADERS + "\n" + sha256Hex(requestBody); - String stringToSign = "SDK-HMAC-SHA256" + "\n" + sdkDate + "\n" + sha256Hex(canonicalRequest); - String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign); // 计算签名 - headers.put("Authorization", "SDK-HMAC-SHA256" + " " + "Access=" + getAccessKey() - + ", " + "SignedHeaders=" + SIGNEDHEADERS + ", " + "Signature=" + signature); - - // 2. 发起请求 - String responseBody = HttpUtils.post(URL, headers, requestBody); - return JSONUtil.parseObj(responseBody); - } - - @Override - public List parseSmsReceiveStatus(String requestBody) { - Map params = HttpUtil.decodeParamMap(requestBody, StandardCharsets.UTF_8); - // 字段参考 https://support.huaweicloud.com/api-msgsms/sms_05_0003.html - return ListUtil.of(new SmsReceiveRespDTO() - .setSuccess("DELIVRD".equals(params.get("status"))) // 是否接收成功 - .setErrorCode(params.get("status")) // 状态报告编码 - .setErrorMsg(params.get("statusDesc")) - .setMobile(params.get("to")) // 手机号 - .setReceiveTime(LocalDateTime.ofInstant(Instant.parse(params.get("updateTime")), ZoneId.of("UTC"))) // 状态报告时间 - .setSerialNo(params.get("smsMsgId")) // 发送序列号 - .setLogId(Long.valueOf(params.get("extend")))); // 用户序列号 - } - - @Override - public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { - // 华为短信模板查询和发送短信,是不同的两套 key 和 secret,与阿里、腾讯的区别较大,这里模板查询校验暂不实现 - String[] strs = apiTemplateId.split(" "); - Assert.isTrue(strs.length == 2, "格式不正确,需要满足:apiTemplateId sender"); - return new SmsTemplateRespDTO().setId(apiTemplateId).setContent(null) - .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(null); - } - - private static void appendToBody(StringBuilder body, String key, String value) { - if (StrUtil.isNotEmpty(value)) { - body.append(key).append(HttpUtils.encodeUtf8(value)); - } - } - -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java deleted file mode 100644 index a041970..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java +++ /dev/null @@ -1,155 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.hutool.core.collection.CollStreamUtil; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.crypto.digest.HmacAlgorithm; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; - -import java.util.*; -import java.util.function.Function; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 七牛云短信客户端的实现类 - * - * @author scholar - * @since 2024/08/26 15:35 - */ -@Slf4j -public class QiniuSmsClient extends AbstractSmsClient { - - private static final String HOST = "sms.qiniuapi.com"; - - public QiniuSmsClient(SmsChannelProperties properties) { - super(properties); - Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); - Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); - } - - public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, - List> templateParams) throws Throwable { - // 1. 执行请求 - // 参考链接 https://developer.qiniu.com/sms/5824/through-the-api-send-text-messages - LinkedHashMap body = new LinkedHashMap<>(); - body.put("template_id", apiTemplateId); - body.put("mobile", mobile); - body.put("parameters", CollStreamUtil.toMap(templateParams, KeyValue::getKey, KeyValue::getValue)); - body.put("seq", Long.toString(sendLogId)); - JSONObject response = request("POST", body, "/v1/message/single"); - - // 2. 解析请求 - if (ObjectUtil.isNotEmpty(response.getStr("error"))) { - // 短信请求失败 - return new SmsSendRespDTO().setSuccess(false) - .setApiCode(response.getStr("error")) - .setApiRequestId(response.getStr("request_id")) - .setApiMsg(response.getStr("message")); - } - return new SmsSendRespDTO().setSuccess(response.containsKey("message_id")) - .setSerialNo(response.getStr("message_id")); - } - - /** - * 请求七牛云短信 - * - * @see - * @param httpMethod http请求方法 - * @param body http请求消息体 - * @param path URL path - * @return 请求结果 - */ - private JSONObject request(String httpMethod, LinkedHashMap body, String path) { - String signDate = DateUtil.date().setTimeZone(TimeZone.getTimeZone("UTC")).toString("yyyyMMdd'T'HHmmss'Z'"); - // 1. 请求头 - Map header = new HashMap<>(4); - header.put("HOST", HOST); - header.put("Authorization", getSignature(httpMethod, path, body != null ? JSONUtil.toJsonStr(body) : "", signDate)); - header.put("Content-Type", "application/json"); - header.put("X-Qiniu-Date", signDate); - - // 2. 发起请求 - String responseBody; - if (Objects.equals(httpMethod, "POST")){ - responseBody = HttpUtils.post("https://" + HOST + path, header, JSONUtil.toJsonStr(body)); - } else { - responseBody = HttpUtils.get("https://" + HOST + path, header); - } - return JSONUtil.parseObj(responseBody); - } - - private String getSignature(String method, String path, String body, String signDate) { - StringBuilder dataToSign = new StringBuilder(); - dataToSign.append(method.toUpperCase()).append(" ").append(path) - .append("\nHost: ").append(HOST) - .append("\n").append("Content-Type").append(": ").append("application/json") - .append("\n").append("X-Qiniu-Date").append(": ").append(signDate) - .append("\n\n"); - if (ObjectUtil.isNotEmpty(body)) { - dataToSign.append(body); - } - String signature = SecureUtil.hmac(HmacAlgorithm.HmacSHA1, properties.getApiSecret()) - .digestBase64(dataToSign.toString(), true); - return "Qiniu " + properties.getApiKey() + ":" + signature; - } - - @Override - public List parseSmsReceiveStatus(String text) { - JSONObject status = JSONUtil.parseObj(text); - // 字段参考 https://developer.qiniu.com/sms/5910/message-push - return convertList(status.getJSONArray("items"), new Function() { - - @Override - public SmsReceiveRespDTO apply(Object item) { - JSONObject statusObj = (JSONObject) item; - return new SmsReceiveRespDTO() - .setSuccess("DELIVRD".equals(statusObj.getStr("status"))) // 是否接收成功 - .setErrorMsg(statusObj.getStr("status")) // 状态报告编码 - .setMobile(statusObj.getStr("mobile")) // 手机号 - .setReceiveTime(LocalDateTimeUtil.of(statusObj.getLong("delivrd_at") * 1000L)) // 状态报告时间 - .setSerialNo(statusObj.getStr("message_id")) // 发送序列号 - .setLogId(statusObj.getLong("seq")); // 用户序列号 - } - - }); - } - - @Override - public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { - // 1. 执行请求 - // 参考链接 https://developer.qiniu.com/sms/5969/query-a-single-template - JSONObject response = request("GET", null, "/v1/template/" + apiTemplateId); - - // 2.2 解析请求 - return new SmsTemplateRespDTO() - .setId(response.getStr("id")) - .setContent(response.getStr("template")) - .setAuditStatus(convertSmsTemplateAuditStatus(response.getStr("audit_status"))) - .setAuditReason(response.getStr("reject_reason")); - } - - @VisibleForTesting - Integer convertSmsTemplateAuditStatus(String templateStatus) { - switch (templateStatus) { - case "passed": return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); - case "reviewing": return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); - case "rejected": return SmsTemplateAuditStatusEnum.FAIL.getStatus(); - default: - throw new IllegalArgumentException(String.format("未知审核状态(%str)", templateStatus)); - } - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java deleted file mode 100644 index da78318..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClientFactory; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.Assert; -import org.springframework.validation.annotation.Validated; - -import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * 短信客户端工厂接口 - * - * @author zzf - */ -@Validated -@Slf4j -public class SmsClientFactoryImpl implements SmsClientFactory { - - /** - * 短信客户端 Map - * key:渠道编号,使用 {@link SmsChannelProperties#getId()} - */ - private final ConcurrentMap channelIdClients = new ConcurrentHashMap<>(); - - /** - * 短信客户端 Map - * key:渠道编码,使用 {@link SmsChannelProperties#getCode()} ()} - * - * 注意,一些场景下,需要获得某个渠道类型的客户端,所以需要使用它。 - * 例如说,解析短信接收结果,是相对通用的,不需要使用某个渠道编号的 {@link #channelIdClients} - */ - private final ConcurrentMap channelCodeClients = new ConcurrentHashMap<>(); - - public SmsClientFactoryImpl() { - // 初始化 channelCodeClients 集合 - Arrays.stream(SmsChannelEnum.values()).forEach(channel -> { - // 创建一个空的 SmsChannelProperties 对象 - SmsChannelProperties properties = new SmsChannelProperties().setCode(channel.getCode()) - .setApiKey("default default").setApiSecret("default"); - // 创建 Sms 客户端 - AbstractSmsClient smsClient = createSmsClient(properties); - channelCodeClients.put(channel.getCode(), smsClient); - }); - } - - @Override - public SmsClient getSmsClient(Long channelId) { - return channelIdClients.get(channelId); - } - - @Override - public SmsClient getSmsClient(String channelCode) { - return channelCodeClients.get(channelCode); - } - - @Override - public SmsClient createOrUpdateSmsClient(SmsChannelProperties properties) { - AbstractSmsClient client = channelIdClients.get(properties.getId()); - if (client == null) { - client = this.createSmsClient(properties); - client.init(); - channelIdClients.put(client.getId(), client); - } else { - client.refresh(properties); - } - return client; - } - - private AbstractSmsClient createSmsClient(SmsChannelProperties properties) { - SmsChannelEnum channelEnum = SmsChannelEnum.getByCode(properties.getCode()); - Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum)); - // 创建客户端 - switch (channelEnum) { - case ALIYUN: return new AliyunSmsClient(properties); - case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(properties); - case TENCENT: return new TencentSmsClient(properties); - case HUAWEI: return new HuaweiSmsClient(properties); - case QINIU: return new QiniuSmsClient(properties); - } - // 创建失败,错误日志 + 抛出异常 - log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties); - throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", properties)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java deleted file mode 100644 index 19cde8c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java +++ /dev/null @@ -1,201 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.hutool.core.date.format.FastDateFormat; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.HexUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.crypto.digest.DigestUtil; -import cn.hutool.crypto.digest.HmacAlgorithm; -import cn.hutool.json.JSONArray; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import com.google.common.annotations.VisibleForTesting; - -import java.nio.charset.StandardCharsets; -import java.util.*; - -import static cn.hutool.crypto.digest.DigestUtil.sha256Hex; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 腾讯云短信功能实现 - * - * 参见 文档 - * - * @author shiwp - */ -public class TencentSmsClient extends AbstractSmsClient { - - private static final String HOST = "sms.tencentcloudapi.com"; - private static final String VERSION = "2021-01-11"; - private static final String REGION = "ap-guangzhou"; - - /** - * 调用成功 code - */ - public static final String API_CODE_SUCCESS = "Ok"; - - /** - * 是否国际/港澳台短信: - * - * 0:表示国内短信。 - * 1:表示国际/港澳台短信。 - */ - private static final long INTERNATIONAL_CHINA = 0L; - - public TencentSmsClient(SmsChannelProperties properties) { - super(properties); - Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); - validateSdkAppId(properties); - } - - /** - * 参数校验腾讯云的 SDK AppId - * - * 原因是:腾讯云发放短信的时候,需要额外的参数 sdkAppId - * - * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。 - * - * @param properties 配置 - */ - private static void validateSdkAppId(SmsChannelProperties properties) { - String combineKey = properties.getApiKey(); - Assert.notEmpty(combineKey, "apiKey 不能为空"); - String[] keys = combineKey.trim().split(" "); - Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]"); - } - - private String getSdkAppId() { - return StrUtil.subAfter(properties.getApiKey(), " ", true); - } - - private String getApiKey() { - return StrUtil.subBefore(properties.getApiKey(), " ", true); - } - - @Override - public SmsSendRespDTO sendSms(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) throws Throwable { - // 1. 执行请求 - // 参考链接 https://cloud.tencent.com/document/product/382/55981 - TreeMap body = new TreeMap<>(); - body.put("PhoneNumberSet", new String[]{mobile}); - body.put("SmsSdkAppId", getSdkAppId()); - body.put("SignName", properties.getSignature()); - body.put("TemplateId", apiTemplateId); - body.put("TemplateParamSet", ArrayUtils.toArray(templateParams, param -> String.valueOf(param.getValue()))); - JSONObject response = request("SendSms", body); - - // 2. 解析请求 - JSONObject responseResult = response.getJSONObject("Response"); - JSONObject error = responseResult.getJSONObject("Error"); - if (error != null) { - return new SmsSendRespDTO().setSuccess(false) - .setApiRequestId(responseResult.getStr("RequestId")) - .setApiCode(error.getStr("Code")) - .setApiMsg(error.getStr("Message")); - } - JSONObject sendResult = responseResult.getJSONArray("SendStatusSet").getJSONObject(0); - return new SmsSendRespDTO().setSuccess(Objects.equals(API_CODE_SUCCESS, sendResult.getStr("Code"))) - .setApiRequestId(responseResult.getStr("RequestId")) - .setSerialNo(sendResult.getStr("SerialNo")) - .setApiMsg(sendResult.getStr("Message")); - } - - @Override - public List parseSmsReceiveStatus(String text) { - JSONArray statuses = JSONUtil.parseArray(text); - // 字段参考 - return convertList(statuses, status -> { - JSONObject statusObj = (JSONObject) status; - return new SmsReceiveRespDTO() - .setSuccess("SUCCESS".equals(statusObj.getStr("report_status"))) // 是否接收成功 - .setErrorCode(statusObj.getStr("errmsg")) // 状态报告编码 - .setMobile(statusObj.getStr("mobile")) // 手机号 - .setReceiveTime(statusObj.getLocalDateTime("user_receive_time", null)) // 状态报告时间 - .setSerialNo(statusObj.getStr("sid")); // 发送序列号 - }); - } - - @Override - public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { - // 1. 构建请求 - // 参考链接 https://cloud.tencent.com/document/product/382/52067 - TreeMap body = new TreeMap<>(); - body.put("International", INTERNATIONAL_CHINA); - body.put("TemplateIdSet", new Integer[]{Integer.valueOf(apiTemplateId)}); - JSONObject response = request("DescribeSmsTemplateList", body); - - // 2. 解析请求 - JSONObject statusResult = response.getJSONObject("Response") - .getJSONArray("DescribeTemplateStatusSet").getJSONObject(0); - return new SmsTemplateRespDTO().setId(apiTemplateId) - .setContent(statusResult.get("TemplateContent").toString()) - .setAuditStatus(convertSmsTemplateAuditStatus(statusResult.getInt("StatusCode"))) - .setAuditReason(statusResult.get("ReviewReply").toString()); - } - - @VisibleForTesting - Integer convertSmsTemplateAuditStatus(int templateStatus) { - switch (templateStatus) { - case 1: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); - case 0: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); - case -1: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); - default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); - } - } - - /** - * 请求腾讯云短信 - * - * @see 签名方法 v3 - * - * @param action 请求的 API 名称 - * @param body 请求参数 - * @return 请求结果 - */ - private JSONObject request(String action, TreeMap body) { - // 1.1 请求 Header - Map headers = new HashMap<>(); - headers.put("Content-Type", "application/json; charset=utf-8"); - headers.put("Host", HOST); - headers.put("X-TC-Action", action); - Date now = new Date(); - String nowStr = FastDateFormat.getInstance("yyyy-MM-dd", TimeZone.getTimeZone("UTC")).format(now); - headers.put("X-TC-Timestamp", String.valueOf(now.getTime() / 1000)); - headers.put("X-TC-Version", VERSION); - headers.put("X-TC-Region", REGION); - - // 1.2 构建签名 Header - String canonicalQueryString = ""; - String canonicalHeaders = "content-type:application/json; charset=utf-8\n" - + "host:" + HOST + "\n" + "x-tc-action:" + action.toLowerCase() + "\n"; - String signedHeaders = "content-type;host;x-tc-action"; - String canonicalRequest = "POST" + "\n" + "/" + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" - + signedHeaders + "\n" + sha256Hex(JSONUtil.toJsonStr(body)); - String credentialScope = nowStr + "/" + "sms" + "/" + "tc3_request"; - String stringToSign = "TC3-HMAC-SHA256" + "\n" + now.getTime() / 1000 + "\n" + credentialScope + "\n" + - sha256Hex(canonicalRequest); - byte[] secretService = hmac256(hmac256(("TC3" + properties.getApiSecret()).getBytes(StandardCharsets.UTF_8), nowStr), "sms"); - String signature = HexUtil.encodeHexStr(hmac256(hmac256(secretService, "tc3_request"), stringToSign)); - headers.put("Authorization", "TC3-HMAC-SHA256" + " " + "Credential=" + getApiKey() + "/" + credentialScope + ", " - + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature); - - // 2. 发起请求 - String responseBody = HttpUtils.post("https://" + HOST, headers, JSONUtil.toJsonStr(body)); - return JSONUtil.parseObj(responseBody); - } - - private static byte[] hmac256(byte[] key, String msg) { - return DigestUtil.hmac(HmacAlgorithm.HmacSHA256, key).digest(msg); - } - -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsChannelEnum.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsChannelEnum.java deleted file mode 100644 index cbbde69..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsChannelEnum.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.enums; - -import cn.hutool.core.util.ArrayUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 短信渠道枚举 - * - * @author zzf - * @since 2021/1/25 10:56 - */ -@Getter -@AllArgsConstructor -public enum SmsChannelEnum { - - DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"), - ALIYUN("ALIYUN", "阿里云"), - TENCENT("TENCENT", "腾讯云"), - HUAWEI("HUAWEI", "华为云"), - QINIU("QINIU", "七牛云"), - ; - - /** - * 编码 - */ - private final String code; - /** - * 名字 - */ - private final String name; - - public static SmsChannelEnum getByCode(String code) { - return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values()); - } - -} - diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java deleted file mode 100644 index 489f69e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/enums/SmsTemplateAuditStatusEnum.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 短信模板的审核状态枚举 - * - * @author 芋道源码 - */ -@AllArgsConstructor -@Getter -public enum SmsTemplateAuditStatusEnum { - - CHECKING(1), - SUCCESS(2), - FAIL(3); - - private final Integer status; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/property/SmsChannelProperties.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/property/SmsChannelProperties.java deleted file mode 100644 index 0362b92..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/property/SmsChannelProperties.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.property; - -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; -import lombok.Data; -import org.springframework.validation.annotation.Validated; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -/** - * 短信渠道配置类 - * - * @author zzf - * @since 2021/1/25 17:01 - */ -@Data -@Validated -public class SmsChannelProperties { - - /** - * 渠道编号 - */ - @NotNull(message = "短信渠道 ID 不能为空") - private Long id; - /** - * 短信签名 - */ - @NotEmpty(message = "短信签名不能为空") - private String signature; - /** - * 渠道编码 - * - * 枚举 {@link SmsChannelEnum} - */ - @NotEmpty(message = "渠道编码不能为空") - private String code; - /** - * 短信 API 的账号 - */ - @NotEmpty(message = "短信 API 的账号不能为空") - private String apiKey; - /** - * 短信 API 的密钥 - */ - @NotEmpty(message = "短信 API 的密钥不能为空") - private String apiSecret; - /** - * 短信发送回调 URL - */ - private String callbackUrl; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/web/config/SystemWebConfiguration.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/web/config/SystemWebConfiguration.java deleted file mode 100644 index 9e6d662..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/web/config/SystemWebConfiguration.java +++ /dev/null @@ -1,24 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.web.config; - -import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration; -import org.springdoc.core.models.GroupedOpenApi; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * system 模块的 web 组件的 Configuration - * - * @author 芋道源码 - */ -@Configuration(proxyBeanMethods = false) -public class SystemWebConfiguration { - - /** - * system 模块的 API 分组 - */ - @Bean - public GroupedOpenApi systemGroupedOpenApi() { - return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("system"); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/web/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/web/package-info.java deleted file mode 100644 index 1a17a3d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/web/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * system 模块的 web 配置 - */ -package cn.iocoder.yudao.module.system.framework.web; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/DemoJob.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/DemoJob.java deleted file mode 100644 index 62b8862..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/DemoJob.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.module.system.job; - -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper; -import org.springframework.stereotype.Component; - -import jakarta.annotation.Resource; -import java.util.List; - -@Component -public class DemoJob implements JobHandler { - - @Resource - private AdminUserMapper adminUserMapper; - - @Override - @TenantJob // 标记多租户 - public String execute(String param) { - System.out.println("当前租户:" + TenantContextHolder.getTenantId()); - List users = adminUserMapper.selectList(); - return "用户数量:" + users.size(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java deleted file mode 100644 index 2d55e44..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.mail; - -import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; -import cn.iocoder.yudao.module.system.service.mail.MailSendService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; - -import jakarta.annotation.Resource; - -/** - * 针对 {@link MailSendMessage} 的消费者 - * - * @author 芋道源码 - */ -@Component -@Slf4j -public class MailSendConsumer { - - @Resource - private MailSendService mailSendService; - - @EventListener - @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步 - public void onMessage(MailSendMessage message) { - log.info("[onMessage][消息内容({})]", message); - mailSendService.doSendMail(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsSendConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsSendConsumer.java deleted file mode 100644 index f1d9602..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/sms/SmsSendConsumer.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.consumer.sms; - -import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; -import cn.iocoder.yudao.module.system.service.sms.SmsSendService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; - -import jakarta.annotation.Resource; - -/** - * 针对 {@link SmsSendMessage} 的消费者 - * - * @author zzf - */ -@Component -@Slf4j -public class SmsSendConsumer { - - @Resource - private SmsSendService smsSendService; - - @EventListener - @Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步 - public void onMessage(SmsSendMessage message) { - log.info("[onMessage][消息内容({})]", message); - smsSendService.doSendSms(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java deleted file mode 100644 index 8d5af7c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.mail; - -import lombok.Data; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; - -/** - * 邮箱发送消息 - * - * @author 芋道源码 - */ -@Data -public class MailSendMessage { - - /** - * 邮件日志编号 - */ - @NotNull(message = "邮件日志编号不能为空") - private Long logId; - /** - * 接收邮件地址 - */ - @NotNull(message = "接收邮件地址不能为空") - private String mail; - /** - * 邮件账号编号 - */ - @NotNull(message = "邮件账号编号不能为空") - private Long accountId; - - /** - * 邮件发件人 - */ - private String nickname; - /** - * 邮件标题 - */ - @NotEmpty(message = "邮件标题不能为空") - private String title; - /** - * 邮件内容 - */ - @NotEmpty(message = "邮件内容不能为空") - private String content; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsSendMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsSendMessage.java deleted file mode 100644 index 21c0c13..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/sms/SmsSendMessage.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.message.sms; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import lombok.Data; - -import jakarta.validation.constraints.NotNull; -import java.util.List; - -/** - * 短信发送消息 - * - * @author 芋道源码 - */ -@Data -public class SmsSendMessage { - - /** - * 短信日志编号 - */ - @NotNull(message = "短信日志编号不能为空") - private Long logId; - /** - * 手机号 - */ - @NotNull(message = "手机号不能为空") - private String mobile; - /** - * 短信渠道编号 - */ - @NotNull(message = "短信渠道编号不能为空") - private Long channelId; - /** - * 短信 API 的模板编号 - */ - @NotNull(message = "短信 API 的模板编号不能为空") - private String apiTemplateId; - /** - * 短信模板参数 - */ - private List> templateParams; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java deleted file mode 100644 index 5a44218..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.mail; - -import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -import jakarta.annotation.Resource; - -/** - * Mail 邮件相关消息的 Producer - * - * @author wangjingyi - * @since 2021/4/19 13:33 - */ -@Slf4j -@Component -public class MailProducer { - - @Resource - private ApplicationContext applicationContext; - - /** - * 发送 {@link MailSendMessage} 消息 - * - * @param sendLogId 发送日志编码 - * @param mail 接收邮件地址 - * @param accountId 邮件账号编号 - * @param nickname 邮件发件人 - * @param title 邮件标题 - * @param content 邮件内容 - */ - public void sendMailSendMessage(Long sendLogId, String mail, Long accountId, - String nickname, String title, String content) { - MailSendMessage message = new MailSendMessage() - .setLogId(sendLogId).setMail(mail).setAccountId(accountId) - .setNickname(nickname).setTitle(title).setContent(content); - applicationContext.publishEvent(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java deleted file mode 100644 index cb3d546..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/sms/SmsProducer.java +++ /dev/null @@ -1,41 +0,0 @@ -package cn.iocoder.yudao.module.system.mq.producer.sms; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.ApplicationContext; -import org.springframework.stereotype.Component; - -import jakarta.annotation.Resource; -import java.util.List; - -/** - * Sms 短信相关消息的 Producer - * - * @author zzf - * @since 2021/3/9 16:35 - */ -@Slf4j -@Component -public class SmsProducer { - - @Resource - private ApplicationContext applicationContext; - - /** - * 发送 {@link SmsSendMessage} 消息 - * - * @param logId 短信日志编号 - * @param mobile 手机号 - * @param channelId 渠道编号 - * @param apiTemplateId 短信模板编号 - * @param templateParams 短信模板参数 - */ - public void sendSmsSendMessage(Long logId, String mobile, - Long channelId, String apiTemplateId, List> templateParams) { - SmsSendMessage message = new SmsSendMessage().setLogId(logId).setMobile(mobile); - message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams); - applicationContext.publishEvent(message); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/package-info.java deleted file mode 100644 index df82ac7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/package-info.java +++ /dev/null @@ -1,8 +0,0 @@ -/** - * system 模块下,我们放通用业务,支撑上层的核心业务。 - * 例如说:用户、部门、权限、数据字典等等 - * - * 1. Controller URL:以 /system/ 开头,避免和其它 Module 冲突 - * 2. DataObject 表名:以 system_ 开头,方便在数据库中区分 - */ -package cn.iocoder.yudao.module.system; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java deleted file mode 100644 index 3901e08..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java +++ /dev/null @@ -1,88 +0,0 @@ -package cn.iocoder.yudao.module.system.service.auth; - -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; - -import jakarta.validation.Valid; - -/** - * 管理后台的认证 Service 接口 - * - * 提供用户的登录、登出的能力 - * - * @author 芋道源码 - */ -public interface AdminAuthService { - - /** - * 验证账号 + 密码。如果通过,则返回用户 - * - * @param username 账号 - * @param password 密码 - * @return 用户 - */ - AdminUserDO authenticate(String username, String password); - - /** - * 账号登录 - * - * @param reqVO 登录信息 - * @return 登录结果 - */ - AuthLoginRespVO login(@Valid AuthLoginReqVO reqVO); - - /** - * 基于 token 退出登录 - * - * @param token token - * @param logType 登出类型 - */ - void logout(String token, Integer logType); - - /** - * 短信验证码发送 - * - * @param reqVO 发送请求 - */ - void sendSmsCode(AuthSmsSendReqVO reqVO); - - /** - * 短信登录 - * - * @param reqVO 登录信息 - * @return 登录结果 - */ - AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO); - - /** - * 社交快捷登录,使用 code 授权码 - * - * @param reqVO 登录信息 - * @return 登录结果 - */ - AuthLoginRespVO socialLogin(@Valid AuthSocialLoginReqVO reqVO); - - /** - * 刷新访问令牌 - * - * @param refreshToken 刷新令牌 - * @return 登录结果 - */ - AuthLoginRespVO refreshToken(String refreshToken); - - /** - * 用户注册 - * - * @param createReqVO 注册用户 - * @return 注册结果 - */ - AuthLoginRespVO register(AuthRegisterReqVO createReqVO); - - /** - * 重置密码 - * - * @param reqVO 验证码信息 - */ - void resetPassword(AuthResetPasswordReqVO reqVO); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java deleted file mode 100644 index 94e589d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java +++ /dev/null @@ -1,303 +0,0 @@ -package cn.iocoder.yudao.module.system.service.auth; - -import cn.hutool.core.util.ObjectUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; -import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; -import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; -import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; -import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; -import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; -import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; -import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2ClientConstants; -import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; -import cn.iocoder.yudao.module.system.service.logger.LoginLogService; -import cn.iocoder.yudao.module.system.service.member.MemberService; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; -import cn.iocoder.yudao.module.system.service.social.SocialUserService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import com.anji.captcha.model.common.ResponseModel; -import com.anji.captcha.model.vo.CaptchaVO; -import com.anji.captcha.service.CaptchaService; -import com.google.common.annotations.VisibleForTesting; -import jakarta.annotation.Resource; -import jakarta.validation.Validator; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Objects; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * Auth Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class AdminAuthServiceImpl implements AdminAuthService { - - @Resource - private AdminUserService userService; - @Resource - private LoginLogService loginLogService; - @Resource - private OAuth2TokenService oauth2TokenService; - @Resource - private SocialUserService socialUserService; - @Resource - private MemberService memberService; - @Resource - private Validator validator; - @Resource - private CaptchaService captchaService; - @Resource - private SmsCodeApi smsCodeApi; - - /** - * 验证码的开关,默认为 true - */ - @Value("${yudao.captcha.enable:true}") - @Setter // 为了单测:开启或者关闭验证码 - private Boolean captchaEnable; - - @Override - public AdminUserDO authenticate(String username, String password) { - final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; - // 校验账号是否存在 - AdminUserDO user = userService.getUserByUsername(username); - if (user == null) { - createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); - throw exception(AUTH_LOGIN_BAD_CREDENTIALS); - } - if (!userService.isPasswordMatch(password, user.getPassword())) { - createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); - throw exception(AUTH_LOGIN_BAD_CREDENTIALS); - } - // 校验是否禁用 - if (CommonStatusEnum.isDisable(user.getStatus())) { - createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED); - throw exception(AUTH_LOGIN_USER_DISABLED); - } - return user; - } - - @Override - public AuthLoginRespVO login(AuthLoginReqVO reqVO) { - // 校验验证码 - validateCaptcha(reqVO); - - // 使用账号密码,进行登录 - AdminUserDO user = authenticate(reqVO.getUsername(), reqVO.getPassword()); - - // 如果 socialType 非空,说明需要绑定社交用户 - if (reqVO.getSocialType() != null) { - socialUserService.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(), - reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())); - } - // 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); - } - - @Override - public void sendSmsCode(AuthSmsSendReqVO reqVO) { - // 如果是重置密码场景,需要校验图形验证码是否正确 - if (Objects.equals(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene(), reqVO.getScene())) { - ResponseModel response = doValidateCaptcha(reqVO); - if (!response.isSuccess()) { - throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); - } - } - - // 登录场景,验证是否存在 - if (userService.getUserByMobile(reqVO.getMobile()) == null) { - throw exception(AUTH_MOBILE_NOT_EXISTS); - } - // 发送验证码 - smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); - } - - @Override - public AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) { - // 校验验证码 - smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP())); - - // 获得用户信息 - AdminUserDO user = userService.getUserByMobile(reqVO.getMobile()); - if (user == null) { - throw exception(USER_NOT_EXISTS); - } - - // 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); - } - - private void createLoginLog(Long userId, String username, - LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { - // 插入登录日志 - LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); - reqDTO.setLogType(logTypeEnum.getType()); - reqDTO.setTraceId(TracerUtils.getTraceId()); - reqDTO.setUserId(userId); - reqDTO.setUserType(getUserType().getValue()); - reqDTO.setUsername(username); - reqDTO.setUserAgent(ServletUtils.getUserAgent()); - reqDTO.setUserIp(ServletUtils.getClientIP()); - reqDTO.setResult(loginResult.getResult()); - loginLogService.createLoginLog(reqDTO); - // 更新最后登录时间 - if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { - userService.updateUserLogin(userId, ServletUtils.getClientIP()); - } - } - - @Override - public AuthLoginRespVO socialLogin(AuthSocialLoginReqVO reqVO) { - // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 - SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), - reqVO.getCode(), reqVO.getState()); - if (socialUser == null || socialUser.getUserId() == null) { - throw exception(AUTH_THIRD_LOGIN_NOT_BIND); - } - - // 获得用户 - AdminUserDO user = userService.getUser(socialUser.getUserId()); - if (user == null) { - throw exception(USER_NOT_EXISTS); - } - - // 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(user.getId(), user.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); - } - - @VisibleForTesting - void validateCaptcha(AuthLoginReqVO reqVO) { - ResponseModel response = doValidateCaptcha(reqVO); - // 校验验证码 - if (!response.isSuccess()) { - // 创建登录失败日志(验证码不正确) - createLoginLog(null, reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME, LoginResultEnum.CAPTCHA_CODE_ERROR); - throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR, response.getRepMsg()); - } - } - - private ResponseModel doValidateCaptcha(CaptchaVerificationReqVO reqVO) { - // 如果验证码关闭,则不进行校验 - if (!captchaEnable) { - return ResponseModel.success(); - } - ValidationUtils.validate(validator, reqVO, CaptchaVerificationReqVO.CodeEnableGroup.class); - CaptchaVO captchaVO = new CaptchaVO(); - captchaVO.setCaptchaVerification(reqVO.getCaptchaVerification()); - return captchaService.verification(captchaVO); - } - - private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) { - // 插入登陆日志 - createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS); - // 创建访问令牌 - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(), - OAuth2ClientConstants.CLIENT_ID_DEFAULT, null); - // 构建返回结果 - return AuthConvert.INSTANCE.convert(accessTokenDO); - } - - @Override - public AuthLoginRespVO refreshToken(String refreshToken) { - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientConstants.CLIENT_ID_DEFAULT); - return AuthConvert.INSTANCE.convert(accessTokenDO); - } - - @Override - public void logout(String token, Integer logType) { - // 删除访问令牌 - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token); - if (accessTokenDO == null) { - return; - } - // 删除成功,则记录登出日志 - createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType); - } - - private void createLogoutLog(Long userId, Integer userType, Integer logType) { - LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); - reqDTO.setLogType(logType); - reqDTO.setTraceId(TracerUtils.getTraceId()); - reqDTO.setUserId(userId); - reqDTO.setUserType(userType); - if (ObjectUtil.equal(getUserType().getValue(), userType)) { - reqDTO.setUsername(getUsername(userId)); - } else { - reqDTO.setUsername(memberService.getMemberUserMobile(userId)); - } - reqDTO.setUserAgent(ServletUtils.getUserAgent()); - reqDTO.setUserIp(ServletUtils.getClientIP()); - reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); - loginLogService.createLoginLog(reqDTO); - } - - private String getUsername(Long userId) { - if (userId == null) { - return null; - } - AdminUserDO user = userService.getUser(userId); - return user != null ? user.getUsername() : null; - } - - private UserTypeEnum getUserType() { - return UserTypeEnum.ADMIN; - } - - @Override - public AuthLoginRespVO register(AuthRegisterReqVO registerReqVO) { - // 1. 校验验证码 - validateCaptcha(registerReqVO); - - // 2. 校验用户名是否已存在 - Long userId = userService.registerUser(registerReqVO); - - // 3. 创建 Token 令牌,记录登录日志 - return createTokenAfterLoginSuccess(userId, registerReqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); - } - - @VisibleForTesting - void validateCaptcha(AuthRegisterReqVO reqVO) { - ResponseModel response = doValidateCaptcha(reqVO); - // 验证不通过 - if (!response.isSuccess()) { - throw exception(AUTH_REGISTER_CAPTCHA_CODE_ERROR, response.getRepMsg()); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void resetPassword(AuthResetPasswordReqVO reqVO) { - AdminUserDO userByMobile = userService.getUserByMobile(reqVO.getMobile()); - if (userByMobile == null) { - throw exception(USER_MOBILE_NOT_EXISTS); - } - - smsCodeApi.useSmsCode(new SmsCodeUseReqDTO() - .setCode(reqVO.getCode()) - .setMobile(reqVO.getMobile()) - .setScene(SmsSceneEnum.ADMIN_MEMBER_RESET_PASSWORD.getScene()) - .setUsedIp(getClientIP()) - ); - - userService.updateUserPassword(userByMobile.getId(), reqVO.getPassword()); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java deleted file mode 100644 index a0b765e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java +++ /dev/null @@ -1,117 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dept; - -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; - -import java.util.*; - -/** - * 部门 Service 接口 - * - * @author 芋道源码 - */ -public interface DeptService { - - /** - * 创建部门 - * - * @param createReqVO 部门信息 - * @return 部门编号 - */ - Long createDept(DeptSaveReqVO createReqVO); - - /** - * 更新部门 - * - * @param updateReqVO 部门信息 - */ - void updateDept(DeptSaveReqVO updateReqVO); - - /** - * 删除部门 - * - * @param id 部门编号 - */ - void deleteDept(Long id); - - /** - * 获得部门信息 - * - * @param id 部门编号 - * @return 部门信息 - */ - DeptDO getDept(Long id); - - /** - * 获得部门信息数组 - * - * @param ids 部门编号数组 - * @return 部门信息数组 - */ - List getDeptList(Collection ids); - - /** - * 筛选部门列表 - * - * @param reqVO 筛选条件请求 VO - * @return 部门列表 - */ - List getDeptList(DeptListReqVO reqVO); - - /** - * 获得指定编号的部门 Map - * - * @param ids 部门编号数组 - * @return 部门 Map - */ - default Map getDeptMap(Collection ids) { - List list = getDeptList(ids); - return CollectionUtils.convertMap(list, DeptDO::getId); - } - - /** - * 获得指定部门的所有子部门 - * - * @param id 部门编号 - * @return 子部门列表 - */ - default List getChildDeptList(Long id) { - return getChildDeptList(Collections.singleton(id)); - } - - /** - * 获得指定部门的所有子部门 - * - * @param ids 部门编号数组 - * @return 子部门列表 - */ - List getChildDeptList(Collection ids); - - /** - * 获得指定领导者的部门列表 - * - * @param id 领导者编号 - * @return 部门列表 - */ - List getDeptListByLeaderUserId(Long id); - - /** - * 获得所有子部门,从缓存中 - * - * @param id 父部门编号 - * @return 子部门列表 - */ - Set getChildDeptIdListFromCache(Long id); - - /** - * 校验部门们是否有效。如下情况,视为无效: - * 1. 部门编号不存在 - * 2. 部门被禁用 - * - * @param ids 角色编号数组 - */ - void validateDeptList(Collection ids); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java deleted file mode 100644 index c0d7b0e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java +++ /dev/null @@ -1,223 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dept; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.util.*; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 部门 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class DeptServiceImpl implements DeptService { - - @Resource - private DeptMapper deptMapper; - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, - allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 - public Long createDept(DeptSaveReqVO createReqVO) { - if (createReqVO.getParentId() == null) { - createReqVO.setParentId(DeptDO.PARENT_ID_ROOT); - } - // 校验父部门的有效性 - validateParentDept(null, createReqVO.getParentId()); - // 校验部门名的唯一性 - validateDeptNameUnique(null, createReqVO.getParentId(), createReqVO.getName()); - - // 插入部门 - DeptDO dept = BeanUtils.toBean(createReqVO, DeptDO.class); - deptMapper.insert(dept); - return dept.getId(); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, - allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 - public void updateDept(DeptSaveReqVO updateReqVO) { - if (updateReqVO.getParentId() == null) { - updateReqVO.setParentId(DeptDO.PARENT_ID_ROOT); - } - // 校验自己存在 - validateDeptExists(updateReqVO.getId()); - // 校验父部门的有效性 - validateParentDept(updateReqVO.getId(), updateReqVO.getParentId()); - // 校验部门名的唯一性 - validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); - - // 更新部门 - DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class); - deptMapper.updateById(updateObj); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, - allEntries = true) // allEntries 清空所有缓存,因为操作一个部门,涉及到多个缓存 - public void deleteDept(Long id) { - // 校验是否存在 - validateDeptExists(id); - // 校验是否有子部门 - if (deptMapper.selectCountByParentId(id) > 0) { - throw exception(DEPT_EXITS_CHILDREN); - } - // 删除部门 - deptMapper.deleteById(id); - } - - @VisibleForTesting - void validateDeptExists(Long id) { - if (id == null) { - return; - } - DeptDO dept = deptMapper.selectById(id); - if (dept == null) { - throw exception(DEPT_NOT_FOUND); - } - } - - @VisibleForTesting - void validateParentDept(Long id, Long parentId) { - if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) { - return; - } - // 1. 不能设置自己为父部门 - if (Objects.equals(id, parentId)) { - throw exception(DEPT_PARENT_ERROR); - } - // 2. 父部门不存在 - DeptDO parentDept = deptMapper.selectById(parentId); - if (parentDept == null) { - throw exception(DEPT_PARENT_NOT_EXITS); - } - // 3. 递归校验父部门,如果父部门是自己的子部门,则报错,避免形成环路 - if (id == null) { // id 为空,说明新增,不需要考虑环路 - return; - } - for (int i = 0; i < Short.MAX_VALUE; i++) { - // 3.1 校验环路 - parentId = parentDept.getParentId(); - if (Objects.equals(id, parentId)) { - throw exception(DEPT_PARENT_IS_CHILD); - } - // 3.2 继续递归下一级父部门 - if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) { - break; - } - parentDept = deptMapper.selectById(parentId); - if (parentDept == null) { - break; - } - } - } - - @VisibleForTesting - void validateDeptNameUnique(Long id, Long parentId, String name) { - DeptDO dept = deptMapper.selectByParentIdAndName(parentId, name); - if (dept == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的部门 - if (id == null) { - throw exception(DEPT_NAME_DUPLICATE); - } - if (ObjectUtil.notEqual(dept.getId(), id)) { - throw exception(DEPT_NAME_DUPLICATE); - } - } - - @Override - public DeptDO getDept(Long id) { - return deptMapper.selectById(id); - } - - @Override - public List getDeptList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return Collections.emptyList(); - } - return deptMapper.selectBatchIds(ids); - } - - @Override - public List getDeptList(DeptListReqVO reqVO) { - List list = deptMapper.selectList(reqVO); - list.sort(Comparator.comparing(DeptDO::getSort)); - return list; - } - - @Override - public List getChildDeptList(Collection ids) { - List children = new LinkedList<>(); - // 遍历每一层 - Collection parentIds = ids; - for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下,存在死循环 - // 查询当前层,所有的子部门 - List depts = deptMapper.selectListByParentId(parentIds); - // 1. 如果没有子部门,则结束遍历 - if (CollUtil.isEmpty(depts)) { - break; - } - // 2. 如果有子部门,继续遍历 - children.addAll(depts); - parentIds = convertSet(depts, DeptDO::getId); - } - return children; - } - - @Override - public List getDeptListByLeaderUserId(Long id) { - return deptMapper.selectListByLeaderUserId(id); - } - - @Override - @DataPermission(enable = false) // 禁用数据权限,避免建立不正确的缓存 - @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id") - public Set getChildDeptIdListFromCache(Long id) { - List children = getChildDeptList(id); - return convertSet(children, DeptDO::getId); - } - - @Override - public void validateDeptList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return; - } - // 获得科室信息 - Map deptMap = getDeptMap(ids); - // 校验 - ids.forEach(id -> { - DeptDO dept = deptMap.get(id); - if (dept == null) { - throw exception(DEPT_NOT_FOUND); - } - if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) { - throw exception(DEPT_NOT_ENABLE, dept.getName()); - } - }); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostService.java deleted file mode 100644 index 25604ce..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostService.java +++ /dev/null @@ -1,84 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dept; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import org.springframework.lang.Nullable; - -import java.util.Collection; -import java.util.List; - -/** - * 岗位 Service 接口 - * - * @author 芋道源码 - */ -public interface PostService { - - /** - * 创建岗位 - * - * @param createReqVO 岗位信息 - * @return 岗位编号 - */ - Long createPost(PostSaveReqVO createReqVO); - - /** - * 更新岗位 - * - * @param updateReqVO 岗位信息 - */ - void updatePost(PostSaveReqVO updateReqVO); - - /** - * 删除岗位信息 - * - * @param id 岗位编号 - */ - void deletePost(Long id); - - /** - * 获得岗位列表 - * - * @param ids 岗位编号数组 - * @return 部门列表 - */ - List getPostList(@Nullable Collection ids); - - /** - * 获得符合条件的岗位列表 - * - * @param ids 岗位编号数组。如果为空,不进行筛选 - * @param statuses 状态数组。如果为空,不进行筛选 - * @return 部门列表 - */ - List getPostList(@Nullable Collection ids, - @Nullable Collection statuses); - - /** - * 获得岗位分页列表 - * - * @param reqVO 分页条件 - * @return 部门分页列表 - */ - PageResult getPostPage(PostPageReqVO reqVO); - - /** - * 获得岗位信息 - * - * @param id 岗位编号 - * @return 岗位信息 - */ - PostDO getPost(Long id); - - /** - * 校验岗位们是否有效。如下情况,视为无效: - * 1. 岗位编号不存在 - * 2. 岗位被禁用 - * - * @param ids 岗位编号数组 - */ - void validatePostList(Collection ids); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImpl.java deleted file mode 100644 index 5e568b0..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImpl.java +++ /dev/null @@ -1,153 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dept; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 岗位 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class PostServiceImpl implements PostService { - - @Resource - private PostMapper postMapper; - - @Override - public Long createPost(PostSaveReqVO createReqVO) { - // 校验正确性 - validatePostForCreateOrUpdate(null, createReqVO.getName(), createReqVO.getCode()); - - // 插入岗位 - PostDO post = BeanUtils.toBean(createReqVO, PostDO.class); - postMapper.insert(post); - return post.getId(); - } - - @Override - public void updatePost(PostSaveReqVO updateReqVO) { - // 校验正确性 - validatePostForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getName(), updateReqVO.getCode()); - - // 更新岗位 - PostDO updateObj = BeanUtils.toBean(updateReqVO, PostDO.class); - postMapper.updateById(updateObj); - } - - @Override - public void deletePost(Long id) { - // 校验是否存在 - validatePostExists(id); - // 删除部门 - postMapper.deleteById(id); - } - - private void validatePostForCreateOrUpdate(Long id, String name, String code) { - // 校验自己存在 - validatePostExists(id); - // 校验岗位名的唯一性 - validatePostNameUnique(id, name); - // 校验岗位编码的唯一性 - validatePostCodeUnique(id, code); - } - - private void validatePostNameUnique(Long id, String name) { - PostDO post = postMapper.selectByName(name); - if (post == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的岗位 - if (id == null) { - throw exception(POST_NAME_DUPLICATE); - } - if (!post.getId().equals(id)) { - throw exception(POST_NAME_DUPLICATE); - } - } - - private void validatePostCodeUnique(Long id, String code) { - PostDO post = postMapper.selectByCode(code); - if (post == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的岗位 - if (id == null) { - throw exception(POST_CODE_DUPLICATE); - } - if (!post.getId().equals(id)) { - throw exception(POST_CODE_DUPLICATE); - } - } - - private void validatePostExists(Long id) { - if (id == null) { - return; - } - if (postMapper.selectById(id) == null) { - throw exception(POST_NOT_FOUND); - } - } - - @Override - public List getPostList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return Collections.emptyList(); - } - return postMapper.selectBatchIds(ids); - } - - @Override - public List getPostList(Collection ids, Collection statuses) { - return postMapper.selectList(ids, statuses); - } - - @Override - public PageResult getPostPage(PostPageReqVO reqVO) { - return postMapper.selectPage(reqVO); - } - - @Override - public PostDO getPost(Long id) { - return postMapper.selectById(id); - } - - @Override - public void validatePostList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return; - } - // 获得岗位信息 - List posts = postMapper.selectBatchIds(ids); - Map postMap = convertMap(posts, PostDO::getId); - // 校验 - ids.forEach(id -> { - PostDO post = postMap.get(id); - if (post == null) { - throw exception(POST_NOT_FOUND); - } - if (!CommonStatusEnum.ENABLE.getStatus().equals(post.getStatus())) { - throw exception(POST_NOT_ENABLE, post.getName()); - } - }); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java deleted file mode 100644 index fc2498f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataService.java +++ /dev/null @@ -1,110 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dict; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; -import org.springframework.lang.Nullable; - -import java.util.Collection; -import java.util.List; - -/** - * 字典数据 Service 接口 - * - * @author ruoyi - */ -public interface DictDataService { - - /** - * 创建字典数据 - * - * @param createReqVO 字典数据信息 - * @return 字典数据编号 - */ - Long createDictData(DictDataSaveReqVO createReqVO); - - /** - * 更新字典数据 - * - * @param updateReqVO 字典数据信息 - */ - void updateDictData(DictDataSaveReqVO updateReqVO); - - /** - * 删除字典数据 - * - * @param id 字典数据编号 - */ - void deleteDictData(Long id); - - /** - * 获得字典数据列表 - * - * @param status 状态 - * @param dictType 字典类型 - * @return 字典数据全列表 - */ - List getDictDataList(@Nullable Integer status, @Nullable String dictType); - - /** - * 获得字典数据分页列表 - * - * @param pageReqVO 分页请求 - * @return 字典数据分页列表 - */ - PageResult getDictDataPage(DictDataPageReqVO pageReqVO); - - /** - * 获得字典数据详情 - * - * @param id 字典数据编号 - * @return 字典数据 - */ - DictDataDO getDictData(Long id); - - /** - * 获得指定字典类型的数据数量 - * - * @param dictType 字典类型 - * @return 数据数量 - */ - long getDictDataCountByDictType(String dictType); - - /** - * 校验字典数据们是否有效。如下情况,视为无效: - * 1. 字典数据不存在 - * 2. 字典数据被禁用 - * - * @param dictType 字典类型 - * @param values 字典数据值的数组 - */ - void validateDictDataList(String dictType, Collection values); - - /** - * 获得指定的字典数据 - * - * @param dictType 字典类型 - * @param value 字典数据值 - * @return 字典数据 - */ - DictDataDO getDictData(String dictType, String value); - - /** - * 解析获得指定的字典数据,从缓存中 - * - * @param dictType 字典类型 - * @param label 字典数据标签 - * @return 字典数据 - */ - DictDataDO parseDictData(String dictType, String label); - - /** - * 获得指定数据类型的字典数据列表 - * - * @param dictType 字典类型 - * @return 字典数据列表 - */ - List getDictDataListByDictType(String dictType); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java deleted file mode 100644 index b15fd50..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImpl.java +++ /dev/null @@ -1,179 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dict; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; -import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper; -import com.google.common.annotations.VisibleForTesting; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.Collection; -import java.util.Comparator; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 字典数据 Service 实现类 - * - * @author ruoyi - */ -@Service -@Slf4j -public class DictDataServiceImpl implements DictDataService { - - /** - * 排序 dictType > sort - */ - private static final Comparator COMPARATOR_TYPE_AND_SORT = Comparator - .comparing(DictDataDO::getDictType) - .thenComparingInt(DictDataDO::getSort); - - @Resource - private DictTypeService dictTypeService; - - @Resource - private DictDataMapper dictDataMapper; - - @Override - public List getDictDataList(Integer status, String dictType) { - List list = dictDataMapper.selectListByStatusAndDictType(status, dictType); - list.sort(COMPARATOR_TYPE_AND_SORT); - return list; - } - - @Override - public PageResult getDictDataPage(DictDataPageReqVO pageReqVO) { - return dictDataMapper.selectPage(pageReqVO); - } - - @Override - public DictDataDO getDictData(Long id) { - return dictDataMapper.selectById(id); - } - - @Override - public Long createDictData(DictDataSaveReqVO createReqVO) { - // 校验字典类型有效 - validateDictTypeExists(createReqVO.getDictType()); - // 校验字典数据的值的唯一性 - validateDictDataValueUnique(null, createReqVO.getDictType(), createReqVO.getValue()); - - // 插入字典类型 - DictDataDO dictData = BeanUtils.toBean(createReqVO, DictDataDO.class); - dictDataMapper.insert(dictData); - return dictData.getId(); - } - - @Override - public void updateDictData(DictDataSaveReqVO updateReqVO) { - // 校验自己存在 - validateDictDataExists(updateReqVO.getId()); - // 校验字典类型有效 - validateDictTypeExists(updateReqVO.getDictType()); - // 校验字典数据的值的唯一性 - validateDictDataValueUnique(updateReqVO.getId(), updateReqVO.getDictType(), updateReqVO.getValue()); - - // 更新字典类型 - DictDataDO updateObj = BeanUtils.toBean(updateReqVO, DictDataDO.class); - dictDataMapper.updateById(updateObj); - } - - @Override - public void deleteDictData(Long id) { - // 校验是否存在 - validateDictDataExists(id); - - // 删除字典数据 - dictDataMapper.deleteById(id); - } - - @Override - public long getDictDataCountByDictType(String dictType) { - return dictDataMapper.selectCountByDictType(dictType); - } - - @VisibleForTesting - public void validateDictDataValueUnique(Long id, String dictType, String value) { - DictDataDO dictData = dictDataMapper.selectByDictTypeAndValue(dictType, value); - if (dictData == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的字典数据 - if (id == null) { - throw exception(DICT_DATA_VALUE_DUPLICATE); - } - if (!dictData.getId().equals(id)) { - throw exception(DICT_DATA_VALUE_DUPLICATE); - } - } - - @VisibleForTesting - public void validateDictDataExists(Long id) { - if (id == null) { - return; - } - DictDataDO dictData = dictDataMapper.selectById(id); - if (dictData == null) { - throw exception(DICT_DATA_NOT_EXISTS); - } - } - - @VisibleForTesting - public void validateDictTypeExists(String type) { - DictTypeDO dictType = dictTypeService.getDictType(type); - if (dictType == null) { - throw exception(DICT_TYPE_NOT_EXISTS); - } - if (!CommonStatusEnum.ENABLE.getStatus().equals(dictType.getStatus())) { - throw exception(DICT_TYPE_NOT_ENABLE); - } - } - - @Override - public void validateDictDataList(String dictType, Collection values) { - if (CollUtil.isEmpty(values)) { - return; - } - Map dictDataMap = CollectionUtils.convertMap( - dictDataMapper.selectByDictTypeAndValues(dictType, values), DictDataDO::getValue); - // 校验 - values.forEach(value -> { - DictDataDO dictData = dictDataMap.get(value); - if (dictData == null) { - throw exception(DICT_DATA_NOT_EXISTS); - } - if (!CommonStatusEnum.ENABLE.getStatus().equals(dictData.getStatus())) { - throw exception(DICT_DATA_NOT_ENABLE, dictData.getLabel()); - } - }); - } - - @Override - public DictDataDO getDictData(String dictType, String value) { - return dictDataMapper.selectByDictTypeAndValue(dictType, value); - } - - @Override - public DictDataDO parseDictData(String dictType, String label) { - return dictDataMapper.selectByDictTypeAndLabel(dictType, label); - } - - @Override - public List getDictDataListByDictType(String dictType) { - List list = dictDataMapper.selectList(DictDataDO::getDictType, dictType); - list.sort(Comparator.comparing(DictDataDO::getSort)); - return list; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeService.java deleted file mode 100644 index 4091189..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeService.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dict; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; - -import java.util.List; - -/** - * 字典类型 Service 接口 - * - * @author 芋道源码 - */ -public interface DictTypeService { - - /** - * 创建字典类型 - * - * @param createReqVO 字典类型信息 - * @return 字典类型编号 - */ - Long createDictType(DictTypeSaveReqVO createReqVO); - - /** - * 更新字典类型 - * - * @param updateReqVO 字典类型信息 - */ - void updateDictType(DictTypeSaveReqVO updateReqVO); - - /** - * 删除字典类型 - * - * @param id 字典类型编号 - */ - void deleteDictType(Long id); - - /** - * 获得字典类型分页列表 - * - * @param pageReqVO 分页请求 - * @return 字典类型分页列表 - */ - PageResult getDictTypePage(DictTypePageReqVO pageReqVO); - - /** - * 获得字典类型详情 - * - * @param id 字典类型编号 - * @return 字典类型 - */ - DictTypeDO getDictType(Long id); - - /** - * 获得字典类型详情 - * - * @param type 字典类型 - * @return 字典类型详情 - */ - DictTypeDO getDictType(String type); - - /** - * 获得全部字典类型列表 - * - * @return 字典类型列表 - */ - List getDictTypeList(); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImpl.java deleted file mode 100644 index 21d2b4f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImpl.java +++ /dev/null @@ -1,140 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dict; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; -import cn.iocoder.yudao.module.system.dal.mysql.dict.DictTypeMapper; -import com.google.common.annotations.VisibleForTesting; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 字典类型 Service 实现类 - * - * @author 芋道源码 - */ -@Service -public class DictTypeServiceImpl implements DictTypeService { - - @Resource - private DictDataService dictDataService; - - @Resource - private DictTypeMapper dictTypeMapper; - - @Override - public PageResult getDictTypePage(DictTypePageReqVO pageReqVO) { - return dictTypeMapper.selectPage(pageReqVO); - } - - @Override - public DictTypeDO getDictType(Long id) { - return dictTypeMapper.selectById(id); - } - - @Override - public DictTypeDO getDictType(String type) { - return dictTypeMapper.selectByType(type); - } - - @Override - public Long createDictType(DictTypeSaveReqVO createReqVO) { - // 校验字典类型的名字的唯一性 - validateDictTypeNameUnique(null, createReqVO.getName()); - // 校验字典类型的类型的唯一性 - validateDictTypeUnique(null, createReqVO.getType()); - - // 插入字典类型 - DictTypeDO dictType = BeanUtils.toBean(createReqVO, DictTypeDO.class); - dictType.setDeletedTime(LocalDateTimeUtils.EMPTY); // 唯一索引,避免 null 值 - dictTypeMapper.insert(dictType); - return dictType.getId(); - } - - @Override - public void updateDictType(DictTypeSaveReqVO updateReqVO) { - // 校验自己存在 - validateDictTypeExists(updateReqVO.getId()); - // 校验字典类型的名字的唯一性 - validateDictTypeNameUnique(updateReqVO.getId(), updateReqVO.getName()); - // 校验字典类型的类型的唯一性 - validateDictTypeUnique(updateReqVO.getId(), updateReqVO.getType()); - - // 更新字典类型 - DictTypeDO updateObj = BeanUtils.toBean(updateReqVO, DictTypeDO.class); - dictTypeMapper.updateById(updateObj); - } - - @Override - public void deleteDictType(Long id) { - // 校验是否存在 - DictTypeDO dictType = validateDictTypeExists(id); - // 校验是否有字典数据 - if (dictDataService.getDictDataCountByDictType(dictType.getType()) > 0) { - throw exception(DICT_TYPE_HAS_CHILDREN); - } - // 删除字典类型 - dictTypeMapper.updateToDelete(id, LocalDateTime.now()); - } - - @Override - public List getDictTypeList() { - return dictTypeMapper.selectList(); - } - - @VisibleForTesting - void validateDictTypeNameUnique(Long id, String name) { - DictTypeDO dictType = dictTypeMapper.selectByName(name); - if (dictType == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 - if (id == null) { - throw exception(DICT_TYPE_NAME_DUPLICATE); - } - if (!dictType.getId().equals(id)) { - throw exception(DICT_TYPE_NAME_DUPLICATE); - } - } - - @VisibleForTesting - void validateDictTypeUnique(Long id, String type) { - if (StrUtil.isEmpty(type)) { - return; - } - DictTypeDO dictType = dictTypeMapper.selectByType(type); - if (dictType == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 - if (id == null) { - throw exception(DICT_TYPE_TYPE_DUPLICATE); - } - if (!dictType.getId().equals(id)) { - throw exception(DICT_TYPE_TYPE_DUPLICATE); - } - } - - @VisibleForTesting - DictTypeDO validateDictTypeExists(Long id) { - if (id == null) { - return null; - } - DictTypeDO dictType = dictTypeMapper.selectById(id); - if (dictType == null) { - throw exception(DICT_TYPE_NOT_EXISTS); - } - return dictType; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogService.java deleted file mode 100644 index ba9722e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogService.java +++ /dev/null @@ -1,30 +0,0 @@ -package cn.iocoder.yudao.module.system.service.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; - -import jakarta.validation.Valid; - -/** - * 登录日志 Service 接口 - */ -public interface LoginLogService { - - /** - * 获得登录日志分页 - * - * @param pageReqVO 分页条件 - * @return 登录日志分页 - */ - PageResult getLoginLogPage(LoginLogPageReqVO pageReqVO); - - /** - * 创建登录日志 - * - * @param reqDTO 日志信息 - */ - void createLoginLog(@Valid LoginLogCreateReqDTO reqDTO); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImpl.java deleted file mode 100644 index 421b91d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImpl.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.module.system.service.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; -import cn.iocoder.yudao.module.system.dal.mysql.logger.LoginLogMapper; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; - -/** - * 登录日志 Service 实现 - */ -@Service -@Validated -public class LoginLogServiceImpl implements LoginLogService { - - @Resource - private LoginLogMapper loginLogMapper; - - @Override - public PageResult getLoginLogPage(LoginLogPageReqVO pageReqVO) { - return loginLogMapper.selectPage(pageReqVO); - } - - @Override - public void createLoginLog(LoginLogCreateReqDTO reqDTO) { - LoginLogDO loginLog = BeanUtils.toBean(reqDTO, LoginLogDO.class); - loginLogMapper.insert(loginLog); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java deleted file mode 100644 index c647e82..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogService.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.service.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; - -/** - * 操作日志 Service 接口 - * - * @author 芋道源码 - */ -public interface OperateLogService { - - /** - * 记录操作日志 - * - * @param createReqDTO 创建请求 - */ - void createOperateLog(OperateLogCreateReqDTO createReqDTO); - - /** - * 获得操作日志分页列表 - * - * @param pageReqVO 分页条件 - * @return 操作日志分页列表 - */ - PageResult getOperateLogPage(OperateLogPageReqVO pageReqVO); - - /** - * 获得操作日志分页列表 - * - * @param pageReqVO 分页条件 - * @return 操作日志分页列表 - */ - PageResult getOperateLogPage(OperateLogPageReqDTO pageReqVO); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java deleted file mode 100644 index 167be55..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -package cn.iocoder.yudao.module.system.service.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; -import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -/** - * 操作日志 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class OperateLogServiceImpl implements OperateLogService { - - @Resource - private OperateLogMapper operateLogMapper; - - @Override - public void createOperateLog(OperateLogCreateReqDTO createReqDTO) { - OperateLogDO log = BeanUtils.toBean(createReqDTO, OperateLogDO.class); - operateLogMapper.insert(log); - } - - @Override - public PageResult getOperateLogPage(OperateLogPageReqVO pageReqVO) { - return operateLogMapper.selectPage(pageReqVO); - } - - @Override - public PageResult getOperateLogPage(OperateLogPageReqDTO pageReqDTO) { - return operateLogMapper.selectPage(pageReqDTO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java deleted file mode 100644 index eda9ff2..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; - -import jakarta.validation.Valid; -import java.util.List; - -/** - * 邮箱账号 Service 接口 - * - * @author wangjingyi - * @since 2022-03-21 - */ -public interface MailAccountService { - - /** - * 创建邮箱账号 - * - * @param createReqVO 邮箱账号信息 - * @return 编号 - */ - Long createMailAccount(@Valid MailAccountSaveReqVO createReqVO); - - /** - * 修改邮箱账号 - * - * @param updateReqVO 邮箱账号信息 - */ - void updateMailAccount(@Valid MailAccountSaveReqVO updateReqVO); - - /** - * 删除邮箱账号 - * - * @param id 编号 - */ - void deleteMailAccount(Long id); - - /** - * 获取邮箱账号信息 - * - * @param id 编号 - * @return 邮箱账号信息 - */ - MailAccountDO getMailAccount(Long id); - - /** - * 从缓存中获取邮箱账号 - * - * @param id 编号 - * @return 邮箱账号 - */ - MailAccountDO getMailAccountFromCache(Long id); - - /** - * 获取邮箱账号分页信息 - * - * @param pageReqVO 邮箱账号分页参数 - * @return 邮箱账号分页信息 - */ - PageResult getMailAccountPage(MailAccountPageReqVO pageReqVO); - - /** - * 获取邮箱数组信息 - * - * @return 邮箱账号信息数组 - */ - List getMailAccountList(); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java deleted file mode 100644 index 3a5879a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java +++ /dev/null @@ -1,99 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; -import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS; - -/** - * 邮箱账号 Service 实现类 - * - * @author wangjingyi - * @since 2022-03-21 - */ -@Service -@Validated -@Slf4j -public class MailAccountServiceImpl implements MailAccountService { - - @Resource - private MailAccountMapper mailAccountMapper; - - @Resource - private MailTemplateService mailTemplateService; - - @Override - public Long createMailAccount(MailAccountSaveReqVO createReqVO) { - MailAccountDO account = BeanUtils.toBean(createReqVO, MailAccountDO.class); - mailAccountMapper.insert(account); - return account.getId(); - } - - @Override - @CacheEvict(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#updateReqVO.id") - public void updateMailAccount(MailAccountSaveReqVO updateReqVO) { - // 校验是否存在 - validateMailAccountExists(updateReqVO.getId()); - - // 更新 - MailAccountDO updateObj = BeanUtils.toBean(updateReqVO, MailAccountDO.class); - mailAccountMapper.updateById(updateObj); - } - - @Override - @CacheEvict(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id") - public void deleteMailAccount(Long id) { - // 校验是否存在账号 - validateMailAccountExists(id); - // 校验是否存在关联模版 - if (mailTemplateService.getMailTemplateCountByAccountId(id) > 0) { - throw exception(MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS); - } - - // 删除 - mailAccountMapper.deleteById(id); - } - - private void validateMailAccountExists(Long id) { - if (mailAccountMapper.selectById(id) == null) { - throw exception(MAIL_ACCOUNT_NOT_EXISTS); - } - } - - @Override - public MailAccountDO getMailAccount(Long id) { - return mailAccountMapper.selectById(id); - } - - @Override - @Cacheable(value = RedisKeyConstants.MAIL_ACCOUNT, key = "#id", unless = "#result == null") - public MailAccountDO getMailAccountFromCache(Long id) { - return getMailAccount(id); - } - - @Override - public PageResult getMailAccountPage(MailAccountPageReqVO pageReqVO) { - return mailAccountMapper.selectPage(pageReqVO); - } - - @Override - public List getMailAccountList() { - return mailAccountMapper.selectList(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java deleted file mode 100644 index 4a0b204..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; - -import java.util.Map; - -/** - * 邮件日志 Service 接口 - * - * @author wangjingyi - * @since 2022-03-21 - */ -public interface MailLogService { - - /** - * 邮件日志分页 - * - * @param pageVO 分页参数 - * @return 分页结果 - */ - PageResult getMailLogPage(MailLogPageReqVO pageVO); - - /** - * 获得指定编号的邮件日志 - * - * @param id 日志编号 - * @return 邮件日志 - */ - MailLogDO getMailLog(Long id); - - /** - * 创建邮件日志 - * - * @param userId 用户编码 - * @param userType 用户类型 - * @param toMail 收件人邮件 - * @param account 邮件账号信息 - * @param template 模版信息 - * @param templateContent 模版内容 - * @param templateParams 模版参数 - * @param isSend 是否发送成功 - * @return 日志编号 - */ - Long createMailLog(Long userId, Integer userType, String toMail, - MailAccountDO account, MailTemplateDO template , - String templateContent, Map templateParams, Boolean isSend); - - /** - * 更新邮件发送结果 - * - * @param logId 日志编号 - * @param messageId 发送后的消息编号 - * @param exception 发送异常 - */ - void updateMailSendResult(Long logId, String messageId, Exception exception); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImpl.java deleted file mode 100644 index 827d0c5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImpl.java +++ /dev/null @@ -1,78 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper; -import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Map; -import java.util.Objects; - -import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage; - -/** - * 邮件日志 Service 实现类 - * - * @author wangjingyi - * @since 2022-03-21 - */ -@Service -@Validated -public class MailLogServiceImpl implements MailLogService { - - @Resource - private MailLogMapper mailLogMapper; - - @Override - public PageResult getMailLogPage(MailLogPageReqVO pageVO) { - return mailLogMapper.selectPage(pageVO); - } - - @Override - public MailLogDO getMailLog(Long id) { - return mailLogMapper.selectById(id); - } - - @Override - public Long createMailLog(Long userId, Integer userType, String toMail, - MailAccountDO account, MailTemplateDO template, - String templateContent, Map templateParams, Boolean isSend) { - MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder(); - // 根据是否要发送,设置状态 - logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus() - : MailSendStatusEnum.IGNORE.getStatus()) - // 用户信息 - .userId(userId).userType(userType).toMail(toMail) - .accountId(account.getId()).fromMail(account.getMail()) - // 模板相关字段 - .templateId(template.getId()).templateCode(template.getCode()).templateNickname(template.getNickname()) - .templateTitle(template.getTitle()).templateContent(templateContent).templateParams(templateParams); - - // 插入数据库 - MailLogDO logDO = logDOBuilder.build(); - mailLogMapper.insert(logDO); - return logDO.getId(); - } - - @Override - public void updateMailSendResult(Long logId, String messageId, Exception exception) { - // 1. 成功 - if (exception == null) { - mailLogMapper.updateById(new MailLogDO().setId(logId).setSendTime(LocalDateTime.now()) - .setSendStatus(MailSendStatusEnum.SUCCESS.getStatus()).setSendMessageId(messageId)); - return; - } - // 2. 失败 - mailLogMapper.updateById(new MailLogDO().setId(logId).setSendTime(LocalDateTime.now()) - .setSendStatus(MailSendStatusEnum.FAILURE.getStatus()).setSendException(getRootCauseMessage(exception))); - - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java deleted file mode 100644 index 8988168..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java +++ /dev/null @@ -1,60 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; - -import java.util.Map; - -/** - * 邮件发送 Service 接口 - * - * @author wangjingyi - * @since 2022-03-21 - */ -public interface MailSendService { - - /** - * 发送单条邮件给管理后台的用户 - * - * @param mail 邮箱 - * @param userId 用户编码 - * @param templateCode 邮件模版编码 - * @param templateParams 邮件模版参数 - * @return 发送日志编号 - */ - Long sendSingleMailToAdmin(String mail, Long userId, - String templateCode, Map templateParams); - - /** - * 发送单条邮件给用户 APP 的用户 - * - * @param mail 邮箱 - * @param userId 用户编码 - * @param templateCode 邮件模版编码 - * @param templateParams 邮件模版参数 - * @return 发送日志编号 - */ - Long sendSingleMailToMember(String mail, Long userId, - String templateCode, Map templateParams); - - /** - * 发送单条邮件给用户 - * - * @param mail 邮箱 - * @param userId 用户编码 - * @param userType 用户类型 - * @param templateCode 邮件模版编码 - * @param templateParams 邮件模版参数 - * @return 发送日志编号 - */ - Long sendSingleMail(String mail, Long userId, Integer userType, - String templateCode, Map templateParams); - - /** - * 执行真正的邮件发送 - * 注意,该方法仅仅提供给 MQ Consumer 使用 - * - * @param message 邮件 - */ - void doSendMail(MailSendMessage message); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java deleted file mode 100644 index 306b05c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java +++ /dev/null @@ -1,173 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; -import cn.iocoder.yudao.module.system.service.member.MemberService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import com.google.common.annotations.VisibleForTesting; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.dromara.hutool.extra.mail.*; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 邮箱发送 Service 实现类 - * - * @author wangjingyi - * @since 2022-03-21 - */ -@Service -@Validated -@Slf4j -public class MailSendServiceImpl implements MailSendService { - - @Resource - private AdminUserService adminUserService; - @Resource - private MemberService memberService; - - @Resource - private MailAccountService mailAccountService; - @Resource - private MailTemplateService mailTemplateService; - - @Resource - private MailLogService mailLogService; - @Resource - private MailProducer mailProducer; - - @Override - public Long sendSingleMailToAdmin(String mail, Long userId, - String templateCode, Map templateParams) { - // 如果 mail 为空,则加载用户编号对应的邮箱 - if (StrUtil.isEmpty(mail)) { - AdminUserDO user = adminUserService.getUser(userId); - if (user != null) { - mail = user.getEmail(); - } - } - // 执行发送 - return sendSingleMail(mail, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams); - } - - @Override - public Long sendSingleMailToMember(String mail, Long userId, - String templateCode, Map templateParams) { - // 如果 mail 为空,则加载用户编号对应的邮箱 - if (StrUtil.isEmpty(mail)) { - mail = memberService.getMemberUserEmail(userId); - } - // 执行发送 - return sendSingleMail(mail, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); - } - - @Override - public Long sendSingleMail(String mail, Long userId, Integer userType, - String templateCode, Map templateParams) { - // 校验邮箱模版是否合法 - MailTemplateDO template = validateMailTemplate(templateCode); - // 校验邮箱账号是否合法 - MailAccountDO account = validateMailAccount(template.getAccountId()); - - // 校验邮箱是否存在 - mail = validateMail(mail); - validateTemplateParams(template, templateParams); - - // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 - Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()); - String title = mailTemplateService.formatMailTemplateContent(template.getTitle(), templateParams); - String content = mailTemplateService.formatMailTemplateContent(template.getContent(), templateParams); - Long sendLogId = mailLogService.createMailLog(userId, userType, mail, - account, template, content, templateParams, isSend); - // 发送 MQ 消息,异步执行发送短信 - if (isSend) { - mailProducer.sendMailSendMessage(sendLogId, mail, account.getId(), - template.getNickname(), title, content); - } - return sendLogId; - } - - @Override - public void doSendMail(MailSendMessage message) { - // 1. 创建发送账号 - MailAccountDO account = validateMailAccount(message.getAccountId()); - MailAccount mailAccount = buildMailAccount(account, message.getNickname()); - // 2. 发送邮件 - try { - String messageId = MailUtil.send(mailAccount, message.getMail(), - message.getTitle(), message.getContent(), true); - // 3. 更新结果(成功) - mailLogService.updateMailSendResult(message.getLogId(), messageId, null); - } catch (Exception e) { - // 3. 更新结果(异常) - mailLogService.updateMailSendResult(message.getLogId(), null, e); - } - } - - private MailAccount buildMailAccount(MailAccountDO account, String nickname) { - String from = StrUtil.isNotEmpty(nickname) ? nickname + " <" + account.getMail() + ">" : account.getMail(); - return new MailAccount().setFrom(from).setAuth(true) - .setUser(account.getUsername()).setPass(account.getPassword().toCharArray()) - .setHost(account.getHost()).setPort(account.getPort()) - .setSslEnable(account.getSslEnable()).setStarttlsEnable(account.getStarttlsEnable()); - } - - @VisibleForTesting - MailTemplateDO validateMailTemplate(String templateCode) { - // 获得邮件模板。考虑到效率,从缓存中获取 - MailTemplateDO template = mailTemplateService.getMailTemplateByCodeFromCache(templateCode); - // 邮件模板不存在 - if (template == null) { - throw exception(MAIL_TEMPLATE_NOT_EXISTS); - } - return template; - } - - @VisibleForTesting - MailAccountDO validateMailAccount(Long accountId) { - // 获得邮箱账号。考虑到效率,从缓存中获取 - MailAccountDO account = mailAccountService.getMailAccountFromCache(accountId); - // 邮箱账号不存在 - if (account == null) { - throw exception(MAIL_ACCOUNT_NOT_EXISTS); - } - return account; - } - - @VisibleForTesting - String validateMail(String mail) { - if (StrUtil.isEmpty(mail)) { - throw exception(MAIL_SEND_MAIL_NOT_EXISTS); - } - return mail; - } - - /** - * 校验邮件参数是否确实 - * - * @param template 邮箱模板 - * @param templateParams 参数列表 - */ - @VisibleForTesting - void validateTemplateParams(MailTemplateDO template, Map templateParams) { - template.getParams().forEach(key -> { - Object value = templateParams.get(key); - if (value == null) { - throw exception(MAIL_SEND_TEMPLATE_PARAM_MISS, key); - } - }); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java deleted file mode 100644 index 006972c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; - -import jakarta.validation.Valid; -import java.util.List; -import java.util.Map; - -/** - * 邮件模版 Service 接口 - * - * @author wangjingyi - * @since 2022-03-21 - */ -public interface MailTemplateService { - - /** - * 邮件模版创建 - * - * @param createReqVO 邮件信息 - * @return 编号 - */ - Long createMailTemplate(@Valid MailTemplateSaveReqVO createReqVO); - - /** - * 邮件模版修改 - * - * @param updateReqVO 邮件信息 - */ - void updateMailTemplate(@Valid MailTemplateSaveReqVO updateReqVO); - - /** - * 邮件模版删除 - * - * @param id 编号 - */ - void deleteMailTemplate(Long id); - - /** - * 获取邮件模版 - * - * @param id 编号 - * @return 邮件模版 - */ - MailTemplateDO getMailTemplate(Long id); - - /** - * 获取邮件模版分页 - * - * @param pageReqVO 模版信息 - * @return 邮件模版分页信息 - */ - PageResult getMailTemplatePage(MailTemplatePageReqVO pageReqVO); - - /** - * 获取邮件模板数组 - * - * @return 模版数组 - */ - List getMailTemplateList(); - - /** - * 从缓存中获取邮件模版 - * - * @param code 模板编码 - * @return 邮件模板 - */ - MailTemplateDO getMailTemplateByCodeFromCache(String code); - - /** - * 邮件模版内容合成 - * - * @param content 邮件模版 - * @param params 合成参数 - * @return 格式化后的内容 - */ - String formatMailTemplateContent(String content, Map params); - - /** - * 获得指定邮件账号下的邮件模板数量 - * - * @param accountId 账号编号 - * @return 数量 - */ - long getMailTemplateCountByAccountId(Long accountId); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java deleted file mode 100644 index 6757750..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java +++ /dev/null @@ -1,138 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.ReUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_CODE_EXISTS; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; - -/** - * 邮箱模版 Service 实现类 - * - * @author wangjingyi - * @since 2022-03-21 - */ -@Service -@Validated -@Slf4j -public class MailTemplateServiceImpl implements MailTemplateService { - - /** - * 正则表达式,匹配 {} 中的变量 - */ - private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}"); - - @Resource - private MailTemplateMapper mailTemplateMapper; - - @Override - public Long createMailTemplate(MailTemplateSaveReqVO createReqVO) { - // 校验 code 是否唯一 - validateCodeUnique(null, createReqVO.getCode()); - - // 插入 - MailTemplateDO template = BeanUtils.toBean(createReqVO, MailTemplateDO.class) - .setParams(parseTemplateContentParams(createReqVO.getContent())); - mailTemplateMapper.insert(template); - return template.getId(); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.MAIL_TEMPLATE, - allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 - public void updateMailTemplate(@Valid MailTemplateSaveReqVO updateReqVO) { - // 校验是否存在 - validateMailTemplateExists(updateReqVO.getId()); - // 校验 code 是否唯一 - validateCodeUnique(updateReqVO.getId(),updateReqVO.getCode()); - - // 更新 - MailTemplateDO updateObj = BeanUtils.toBean(updateReqVO, MailTemplateDO.class) - .setParams(parseTemplateContentParams(updateReqVO.getContent())); - mailTemplateMapper.updateById(updateObj); - } - - @VisibleForTesting - void validateCodeUnique(Long id, String code) { - MailTemplateDO template = mailTemplateMapper.selectByCode(code); - if (template == null) { - return; - } - // 存在 template 记录的情况下 - if (id == null // 新增时,说明重复 - || ObjUtil.notEqual(id, template.getId())) { // 更新时,如果 id 不一致,说明重复 - throw exception(MAIL_TEMPLATE_CODE_EXISTS); - } - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.MAIL_TEMPLATE, - allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 - public void deleteMailTemplate(Long id) { - // 校验是否存在 - validateMailTemplateExists(id); - - // 删除 - mailTemplateMapper.deleteById(id); - } - - private void validateMailTemplateExists(Long id) { - if (mailTemplateMapper.selectById(id) == null) { - throw exception(MAIL_TEMPLATE_NOT_EXISTS); - } - } - - @Override - public MailTemplateDO getMailTemplate(Long id) {return mailTemplateMapper.selectById(id);} - - @Override - @Cacheable(value = RedisKeyConstants.MAIL_TEMPLATE, key = "#code", unless = "#result == null") - public MailTemplateDO getMailTemplateByCodeFromCache(String code) { - return mailTemplateMapper.selectByCode(code); - } - - @Override - public PageResult getMailTemplatePage(MailTemplatePageReqVO pageReqVO) { - return mailTemplateMapper.selectPage(pageReqVO); - } - - @Override - public List getMailTemplateList() {return mailTemplateMapper.selectList();} - - @Override - public String formatMailTemplateContent(String content, Map params) { - return StrUtil.format(content, params); - } - - @VisibleForTesting - public List parseTemplateContentParams(String content) { - return ReUtil.findAllGroup1(PATTERN_PARAMS, content); - } - - @Override - public long getMailTemplateCountByAccountId(Long accountId) { - return mailTemplateMapper.selectCountByAccountId(accountId); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberService.java deleted file mode 100644 index 933e5bf..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberService.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.module.system.service.member; - -/** - * Member Service 接口 - * - * @author 芋道源码 - */ -public interface MemberService { - - /** - * 获得会员用户的手机号码 - * - * @param id 会员用户编号 - * @return 手机号码 - */ - String getMemberUserMobile(Long id); - - /** - * 获得会员用户的邮箱 - * - * @param id 会员用户编号 - * @return 邮箱 - */ - String getMemberUserEmail(Long id); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberServiceImpl.java deleted file mode 100644 index 7b31e04..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberServiceImpl.java +++ /dev/null @@ -1,54 +0,0 @@ -package cn.iocoder.yudao.module.system.service.member; - -import cn.hutool.core.util.ClassUtil; -import cn.hutool.core.util.ReflectUtil; -import cn.hutool.extra.spring.SpringUtil; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -/** - * Member Service 实现类 - * - * @author 芋道源码 - */ -@Service -public class MemberServiceImpl implements MemberService { - - @Value("${yudao.info.base-package}") - private String basePackage; - - private volatile Object memberUserApi; - - @Override - public String getMemberUserMobile(Long id) { - Object user = getMemberUser(id); - if (user == null) { - return null; - } - return ReflectUtil.invoke(user, "getMobile"); - } - - @Override - public String getMemberUserEmail(Long id) { - Object user = getMemberUser(id); - if (user == null) { - return null; - } - return ReflectUtil.invoke(user, "getEmail"); - } - - private Object getMemberUser(Long id) { - if (id == null) { - return null; - } - return ReflectUtil.invoke(getMemberUserApi(), "getUser", id); - } - - private Object getMemberUserApi() { - if (memberUserApi == null) { - memberUserApi = SpringUtil.getBean(ClassUtil.loadClass(String.format("%s.module.member.api.user.MemberUserApi", basePackage))); - } - return memberUserApi; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/package-info.java deleted file mode 100644 index ce080b4..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * yudao-module-member 模块的适配,解除 yudao-module-system 对它们的依赖 - */ -package cn.iocoder.yudao.module.system.service.member; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeService.java deleted file mode 100644 index c935c49..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeService.java +++ /dev/null @@ -1,51 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notice; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; - -/** - * 通知公告 Service 接口 - */ -public interface NoticeService { - - /** - * 创建通知公告 - * - * @param createReqVO 通知公告 - * @return 编号 - */ - Long createNotice(NoticeSaveReqVO createReqVO); - - /** - * 更新通知公告 - * - * @param reqVO 通知公告 - */ - void updateNotice(NoticeSaveReqVO reqVO); - - /** - * 删除通知公告 - * - * @param id 编号 - */ - void deleteNotice(Long id); - - /** - * 获得通知公告分页列表 - * - * @param reqVO 分页条件 - * @return 部门分页列表 - */ - PageResult getNoticePage(NoticePageReqVO reqVO); - - /** - * 获得通知公告 - * - * @param id 编号 - * @return 通知公告 - */ - NoticeDO getNotice(Long id); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImpl.java deleted file mode 100644 index e6648ff..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImpl.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notice; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; -import cn.iocoder.yudao.module.system.dal.mysql.notice.NoticeMapper; -import com.google.common.annotations.VisibleForTesting; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND; - -/** - * 通知公告 Service 实现类 - * - * @author 芋道源码 - */ -@Service -public class NoticeServiceImpl implements NoticeService { - - @Resource - private NoticeMapper noticeMapper; - - @Override - public Long createNotice(NoticeSaveReqVO createReqVO) { - NoticeDO notice = BeanUtils.toBean(createReqVO, NoticeDO.class); - noticeMapper.insert(notice); - return notice.getId(); - } - - @Override - public void updateNotice(NoticeSaveReqVO updateReqVO) { - // 校验是否存在 - validateNoticeExists(updateReqVO.getId()); - // 更新通知公告 - NoticeDO updateObj = BeanUtils.toBean(updateReqVO, NoticeDO.class); - noticeMapper.updateById(updateObj); - } - - @Override - public void deleteNotice(Long id) { - // 校验是否存在 - validateNoticeExists(id); - // 删除通知公告 - noticeMapper.deleteById(id); - } - - @Override - public PageResult getNoticePage(NoticePageReqVO reqVO) { - return noticeMapper.selectPage(reqVO); - } - - @Override - public NoticeDO getNotice(Long id) { - return noticeMapper.selectById(id); - } - - @VisibleForTesting - public void validateNoticeExists(Long id) { - if (id == null) { - return; - } - NoticeDO notice = noticeMapper.selectById(id); - if (notice == null) { - throw exception(NOTICE_NOT_FOUND); - } - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageService.java deleted file mode 100644 index b06aef3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageService.java +++ /dev/null @@ -1,97 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notify; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * 站内信 Service 接口 - * - * @author xrcoder - */ -public interface NotifyMessageService { - - /** - * 创建站内信 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param template 模版信息 - * @param templateContent 模版内容 - * @param templateParams 模版参数 - * @return 站内信编号 - */ - Long createNotifyMessage(Long userId, Integer userType, - NotifyTemplateDO template, String templateContent, Map templateParams); - - /** - * 获得站内信分页 - * - * @param pageReqVO 分页查询 - * @return 站内信分页 - */ - PageResult getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO); - - /** - * 获得【我的】站内信分页 - * - * @param pageReqVO 分页查询 - * @param userId 用户编号 - * @param userType 用户类型 - * @return 站内信分页 - */ - PageResult getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType); - - /** - * 获得站内信 - * - * @param id 编号 - * @return 站内信 - */ - NotifyMessageDO getNotifyMessage(Long id); - - /** - * 获得【我的】未读站内信列表 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param size 数量 - * @return 站内信列表 - */ - List getUnreadNotifyMessageList(Long userId, Integer userType, Integer size); - - /** - * 统计用户未读站内信条数 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @return 返回未读站内信条数 - */ - Long getUnreadNotifyMessageCount(Long userId, Integer userType); - - /** - * 标记站内信为已读 - * - * @param ids 站内信编号集合 - * @param userId 用户编号 - * @param userType 用户类型 - * @return 更新到的条数 - */ - int updateNotifyMessageRead(Collection ids, Long userId, Integer userType); - - /** - * 标记所有站内信为已读 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @return 更新到的条数 - */ - int updateAllNotifyMessageRead(Long userId, Integer userType); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImpl.java deleted file mode 100644 index 9593473..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImpl.java +++ /dev/null @@ -1,75 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notify; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * 站内信 Service 实现类 - * - * @author xrcoder - */ -@Service -@Validated -public class NotifyMessageServiceImpl implements NotifyMessageService { - - @Resource - private NotifyMessageMapper notifyMessageMapper; - - @Override - public Long createNotifyMessage(Long userId, Integer userType, - NotifyTemplateDO template, String templateContent, Map templateParams) { - NotifyMessageDO message = new NotifyMessageDO().setUserId(userId).setUserType(userType) - .setTemplateId(template.getId()).setTemplateCode(template.getCode()) - .setTemplateType(template.getType()).setTemplateNickname(template.getNickname()) - .setTemplateContent(templateContent).setTemplateParams(templateParams).setReadStatus(false); - notifyMessageMapper.insert(message); - return message.getId(); - } - - @Override - public PageResult getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO) { - return notifyMessageMapper.selectPage(pageReqVO); - } - - @Override - public PageResult getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType) { - return notifyMessageMapper.selectPage(pageReqVO, userId, userType); - } - - @Override - public NotifyMessageDO getNotifyMessage(Long id) { - return notifyMessageMapper.selectById(id); - } - - @Override - public List getUnreadNotifyMessageList(Long userId, Integer userType, Integer size) { - return notifyMessageMapper.selectUnreadListByUserIdAndUserType(userId, userType, size); - } - - @Override - public Long getUnreadNotifyMessageCount(Long userId, Integer userType) { - return notifyMessageMapper.selectUnreadCountByUserIdAndUserType(userId, userType); - } - - @Override - public int updateNotifyMessageRead(Collection ids, Long userId, Integer userType) { - return notifyMessageMapper.updateListRead(ids, userId, userType); - } - - @Override - public int updateAllNotifyMessageRead(Long userId, Integer userType) { - return notifyMessageMapper.updateListRead(userId, userType); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java deleted file mode 100644 index d848bcb..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java +++ /dev/null @@ -1,55 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notify; - -import java.util.List; -import java.util.Map; - -/** - * 站内信发送 Service 接口 - * - * @author xrcoder - */ -public interface NotifySendService { - - /** - * 发送单条站内信给管理后台的用户 - * - * 在 mobile 为空时,使用 userId 加载对应管理员的手机号 - * - * @param userId 用户编号 - * @param templateCode 短信模板编号 - * @param templateParams 短信模板参数 - * @return 发送日志编号 - */ - Long sendSingleNotifyToAdmin(Long userId, - String templateCode, Map templateParams); - /** - * 发送单条站内信给用户 APP 的用户 - * - * 在 mobile 为空时,使用 userId 加载对应会员的手机号 - * - * @param userId 用户编号 - * @param templateCode 站内信模板编号 - * @param templateParams 站内信模板参数 - * @return 发送日志编号 - */ - Long sendSingleNotifyToMember(Long userId, - String templateCode, Map templateParams); - - /** - * 发送单条站内信给用户 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param templateCode 站内信模板编号 - * @param templateParams 站内信模板参数 - * @return 发送日志编号 - */ - Long sendSingleNotify( Long userId, Integer userType, - String templateCode, Map templateParams); - - default void sendBatchNotify(List mobiles, List userIds, Integer userType, - String templateCode, Map templateParams) { - throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!"); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java deleted file mode 100644 index a47d7fe..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notify; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.util.Map; -import java.util.Objects; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 站内信发送 Service 实现类 - * - * @author xrcoder - */ -@Service -@Validated -@Slf4j -public class NotifySendServiceImpl implements NotifySendService { - - @Resource - private NotifyTemplateService notifyTemplateService; - - @Resource - private NotifyMessageService notifyMessageService; - - @Override - public Long sendSingleNotifyToAdmin(Long userId, String templateCode, Map templateParams) { - return sendSingleNotify(userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams); - } - - @Override - public Long sendSingleNotifyToMember(Long userId, String templateCode, Map templateParams) { - return sendSingleNotify(userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); - } - - @Override - public Long sendSingleNotify(Long userId, Integer userType, String templateCode, Map templateParams) { - // 校验模版 - NotifyTemplateDO template = validateNotifyTemplate(templateCode); - if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { - log.info("[sendSingleNotify][模版({})已经关闭,无法给用户({}/{})发送]", templateCode, userId, userType); - return null; - } - // 校验参数 - validateTemplateParams(template, templateParams); - - // 发送站内信 - String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), templateParams); - return notifyMessageService.createNotifyMessage(userId, userType, template, content, templateParams); - } - - @VisibleForTesting - public NotifyTemplateDO validateNotifyTemplate(String templateCode) { - // 获得站内信模板。考虑到效率,从缓存中获取 - NotifyTemplateDO template = notifyTemplateService.getNotifyTemplateByCodeFromCache(templateCode); - // 站内信模板不存在 - if (template == null) { - throw exception(NOTICE_NOT_FOUND); - } - return template; - } - - /** - * 校验站内信模版参数是否确实 - * - * @param template 邮箱模板 - * @param templateParams 参数列表 - */ - @VisibleForTesting - public void validateTemplateParams(NotifyTemplateDO template, Map templateParams) { - template.getParams().forEach(key -> { - Object value = templateParams.get(key); - if (value == null) { - throw exception(NOTIFY_SEND_TEMPLATE_PARAM_MISS, key); - } - }); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java deleted file mode 100644 index bac122d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java +++ /dev/null @@ -1,73 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notify; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; - -import jakarta.validation.Valid; -import java.util.Map; - -/** - * 站内信模版 Service 接口 - * - * @author xrcoder - */ -public interface NotifyTemplateService { - - /** - * 创建站内信模版 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createNotifyTemplate(@Valid NotifyTemplateSaveReqVO createReqVO); - - /** - * 更新站内信模版 - * - * @param updateReqVO 更新信息 - */ - void updateNotifyTemplate(@Valid NotifyTemplateSaveReqVO updateReqVO); - - /** - * 删除站内信模版 - * - * @param id 编号 - */ - void deleteNotifyTemplate(Long id); - - /** - * 获得站内信模版 - * - * @param id 编号 - * @return 站内信模版 - */ - NotifyTemplateDO getNotifyTemplate(Long id); - - /** - * 获得站内信模板,从缓存中 - * - * @param code 模板编码 - * @return 站内信模板 - */ - NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code); - - /** - * 获得站内信模版分页 - * - * @param pageReqVO 分页查询 - * @return 站内信模版分页 - */ - PageResult getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO); - - /** - * 格式化站内信内容 - * - * @param content 站内信模板的内容 - * @param params 站内信内容的参数 - * @return 格式化后的内容 - */ - String formatNotifyTemplateContent(String content, Map params); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java deleted file mode 100644 index d52b2b7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java +++ /dev/null @@ -1,138 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notify; - -import cn.hutool.core.util.ReUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_CODE_DUPLICATE; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS; - -/** - * 站内信模版 Service 实现类 - * - * @author xrcoder - */ -@Service -@Validated -@Slf4j -public class NotifyTemplateServiceImpl implements NotifyTemplateService { - - /** - * 正则表达式,匹配 {} 中的变量 - */ - private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}"); - - @Resource - private NotifyTemplateMapper notifyTemplateMapper; - - @Override - public Long createNotifyTemplate(NotifyTemplateSaveReqVO createReqVO) { - // 校验站内信编码是否重复 - validateNotifyTemplateCodeDuplicate(null, createReqVO.getCode()); - - // 插入 - NotifyTemplateDO notifyTemplate = BeanUtils.toBean(createReqVO, NotifyTemplateDO.class); - notifyTemplate.setParams(parseTemplateContentParams(notifyTemplate.getContent())); - notifyTemplateMapper.insert(notifyTemplate); - return notifyTemplate.getId(); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, - allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 - public void updateNotifyTemplate(NotifyTemplateSaveReqVO updateReqVO) { - // 校验存在 - validateNotifyTemplateExists(updateReqVO.getId()); - // 校验站内信编码是否重复 - validateNotifyTemplateCodeDuplicate(updateReqVO.getId(), updateReqVO.getCode()); - - // 更新 - NotifyTemplateDO updateObj = BeanUtils.toBean(updateReqVO, NotifyTemplateDO.class); - updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); - notifyTemplateMapper.updateById(updateObj); - } - - @VisibleForTesting - public List parseTemplateContentParams(String content) { - return ReUtil.findAllGroup1(PATTERN_PARAMS, content); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, - allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 - public void deleteNotifyTemplate(Long id) { - // 校验存在 - validateNotifyTemplateExists(id); - // 删除 - notifyTemplateMapper.deleteById(id); - } - - private void validateNotifyTemplateExists(Long id) { - if (notifyTemplateMapper.selectById(id) == null) { - throw exception(NOTIFY_TEMPLATE_NOT_EXISTS); - } - } - - @Override - public NotifyTemplateDO getNotifyTemplate(Long id) { - return notifyTemplateMapper.selectById(id); - } - - @Override - @Cacheable(cacheNames = RedisKeyConstants.NOTIFY_TEMPLATE, key = "#code", - unless = "#result == null") - public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) { - return notifyTemplateMapper.selectByCode(code); - } - - @Override - public PageResult getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) { - return notifyTemplateMapper.selectPage(pageReqVO); - } - - @VisibleForTesting - void validateNotifyTemplateCodeDuplicate(Long id, String code) { - NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code); - if (template == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 - if (id == null) { - throw exception(NOTIFY_TEMPLATE_CODE_DUPLICATE, code); - } - if (!template.getId().equals(id)) { - throw exception(NOTIFY_TEMPLATE_CODE_DUPLICATE, code); - } - } - - /** - * 格式化站内信内容 - * - * @param content 站内信模板的内容 - * @param params 站内信内容的参数 - * @return 格式化后的内容 - */ - @Override - public String formatNotifyTemplateContent(String content, Map params) { - return StrUtil.format(content, params); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java deleted file mode 100644 index def3e5e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveService.java +++ /dev/null @@ -1,52 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * OAuth2 批准 Service 接口 - * - * 从功能上,和 Spring Security OAuth 的 ApprovalStoreUserApprovalHandler 的功能,记录用户针对指定客户端的授权,减少手动确定。 - * - * @author 芋道源码 - */ -public interface OAuth2ApproveService { - - /** - * 获得指定用户,针对指定客户端的指定授权,是否通过 - * - * 参考 ApprovalStoreUserApprovalHandler 的 checkForPreApproval 方法 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param clientId 客户端编号 - * @param requestedScopes 授权范围 - * @return 是否授权通过 - */ - boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection requestedScopes); - - /** - * 在用户发起批准时,基于 scopes 的选项,计算最终是否通过 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param clientId 客户端编号 - * @param requestedScopes 授权范围 - * @return 是否授权通过 - */ - boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map requestedScopes); - - /** - * 获得用户的批准列表,排除已过期的 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param clientId 客户端编号 - * @return 是否授权通过 - */ - List getApproveList(Long userId, Integer userType, String clientId); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java deleted file mode 100644 index 7cd5271..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImpl.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.util.date.DateUtils; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ApproveMapper; -import com.google.common.annotations.VisibleForTesting; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.util.*; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; - -/** - * OAuth2 批准 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class OAuth2ApproveServiceImpl implements OAuth2ApproveService { - - /** - * 批准的过期时间,默认 30 天 - */ - private static final Integer TIMEOUT = 30 * 24 * 60 * 60; // 单位:秒 - - @Resource - private OAuth2ClientService oauth2ClientService; - - @Resource - private OAuth2ApproveMapper oauth2ApproveMapper; - - @Override - @Transactional - public boolean checkForPreApproval(Long userId, Integer userType, String clientId, Collection requestedScopes) { - // 第一步,基于 Client 的自动授权计算,如果 scopes 都在自动授权中,则返回 true 通过 - OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); - Assert.notNull(clientDO, "客户端不能为空"); // 防御性编程 - if (CollUtil.containsAll(clientDO.getAutoApproveScopes(), requestedScopes)) { - // gh-877 - if all scopes are auto approved, approvals still need to be added to the approval store. - LocalDateTime expireTime = LocalDateTime.now().plusSeconds(TIMEOUT); - for (String scope : requestedScopes) { - saveApprove(userId, userType, clientId, scope, true, expireTime); - } - return true; - } - - // 第二步,算上用户已经批准的授权。如果 scopes 都包含,则返回 true - List approveDOs = getApproveList(userId, userType, clientId); - Set scopes = convertSet(approveDOs, OAuth2ApproveDO::getScope, - OAuth2ApproveDO::getApproved); // 只保留未过期的 + 同意的 - return CollUtil.containsAll(scopes, requestedScopes); - } - - @Override - @Transactional - public boolean updateAfterApproval(Long userId, Integer userType, String clientId, Map requestedScopes) { - // 如果 requestedScopes 为空,说明没有要求,则返回 true 通过 - if (CollUtil.isEmpty(requestedScopes)) { - return true; - } - - // 更新批准的信息 - boolean success = false; // 需要至少有一个同意 - LocalDateTime expireTime = LocalDateTime.now().plusSeconds(TIMEOUT); - for (Map.Entry entry : requestedScopes.entrySet()) { - if (entry.getValue()) { - success = true; - } - saveApprove(userId, userType, clientId, entry.getKey(), entry.getValue(), expireTime); - } - return success; - } - - @Override - public List getApproveList(Long userId, Integer userType, String clientId) { - List approveDOs = oauth2ApproveMapper.selectListByUserIdAndUserTypeAndClientId( - userId, userType, clientId); - approveDOs.removeIf(o -> DateUtils.isExpired(o.getExpiresTime())); - return approveDOs; - } - - @VisibleForTesting - void saveApprove(Long userId, Integer userType, String clientId, - String scope, Boolean approved, LocalDateTime expireTime) { - // 先更新 - OAuth2ApproveDO approveDO = new OAuth2ApproveDO().setUserId(userId).setUserType(userType) - .setClientId(clientId).setScope(scope).setApproved(approved).setExpiresTime(expireTime); - if (oauth2ApproveMapper.update(approveDO) == 1) { - return; - } - // 失败,则说明不存在,进行更新 - oauth2ApproveMapper.insert(approveDO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java deleted file mode 100644 index 1d6074e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientService.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; - -import jakarta.validation.Valid; -import java.util.Collection; - -/** - * OAuth2.0 Client Service 接口 - * - * 从功能上,和 JdbcClientDetailsService 的功能,提供客户端的操作 - * - * @author 芋道源码 - */ -public interface OAuth2ClientService { - - /** - * 创建 OAuth2 客户端 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createOAuth2Client(@Valid OAuth2ClientSaveReqVO createReqVO); - - /** - * 更新 OAuth2 客户端 - * - * @param updateReqVO 更新信息 - */ - void updateOAuth2Client(@Valid OAuth2ClientSaveReqVO updateReqVO); - - /** - * 删除 OAuth2 客户端 - * - * @param id 编号 - */ - void deleteOAuth2Client(Long id); - - /** - * 获得 OAuth2 客户端 - * - * @param id 编号 - * @return OAuth2 客户端 - */ - OAuth2ClientDO getOAuth2Client(Long id); - - /** - * 获得 OAuth2 客户端,从缓存中 - * - * @param clientId 客户端编号 - * @return OAuth2 客户端 - */ - OAuth2ClientDO getOAuth2ClientFromCache(String clientId); - - /** - * 获得 OAuth2 客户端分页 - * - * @param pageReqVO 分页查询 - * @return OAuth2 客户端分页 - */ - PageResult getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO); - - /** - * 从缓存中,校验客户端是否合法 - * - * @return 客户端 - */ - default OAuth2ClientDO validOAuthClientFromCache(String clientId) { - return validOAuthClientFromCache(clientId, null, null, null, null); - } - - /** - * 从缓存中,校验客户端是否合法 - * - * 非空时,进行校验 - * - * @param clientId 客户端编号 - * @param clientSecret 客户端密钥 - * @param authorizedGrantType 授权方式 - * @param scopes 授权范围 - * @param redirectUri 重定向地址 - * @return 客户端 - */ - OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType, - Collection scopes, String redirectUri); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java deleted file mode 100644 index 3c22954..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java +++ /dev/null @@ -1,153 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.util.string.StrUtils; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.util.Collection; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * OAuth2.0 Client Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class OAuth2ClientServiceImpl implements OAuth2ClientService { - - @Resource - private OAuth2ClientMapper oauth2ClientMapper; - - @Override - public Long createOAuth2Client(OAuth2ClientSaveReqVO createReqVO) { - validateClientIdExists(null, createReqVO.getClientId()); - // 插入 - OAuth2ClientDO client = BeanUtils.toBean(createReqVO, OAuth2ClientDO.class); - oauth2ClientMapper.insert(client); - return client.getId(); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT, - allEntries = true) // allEntries 清空所有缓存,因为可能修改到 clientId 字段,不好清理 - public void updateOAuth2Client(OAuth2ClientSaveReqVO updateReqVO) { - // 校验存在 - validateOAuth2ClientExists(updateReqVO.getId()); - // 校验 Client 未被占用 - validateClientIdExists(updateReqVO.getId(), updateReqVO.getClientId()); - - // 更新 - OAuth2ClientDO updateObj = BeanUtils.toBean(updateReqVO, OAuth2ClientDO.class); - oauth2ClientMapper.updateById(updateObj); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.OAUTH_CLIENT, - allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 key,不好清理 - public void deleteOAuth2Client(Long id) { - // 校验存在 - validateOAuth2ClientExists(id); - // 删除 - oauth2ClientMapper.deleteById(id); - } - - private void validateOAuth2ClientExists(Long id) { - if (oauth2ClientMapper.selectById(id) == null) { - throw exception(OAUTH2_CLIENT_NOT_EXISTS); - } - } - - @VisibleForTesting - void validateClientIdExists(Long id, String clientId) { - OAuth2ClientDO client = oauth2ClientMapper.selectByClientId(clientId); - if (client == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的客户端 - if (id == null) { - throw exception(OAUTH2_CLIENT_EXISTS); - } - if (!client.getId().equals(id)) { - throw exception(OAUTH2_CLIENT_EXISTS); - } - } - - @Override - public OAuth2ClientDO getOAuth2Client(Long id) { - return oauth2ClientMapper.selectById(id); - } - - @Override - @Cacheable(cacheNames = RedisKeyConstants.OAUTH_CLIENT, key = "#clientId", - unless = "#result == null") - public OAuth2ClientDO getOAuth2ClientFromCache(String clientId) { - return oauth2ClientMapper.selectByClientId(clientId); - } - - @Override - public PageResult getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO) { - return oauth2ClientMapper.selectPage(pageReqVO); - } - - @Override - public OAuth2ClientDO validOAuthClientFromCache(String clientId, String clientSecret, String authorizedGrantType, - Collection scopes, String redirectUri) { - // 校验客户端存在、且开启 - OAuth2ClientDO client = getSelf().getOAuth2ClientFromCache(clientId); - if (client == null) { - throw exception(OAUTH2_CLIENT_NOT_EXISTS); - } - if (CommonStatusEnum.isDisable(client.getStatus())) { - throw exception(OAUTH2_CLIENT_DISABLE); - } - - // 校验客户端密钥 - if (StrUtil.isNotEmpty(clientSecret) && ObjectUtil.notEqual(client.getSecret(), clientSecret)) { - throw exception(OAUTH2_CLIENT_CLIENT_SECRET_ERROR); - } - // 校验授权方式 - if (StrUtil.isNotEmpty(authorizedGrantType) && !CollUtil.contains(client.getAuthorizedGrantTypes(), authorizedGrantType)) { - throw exception(OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); - } - // 校验授权范围 - if (CollUtil.isNotEmpty(scopes) && !CollUtil.containsAll(client.getScopes(), scopes)) { - throw exception(OAUTH2_CLIENT_SCOPE_OVER); - } - // 校验回调地址 - if (StrUtil.isNotEmpty(redirectUri) && !StrUtils.startWithAny(redirectUri, client.getRedirectUris())) { - throw exception(OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, redirectUri); - } - return client; - } - - /** - * 获得自身的代理对象,解决 AOP 生效问题 - * - * @return 自己 - */ - private OAuth2ClientServiceImpl getSelf() { - return SpringUtil.getBean(getClass()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java deleted file mode 100644 index 5ace87e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeService.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; - -import java.util.List; - -/** - * OAuth2.0 授权码 Service 接口 - * - * 从功能上,和 Spring Security OAuth 的 JdbcAuthorizationCodeServices 的功能,提供授权码的操作 - * - * @author 芋道源码 - */ -public interface OAuth2CodeService { - - /** - * 创建授权码 - * - * 参考 JdbcAuthorizationCodeServices 的 createAuthorizationCode 方法 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param clientId 客户端编号 - * @param scopes 授权范围 - * @param redirectUri 重定向 URI - * @param state 状态 - * @return 授权码的信息 - */ - OAuth2CodeDO createAuthorizationCode(Long userId, Integer userType, String clientId, - List scopes, String redirectUri, String state); - - /** - * 使用授权码 - * - * @param code 授权码 - */ - OAuth2CodeDO consumeAuthorizationCode(String code); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java deleted file mode 100644 index 11150fa..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImpl.java +++ /dev/null @@ -1,64 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.hutool.core.util.IdUtil; -import cn.iocoder.yudao.framework.common.util.date.DateUtils; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2CodeMapper; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_EXPIRE; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_NOT_EXISTS; - -/** - * OAuth2.0 授权码 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class OAuth2CodeServiceImpl implements OAuth2CodeService { - - /** - * 授权码的过期时间,默认 5 分钟 - */ - private static final Integer TIMEOUT = 5 * 60; - - @Resource - private OAuth2CodeMapper oauth2CodeMapper; - - @Override - public OAuth2CodeDO createAuthorizationCode(Long userId, Integer userType, String clientId, - List scopes, String redirectUri, String state) { - OAuth2CodeDO codeDO = new OAuth2CodeDO().setCode(generateCode()) - .setUserId(userId).setUserType(userType) - .setClientId(clientId).setScopes(scopes) - .setExpiresTime(LocalDateTime.now().plusSeconds(TIMEOUT)) - .setRedirectUri(redirectUri).setState(state); - oauth2CodeMapper.insert(codeDO); - return codeDO; - } - - @Override - public OAuth2CodeDO consumeAuthorizationCode(String code) { - OAuth2CodeDO codeDO = oauth2CodeMapper.selectByCode(code); - if (codeDO == null) { - throw exception(OAUTH2_CODE_NOT_EXISTS); - } - if (DateUtils.isExpired(codeDO.getExpiresTime())) { - throw exception(OAUTH2_CODE_EXPIRE); - } - oauth2CodeMapper.deleteById(codeDO.getId()); - return codeDO; - } - - private static String generateCode() { - return IdUtil.fastSimpleUUID(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java deleted file mode 100644 index fef1d5e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantService.java +++ /dev/null @@ -1,113 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; - -import java.util.List; - -/** - * OAuth2 授予 Service 接口 - * - * 从功能上,和 Spring Security OAuth 的 TokenGranter 的功能,提供访问令牌、刷新令牌的操作 - * - * 将自身的 AdminUser 用户,授权给第三方应用,采用 OAuth2.0 的协议。 - * - * 问题:为什么自身也作为一个第三方应用,也走这套流程呢? - * 回复:当然可以这么做,采用 password 模式。考虑到大多数开发者使用不到这个特性,OAuth2.0 毕竟有一定学习成本,所以暂时没有采取这种方式。 - * - * @author 芋道源码 - */ -public interface OAuth2GrantService { - - /** - * 简化模式 - * - * 对应 Spring Security OAuth2 的 ImplicitTokenGranter 功能 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param clientId 客户端编号 - * @param scopes 授权范围 - * @return 访问令牌 - */ - OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, - String clientId, List scopes); - - /** - * 授权码模式,第一阶段,获得 code 授权码 - * - * 对应 Spring Security OAuth2 的 AuthorizationEndpoint 的 generateCode 方法 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param clientId 客户端编号 - * @param scopes 授权范围 - * @param redirectUri 重定向 URI - * @param state 状态 - * @return 授权码 - */ - String grantAuthorizationCodeForCode(Long userId, Integer userType, - String clientId, List scopes, - String redirectUri, String state); - - /** - * 授权码模式,第二阶段,获得 accessToken 访问令牌 - * - * 对应 Spring Security OAuth2 的 AuthorizationCodeTokenGranter 功能 - * - * @param clientId 客户端编号 - * @param code 授权码 - * @param redirectUri 重定向 URI - * @param state 状态 - * @return 访问令牌 - */ - OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code, - String redirectUri, String state); - - /** - * 密码模式 - * - * 对应 Spring Security OAuth2 的 ResourceOwnerPasswordTokenGranter 功能 - * - * @param username 账号 - * @param password 密码 - * @param clientId 客户端编号 - * @param scopes 授权范围 - * @return 访问令牌 - */ - OAuth2AccessTokenDO grantPassword(String username, String password, - String clientId, List scopes); - - /** - * 刷新模式 - * - * 对应 Spring Security OAuth2 的 ResourceOwnerPasswordTokenGranter 功能 - * - * @param refreshToken 刷新令牌 - * @param clientId 客户端编号 - * @return 访问令牌 - */ - OAuth2AccessTokenDO grantRefreshToken(String refreshToken, String clientId); - - /** - * 客户端模式 - * - * 对应 Spring Security OAuth2 的 ClientCredentialsTokenGranter 功能 - * - * @param clientId 客户端编号 - * @param scopes 授权范围 - * @return 访问令牌 - */ - OAuth2AccessTokenDO grantClientCredentials(String clientId, List scopes); - - /** - * 移除访问令牌 - * - * 对应 Spring Security OAuth2 的 ConsumerTokenServices 的 revokeToken 方法 - * - * @param accessToken 访问令牌 - * @param clientId 客户端编号 - * @return 是否移除到 - */ - boolean revokeToken(String clientId, String accessToken); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java deleted file mode 100644 index e95fecc..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImpl.java +++ /dev/null @@ -1,104 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants; -import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; - -/** - * OAuth2 授予 Service 实现类 - * - * @author 芋道源码 - */ -@Service -public class OAuth2GrantServiceImpl implements OAuth2GrantService { - - @Resource - private OAuth2TokenService oauth2TokenService; - @Resource - private OAuth2CodeService oauth2CodeService; - @Resource - private AdminAuthService adminAuthService; - - @Override - public OAuth2AccessTokenDO grantImplicit(Long userId, Integer userType, - String clientId, List scopes) { - return oauth2TokenService.createAccessToken(userId, userType, clientId, scopes); - } - - @Override - public String grantAuthorizationCodeForCode(Long userId, Integer userType, - String clientId, List scopes, - String redirectUri, String state) { - return oauth2CodeService.createAuthorizationCode(userId, userType, clientId, scopes, - redirectUri, state).getCode(); - } - - @Override - public OAuth2AccessTokenDO grantAuthorizationCodeForAccessToken(String clientId, String code, - String redirectUri, String state) { - OAuth2CodeDO codeDO = oauth2CodeService.consumeAuthorizationCode(code); - Assert.notNull(codeDO, "授权码不能为空"); // 防御性编程 - // 校验 clientId 是否匹配 - if (!StrUtil.equals(clientId, codeDO.getClientId())) { - throw exception(ErrorCodeConstants.OAUTH2_GRANT_CLIENT_ID_MISMATCH); - } - // 校验 redirectUri 是否匹配 - if (!StrUtil.equals(redirectUri, codeDO.getRedirectUri())) { - throw exception(ErrorCodeConstants.OAUTH2_GRANT_REDIRECT_URI_MISMATCH); - } - // 校验 state 是否匹配 - state = StrUtil.nullToDefault(state, ""); // 数据库 state 为 null 时,会设置为 "" 空串 - if (!StrUtil.equals(state, codeDO.getState())) { - throw exception(ErrorCodeConstants.OAUTH2_GRANT_STATE_MISMATCH); - } - - // 创建访问令牌 - return oauth2TokenService.createAccessToken(codeDO.getUserId(), codeDO.getUserType(), - codeDO.getClientId(), codeDO.getScopes()); - } - - @Override - public OAuth2AccessTokenDO grantPassword(String username, String password, String clientId, List scopes) { - // 使用账号 + 密码进行登录 - AdminUserDO user = adminAuthService.authenticate(username, password); - Assert.notNull(user, "用户不能为空!"); // 防御性编程 - - // 创建访问令牌 - return oauth2TokenService.createAccessToken(user.getId(), UserTypeEnum.ADMIN.getValue(), clientId, scopes); - } - - @Override - public OAuth2AccessTokenDO grantRefreshToken(String refreshToken, String clientId) { - return oauth2TokenService.refreshAccessToken(refreshToken, clientId); - } - - @Override - public OAuth2AccessTokenDO grantClientCredentials(String clientId, List scopes) { - // TODO 芋艿:项目中使用 OAuth2 解决的是三方应用的授权,内部的 SSO 等问题,所以暂时不考虑 client_credentials 这个场景 - throw new UnsupportedOperationException("暂时不支持 client_credentials 授权模式"); - } - - @Override - public boolean revokeToken(String clientId, String accessToken) { - // 先查询,保证 clientId 时匹配的 - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.getAccessToken(accessToken); - if (accessTokenDO == null || ObjectUtil.notEqual(clientId, accessTokenDO.getClientId())) { - return false; - } - // 再删除 - return oauth2TokenService.removeAccessToken(accessToken) != null; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java deleted file mode 100644 index 977d935..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenService.java +++ /dev/null @@ -1,80 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; - -import java.util.List; - -/** - * OAuth2.0 Token Service 接口 - * - * 从功能上,和 Spring Security OAuth 的 DefaultTokenServices + JdbcTokenStore 的功能,提供访问令牌、刷新令牌的操作 - * - * @author 芋道源码 - */ -public interface OAuth2TokenService { - - /** - * 创建访问令牌 - * 注意:该流程中,会包含创建刷新令牌的创建 - * - * 参考 DefaultTokenServices 的 createAccessToken 方法 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @param clientId 客户端编号 - * @param scopes 授权范围 - * @return 访问令牌的信息 - */ - OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List scopes); - - /** - * 刷新访问令牌 - * - * 参考 DefaultTokenServices 的 refreshAccessToken 方法 - * - * @param refreshToken 刷新令牌 - * @param clientId 客户端编号 - * @return 访问令牌的信息 - */ - OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId); - - /** - * 获得访问令牌 - * - * 参考 DefaultTokenServices 的 getAccessToken 方法 - * - * @param accessToken 访问令牌 - * @return 访问令牌的信息 - */ - OAuth2AccessTokenDO getAccessToken(String accessToken); - - /** - * 校验访问令牌 - * - * @param accessToken 访问令牌 - * @return 访问令牌的信息 - */ - OAuth2AccessTokenDO checkAccessToken(String accessToken); - - /** - * 移除访问令牌 - * 注意:该流程中,会移除相关的刷新令牌 - * - * 参考 DefaultTokenServices 的 revokeToken 方法 - * - * @param accessToken 刷新令牌 - * @return 访问令牌的信息 - */ - OAuth2AccessTokenDO removeAccessToken(String accessToken); - - /** - * 获得访问令牌分页 - * - * @param reqVO 请求 - * @return 访问令牌分页 - */ - PageResult getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java deleted file mode 100644 index fb0e756..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImpl.java +++ /dev/null @@ -1,219 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.date.DateUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.security.core.LoginUser; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper; -import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import jakarta.annotation.Resource; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; - -/** - * OAuth2.0 Token Service 实现类 - * - * @author 芋道源码 - */ -@Service -public class OAuth2TokenServiceImpl implements OAuth2TokenService { - - @Resource - private OAuth2AccessTokenMapper oauth2AccessTokenMapper; - @Resource - private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper; - - @Resource - private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO; - - @Resource - private OAuth2ClientService oauth2ClientService; - @Resource - @Lazy // 懒加载,避免循环依赖 - private AdminUserService adminUserService; - - @Override - @Transactional(rollbackFor = Exception.class) - public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, String clientId, List scopes) { - OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); - // 创建刷新令牌 - OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO, scopes); - // 创建访问令牌 - return createOAuth2AccessToken(refreshTokenDO, clientDO); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, String clientId) { - // 查询访问令牌 - OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken); - if (refreshTokenDO == null) { - throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "无效的刷新令牌"); - } - - // 校验 Client 匹配 - OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); - if (ObjectUtil.notEqual(clientId, refreshTokenDO.getClientId())) { - throw exception0(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), "刷新令牌的客户端编号不正确"); - } - - // 移除相关的访问令牌 - List accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshToken); - if (CollUtil.isNotEmpty(accessTokenDOs)) { - oauth2AccessTokenMapper.deleteByIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getId)); - oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken)); - } - - // 已过期的情况下,删除刷新令牌 - if (DateUtils.isExpired(refreshTokenDO.getExpiresTime())) { - oauth2RefreshTokenMapper.deleteById(refreshTokenDO.getId()); - throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "刷新令牌已过期"); - } - - // 创建访问令牌 - return createOAuth2AccessToken(refreshTokenDO, clientDO); - } - - @Override - public OAuth2AccessTokenDO getAccessToken(String accessToken) { - // 优先从 Redis 中获取 - OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken); - if (accessTokenDO != null) { - return accessTokenDO; - } - - // 获取不到,从 MySQL 中获取访问令牌 - accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken); - if (accessTokenDO == null) { - // 特殊:从 MySQL 中获取刷新令牌。原因:解决部分场景不方便刷新访问令牌场景 - // 例如说,积木报表只允许传递 token,不允许传递 refresh_token,导致无法刷新访问令牌 - // 再例如说,前端 WebSocket 的 token 直接跟在 url 上,无法传递 refresh_token - OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(accessToken); - if (refreshTokenDO != null && !DateUtils.isExpired(refreshTokenDO.getExpiresTime())) { - accessTokenDO = convertToAccessToken(refreshTokenDO); - } - } - - // 如果在 MySQL 存在,则往 Redis 中写入 - if (accessTokenDO != null && !DateUtils.isExpired(accessTokenDO.getExpiresTime())) { - oauth2AccessTokenRedisDAO.set(accessTokenDO); - } - return accessTokenDO; - } - - @Override - public OAuth2AccessTokenDO checkAccessToken(String accessToken) { - OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken); - if (accessTokenDO == null) { - throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌不存在"); - } - if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) { - throw exception0(GlobalErrorCodeConstants.UNAUTHORIZED.getCode(), "访问令牌已过期"); - } - return accessTokenDO; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public OAuth2AccessTokenDO removeAccessToken(String accessToken) { - // 删除访问令牌 - OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken); - if (accessTokenDO == null) { - return null; - } - oauth2AccessTokenMapper.deleteById(accessTokenDO.getId()); - oauth2AccessTokenRedisDAO.delete(accessToken); - // 删除刷新令牌 - oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken()); - return accessTokenDO; - } - - @Override - public PageResult getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) { - return oauth2AccessTokenMapper.selectPage(reqVO); - } - - private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) { - OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken()) - .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()) - .setUserInfo(buildUserInfo(refreshTokenDO.getUserId(), refreshTokenDO.getUserType())) - .setClientId(clientDO.getClientId()).setScopes(refreshTokenDO.getScopes()) - .setRefreshToken(refreshTokenDO.getRefreshToken()) - .setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getAccessTokenValiditySeconds())); - accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号 - oauth2AccessTokenMapper.insert(accessTokenDO); - // 记录到 Redis 中 - oauth2AccessTokenRedisDAO.set(accessTokenDO); - return accessTokenDO; - } - - private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO, List scopes) { - OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken()) - .setUserId(userId).setUserType(userType) - .setClientId(clientDO.getClientId()).setScopes(scopes) - .setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getRefreshTokenValiditySeconds())); - oauth2RefreshTokenMapper.insert(refreshToken); - return refreshToken; - } - - private OAuth2AccessTokenDO convertToAccessToken(OAuth2RefreshTokenDO refreshTokenDO) { - OAuth2AccessTokenDO accessTokenDO = BeanUtils.toBean(refreshTokenDO, OAuth2AccessTokenDO.class) - .setAccessToken(refreshTokenDO.getRefreshToken()); - TenantUtils.execute(refreshTokenDO.getTenantId(), - () -> accessTokenDO.setUserInfo(buildUserInfo(refreshTokenDO.getUserId(), refreshTokenDO.getUserType()))); - return accessTokenDO; - } - - /** - * 加载用户信息,方便 {@link cn.iocoder.yudao.framework.security.core.LoginUser} 获取到昵称、部门等信息 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @return 用户信息 - */ - private Map buildUserInfo(Long userId, Integer userType) { - if (userType.equals(UserTypeEnum.ADMIN.getValue())) { - AdminUserDO user = adminUserService.getUser(userId); - return MapUtil.builder(LoginUser.INFO_KEY_NICKNAME, user.getNickname()) - .put(LoginUser.INFO_KEY_DEPT_ID, StrUtil.toStringOrNull(user.getDeptId())).build(); - } else if (userType.equals(UserTypeEnum.MEMBER.getValue())) { - // 注意:目前 Member 暂时不读取,可以按需实现 - return Collections.emptyMap(); - } - return null; - } - - private static String generateAccessToken() { - return IdUtil.fastSimpleUUID(); - } - - private static String generateRefreshToken() { - return IdUtil.fastSimpleUUID(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java deleted file mode 100644 index d74dc61..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuService.java +++ /dev/null @@ -1,95 +0,0 @@ -package cn.iocoder.yudao.module.system.service.permission; - -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; - -import java.util.Collection; -import java.util.List; - -/** - * 菜单 Service 接口 - * - * @author 芋道源码 - */ -public interface MenuService { - - /** - * 创建菜单 - * - * @param createReqVO 菜单信息 - * @return 创建出来的菜单编号 - */ - Long createMenu(MenuSaveVO createReqVO); - - /** - * 更新菜单 - * - * @param updateReqVO 菜单信息 - */ - void updateMenu(MenuSaveVO updateReqVO); - - /** - * 删除菜单 - * - * @param id 菜单编号 - */ - void deleteMenu(Long id); - - /** - * 获得所有菜单列表 - * - * @return 菜单列表 - */ - List getMenuList(); - - /** - * 基于租户,筛选菜单列表 - * 注意,如果是系统租户,返回的还是全菜单 - * - * @param reqVO 筛选条件请求 VO - * @return 菜单列表 - */ - List getMenuListByTenant(MenuListReqVO reqVO); - - /** - * 过滤掉关闭的菜单及其子菜单 - * - * @param list 菜单列表 - * @return 过滤后的菜单列表 - */ - List filterDisableMenus(List list); - - /** - * 筛选菜单列表 - * - * @param reqVO 筛选条件请求 VO - * @return 菜单列表 - */ - List getMenuList(MenuListReqVO reqVO); - - /** - * 获得权限对应的菜单编号数组 - * - * @param permission 权限标识 - * @return 数组 - */ - List getMenuIdListByPermissionFromCache(String permission); - - /** - * 获得菜单 - * - * @param id 菜单编号 - * @return 菜单 - */ - MenuDO getMenu(Long id); - - /** - * 获得菜单数组 - * - * @param ids 菜单编号数组 - * @return 菜单数组 - */ - List getMenuList(Collection ids); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java deleted file mode 100644 index 5378108..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java +++ /dev/null @@ -1,289 +0,0 @@ -package cn.iocoder.yudao.module.system.service.permission; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; -import cn.iocoder.yudao.module.system.service.tenant.TenantService; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Lists; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 菜单 Service 实现 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class MenuServiceImpl implements MenuService { - - @Resource - private MenuMapper menuMapper; - @Resource - private PermissionService permissionService; - @Resource - @Lazy // 延迟,避免循环依赖报错 - private TenantService tenantService; - - @Override - @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#createReqVO.permission", - condition = "#createReqVO.permission != null") - public Long createMenu(MenuSaveVO createReqVO) { - // 校验父菜单存在 - validateParentMenu(createReqVO.getParentId(), null); - // 校验菜单(自己) - validateMenuName(createReqVO.getParentId(), createReqVO.getName(), null); - validateMenuComponentName(createReqVO.getComponentName(), null); - - // 插入数据库 - MenuDO menu = BeanUtils.toBean(createReqVO, MenuDO.class); - initMenuProperty(menu); - menuMapper.insert(menu); - // 返回 - return menu.getId(); - } - - @Override - @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, - allEntries = true) // allEntries 清空所有缓存,因为 permission 如果变更,涉及到新老两个 permission。直接清理,简单有效 - public void updateMenu(MenuSaveVO updateReqVO) { - // 校验更新的菜单是否存在 - if (menuMapper.selectById(updateReqVO.getId()) == null) { - throw exception(MENU_NOT_EXISTS); - } - // 校验父菜单存在 - validateParentMenu(updateReqVO.getParentId(), updateReqVO.getId()); - // 校验菜单(自己) - validateMenuName(updateReqVO.getParentId(), updateReqVO.getName(), updateReqVO.getId()); - validateMenuComponentName(updateReqVO.getComponentName(), updateReqVO.getId()); - - // 更新到数据库 - MenuDO updateObj = BeanUtils.toBean(updateReqVO, MenuDO.class); - initMenuProperty(updateObj); - menuMapper.updateById(updateObj); - } - - @Override - @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, - allEntries = true) // allEntries 清空所有缓存,因为此时不知道 id 对应的 permission 是多少。直接清理,简单有效 - public void deleteMenu(Long id) { - // 校验是否还有子菜单 - if (menuMapper.selectCountByParentId(id) > 0) { - throw exception(MENU_EXISTS_CHILDREN); - } - // 校验删除的菜单是否存在 - if (menuMapper.selectById(id) == null) { - throw exception(MENU_NOT_EXISTS); - } - // 标记删除 - menuMapper.deleteById(id); - // 删除授予给角色的权限 - permissionService.processMenuDeleted(id); - } - - @Override - public List getMenuList() { - return menuMapper.selectList(); - } - - @Override - public List getMenuListByTenant(MenuListReqVO reqVO) { - // 查询所有菜单,并过滤掉关闭的节点 - List menus = getMenuList(reqVO); - // 开启多租户的情况下,需要过滤掉未开通的菜单 - tenantService.handleTenantMenu(menuIds -> menus.removeIf(menu -> !CollUtil.contains(menuIds, menu.getId()))); - return menus; - } - - @Override - public List filterDisableMenus(List menuList) { - if (CollUtil.isEmpty(menuList)){ - return Collections.emptyList(); - } - Map menuMap = convertMap(menuList, MenuDO::getId); - - // 遍历 menu 菜单,查找不是禁用的菜单,添加到 enabledMenus 结果 - List enabledMenus = new ArrayList<>(); - Set disabledMenuCache = new HashSet<>(); // 存下递归搜索过被禁用的菜单,防止重复的搜索 - for (MenuDO menu : menuList) { - if (isMenuDisabled(menu, menuMap, disabledMenuCache)) { - continue; - } - enabledMenus.add(menu); - } - return enabledMenus; - } - - private boolean isMenuDisabled(MenuDO node, Map menuMap, Set disabledMenuCache) { - // 如果已经判定是禁用的节点,直接结束 - if (disabledMenuCache.contains(node.getId())) { - return true; - } - - // 1. 先判断自身是否禁用 - if (CommonStatusEnum.isDisable(node.getStatus())) { - disabledMenuCache.add(node.getId()); - return true; - } - - // 2. 遍历到 parentId 为根节点,则无需判断 - Long parentId = node.getParentId(); - if (ObjUtil.equal(parentId, ID_ROOT)) { - return false; - } - - // 3. 继续遍历 parent 节点 - MenuDO parent = menuMap.get(parentId); - if (parent == null || isMenuDisabled(parent, menuMap, disabledMenuCache)) { - disabledMenuCache.add(node.getId()); - return true; - } - return false; - } - - @Override - public List getMenuList(MenuListReqVO reqVO) { - return menuMapper.selectList(reqVO); - } - - @Override - @Cacheable(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, key = "#permission") - public List getMenuIdListByPermissionFromCache(String permission) { - List menus = menuMapper.selectListByPermission(permission); - return convertList(menus, MenuDO::getId); - } - - @Override - public MenuDO getMenu(Long id) { - return menuMapper.selectById(id); - } - - @Override - public List getMenuList(Collection ids) { - // 当 ids 为空时,返回一个空的实例对象 - if (CollUtil.isEmpty(ids)) { - return Lists.newArrayList(); - } - return menuMapper.selectBatchIds(ids); - } - - /** - * 校验父菜单是否合法 - *

- * 1. 不能设置自己为父菜单 - * 2. 父菜单不存在 - * 3. 父菜单必须是 {@link MenuTypeEnum#MENU} 菜单类型 - * - * @param parentId 父菜单编号 - * @param childId 当前菜单编号 - */ - @VisibleForTesting - void validateParentMenu(Long parentId, Long childId) { - if (parentId == null || ID_ROOT.equals(parentId)) { - return; - } - // 不能设置自己为父菜单 - if (parentId.equals(childId)) { - throw exception(MENU_PARENT_ERROR); - } - MenuDO menu = menuMapper.selectById(parentId); - // 父菜单不存在 - if (menu == null) { - throw exception(MENU_PARENT_NOT_EXISTS); - } - // 父菜单必须是目录或者菜单类型 - if (!MenuTypeEnum.DIR.getType().equals(menu.getType()) - && !MenuTypeEnum.MENU.getType().equals(menu.getType())) { - throw exception(MENU_PARENT_NOT_DIR_OR_MENU); - } - } - - /** - * 校验菜单是否合法 - *

- * 1. 校验相同父菜单编号下,是否存在相同的菜单名 - * - * @param name 菜单名字 - * @param parentId 父菜单编号 - * @param id 菜单编号 - */ - @VisibleForTesting - void validateMenuName(Long parentId, String name, Long id) { - MenuDO menu = menuMapper.selectByParentIdAndName(parentId, name); - if (menu == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的菜单 - if (id == null) { - throw exception(MENU_NAME_DUPLICATE); - } - if (!menu.getId().equals(id)) { - throw exception(MENU_NAME_DUPLICATE); - } - } - - /** - * 校验菜单组件名是否合法 - * - * @param componentName 组件名 - * @param id 菜单编号 - */ - @VisibleForTesting - void validateMenuComponentName(String componentName, Long id) { - if (StrUtil.isBlank(componentName)) { - return; - } - MenuDO menu = menuMapper.selectByComponentName(componentName); - if (menu == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的菜单 - if (id == null) { - return; - } - if (!menu.getId().equals(id)) { - throw exception(MENU_COMPONENT_NAME_DUPLICATE); - } - } - - /** - * 初始化菜单的通用属性。 - *

- * 例如说,只有目录或者菜单类型的菜单,才设置 icon - * - * @param menu 菜单 - */ - private void initMenuProperty(MenuDO menu) { - // 菜单为按钮类型时,无需 component、icon、path 属性,进行置空 - if (MenuTypeEnum.BUTTON.getType().equals(menu.getType())) { - menu.setComponent(""); - menu.setComponentName(""); - menu.setIcon(""); - menu.setPath(""); - } - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java deleted file mode 100644 index f7f8122..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionService.java +++ /dev/null @@ -1,146 +0,0 @@ -package cn.iocoder.yudao.module.system.service.permission; - -import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; - -import java.util.Collection; -import java.util.Set; - -import static java.util.Collections.singleton; - -/** - * 权限 Service 接口 - *

- * 提供用户-角色、角色-菜单、角色-部门的关联权限处理 - * - * @author 芋道源码 - */ -public interface PermissionService { - - /** - * 判断是否有权限,任一一个即可 - * - * @param userId 用户编号 - * @param permissions 权限 - * @return 是否 - */ - boolean hasAnyPermissions(Long userId, String... permissions); - - /** - * 判断是否有角色,任一一个即可 - * - * @param roles 角色数组 - * @return 是否 - */ - boolean hasAnyRoles(Long userId, String... roles); - - // ========== 角色-菜单的相关方法 ========== - - /** - * 设置角色菜单 - * - * @param roleId 角色编号 - * @param menuIds 菜单编号集合 - */ - void assignRoleMenu(Long roleId, Set menuIds); - - /** - * 处理角色删除时,删除关联授权数据 - * - * @param roleId 角色编号 - */ - void processRoleDeleted(Long roleId); - - /** - * 处理菜单删除时,删除关联授权数据 - * - * @param menuId 菜单编号 - */ - void processMenuDeleted(Long menuId); - - /** - * 获得角色拥有的菜单编号集合 - * - * @param roleId 角色编号 - * @return 菜单编号集合 - */ - default Set getRoleMenuListByRoleId(Long roleId) { - return getRoleMenuListByRoleId(singleton(roleId)); - } - - /** - * 获得角色们拥有的菜单编号集合 - * - * @param roleIds 角色编号数组 - * @return 菜单编号集合 - */ - Set getRoleMenuListByRoleId(Collection roleIds); - - /** - * 获得拥有指定菜单的角色编号数组,从缓存中获取 - * - * @param menuId 菜单编号 - * @return 角色编号数组 - */ - Set getMenuRoleIdListByMenuIdFromCache(Long menuId); - - // ========== 用户-角色的相关方法 ========== - - /** - * 设置用户角色 - * - * @param userId 角色编号 - * @param roleIds 角色编号集合 - */ - void assignUserRole(Long userId, Set roleIds); - - /** - * 处理用户删除时,删除关联授权数据 - * - * @param userId 用户编号 - */ - void processUserDeleted(Long userId); - - /** - * 获得拥有多个角色的用户编号集合 - * - * @param roleIds 角色编号集合 - * @return 用户编号集合 - */ - Set getUserRoleIdListByRoleId(Collection roleIds); - - /** - * 获得用户拥有的角色编号集合 - * - * @param userId 用户编号 - * @return 角色编号集合 - */ - Set getUserRoleIdListByUserId(Long userId); - - /** - * 获得用户拥有的角色编号集合,从缓存中获取 - * - * @param userId 用户编号 - * @return 角色编号集合 - */ - Set getUserRoleIdListByUserIdFromCache(Long userId); - - // ========== 用户-部门的相关方法 ========== - - /** - * 设置角色的数据权限 - * - * @param roleId 角色编号 - * @param dataScope 数据范围 - * @param dataScopeDeptIds 部门编号数组 - */ - void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds); - - /** - * 获得登陆用户的部门数据权限 - * - * @param userId 用户编号 - * @return 部门数据权限 - */ - DeptDataPermissionRespDTO getDeptDataPermission(Long userId); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java deleted file mode 100644 index 8eb5b09..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java +++ /dev/null @@ -1,341 +0,0 @@ -package cn.iocoder.yudao.module.system.service.permission; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; -import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; -import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; -import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import com.baomidou.dynamic.datasource.annotation.DSTransactional; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Suppliers; -import com.google.common.collect.Sets; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.cache.annotation.Caching; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import jakarta.annotation.Resource; -import java.util.*; -import java.util.function.Supplier; - -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; - -/** - * 权限 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class PermissionServiceImpl implements PermissionService { - - @Resource - private RoleMenuMapper roleMenuMapper; - @Resource - private UserRoleMapper userRoleMapper; - - @Resource - private RoleService roleService; - @Resource - private MenuService menuService; - @Resource - private DeptService deptService; - @Resource - private AdminUserService userService; - - @Override - public boolean hasAnyPermissions(Long userId, String... permissions) { - // 如果为空,说明已经有权限 - if (ArrayUtil.isEmpty(permissions)) { - return true; - } - - // 获得当前登录的角色。如果为空,说明没有权限 - List roles = getEnableUserRoleListByUserIdFromCache(userId); - if (CollUtil.isEmpty(roles)) { - return false; - } - - // 情况一:遍历判断每个权限,如果有一满足,说明有权限 - for (String permission : permissions) { - if (hasAnyPermission(roles, permission)) { - return true; - } - } - - // 情况二:如果是超管,也说明有权限 - return roleService.hasAnySuperAdmin(convertSet(roles, RoleDO::getId)); - } - - /** - * 判断指定角色,是否拥有该 permission 权限 - * - * @param roles 指定角色数组 - * @param permission 权限标识 - * @return 是否拥有 - */ - private boolean hasAnyPermission(List roles, String permission) { - List menuIds = menuService.getMenuIdListByPermissionFromCache(permission); - // 采用严格模式,如果权限找不到对应的 Menu 的话,也认为没有权限 - if (CollUtil.isEmpty(menuIds)) { - return false; - } - - // 判断是否有权限 - Set roleIds = convertSet(roles, RoleDO::getId); - for (Long menuId : menuIds) { - // 获得拥有该菜单的角色编号集合 - Set menuRoleIds = getSelf().getMenuRoleIdListByMenuIdFromCache(menuId); - // 如果有交集,说明有权限 - if (CollUtil.containsAny(menuRoleIds, roleIds)) { - return true; - } - } - return false; - } - - @Override - public boolean hasAnyRoles(Long userId, String... roles) { - // 如果为空,说明已经有权限 - if (ArrayUtil.isEmpty(roles)) { - return true; - } - - // 获得当前登录的角色。如果为空,说明没有权限 - List roleList = getEnableUserRoleListByUserIdFromCache(userId); - if (CollUtil.isEmpty(roleList)) { - return false; - } - - // 判断是否有角色 - Set userRoles = convertSet(roleList, RoleDO::getCode); - return CollUtil.containsAny(userRoles, Sets.newHashSet(roles)); - } - - // ========== 角色-菜单的相关方法 ========== - - @Override - @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 - @Caching(evict = { - @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, - allEntries = true), - @CacheEvict(value = RedisKeyConstants.PERMISSION_MENU_ID_LIST, - allEntries = true) // allEntries 清空所有缓存,主要一次更新涉及到的 menuIds 较多,反倒批量会更快 - }) - public void assignRoleMenu(Long roleId, Set menuIds) { - // 获得角色拥有菜单编号 - Set dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), RoleMenuDO::getMenuId); - // 计算新增和删除的菜单编号 - Set menuIdList = CollUtil.emptyIfNull(menuIds); - Collection createMenuIds = CollUtil.subtract(menuIdList, dbMenuIds); - Collection deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIdList); - // 执行新增和删除。对于已经授权的菜单,不用做任何处理 - if (CollUtil.isNotEmpty(createMenuIds)) { - roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> { - RoleMenuDO entity = new RoleMenuDO(); - entity.setRoleId(roleId); - entity.setMenuId(menuId); - return entity; - })); - } - if (CollUtil.isNotEmpty(deleteMenuIds)) { - roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - @Caching(evict = { - @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, - allEntries = true), // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 menu 缓存们 - @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, - allEntries = true) // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 user 缓存们 - }) - public void processRoleDeleted(Long roleId) { - // 标记删除 UserRole - userRoleMapper.deleteListByRoleId(roleId); - // 标记删除 RoleMenu - roleMenuMapper.deleteListByRoleId(roleId); - } - - @Override - @CacheEvict(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId") - public void processMenuDeleted(Long menuId) { - roleMenuMapper.deleteListByMenuId(menuId); - } - - @Override - public Set getRoleMenuListByRoleId(Collection roleIds) { - if (CollUtil.isEmpty(roleIds)) { - return Collections.emptySet(); - } - - // 如果是管理员的情况下,获取全部菜单编号 - if (roleService.hasAnySuperAdmin(roleIds)) { - return convertSet(menuService.getMenuList(), MenuDO::getId); - } - // 如果是非管理员的情况下,获得拥有的菜单编号 - return convertSet(roleMenuMapper.selectListByRoleId(roleIds), RoleMenuDO::getMenuId); - } - - @Override - @Cacheable(value = RedisKeyConstants.MENU_ROLE_ID_LIST, key = "#menuId") - public Set getMenuRoleIdListByMenuIdFromCache(Long menuId) { - return convertSet(roleMenuMapper.selectListByMenuId(menuId), RoleMenuDO::getRoleId); - } - - // ========== 用户-角色的相关方法 ========== - - @Override - @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 - @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") - public void assignUserRole(Long userId, Set roleIds) { - // 获得角色拥有角色编号 - Set dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId), - UserRoleDO::getRoleId); - // 计算新增和删除的角色编号 - Set roleIdList = CollUtil.emptyIfNull(roleIds); - Collection createRoleIds = CollUtil.subtract(roleIdList, dbRoleIds); - Collection deleteMenuIds = CollUtil.subtract(dbRoleIds, roleIdList); - // 执行新增和删除。对于已经授权的角色,不用做任何处理 - if (!CollectionUtil.isEmpty(createRoleIds)) { - userRoleMapper.insertBatch(CollectionUtils.convertList(createRoleIds, roleId -> { - UserRoleDO entity = new UserRoleDO(); - entity.setUserId(userId); - entity.setRoleId(roleId); - return entity; - })); - } - if (!CollectionUtil.isEmpty(deleteMenuIds)) { - userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds); - } - } - - @Override - @CacheEvict(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") - public void processUserDeleted(Long userId) { - userRoleMapper.deleteListByUserId(userId); - } - - @Override - public Set getUserRoleIdListByUserId(Long userId) { - return convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId); - } - - @Override - @Cacheable(value = RedisKeyConstants.USER_ROLE_ID_LIST, key = "#userId") - public Set getUserRoleIdListByUserIdFromCache(Long userId) { - return getUserRoleIdListByUserId(userId); - } - - @Override - public Set getUserRoleIdListByRoleId(Collection roleIds) { - return convertSet(userRoleMapper.selectListByRoleIds(roleIds), UserRoleDO::getUserId); - } - - /** - * 获得用户拥有的角色,并且这些角色是开启状态的 - * - * @param userId 用户编号 - * @return 用户拥有的角色 - */ - @VisibleForTesting - List getEnableUserRoleListByUserIdFromCache(Long userId) { - // 获得用户拥有的角色编号 - Set roleIds = getSelf().getUserRoleIdListByUserIdFromCache(userId); - // 获得角色数组,并移除被禁用的 - List roles = roleService.getRoleListFromCache(roleIds); - roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())); - return roles; - } - - // ========== 用户-部门的相关方法 ========== - - @Override - public void assignRoleDataScope(Long roleId, Integer dataScope, Set dataScopeDeptIds) { - roleService.updateRoleDataScope(roleId, dataScope, dataScopeDeptIds); - } - - @Override - @DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题 - public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) { - // 获得用户的角色 - List roles = getEnableUserRoleListByUserIdFromCache(userId); - - // 如果角色为空,则只能查看自己 - DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO(); - if (CollUtil.isEmpty(roles)) { - result.setSelf(true); - return result; - } - - // 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询 - Supplier userDeptId = Suppliers.memoize(() -> userService.getUser(userId).getDeptId()); - // 遍历每个角色,计算 - for (RoleDO role : roles) { - // 为空时,跳过 - if (role.getDataScope() == null) { - continue; - } - // 情况一,ALL - if (Objects.equals(role.getDataScope(), DataScopeEnum.ALL.getScope())) { - result.setAll(true); - continue; - } - // 情况二,DEPT_CUSTOM - if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_CUSTOM.getScope())) { - CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds()); - // 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。 - // 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉 - CollUtil.addAll(result.getDeptIds(), userDeptId.get()); - continue; - } - // 情况三,DEPT_ONLY - if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) { - CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptId.get()); - continue; - } - // 情况四,DEPT_DEPT_AND_CHILD - if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) { - CollUtil.addAll(result.getDeptIds(), deptService.getChildDeptIdListFromCache(userDeptId.get())); - // 添加本身部门编号 - CollUtil.addAll(result.getDeptIds(), userDeptId.get()); - continue; - } - // 情况五,SELF - if (Objects.equals(role.getDataScope(), DataScopeEnum.SELF.getScope())) { - result.setSelf(true); - continue; - } - // 未知情况,error log 即可 - log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, toJsonString(result)); - } - return result; - } - - /** - * 获得自身的代理对象,解决 AOP 生效问题 - * - * @return 自己 - */ - private PermissionServiceImpl getSelf() { - return SpringUtil.getBean(getClass()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java deleted file mode 100644 index 6de8b51..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java +++ /dev/null @@ -1,124 +0,0 @@ -package cn.iocoder.yudao.module.system.service.permission; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; - -import jakarta.validation.Valid; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -/** - * 角色 Service 接口 - * - * @author 芋道源码 - */ -public interface RoleService { - - /** - * 创建角色 - * - * @param createReqVO 创建角色信息 - * @param type 角色类型 - * @return 角色编号 - */ - Long createRole(@Valid RoleSaveReqVO createReqVO, Integer type); - - /** - * 更新角色 - * - * @param updateReqVO 更新角色信息 - */ - void updateRole(@Valid RoleSaveReqVO updateReqVO); - - /** - * 删除角色 - * - * @param id 角色编号 - */ - void deleteRole(Long id); - - /** - * 设置角色的数据权限 - * - * @param id 角色编号 - * @param dataScope 数据范围 - * @param dataScopeDeptIds 部门编号数组 - */ - void updateRoleDataScope(Long id, Integer dataScope, Set dataScopeDeptIds); - - /** - * 获得角色 - * - * @param id 角色编号 - * @return 角色 - */ - RoleDO getRole(Long id); - - /** - * 获得角色,从缓存中 - * - * @param id 角色编号 - * @return 角色 - */ - RoleDO getRoleFromCache(Long id); - - /** - * 获得角色列表 - * - * @param ids 角色编号数组 - * @return 角色列表 - */ - List getRoleList(Collection ids); - - /** - * 获得角色数组,从缓存中 - * - * @param ids 角色编号数组 - * @return 角色数组 - */ - List getRoleListFromCache(Collection ids); - - /** - * 获得角色列表 - * - * @param statuses 筛选的状态 - * @return 角色列表 - */ - List getRoleListByStatus(Collection statuses); - - /** - * 获得所有角色列表 - * - * @return 角色列表 - */ - List getRoleList(); - - /** - * 获得角色分页 - * - * @param reqVO 角色分页查询 - * @return 角色分页结果 - */ - PageResult getRolePage(RolePageReqVO reqVO); - - /** - * 判断角色编号数组中,是否有管理员 - * - * @param ids 角色编号数组 - * @return 是否有管理员 - */ - boolean hasAnySuperAdmin(Collection ids); - - /** - * 校验角色们是否有效。如下情况,视为无效: - * 1. 角色编号不存在 - * 2. 角色被禁用 - * - * @param ids 角色编号数组 - */ - void validateRoleList(Collection ids); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java deleted file mode 100644 index c18ef31..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java +++ /dev/null @@ -1,262 +0,0 @@ -package cn.iocoder.yudao.module.system.service.permission; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; -import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; -import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; -import com.google.common.annotations.VisibleForTesting; -import com.mzt.logapi.context.LogRecordContext; -import com.mzt.logapi.service.impl.DiffParseFunction; -import com.mzt.logapi.starter.annotation.LogRecord; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.StringUtils; - -import java.util.*; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*; - -/** - * 角色 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class RoleServiceImpl implements RoleService { - - @Resource - private PermissionService permissionService; - - @Resource - private RoleMapper roleMapper; - - @Override - @Transactional(rollbackFor = Exception.class) - @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_CREATE_SUB_TYPE, bizNo = "{{#role.id}}", - success = SYSTEM_ROLE_CREATE_SUCCESS) - public Long createRole(RoleSaveReqVO createReqVO, Integer type) { - // 1. 校验角色 - validateRoleDuplicate(createReqVO.getName(), createReqVO.getCode(), null); - - // 2. 插入到数据库 - RoleDO role = BeanUtils.toBean(createReqVO, RoleDO.class) - .setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType())) - .setStatus(ObjUtil.defaultIfNull(createReqVO.getStatus(), CommonStatusEnum.ENABLE.getStatus())) - .setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限 - roleMapper.insert(role); - - // 3. 记录操作日志上下文 - LogRecordContext.putVariable("role", role); - return role.getId(); - } - - @Override - @CacheEvict(value = RedisKeyConstants.ROLE, key = "#updateReqVO.id") - @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", - success = SYSTEM_ROLE_UPDATE_SUCCESS) - public void updateRole(RoleSaveReqVO updateReqVO) { - // 1.1 校验是否可以更新 - RoleDO role = validateRoleForUpdate(updateReqVO.getId()); - // 1.2 校验角色的唯一字段是否重复 - validateRoleDuplicate(updateReqVO.getName(), updateReqVO.getCode(), updateReqVO.getId()); - - // 2. 更新到数据库 - RoleDO updateObj = BeanUtils.toBean(updateReqVO, RoleDO.class); - roleMapper.updateById(updateObj); - - // 3. 记录操作日志上下文 - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(role, RoleSaveReqVO.class)); - LogRecordContext.putVariable("role", role); - } - - @Override - @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") - public void updateRoleDataScope(Long id, Integer dataScope, Set dataScopeDeptIds) { - // 校验是否可以更新 - validateRoleForUpdate(id); - - // 更新数据范围 - RoleDO updateObject = new RoleDO(); - updateObject.setId(id); - updateObject.setDataScope(dataScope); - updateObject.setDataScopeDeptIds(dataScopeDeptIds); - roleMapper.updateById(updateObject); - } - - @Override - @Transactional(rollbackFor = Exception.class) - @CacheEvict(value = RedisKeyConstants.ROLE, key = "#id") - @LogRecord(type = SYSTEM_ROLE_TYPE, subType = SYSTEM_ROLE_DELETE_SUB_TYPE, bizNo = "{{#id}}", - success = SYSTEM_ROLE_DELETE_SUCCESS) - public void deleteRole(Long id) { - // 1. 校验是否可以更新 - RoleDO role = validateRoleForUpdate(id); - - // 2.1 标记删除 - roleMapper.deleteById(id); - // 2.2 删除相关数据 - permissionService.processRoleDeleted(id); - - // 3. 记录操作日志上下文 - LogRecordContext.putVariable("role", role); - } - - /** - * 校验角色的唯一字段是否重复 - * - * 1. 是否存在相同名字的角色 - * 2. 是否存在相同编码的角色 - * - * @param name 角色名字 - * @param code 角色额编码 - * @param id 角色编号 - */ - @VisibleForTesting - void validateRoleDuplicate(String name, String code, Long id) { - // 0. 超级管理员,不允许创建 - if (RoleCodeEnum.isSuperAdmin(code)) { - throw exception(ROLE_ADMIN_CODE_ERROR, code); - } - // 1. 该 name 名字被其它角色所使用 - RoleDO role = roleMapper.selectByName(name); - if (role != null && !role.getId().equals(id)) { - throw exception(ROLE_NAME_DUPLICATE, name); - } - // 2. 是否存在相同编码的角色 - if (!StringUtils.hasText(code)) { - return; - } - // 该 code 编码被其它角色所使用 - role = roleMapper.selectByCode(code); - if (role != null && !role.getId().equals(id)) { - throw exception(ROLE_CODE_DUPLICATE, code); - } - } - - /** - * 校验角色是否可以被更新 - * - * @param id 角色编号 - */ - @VisibleForTesting - RoleDO validateRoleForUpdate(Long id) { - RoleDO role = roleMapper.selectById(id); - if (role == null) { - throw exception(ROLE_NOT_EXISTS); - } - // 内置角色,不允许删除 - if (RoleTypeEnum.SYSTEM.getType().equals(role.getType())) { - throw exception(ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); - } - return role; - } - - @Override - public RoleDO getRole(Long id) { - return roleMapper.selectById(id); - } - - @Override - @Cacheable(value = RedisKeyConstants.ROLE, key = "#id", - unless = "#result == null") - public RoleDO getRoleFromCache(Long id) { - return roleMapper.selectById(id); - } - - - @Override - public List getRoleListByStatus(Collection statuses) { - return roleMapper.selectListByStatus(statuses); - } - - @Override - public List getRoleList() { - return roleMapper.selectList(); - } - - @Override - public List getRoleList(Collection ids) { - if (CollectionUtil.isEmpty(ids)) { - return Collections.emptyList(); - } - return roleMapper.selectBatchIds(ids); - } - - @Override - public List getRoleListFromCache(Collection ids) { - if (CollectionUtil.isEmpty(ids)) { - return Collections.emptyList(); - } - // 这里采用 for 循环从缓存中获取,主要考虑 Spring CacheManager 无法批量操作的问题 - RoleServiceImpl self = getSelf(); - return CollectionUtils.convertList(ids, self::getRoleFromCache); - } - - @Override - public PageResult getRolePage(RolePageReqVO reqVO) { - return roleMapper.selectPage(reqVO); - } - - @Override - public boolean hasAnySuperAdmin(Collection ids) { - if (CollectionUtil.isEmpty(ids)) { - return false; - } - RoleServiceImpl self = getSelf(); - return ids.stream().anyMatch(id -> { - RoleDO role = self.getRoleFromCache(id); - return role != null && RoleCodeEnum.isSuperAdmin(role.getCode()); - }); - } - - @Override - public void validateRoleList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return; - } - // 获得角色信息 - List roles = roleMapper.selectBatchIds(ids); - Map roleMap = convertMap(roles, RoleDO::getId); - // 校验 - ids.forEach(id -> { - RoleDO role = roleMap.get(id); - if (role == null) { - throw exception(ROLE_NOT_EXISTS); - } - if (!CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus())) { - throw exception(ROLE_IS_DISABLE, role.getName()); - } - }); - } - - /** - * 获得自身的代理对象,解决 AOP 生效问题 - * - * @return 自己 - */ - private RoleServiceImpl getSelf() { - return SpringUtil.getBean(getClass()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java deleted file mode 100644 index c33c775..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java +++ /dev/null @@ -1,81 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; - -import jakarta.validation.Valid; -import java.util.List; - -/** - * 短信渠道 Service 接口 - * - * @author zzf - * @since 2021/1/25 9:24 - */ -public interface SmsChannelService { - - /** - * 创建短信渠道 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createSmsChannel(@Valid SmsChannelSaveReqVO createReqVO); - - /** - * 更新短信渠道 - * - * @param updateReqVO 更新信息 - */ - void updateSmsChannel(@Valid SmsChannelSaveReqVO updateReqVO); - - /** - * 删除短信渠道 - * - * @param id 编号 - */ - void deleteSmsChannel(Long id); - - /** - * 获得短信渠道 - * - * @param id 编号 - * @return 短信渠道 - */ - SmsChannelDO getSmsChannel(Long id); - - /** - * 获得所有短信渠道列表 - * - * @return 短信渠道列表 - */ - List getSmsChannelList(); - - /** - * 获得短信渠道分页 - * - * @param pageReqVO 分页查询 - * @return 短信渠道分页 - */ - PageResult getSmsChannelPage(SmsChannelPageReqVO pageReqVO); - - /** - * 获得短信客户端 - * - * @param id 编号 - * @return 短信客户端 - */ - SmsClient getSmsClient(Long id); - - /** - * 获得短信客户端 - * - * @param code 编码 - * @return 短信客户端 - */ - SmsClient getSmsClient(String code); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java deleted file mode 100644 index 5c6f36d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; -import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClientFactory; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; - -/** - * 短信渠道 Service 实现类 - * - * @author zzf - */ -@Service -@Slf4j -public class SmsChannelServiceImpl implements SmsChannelService { - - @Resource - private SmsClientFactory smsClientFactory; - - @Resource - private SmsChannelMapper smsChannelMapper; - - @Resource - private SmsTemplateService smsTemplateService; - - @Override - public Long createSmsChannel(SmsChannelSaveReqVO createReqVO) { - SmsChannelDO channel = BeanUtils.toBean(createReqVO, SmsChannelDO.class); - smsChannelMapper.insert(channel); - return channel.getId(); - } - - @Override - public void updateSmsChannel(SmsChannelSaveReqVO updateReqVO) { - // 校验存在 - validateSmsChannelExists(updateReqVO.getId()); - // 更新 - SmsChannelDO updateObj = BeanUtils.toBean(updateReqVO, SmsChannelDO.class); - smsChannelMapper.updateById(updateObj); - } - - @Override - public void deleteSmsChannel(Long id) { - // 校验存在 - validateSmsChannelExists(id); - // 校验是否有在使用该账号的模版 - if (smsTemplateService.getSmsTemplateCountByChannelId(id) > 0) { - throw exception(SMS_CHANNEL_HAS_CHILDREN); - } - // 删除 - smsChannelMapper.deleteById(id); - } - - private SmsChannelDO validateSmsChannelExists(Long id) { - SmsChannelDO channel = smsChannelMapper.selectById(id); - if (channel == null) { - throw exception(SMS_CHANNEL_NOT_EXISTS); - } - return channel; - } - - @Override - public SmsChannelDO getSmsChannel(Long id) { - return smsChannelMapper.selectById(id); - } - - @Override - public List getSmsChannelList() { - return smsChannelMapper.selectList(); - } - - @Override - public PageResult getSmsChannelPage(SmsChannelPageReqVO pageReqVO) { - return smsChannelMapper.selectPage(pageReqVO); - } - - @Override - public SmsClient getSmsClient(Long id) { - SmsChannelDO channel = smsChannelMapper.selectById(id); - SmsChannelProperties properties = BeanUtils.toBean(channel, SmsChannelProperties.class); - return smsClientFactory.createOrUpdateSmsClient(properties); - } - - @Override - public SmsClient getSmsClient(String code) { - return smsClientFactory.getSmsClient(code); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeService.java deleted file mode 100644 index 86242f9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeService.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; - -import jakarta.validation.Valid; - -/** - * 短信验证码 Service 接口 - * - * @author 芋道源码 - */ -public interface SmsCodeService { - - /** - * 创建短信验证码,并进行发送 - * - * @param reqDTO 发送请求 - */ - void sendSmsCode(@Valid SmsCodeSendReqDTO reqDTO); - - /** - * 验证短信验证码,并进行使用 - * 如果正确,则将验证码标记成已使用 - * 如果错误,则抛出 {@link ServiceException} 异常 - * - * @param reqDTO 使用请求 - */ - void useSmsCode(@Valid SmsCodeUseReqDTO reqDTO); - - /** - * 检查验证码是否有效 - * - * @param reqDTO 校验请求 - */ - void validateSmsCode(@Valid SmsCodeValidateReqDTO reqDTO); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java deleted file mode 100644 index 6b77277..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImpl.java +++ /dev/null @@ -1,112 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO; -import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsCodeMapper; -import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; -import cn.iocoder.yudao.module.system.framework.sms.config.SmsCodeProperties; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.randomInt; -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.isToday; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 短信验证码 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class SmsCodeServiceImpl implements SmsCodeService { - - @Resource - private SmsCodeProperties smsCodeProperties; - - @Resource - private SmsCodeMapper smsCodeMapper; - - @Resource - private SmsSendService smsSendService; - - @Override - public void sendSmsCode(SmsCodeSendReqDTO reqDTO) { - SmsSceneEnum sceneEnum = SmsSceneEnum.getCodeByScene(reqDTO.getScene()); - Assert.notNull(sceneEnum, "验证码场景({}) 查找不到配置", reqDTO.getScene()); - // 创建验证码 - String code = createSmsCode(reqDTO.getMobile(), reqDTO.getScene(), reqDTO.getCreateIp()); - // 发送验证码 - smsSendService.sendSingleSms(reqDTO.getMobile(), null, null, - sceneEnum.getTemplateCode(), MapUtil.of("code", code)); - } - - private String createSmsCode(String mobile, Integer scene, String ip) { - // 校验是否可以发送验证码,不用筛选场景 - SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, null, null); - if (lastSmsCode != null) { - if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis() - < smsCodeProperties.getSendFrequency().toMillis()) { // 发送过于频繁 - throw exception(SMS_CODE_SEND_TOO_FAST); - } - if (isToday(lastSmsCode.getCreateTime()) && // 必须是今天,才能计算超过当天的上限 - lastSmsCode.getTodayIndex() >= smsCodeProperties.getSendMaximumQuantityPerDay()) { // 超过当天发送的上限。 - throw exception(SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); - } - // TODO 芋艿:提升,每个 IP 每天可发送数量 - // TODO 芋艿:提升,每个 IP 每小时可发送数量 - } - - // 创建验证码记录 - String code = String.format("%0" + smsCodeProperties.getEndCode().toString().length() + "d", - randomInt(smsCodeProperties.getBeginCode(), smsCodeProperties.getEndCode() + 1)); - SmsCodeDO newSmsCode = SmsCodeDO.builder().mobile(mobile).code(code).scene(scene) - .todayIndex(lastSmsCode != null && isToday(lastSmsCode.getCreateTime()) ? lastSmsCode.getTodayIndex() + 1 : 1) - .createIp(ip).used(false).build(); - smsCodeMapper.insert(newSmsCode); - return code; - } - - @Override - public void useSmsCode(SmsCodeUseReqDTO reqDTO) { - // 检测验证码是否有效 - SmsCodeDO lastSmsCode = validateSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); - // 使用验证码 - smsCodeMapper.updateById(SmsCodeDO.builder().id(lastSmsCode.getId()) - .used(true).usedTime(LocalDateTime.now()).usedIp(reqDTO.getUsedIp()).build()); - } - - @Override - public void validateSmsCode(SmsCodeValidateReqDTO reqDTO) { - validateSmsCode0(reqDTO.getMobile(), reqDTO.getCode(), reqDTO.getScene()); - } - - private SmsCodeDO validateSmsCode0(String mobile, String code, Integer scene) { - // 校验验证码 - SmsCodeDO lastSmsCode = smsCodeMapper.selectLastByMobile(mobile, code, scene); - // 若验证码不存在,抛出异常 - if (lastSmsCode == null) { - throw exception(SMS_CODE_NOT_FOUND); - } - // 超过时间 - if (LocalDateTimeUtil.between(lastSmsCode.getCreateTime(), LocalDateTime.now()).toMillis() - >= smsCodeProperties.getExpireTimes().toMillis()) { // 验证码已过期 - throw exception(SMS_CODE_EXPIRED); - } - // 判断验证码是否已被使用 - if (Boolean.TRUE.equals(lastSmsCode.getUsed())) { - throw exception(SMS_CODE_USED); - } - return lastSmsCode; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java deleted file mode 100644 index 0c86c0f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; - -import java.time.LocalDateTime; -import java.util.Map; - -/** - * 短信日志 Service 接口 - * - * @author zzf - * @date 13:48 2021/3/2 - */ -public interface SmsLogService { - - /** - * 创建短信日志 - * - * @param mobile 手机号 - * @param userId 用户编号 - * @param userType 用户类型 - * @param isSend 是否发送 - * @param template 短信模板 - * @param templateContent 短信内容 - * @param templateParams 短信参数 - * @return 发送日志编号 - */ - Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend, - SmsTemplateDO template, String templateContent, Map templateParams); - - /** - * 更新日志的发送结果 - * - * @param id 日志编号 - * @param success 发送是否成功 - * @param apiSendCode 短信 API 发送结果的编码 - * @param apiSendMsg 短信 API 发送失败的提示 - * @param apiRequestId 短信 API 发送返回的唯一请求 ID - * @param apiSerialNo 短信 API 发送返回的序号 - */ - void updateSmsSendResult(Long id, Boolean success, - String apiSendCode, String apiSendMsg, - String apiRequestId, String apiSerialNo); - - /** - * 更新日志的接收结果 - * - * @param id 日志编号 - * @param success 是否接收成功 - * @param receiveTime 用户接收时间 - * @param apiReceiveCode API 接收结果的编码 - * @param apiReceiveMsg API 接收结果的说明 - */ - void updateSmsReceiveResult(Long id, Boolean success, - LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg); - - /** - * 获得短信日志分页 - * - * @param pageReqVO 分页查询 - * @return 短信日志分页 - */ - PageResult getSmsLogPage(SmsLogPageReqVO pageReqVO); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java deleted file mode 100644 index 4f969ce..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java +++ /dev/null @@ -1,79 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper; -import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum; -import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Map; -import java.util.Objects; - -/** - * 短信日志 Service 实现类 - * - * @author zzf - */ -@Slf4j -@Service -public class SmsLogServiceImpl implements SmsLogService { - - @Resource - private SmsLogMapper smsLogMapper; - - @Override - public Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend, - SmsTemplateDO template, String templateContent, Map templateParams) { - SmsLogDO.SmsLogDOBuilder logBuilder = SmsLogDO.builder(); - // 根据是否要发送,设置状态 - logBuilder.sendStatus(Objects.equals(isSend, true) ? SmsSendStatusEnum.INIT.getStatus() - : SmsSendStatusEnum.IGNORE.getStatus()); - // 设置手机相关字段 - logBuilder.mobile(mobile).userId(userId).userType(userType); - // 设置模板相关字段 - logBuilder.templateId(template.getId()).templateCode(template.getCode()).templateType(template.getType()); - logBuilder.templateContent(templateContent).templateParams(templateParams) - .apiTemplateId(template.getApiTemplateId()); - // 设置渠道相关字段 - logBuilder.channelId(template.getChannelId()).channelCode(template.getChannelCode()); - // 设置接收相关字段 - logBuilder.receiveStatus(SmsReceiveStatusEnum.INIT.getStatus()); - - // 插入数据库 - SmsLogDO logDO = logBuilder.build(); - smsLogMapper.insert(logDO); - return logDO.getId(); - } - - @Override - public void updateSmsSendResult(Long id, Boolean success, - String apiSendCode, String apiSendMsg, - String apiRequestId, String apiSerialNo) { - SmsSendStatusEnum sendStatus = success ? SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE; - smsLogMapper.updateById(SmsLogDO.builder().id(id) - .sendStatus(sendStatus.getStatus()).sendTime(LocalDateTime.now()) - .apiSendCode(apiSendCode).apiSendMsg(apiSendMsg) - .apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build()); - } - - @Override - public void updateSmsReceiveResult(Long id, Boolean success, LocalDateTime receiveTime, - String apiReceiveCode, String apiReceiveMsg) { - SmsReceiveStatusEnum receiveStatus = Objects.equals(success, true) ? - SmsReceiveStatusEnum.SUCCESS : SmsReceiveStatusEnum.FAILURE; - smsLogMapper.updateById(SmsLogDO.builder().id(id).receiveStatus(receiveStatus.getStatus()) - .receiveTime(receiveTime).apiReceiveCode(apiReceiveCode).apiReceiveMsg(apiReceiveMsg).build()); - } - - @Override - public PageResult getSmsLogPage(SmsLogPageReqVO pageReqVO) { - return smsLogMapper.selectPage(pageReqVO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendService.java deleted file mode 100644 index 3c42cf5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendService.java +++ /dev/null @@ -1,78 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; - -import java.util.List; -import java.util.Map; - -/** - * 短信发送 Service 接口 - * - * @author 芋道源码 - */ -public interface SmsSendService { - - /** - * 发送单条短信给管理后台的用户 - * - * 在 mobile 为空时,使用 userId 加载对应管理员的手机号 - * - * @param mobile 手机号 - * @param userId 用户编号 - * @param templateCode 短信模板编号 - * @param templateParams 短信模板参数 - * @return 发送日志编号 - */ - Long sendSingleSmsToAdmin(String mobile, Long userId, - String templateCode, Map templateParams); - - /** - * 发送单条短信给用户 APP 的用户 - * - * 在 mobile 为空时,使用 userId 加载对应会员的手机号 - * - * @param mobile 手机号 - * @param userId 用户编号 - * @param templateCode 短信模板编号 - * @param templateParams 短信模板参数 - * @return 发送日志编号 - */ - Long sendSingleSmsToMember(String mobile, Long userId, - String templateCode, Map templateParams); - - /** - * 发送单条短信给用户 - * - * @param mobile 手机号 - * @param userId 用户编号 - * @param userType 用户类型 - * @param templateCode 短信模板编号 - * @param templateParams 短信模板参数 - * @return 发送日志编号 - */ - Long sendSingleSms(String mobile, Long userId, Integer userType, - String templateCode, Map templateParams); - - default void sendBatchSms(List mobiles, List userIds, Integer userType, - String templateCode, Map templateParams) { - throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!"); - } - - /** - * 执行真正的短信发送 - * 注意,该方法仅仅提供给 MQ Consumer 使用 - * - * @param message 短信 - */ - void doSendSms(SmsSendMessage message); - - /** - * 接收短信的接收结果 - * - * @param channelCode 渠道编码 - * @param text 结果内容 - * @throws Throwable 处理失败时,抛出异常 - */ - void receiveSmsStatus(String channelCode, String text) throws Throwable; - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java deleted file mode 100644 index 41f429e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java +++ /dev/null @@ -1,191 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; -import cn.iocoder.yudao.module.system.service.member.MemberService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 短信发送 Service 发送的实现 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class SmsSendServiceImpl implements SmsSendService { - - @Resource - private AdminUserService adminUserService; - @Resource - private MemberService memberService; - @Resource - private SmsChannelService smsChannelService; - @Resource - private SmsTemplateService smsTemplateService; - @Resource - private SmsLogService smsLogService; - - @Resource - private SmsProducer smsProducer; - - @Override - @DataPermission(enable = false) // 发送短信时,无需考虑数据权限 - public Long sendSingleSmsToAdmin(String mobile, Long userId, String templateCode, Map templateParams) { - // 如果 mobile 为空,则加载用户编号对应的手机号 - if (StrUtil.isEmpty(mobile)) { - AdminUserDO user = adminUserService.getUser(userId); - if (user != null) { - mobile = user.getMobile(); - } - } - // 执行发送 - return sendSingleSms(mobile, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams); - } - - @Override - public Long sendSingleSmsToMember(String mobile, Long userId, String templateCode, Map templateParams) { - // 如果 mobile 为空,则加载用户编号对应的手机号 - if (StrUtil.isEmpty(mobile)) { - mobile = memberService.getMemberUserMobile(userId); - } - // 执行发送 - return sendSingleSms(mobile, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); - } - - @Override - public Long sendSingleSms(String mobile, Long userId, Integer userType, - String templateCode, Map templateParams) { - // 校验短信模板是否合法 - SmsTemplateDO template = validateSmsTemplate(templateCode); - // 校验短信渠道是否合法 - SmsChannelDO smsChannel = validateSmsChannel(template.getChannelId()); - - // 校验手机号码是否存在 - mobile = validateMobile(mobile); - // 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志 - List> newTemplateParams = buildTemplateParams(template, templateParams); - - // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 - Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()) - && CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus()); - String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams); - Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams); - - // 发送 MQ 消息,异步执行发送短信 - if (isSend) { - smsProducer.sendSmsSendMessage(sendLogId, mobile, template.getChannelId(), - template.getApiTemplateId(), newTemplateParams); - } - return sendLogId; - } - - @VisibleForTesting - SmsChannelDO validateSmsChannel(Long channelId) { - // 获得短信模板。考虑到效率,从缓存中获取 - SmsChannelDO channelDO = smsChannelService.getSmsChannel(channelId); - // 短信模板不存在 - if (channelDO == null) { - throw exception(SMS_CHANNEL_NOT_EXISTS); - } - return channelDO; - } - - @VisibleForTesting - SmsTemplateDO validateSmsTemplate(String templateCode) { - // 获得短信模板。考虑到效率,从缓存中获取 - SmsTemplateDO template = smsTemplateService.getSmsTemplateByCodeFromCache(templateCode); - // 短信模板不存在 - if (template == null) { - throw exception(SMS_SEND_TEMPLATE_NOT_EXISTS); - } - return template; - } - - /** - * 将参数模板,处理成有序的 KeyValue 数组 - *

- * 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说 腾讯云 - * - * @param template 短信模板 - * @param templateParams 原始参数 - * @return 处理后的参数 - */ - @VisibleForTesting - List> buildTemplateParams(SmsTemplateDO template, Map templateParams) { - return template.getParams().stream().map(key -> { - Object value = templateParams.get(key); - if (value == null) { - throw exception(SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, key); - } - return new KeyValue<>(key, value); - }).collect(Collectors.toList()); - } - - @VisibleForTesting - public String validateMobile(String mobile) { - if (StrUtil.isEmpty(mobile)) { - throw exception(SMS_SEND_MOBILE_NOT_EXISTS); - } - return mobile; - } - - @Override - public void doSendSms(SmsSendMessage message) { - // 获得渠道对应的 SmsClient 客户端 - SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId()); - Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId()); - // 发送短信 - try { - SmsSendRespDTO sendResponse = smsClient.sendSms(message.getLogId(), message.getMobile(), - message.getApiTemplateId(), message.getTemplateParams()); - smsLogService.updateSmsSendResult(message.getLogId(), sendResponse.getSuccess(), - sendResponse.getApiCode(), sendResponse.getApiMsg(), - sendResponse.getApiRequestId(), sendResponse.getSerialNo()); - } catch (Throwable ex) { - log.error("[doSendSms][发送短信异常,日志编号({})]", message.getLogId(), ex); - smsLogService.updateSmsSendResult(message.getLogId(), false, - "EXCEPTION", ExceptionUtil.getRootCauseMessage(ex), null, null); - } - } - - @Override - public void receiveSmsStatus(String channelCode, String text) throws Throwable { - // 获得渠道对应的 SmsClient 客户端 - SmsClient smsClient = smsChannelService.getSmsClient(channelCode); - Assert.notNull(smsClient, "短信客户端({}) 不存在", channelCode); - // 解析内容 - List receiveResults = smsClient.parseSmsReceiveStatus(text); - if (CollUtil.isEmpty(receiveResults)) { - return; - } - // 更新短信日志的接收结果. 因为量一般不大,所以先使用 for 循环更新 - receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(result.getLogId(), - result.getSuccess(), result.getReceiveTime(), result.getErrorCode(), result.getErrorMsg())); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java deleted file mode 100644 index 65cdba5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java +++ /dev/null @@ -1,82 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; - -import jakarta.validation.Valid; -import java.util.Map; - -/** - * 短信模板 Service 接口 - * - * @author zzf - * @since 2021/1/25 9:24 - */ -public interface SmsTemplateService { - - /** - * 创建短信模板 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createSmsTemplate(@Valid SmsTemplateSaveReqVO createReqVO); - - /** - * 更新短信模板 - * - * @param updateReqVO 更新信息 - */ - void updateSmsTemplate(@Valid SmsTemplateSaveReqVO updateReqVO); - - /** - * 删除短信模板 - * - * @param id 编号 - */ - void deleteSmsTemplate(Long id); - - /** - * 获得短信模板 - * - * @param id 编号 - * @return 短信模板 - */ - SmsTemplateDO getSmsTemplate(Long id); - - /** - * 获得短信模板,从缓存中 - * - * @param code 模板编码 - * @return 短信模板 - */ - SmsTemplateDO getSmsTemplateByCodeFromCache(String code); - - /** - * 获得短信模板分页 - * - * @param pageReqVO 分页查询 - * @return 短信模板分页 - */ - PageResult getSmsTemplatePage(SmsTemplatePageReqVO pageReqVO); - - /** - * 获得指定短信渠道下的短信模板数量 - * - * @param channelId 短信渠道编号 - * @return 数量 - */ - Long getSmsTemplateCountByChannelId(Long channelId); - - /** - * 格式化短信内容 - * - * @param content 短信模板的内容 - * @param params 内容的参数 - * @return 格式化后的内容 - */ - String formatSmsTemplateContent(String content, Map params); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java deleted file mode 100644 index a8762a9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java +++ /dev/null @@ -1,199 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.hutool.core.exceptions.ExceptionUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ReUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import com.google.common.annotations.VisibleForTesting; -import lombok.extern.slf4j.Slf4j; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; - -import jakarta.annotation.Resource; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Pattern; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 短信模板 Service 实现类 - * - * @author zzf - * @since 2021/1/25 9:25 - */ -@Service -@Slf4j -public class SmsTemplateServiceImpl implements SmsTemplateService { - - /** - * 正则表达式,匹配 {} 中的变量 - */ - private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}"); - - @Resource - private SmsTemplateMapper smsTemplateMapper; - - @Resource - private SmsChannelService smsChannelService; - - @Override - public Long createSmsTemplate(SmsTemplateSaveReqVO createReqVO) { - // 校验短信渠道 - SmsChannelDO channelDO = validateSmsChannel(createReqVO.getChannelId()); - // 校验短信编码是否重复 - validateSmsTemplateCodeDuplicate(null, createReqVO.getCode()); - // 校验短信模板 - validateApiTemplate(createReqVO.getChannelId(), createReqVO.getApiTemplateId()); - - // 插入 - SmsTemplateDO template = BeanUtils.toBean(createReqVO, SmsTemplateDO.class); - template.setParams(parseTemplateContentParams(template.getContent())); - template.setChannelCode(channelDO.getCode()); - smsTemplateMapper.insert(template); - // 返回 - return template.getId(); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, - allEntries = true) // allEntries 清空所有缓存,因为可能修改到 code 字段,不好清理 - public void updateSmsTemplate(SmsTemplateSaveReqVO updateReqVO) { - // 校验存在 - validateSmsTemplateExists(updateReqVO.getId()); - // 校验短信渠道 - SmsChannelDO channelDO = validateSmsChannel(updateReqVO.getChannelId()); - // 校验短信编码是否重复 - validateSmsTemplateCodeDuplicate(updateReqVO.getId(), updateReqVO.getCode()); - // 校验短信模板 - validateApiTemplate(updateReqVO.getChannelId(), updateReqVO.getApiTemplateId()); - - // 更新 - SmsTemplateDO updateObj = BeanUtils.toBean(updateReqVO, SmsTemplateDO.class); - updateObj.setParams(parseTemplateContentParams(updateObj.getContent())); - updateObj.setChannelCode(channelDO.getCode()); - smsTemplateMapper.updateById(updateObj); - } - - @Override - @CacheEvict(cacheNames = RedisKeyConstants.SMS_TEMPLATE, - allEntries = true) // allEntries 清空所有缓存,因为 id 不是直接的缓存 code,不好清理 - public void deleteSmsTemplate(Long id) { - // 校验存在 - validateSmsTemplateExists(id); - // 更新 - smsTemplateMapper.deleteById(id); - } - - private void validateSmsTemplateExists(Long id) { - if (smsTemplateMapper.selectById(id) == null) { - throw exception(SMS_TEMPLATE_NOT_EXISTS); - } - } - - @Override - public SmsTemplateDO getSmsTemplate(Long id) { - return smsTemplateMapper.selectById(id); - } - - @Override - @Cacheable(cacheNames = RedisKeyConstants.SMS_TEMPLATE, key = "#code", - unless = "#result == null") - public SmsTemplateDO getSmsTemplateByCodeFromCache(String code) { - return smsTemplateMapper.selectByCode(code); - } - - @Override - public PageResult getSmsTemplatePage(SmsTemplatePageReqVO pageReqVO) { - return smsTemplateMapper.selectPage(pageReqVO); - } - - @Override - public Long getSmsTemplateCountByChannelId(Long channelId) { - return smsTemplateMapper.selectCountByChannelId(channelId); - } - - @VisibleForTesting - public SmsChannelDO validateSmsChannel(Long channelId) { - SmsChannelDO channelDO = smsChannelService.getSmsChannel(channelId); - if (channelDO == null) { - throw exception(SMS_CHANNEL_NOT_EXISTS); - } - if (CommonStatusEnum.isDisable(channelDO.getStatus())) { - throw exception(SMS_CHANNEL_DISABLE); - } - return channelDO; - } - - @VisibleForTesting - public void validateSmsTemplateCodeDuplicate(Long id, String code) { - SmsTemplateDO template = smsTemplateMapper.selectByCode(code); - if (template == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的字典类型 - if (id == null) { - throw exception(SMS_TEMPLATE_CODE_DUPLICATE, code); - } - if (!template.getId().equals(id)) { - throw exception(SMS_TEMPLATE_CODE_DUPLICATE, code); - } - } - - /** - * 校验 API 短信平台的模板是否有效 - * - * @param channelId 渠道编号 - * @param apiTemplateId API 模板编号 - */ - @VisibleForTesting - void validateApiTemplate(Long channelId, String apiTemplateId) { - // 获得短信模板 - SmsClient smsClient = smsChannelService.getSmsClient(channelId); - Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId)); - SmsTemplateRespDTO template; - try { - template = smsClient.getSmsTemplate(apiTemplateId); - } catch (Throwable ex) { - throw exception(SMS_TEMPLATE_API_ERROR, ExceptionUtil.getRootCauseMessage(ex)); - } - // 校验短信模版 - if (template == null) { - throw exception(SMS_TEMPLATE_API_NOT_FOUND); - } - if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.CHECKING.getStatus())) { - throw exception(SMS_TEMPLATE_API_AUDIT_CHECKING); - } - if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.FAIL.getStatus())) { - throw exception(SMS_TEMPLATE_API_AUDIT_FAIL, template.getAuditReason()); - } - Assert.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), - String.format("短信模板(%s) 审核状态(%d) 不正确", apiTemplateId, template.getAuditStatus())); - } - - @Override - public String formatSmsTemplateContent(String content, Map params) { - return StrUtil.format(content, params); - } - - @VisibleForTesting - List parseTemplateContentParams(String content) { - return ReUtil.findAllGroup1(PATTERN_PARAMS, content); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java deleted file mode 100644 index 91bb9c1..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientService.java +++ /dev/null @@ -1,153 +0,0 @@ -package cn.iocoder.yudao.module.system.service.social; - -import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import jakarta.validation.Valid; -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo; -import me.zhyd.oauth.model.AuthUser; - -import java.util.List; - -/** - * 社交应用 Service 接口 - * - * @author 芋道源码 - */ -public interface SocialClientService { - - /** - * 获得社交平台的授权 URL - * - * @param socialType 社交平台的类型 {@link SocialTypeEnum} - * @param userType 用户类型 - * @param redirectUri 重定向 URL - * @return 社交平台的授权 URL - */ - String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri); - - /** - * 请求社交平台,获得授权的用户 - * - * @param socialType 社交平台的类型 - * @param userType 用户类型 - * @param code 授权码 - * @param state 授权 state - * @return 授权的用户 - */ - AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state); - - // =================== 微信公众号独有 =================== - - /** - * 创建微信公众号的 JS SDK 初始化所需的签名 - * - * @param userType 用户类型 - * @param url 访问的 URL 地址 - * @return 签名 - */ - WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url); - - // =================== 微信小程序独有 =================== - - /** - * 获得微信小程序的手机信息 - * - * @param userType 用户类型 - * @param phoneCode 手机授权码 - * @return 手机信息 - */ - WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode); - - /** - * 获得小程序二维码 - * - * @param reqVO 请求信息 - * @return 小程序二维码 - */ - byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO); - - /** - * 获得微信小程订阅模板 - * - * 缓存的目的:考虑到微信小程序订阅消息选择好模版后几乎不会变动,缓存增加查询效率 - * - * @param userType 用户类型 - * @return 微信小程订阅模板 - */ - List getSubscribeTemplateList(Integer userType); - - /** - * 发送微信小程序订阅消息 - * - * @param reqDTO 请求 - * @param templateId 模版编号 - * @param openId 会员 openId - */ - void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId); - - /** - * 上传订单发货到微信小程序 - * - * @param userType 用户类型 - * @param reqDTO 请求 - */ - void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO); - - /** - * 通知订单收货到微信小程序 - * - * @param userType 用户类型 - * @param reqDTO 请求 - */ - void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO); - - // =================== 客户端管理 =================== - - /** - * 创建社交客户端 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createSocialClient(@Valid SocialClientSaveReqVO createReqVO); - - /** - * 更新社交客户端 - * - * @param updateReqVO 更新信息 - */ - void updateSocialClient(@Valid SocialClientSaveReqVO updateReqVO); - - /** - * 删除社交客户端 - * - * @param id 编号 - */ - void deleteSocialClient(Long id); - - /** - * 获得社交客户端 - * - * @param id 编号 - * @return 社交客户端 - */ - SocialClientDO getSocialClient(Long id); - - /** - * 获得社交客户端分页 - * - * @param pageReqVO 分页查询 - * @return 社交客户端分页 - */ - PageResult getSocialClientPage(SocialClientPageReqVO pageReqVO); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java deleted file mode 100644 index b5c9d88..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImpl.java +++ /dev/null @@ -1,506 +0,0 @@ -package cn.iocoder.yudao.module.system.service.social; - -import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.api.WxMaSubscribeService; -import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl; -import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; -import cn.binarywang.wx.miniapp.bean.WxMaSubscribeMessage; -import cn.binarywang.wx.miniapp.bean.shop.request.shipping.*; -import cn.binarywang.wx.miniapp.bean.shop.response.WxMaOrderShippingInfoBaseResponse; -import cn.binarywang.wx.miniapp.config.impl.WxMaRedisBetterConfigImpl; -import cn.binarywang.wx.miniapp.constant.WxMaConstants; -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.DesensitizedUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.ReflectUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.cache.CacheUtils; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderNotifyConfirmReceiveReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaOrderUploadShippingInfoReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxaSubscribeMessageSendReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; -import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper; -import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import cn.iocoder.yudao.module.system.framework.justauth.core.AuthRequestFactory; -import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; -import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; -import jakarta.annotation.Resource; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.bean.subscribemsg.TemplateInfo; -import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.common.redis.RedisTemplateWxRedisOps; -import me.chanjar.weixin.mp.api.WxMpService; -import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; -import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl; -import me.zhyd.oauth.config.AuthConfig; -import me.zhyd.oauth.model.AuthCallback; -import me.zhyd.oauth.model.AuthResponse; -import me.zhyd.oauth.model.AuthUser; -import me.zhyd.oauth.request.AuthRequest; -import me.zhyd.oauth.utils.AuthStateUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Service; - -import java.time.Duration; -import java.time.ZonedDateTime; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.UTC_MS_WITH_XXX_OFFSET_FORMATTER; -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Collections.singletonList; - -/** - * 社交应用 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Slf4j -public class SocialClientServiceImpl implements SocialClientService { - - /** - * 小程序码要打开的小程序版本 - * - * 1. release:正式版 - * 2. trial:体验版 - * 3. developer:开发版 - */ - @Value("${yudao.wxa-code.env-version:release}") - public String envVersion; - /** - * 订阅消息跳转小程序类型 - * - * 1. developer:开发版 - * 2. trial:体验版 - * 3. formal:正式版 - */ - @Value("${yudao.wxa-subscribe-message.miniprogram-state:formal}") - public String miniprogramState; - - @Resource - private AuthRequestFactory authRequestFactory; - - @Resource - private WxMpService wxMpService; - @Resource - private WxMpProperties wxMpProperties; - @Resource - private StringRedisTemplate stringRedisTemplate; // WxMpService 需要使用到,所以在 Service 注入了它 - /** - * 缓存 WxMpService 对象 - * - * key:使用微信公众号的 appId + secret 拼接,即 {@link SocialClientDO} 的 clientId 和 clientSecret 属性。 - * 为什么 key 使用这种格式?因为 {@link SocialClientDO} 在管理后台可以变更,通过这个 key 存储它的单例。 - * - * 为什么要做 WxMpService 缓存?因为 WxMpService 构建成本比较大,所以尽量保证它是单例。 - */ - private final LoadingCache wxMpServiceCache = CacheUtils.buildAsyncReloadingCache( - Duration.ofSeconds(10L), - new CacheLoader() { - - @Override - public WxMpService load(String key) { - String[] keys = key.split(":"); - return buildWxMpService(keys[0], keys[1]); - } - - }); - - @Resource - private WxMaService wxMaService; - @Resource - private WxMaProperties wxMaProperties; - /** - * 缓存 WxMaService 对象 - * - * 说明同 {@link #wxMpServiceCache} 变量 - */ - private final LoadingCache wxMaServiceCache = CacheUtils.buildAsyncReloadingCache( - Duration.ofSeconds(10L), - new CacheLoader() { - - @Override - public WxMaService load(String key) { - String[] keys = key.split(":"); - return buildWxMaService(keys[0], keys[1]); - } - - }); - - @Resource - private SocialClientMapper socialClientMapper; - - @Override - public String getAuthorizeUrl(Integer socialType, Integer userType, String redirectUri) { - // 获得对应的 AuthRequest 实现 - AuthRequest authRequest = buildAuthRequest(socialType, userType); - // 生成跳转地址 - String authorizeUri = authRequest.authorize(AuthStateUtils.createState()); - return HttpUtils.replaceUrlQuery(authorizeUri, "redirect_uri", redirectUri); - } - - @Override - public AuthUser getAuthUser(Integer socialType, Integer userType, String code, String state) { - // 构建请求 - AuthRequest authRequest = buildAuthRequest(socialType, userType); - AuthCallback authCallback = AuthCallback.builder().code(code).state(state).build(); - // 执行请求 - AuthResponse authResponse = authRequest.login(authCallback); - log.info("[getAuthUser][请求社交平台 type({}) request({}) response({})]", socialType, - toJsonString(authCallback), toJsonString(authResponse)); - if (!authResponse.ok()) { - throw exception(SOCIAL_USER_AUTH_FAILURE, authResponse.getMsg()); - } - return (AuthUser) authResponse.getData(); - } - - /** - * 构建 AuthRequest 对象,支持多租户配置 - * - * @param socialType 社交类型 - * @param userType 用户类型 - * @return AuthRequest 对象 - */ - @VisibleForTesting - AuthRequest buildAuthRequest(Integer socialType, Integer userType) { - // 1. 先查找默认的配置项,从 application-*.yaml 中读取 - AuthRequest request = authRequestFactory.get(SocialTypeEnum.valueOfType(socialType).getSource()); - Assert.notNull(request, String.format("社交平台(%d) 不存在", socialType)); - // 2. 查询 DB 的配置项,如果存在则进行覆盖 - SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType(socialType, userType); - if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - // 2.1 构造新的 AuthConfig 对象 - AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(request, "config"); - AuthConfig newAuthConfig = ReflectUtil.newInstance(authConfig.getClass()); - BeanUtil.copyProperties(authConfig, newAuthConfig); - // 2.2 修改对应的 clientId + clientSecret 密钥 - newAuthConfig.setClientId(client.getClientId()); - newAuthConfig.setClientSecret(client.getClientSecret()); - if (client.getAgentId() != null) { // 如果有 agentId 则修改 agentId - newAuthConfig.setAgentId(client.getAgentId()); - } - // 2.3 设置会 request 里,进行后续使用 - ReflectUtil.setFieldValue(request, "config", newAuthConfig); - } - return request; - } - - // =================== 微信公众号独有 =================== - - @Override - @SneakyThrows - public WxJsapiSignature createWxMpJsapiSignature(Integer userType, String url) { - WxMpService service = getWxMpService(userType); - return service.createJsapiSignature(url); - } - - /** - * 获得 clientId + clientSecret 对应的 WxMpService 对象 - * - * @param userType 用户类型 - * @return WxMpService 对象 - */ - @VisibleForTesting - WxMpService getWxMpService(Integer userType) { - // 第一步,查询 DB 的配置项,获得对应的 WxMpService 对象 - SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( - SocialTypeEnum.WECHAT_MP.getType(), userType); - if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - return wxMpServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret()); - } - // 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMpService 对象 - return wxMpService; - } - - /** - * 创建 clientId + clientSecret 对应的 WxMpService 对象 - * - * @param clientId 微信公众号 appId - * @param clientSecret 微信公众号 secret - * @return WxMpService 对象 - */ - public WxMpService buildWxMpService(String clientId, String clientSecret) { - // 第一步,创建 WxMpRedisConfigImpl 对象 - WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl( - new RedisTemplateWxRedisOps(stringRedisTemplate), - wxMpProperties.getConfigStorage().getKeyPrefix()); - configStorage.setAppId(clientId); - configStorage.setSecret(clientSecret); - - // 第二步,创建 WxMpService 对象 - WxMpService service = new WxMpServiceImpl(); - service.setWxMpConfigStorage(configStorage); - return service; - } - - // =================== 微信小程序独有 =================== - - @Override - public WxMaPhoneNumberInfo getWxMaPhoneNumberInfo(Integer userType, String phoneCode) { - WxMaService service = getWxMaService(userType); - try { - return service.getUserService().getPhoneNoInfo(phoneCode); - } catch (WxErrorException e) { - log.error("[getPhoneNoInfo][userType({}) phoneCode({}) 获得手机号失败]", userType, phoneCode, e); - throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR); - } - } - - @Override - public byte[] getWxaQrcode(SocialWxQrcodeReqDTO reqVO) { - WxMaService service = getWxMaService(UserTypeEnum.MEMBER.getValue()); - try { - return service.getQrcodeService().createWxaCodeUnlimitBytes( - ObjUtil.defaultIfEmpty(reqVO.getScene(), SocialWxQrcodeReqDTO.SCENE), - reqVO.getPath(), - ObjUtil.defaultIfNull(reqVO.getCheckPath(), SocialWxQrcodeReqDTO.CHECK_PATH), - envVersion, - ObjUtil.defaultIfNull(reqVO.getWidth(), SocialWxQrcodeReqDTO.WIDTH), - ObjUtil.defaultIfNull(reqVO.getAutoColor(), SocialWxQrcodeReqDTO.AUTO_COLOR), - null, - ObjUtil.defaultIfNull(reqVO.getHyaline(), SocialWxQrcodeReqDTO.HYALINE)); - } catch (WxErrorException e) { - log.error("[getWxQrcode][reqVO({}) 获得小程序码失败]", reqVO, e); - throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_QRCODE_ERROR); - } - } - - @Override - @Cacheable(cacheNames = RedisKeyConstants.WXA_SUBSCRIBE_TEMPLATE, key = "#userType", - unless = "#result == null") - public List getSubscribeTemplateList(Integer userType) { - WxMaService service = getWxMaService(userType); - try { - WxMaSubscribeService subscribeService = service.getSubscribeService(); - return subscribeService.getTemplateList(); - } catch (WxErrorException e) { - log.error("[getSubscribeTemplate][userType({}) 获得小程序订阅消息模版]", userType, e); - throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_TEMPLATE_ERROR); - } - } - - @Override - public void sendSubscribeMessage(SocialWxaSubscribeMessageSendReqDTO reqDTO, String templateId, String openId) { - WxMaService service = getWxMaService(reqDTO.getUserType()); - try { - WxMaSubscribeService subscribeService = service.getSubscribeService(); - subscribeService.sendSubscribeMsg(buildMessageSendReqDTO(reqDTO, templateId, openId)); - } catch (WxErrorException e) { - log.error("[sendSubscribeMessage][reqVO({}) templateId({}) openId({}) 发送小程序订阅消息失败]", reqDTO, templateId, openId, e); - throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_SUBSCRIBE_MESSAGE_ERROR); - } - } - - /** - * 构建发送消息请求参数 - * - * @param reqDTO 请求 - * @param templateId 模版编号 - * @param openId 会员 openId - * @return 微信小程序订阅消息请求参数 - */ - private WxMaSubscribeMessage buildMessageSendReqDTO(SocialWxaSubscribeMessageSendReqDTO reqDTO, - String templateId, String openId) { - // 设置订阅消息基本参数 - WxMaSubscribeMessage subscribeMessage = new WxMaSubscribeMessage().setLang(WxMaConstants.MiniProgramLang.ZH_CN) - .setMiniprogramState(miniprogramState).setTemplateId(templateId).setToUser(openId).setPage(reqDTO.getPage()); - // 设置具体消息参数 - Map messages = reqDTO.getMessages(); - if (CollUtil.isNotEmpty(messages)) { - reqDTO.getMessages().keySet().forEach(key -> findAndThen(messages, key, value -> - subscribeMessage.addData(new WxMaSubscribeMessage.MsgData(key, value)))); - } - return subscribeMessage; - } - - @Override - public void uploadWxaOrderShippingInfo(Integer userType, SocialWxaOrderUploadShippingInfoReqDTO reqDTO) { - WxMaService service = getWxMaService(userType); - List shippingList; - if (Objects.equals(reqDTO.getLogisticsType(), SocialWxaOrderUploadShippingInfoReqDTO.LOGISTICS_TYPE_EXPRESS)) { - shippingList = singletonList(ShippingListBean.builder() - .trackingNo(reqDTO.getLogisticsNo()) - .expressCompany(reqDTO.getExpressCompany()) - .itemDesc(reqDTO.getItemDesc()) - .contact(ContactBean.builder().receiverContact(DesensitizedUtil.mobilePhone(reqDTO.getReceiverContact())).build()) - .build()); - } else { - shippingList = singletonList(ShippingListBean.builder().itemDesc(reqDTO.getItemDesc()).build()); - } - WxMaOrderShippingInfoUploadRequest request = WxMaOrderShippingInfoUploadRequest.builder() - .orderKey(OrderKeyBean.builder() - .orderNumberType(2) // 使用原支付交易对应的微信订单号,即渠道单号 - .transactionId(reqDTO.getTransactionId()) - .build()) - .logisticsType(reqDTO.getLogisticsType()) // 配送方式 - .deliveryMode(1) // 统一发货 - .shippingList(shippingList) - .payer(PayerBean.builder().openid(reqDTO.getOpenid()).build()) - .uploadTime(ZonedDateTime.now().format(UTC_MS_WITH_XXX_OFFSET_FORMATTER)) - .build(); - try { - WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().upload(request); - if (response.getErrCode() != 0) { - log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({}) response({})]", request, response); - throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, response.getErrMsg()); - } - log.info("[uploadWxaOrderShippingInfo][上传微信小程序发货信息成功:request({}) response({})]", request, response); - } catch (WxErrorException ex) { - log.error("[uploadWxaOrderShippingInfo][上传微信小程序发货信息失败:request({})]", request, ex); - throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_UPLOAD_SHIPPING_INFO_ERROR, ex.getError().getErrorMsg()); - } - } - - @Override - public void notifyWxaOrderConfirmReceive(Integer userType, SocialWxaOrderNotifyConfirmReceiveReqDTO reqDTO) { - WxMaService service = getWxMaService(userType); - WxMaOrderShippingInfoNotifyConfirmRequest request = WxMaOrderShippingInfoNotifyConfirmRequest.builder() - .transactionId(reqDTO.getTransactionId()) - .receivedTime(LocalDateTimeUtil.toEpochMilli(reqDTO.getReceivedTime())) - .build(); - try { - WxMaOrderShippingInfoBaseResponse response = service.getWxMaOrderShippingService().notifyConfirmReceive(request); - if (response.getErrCode() != 0) { - log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({}) response({})]", request, response); - throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, response.getErrMsg()); - } - log.info("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序成功:request({}) response({})]", request, response); - } catch (WxErrorException ex) { - log.error("[notifyWxaOrderConfirmReceive][确认收货提醒到微信小程序失败:request({})]", request, ex); - throw exception(SOCIAL_CLIENT_WEIXIN_MINI_APP_ORDER_NOTIFY_CONFIRM_RECEIVE_ERROR, ex.getError().getErrorMsg()); - } - } - - /** - * 获得 clientId + clientSecret 对应的 WxMpService 对象 - * - * @param userType 用户类型 - * @return WxMpService 对象 - */ - @VisibleForTesting - WxMaService getWxMaService(Integer userType) { - // 第一步,查询 DB 的配置项,获得对应的 WxMaService 对象 - SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( - SocialTypeEnum.WECHAT_MINI_PROGRAM.getType(), userType); - if (client != null && Objects.equals(client.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { - return wxMaServiceCache.getUnchecked(client.getClientId() + ":" + client.getClientSecret()); - } - // 第二步,不存在 DB 配置项,则使用 application-*.yaml 对应的 WxMaService 对象 - return wxMaService; - } - - /** - * 创建 clientId + clientSecret 对应的 WxMaService 对象 - * - * @param clientId 微信小程序 appId - * @param clientSecret 微信小程序 secret - * @return WxMaService 对象 - */ - private WxMaService buildWxMaService(String clientId, String clientSecret) { - // 第一步,创建 WxMaRedisBetterConfigImpl 对象 - WxMaRedisBetterConfigImpl configStorage = new WxMaRedisBetterConfigImpl( - new RedisTemplateWxRedisOps(stringRedisTemplate), - wxMaProperties.getConfigStorage().getKeyPrefix()); - configStorage.setAppid(clientId); - configStorage.setSecret(clientSecret); - - // 第二步,创建 WxMpService 对象 - WxMaService service = new WxMaServiceImpl(); - service.setWxMaConfig(configStorage); - return service; - } - - // =================== 客户端管理 =================== - - @Override - public Long createSocialClient(SocialClientSaveReqVO createReqVO) { - // 校验重复 - validateSocialClientUnique(null, createReqVO.getUserType(), createReqVO.getSocialType()); - - // 插入 - SocialClientDO client = BeanUtils.toBean(createReqVO, SocialClientDO.class); - socialClientMapper.insert(client); - return client.getId(); - } - - @Override - public void updateSocialClient(SocialClientSaveReqVO updateReqVO) { - // 校验存在 - validateSocialClientExists(updateReqVO.getId()); - // 校验重复 - validateSocialClientUnique(updateReqVO.getId(), updateReqVO.getUserType(), updateReqVO.getSocialType()); - - // 更新 - SocialClientDO updateObj = BeanUtils.toBean(updateReqVO, SocialClientDO.class); - socialClientMapper.updateById(updateObj); - } - - @Override - public void deleteSocialClient(Long id) { - // 校验存在 - validateSocialClientExists(id); - // 删除 - socialClientMapper.deleteById(id); - } - - private void validateSocialClientExists(Long id) { - if (socialClientMapper.selectById(id) == null) { - throw exception(SOCIAL_CLIENT_NOT_EXISTS); - } - } - - /** - * 校验社交应用是否重复,需要保证 userType + socialType 唯一 - * - * 原因是,不同端(userType)选择某个社交登录(socialType)时,需要通过 {@link #buildAuthRequest(Integer, Integer)} 构建对应的请求 - * - * @param id 编号 - * @param userType 用户类型 - * @param socialType 社交类型 - */ - private void validateSocialClientUnique(Long id, Integer userType, Integer socialType) { - SocialClientDO client = socialClientMapper.selectBySocialTypeAndUserType( - socialType, userType); - if (client == null) { - return; - } - if (id == null // 新增时,说明重复 - || ObjUtil.notEqual(id, client.getId())) { // 更新时,如果 id 不一致,说明重复 - throw exception(SOCIAL_CLIENT_UNIQUE); - } - } - - @Override - public SocialClientDO getSocialClient(Long id) { - return socialClientMapper.selectById(id); - } - - @Override - public PageResult getSocialClientPage(SocialClientPageReqVO pageReqVO) { - return socialClientMapper.selectPage(pageReqVO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java deleted file mode 100644 index 73d5768..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserService.java +++ /dev/null @@ -1,90 +0,0 @@ -package cn.iocoder.yudao.module.system.service.social; - -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialWxQrcodeReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; - -import jakarta.validation.Valid; -import java.util.List; - -/** - * 社交用户 Service 接口,例如说社交平台的授权登录 - * - * @author 芋道源码 - */ -public interface SocialUserService { - - /** - * 获得指定用户的社交用户列表 - * - * @param userId 用户编号 - * @param userType 用户类型 - * @return 社交用户列表 - */ - List getSocialUserList(Long userId, Integer userType); - - /** - * 绑定社交用户 - * - * @param reqDTO 绑定信息 - * @return 社交用户 openid - */ - String bindSocialUser(@Valid SocialUserBindReqDTO reqDTO); - - /** - * 取消绑定社交用户 - * - * @param userId 用户编号 - * @param userType 全局用户类型 - * @param socialType 社交平台的类型 {@link SocialTypeEnum} - * @param openid 社交平台的 openid - */ - void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid); - - /** - * 获得社交用户,基于 userId - * - * @param userType 用户类型 - * @param userId 用户编号 - * @param socialType 社交平台的类型 - * @return 社交用户 - */ - SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType); - - /** - * 获得社交用户 - * - * 在认证信息不正确的情况下,也会抛出 {@link ServiceException} 业务异常 - * - * @param userType 用户类型 - * @param socialType 社交平台的类型 - * @param code 授权码 - * @param state state - * @return 社交用户 - */ - SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state); - - // ==================== 社交用户 CRUD ==================== - - /** - * 获得社交用户 - * - * @param id 编号 - * @return 社交用户 - */ - SocialUserDO getSocialUser(Long id); - - /** - * 获得社交用户分页 - * - * @param pageReqVO 分页查询 - * @return 社交用户分页 - */ - PageResult getSocialUserPage(SocialUserPageReqVO pageReqVO); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java deleted file mode 100644 index f3cad58..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImpl.java +++ /dev/null @@ -1,173 +0,0 @@ -package cn.iocoder.yudao.module.system.service.social; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; -import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; -import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import jakarta.annotation.Resource; -import jakarta.validation.constraints.NotNull; -import lombok.extern.slf4j.Slf4j; -import me.zhyd.oauth.model.AuthUser; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import java.util.Collections; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND; - -/** - * 社交用户 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class SocialUserServiceImpl implements SocialUserService { - - @Resource - private SocialUserBindMapper socialUserBindMapper; - @Resource - private SocialUserMapper socialUserMapper; - - @Resource - private SocialClientService socialClientService; - - @Override - public List getSocialUserList(Long userId, Integer userType) { - // 获得绑定 - List socialUserBinds = socialUserBindMapper.selectListByUserIdAndUserType(userId, userType); - if (CollUtil.isEmpty(socialUserBinds)) { - return Collections.emptyList(); - } - // 获得社交用户 - return socialUserMapper.selectBatchIds(convertSet(socialUserBinds, SocialUserBindDO::getSocialUserId)); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public String bindSocialUser(SocialUserBindReqDTO reqDTO) { - // 获得社交用户 - SocialUserDO socialUser = authSocialUser(reqDTO.getSocialType(), reqDTO.getUserType(), - reqDTO.getCode(), reqDTO.getState()); - Assert.notNull(socialUser, "社交用户不能为空"); - - // 社交用户可能之前绑定过别的用户,需要进行解绑 - socialUserBindMapper.deleteByUserTypeAndSocialUserId(reqDTO.getUserType(), socialUser.getId()); - - // 用户可能之前已经绑定过该社交类型,需要进行解绑 - socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(reqDTO.getUserType(), reqDTO.getUserId(), - socialUser.getType()); - - // 绑定当前登录的社交用户 - SocialUserBindDO socialUserBind = SocialUserBindDO.builder() - .userId(reqDTO.getUserId()).userType(reqDTO.getUserType()) - .socialUserId(socialUser.getId()).socialType(socialUser.getType()).build(); - socialUserBindMapper.insert(socialUserBind); - return socialUser.getOpenid(); - } - - @Override - public void unbindSocialUser(Long userId, Integer userType, Integer socialType, String openid) { - // 获得 openid 对应的 SocialUserDO 社交用户 - SocialUserDO socialUser = socialUserMapper.selectByTypeAndOpenid(socialType, openid); - if (socialUser == null) { - throw exception(SOCIAL_USER_NOT_FOUND); - } - - // 获得对应的社交绑定关系 - socialUserBindMapper.deleteByUserTypeAndUserIdAndSocialType(userType, userId, socialUser.getType()); - } - - @Override - public SocialUserRespDTO getSocialUserByUserId(Integer userType, Long userId, Integer socialType) { - // 获得绑定用户 - SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserIdAndUserTypeAndSocialType(userId, userType, socialType); - if (socialUserBind == null) { - return null; - } - // 获得社交用户 - SocialUserDO socialUser = socialUserMapper.selectById(socialUserBind.getSocialUserId()); - Assert.notNull(socialUser, "社交用户不能为空"); - return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(), - socialUserBind.getUserId()); - } - - @Override - public SocialUserRespDTO getSocialUserByCode(Integer userType, Integer socialType, String code, String state) { - // 获得社交用户 - SocialUserDO socialUser = authSocialUser(socialType, userType, code, state); - Assert.notNull(socialUser, "社交用户不能为空"); - - // 获得绑定用户 - SocialUserBindDO socialUserBind = socialUserBindMapper.selectByUserTypeAndSocialUserId(userType, - socialUser.getId()); - return new SocialUserRespDTO(socialUser.getOpenid(), socialUser.getNickname(), socialUser.getAvatar(), - socialUserBind != null ? socialUserBind.getUserId() : null); - } - - /** - * 授权获得对应的社交用户 - * 如果授权失败,则会抛出 {@link ServiceException} 异常 - * - * @param socialType 社交平台的类型 {@link SocialTypeEnum} - * @param userType 用户类型 - * @param code 授权码 - * @param state state - * @return 授权用户 - */ - @NotNull - public SocialUserDO authSocialUser(Integer socialType, Integer userType, String code, String state) { - // 优先从 DB 中获取,因为 code 有且可以使用一次。 - // 在社交登录时,当未绑定 User 时,需要绑定登录,此时需要 code 使用两次 - SocialUserDO socialUser = socialUserMapper.selectByTypeAndCodeAnState(socialType, code, state); - if (socialUser != null) { - return socialUser; - } - - // 请求获取 - AuthUser authUser = socialClientService.getAuthUser(socialType, userType, code, state); - Assert.notNull(authUser, "三方用户不能为空"); - - // 保存到 DB 中 - socialUser = socialUserMapper.selectByTypeAndOpenid(socialType, authUser.getUuid()); - if (socialUser == null) { - socialUser = new SocialUserDO(); - } - socialUser.setType(socialType).setCode(code).setState(state) // 需要保存 code + state 字段,保证后续可查询 - .setOpenid(authUser.getUuid()).setToken(authUser.getToken().getAccessToken()).setRawTokenInfo((toJsonString(authUser.getToken()))) - .setNickname(authUser.getNickname()).setAvatar(authUser.getAvatar()).setRawUserInfo(toJsonString(authUser.getRawUserInfo())); - if (socialUser.getId() == null) { - socialUserMapper.insert(socialUser); - } else { - socialUserMapper.updateById(socialUser); - } - return socialUser; - } - - // ==================== 社交用户 CRUD ==================== - - @Override - public SocialUserDO getSocialUser(Long id) { - return socialUserMapper.selectById(id); - } - - @Override - public PageResult getSocialUserPage(SocialUserPageReqVO pageReqVO) { - return socialUserMapper.selectPage(pageReqVO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageService.java deleted file mode 100755 index 05318f8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageService.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.iocoder.yudao.module.system.service.tenant; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; - -import jakarta.validation.Valid; -import java.util.List; - -/** - * 租户套餐 Service 接口 - * - * @author 芋道源码 - */ -public interface TenantPackageService { - - /** - * 创建租户套餐 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createTenantPackage(@Valid TenantPackageSaveReqVO createReqVO); - - /** - * 更新租户套餐 - * - * @param updateReqVO 更新信息 - */ - void updateTenantPackage(@Valid TenantPackageSaveReqVO updateReqVO); - - /** - * 删除租户套餐 - * - * @param id 编号 - */ - void deleteTenantPackage(Long id); - - /** - * 获得租户套餐 - * - * @param id 编号 - * @return 租户套餐 - */ - TenantPackageDO getTenantPackage(Long id); - - /** - * 获得租户套餐分页 - * - * @param pageReqVO 分页查询 - * @return 租户套餐分页 - */ - PageResult getTenantPackagePage(TenantPackagePageReqVO pageReqVO); - - /** - * 校验租户套餐 - * - * @param id 编号 - * @return 租户套餐 - */ - TenantPackageDO validTenantPackage(Long id); - - /** - * 获得指定状态的租户套餐列表 - * - * @param status 状态 - * @return 租户套餐 - */ - List getTenantPackageListByStatus(Integer status); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java deleted file mode 100755 index 34209e5..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java +++ /dev/null @@ -1,139 +0,0 @@ -package cn.iocoder.yudao.module.system.service.tenant; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; -import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper; -import com.baomidou.dynamic.datasource.annotation.DSTransactional; -import com.google.common.annotations.VisibleForTesting; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; - -/** - * 租户套餐 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -public class TenantPackageServiceImpl implements TenantPackageService { - - @Resource - private TenantPackageMapper tenantPackageMapper; - - @Resource - @Lazy // 避免循环依赖的报错 - private TenantService tenantService; - - @Override - public Long createTenantPackage(TenantPackageSaveReqVO createReqVO) { - // 校验套餐名是否重复 - validateTenantPackageNameUnique(null, createReqVO.getName()); - // 插入 - TenantPackageDO tenantPackage = BeanUtils.toBean(createReqVO, TenantPackageDO.class); - tenantPackageMapper.insert(tenantPackage); - // 返回 - return tenantPackage.getId(); - } - - @Override - @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 - public void updateTenantPackage(TenantPackageSaveReqVO updateReqVO) { - // 校验存在 - TenantPackageDO tenantPackage = validateTenantPackageExists(updateReqVO.getId()); - // 校验套餐名是否重复 - validateTenantPackageNameUnique(updateReqVO.getId(), updateReqVO.getName()); - // 更新 - TenantPackageDO updateObj = BeanUtils.toBean(updateReqVO, TenantPackageDO.class); - tenantPackageMapper.updateById(updateObj); - // 如果菜单发生变化,则修改每个租户的菜单 - if (!CollUtil.isEqualList(tenantPackage.getMenuIds(), updateReqVO.getMenuIds())) { - List tenants = tenantService.getTenantListByPackageId(tenantPackage.getId()); - tenants.forEach(tenant -> tenantService.updateTenantRoleMenu(tenant.getId(), updateReqVO.getMenuIds())); - } - } - - @Override - public void deleteTenantPackage(Long id) { - // 校验存在 - validateTenantPackageExists(id); - // 校验正在使用 - validateTenantUsed(id); - // 删除 - tenantPackageMapper.deleteById(id); - } - - private TenantPackageDO validateTenantPackageExists(Long id) { - TenantPackageDO tenantPackage = tenantPackageMapper.selectById(id); - if (tenantPackage == null) { - throw exception(TENANT_PACKAGE_NOT_EXISTS); - } - return tenantPackage; - } - - private void validateTenantUsed(Long id) { - if (tenantService.getTenantCountByPackageId(id) > 0) { - throw exception(TENANT_PACKAGE_USED); - } - } - - @Override - public TenantPackageDO getTenantPackage(Long id) { - return tenantPackageMapper.selectById(id); - } - - @Override - public PageResult getTenantPackagePage(TenantPackagePageReqVO pageReqVO) { - return tenantPackageMapper.selectPage(pageReqVO); - } - - @Override - public TenantPackageDO validTenantPackage(Long id) { - TenantPackageDO tenantPackage = tenantPackageMapper.selectById(id); - if (tenantPackage == null) { - throw exception(TENANT_PACKAGE_NOT_EXISTS); - } - if (tenantPackage.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { - throw exception(TENANT_PACKAGE_DISABLE, tenantPackage.getName()); - } - return tenantPackage; - } - - @Override - public List getTenantPackageListByStatus(Integer status) { - return tenantPackageMapper.selectListByStatus(status); - } - - - @VisibleForTesting - void validateTenantPackageNameUnique(Long id, String name) { - if (StrUtil.isBlank(name)) { - return; - } - TenantPackageDO tenantPackage = tenantPackageMapper.selectByName(name); - if (tenantPackage == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的用户 - if (id == null) { - throw exception(TENANT_PACKAGE_NAME_DUPLICATE); - } - if (!tenantPackage.getId().equals(id)) { - throw exception(TENANT_PACKAGE_NAME_DUPLICATE); - } - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java deleted file mode 100755 index 425d18d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantService.java +++ /dev/null @@ -1,138 +0,0 @@ -package cn.iocoder.yudao.module.system.service.tenant; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; -import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; -import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; - -import jakarta.validation.Valid; -import java.util.List; -import java.util.Set; - -/** - * 租户 Service 接口 - * - * @author 芋道源码 - */ -public interface TenantService { - - /** - * 创建租户 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createTenant(@Valid TenantSaveReqVO createReqVO); - - /** - * 更新租户 - * - * @param updateReqVO 更新信息 - */ - void updateTenant(@Valid TenantSaveReqVO updateReqVO); - - /** - * 更新租户的角色菜单 - * - * @param tenantId 租户编号 - * @param menuIds 菜单编号数组 - */ - void updateTenantRoleMenu(Long tenantId, Set menuIds); - - /** - * 删除租户 - * - * @param id 编号 - */ - void deleteTenant(Long id); - - /** - * 获得租户 - * - * @param id 编号 - * @return 租户 - */ - TenantDO getTenant(Long id); - - /** - * 获得租户分页 - * - * @param pageReqVO 分页查询 - * @return 租户分页 - */ - PageResult getTenantPage(TenantPageReqVO pageReqVO); - - /** - * 获得名字对应的租户 - * - * @param name 租户名 - * @return 租户 - */ - TenantDO getTenantByName(String name); - - /** - * 获得域名对应的租户 - * - * @param website 域名 - * @return 租户 - */ - TenantDO getTenantByWebsite(String website); - - /** - * 获得使用指定套餐的租户数量 - * - * @param packageId 租户套餐编号 - * @return 租户数量 - */ - Long getTenantCountByPackageId(Long packageId); - - /** - * 获得使用指定套餐的租户数组 - * - * @param packageId 租户套餐编号 - * @return 租户数组 - */ - List getTenantListByPackageId(Long packageId); - - /** - * 获得指定状态的租户列表 - * - * @param status 状态 - * @return 租户列表 - */ - List getTenantListByStatus(Integer status); - - /** - * 进行租户的信息处理逻辑 - * 其中,租户编号从 {@link TenantContextHolder} 上下文中获取 - * - * @param handler 处理器 - */ - void handleTenantInfo(TenantInfoHandler handler); - - /** - * 进行租户的菜单处理逻辑 - * 其中,租户编号从 {@link TenantContextHolder} 上下文中获取 - * - * @param handler 处理器 - */ - void handleTenantMenu(TenantMenuHandler handler); - - /** - * 获得所有租户 - * - * @return 租户编号数组 - */ - List getTenantIdList(); - - /** - * 校验租户是否合法 - * - * @param id 租户编号 - */ - void validTenant(Long id); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java deleted file mode 100755 index 5060b16..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java +++ /dev/null @@ -1,313 +0,0 @@ -package cn.iocoder.yudao.module.system.service.tenant; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.date.DateUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; -import cn.iocoder.yudao.framework.tenant.config.TenantProperties; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; -import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; -import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper; -import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; -import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; -import cn.iocoder.yudao.module.system.service.permission.MenuService; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import cn.iocoder.yudao.module.system.service.permission.RoleService; -import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; -import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import com.baomidou.dynamic.datasource.annotation.DSTransactional; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import jakarta.annotation.Resource; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Collections.singleton; - -/** - * 租户 Service 实现类 - * - * @author 芋道源码 - */ -@Service -@Validated -@Slf4j -public class TenantServiceImpl implements TenantService { - - @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") - @Autowired(required = false) // 由于 yudao.tenant.enable 配置项,可以关闭多租户的功能,所以这里只能不强制注入 - private TenantProperties tenantProperties; - - @Resource - private TenantMapper tenantMapper; - - @Resource - private TenantPackageService tenantPackageService; - @Resource - @Lazy // 延迟,避免循环依赖报错 - private AdminUserService userService; - @Resource - private RoleService roleService; - @Resource - private MenuService menuService; - @Resource - private PermissionService permissionService; - - @Override - public List getTenantIdList() { - List tenants = tenantMapper.selectList(); - return CollectionUtils.convertList(tenants, TenantDO::getId); - } - - @Override - public void validTenant(Long id) { - TenantDO tenant = getTenant(id); - if (tenant == null) { - throw exception(TENANT_NOT_EXISTS); - } - if (tenant.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { - throw exception(TENANT_DISABLE, tenant.getName()); - } - if (DateUtils.isExpired(tenant.getExpireTime())) { - throw exception(TENANT_EXPIRE, tenant.getName()); - } - } - - @Override - @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 - @DataPermission(enable = false) // 参见 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1154 说明 - public Long createTenant(TenantSaveReqVO createReqVO) { - // 校验租户名称是否重复 - validTenantNameDuplicate(createReqVO.getName(), null); - // 校验租户域名是否重复 - validTenantWebsiteDuplicate(createReqVO.getWebsite(), null); - // 校验套餐被禁用 - TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(createReqVO.getPackageId()); - - // 创建租户 - TenantDO tenant = BeanUtils.toBean(createReqVO, TenantDO.class); - tenantMapper.insert(tenant); - // 创建租户的管理员 - TenantUtils.execute(tenant.getId(), () -> { - // 创建角色 - Long roleId = createRole(tenantPackage); - // 创建用户,并分配角色 - Long userId = createUser(roleId, createReqVO); - // 修改租户的管理员 - tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId)); - }); - return tenant.getId(); - } - - private Long createUser(Long roleId, TenantSaveReqVO createReqVO) { - // 创建用户 - Long userId = userService.createUser(TenantConvert.INSTANCE.convert02(createReqVO)); - // 分配角色 - permissionService.assignUserRole(userId, singleton(roleId)); - return userId; - } - - private Long createRole(TenantPackageDO tenantPackage) { - // 创建角色 - RoleSaveReqVO reqVO = new RoleSaveReqVO(); - reqVO.setName(RoleCodeEnum.TENANT_ADMIN.getName()).setCode(RoleCodeEnum.TENANT_ADMIN.getCode()) - .setSort(0).setRemark("系统自动生成"); - Long roleId = roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType()); - // 分配权限 - permissionService.assignRoleMenu(roleId, tenantPackage.getMenuIds()); - return roleId; - } - - @Override - @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换 - public void updateTenant(TenantSaveReqVO updateReqVO) { - // 校验存在 - TenantDO tenant = validateUpdateTenant(updateReqVO.getId()); - // 校验租户名称是否重复 - validTenantNameDuplicate(updateReqVO.getName(), updateReqVO.getId()); - // 校验租户域名是否重复 - validTenantWebsiteDuplicate(updateReqVO.getWebsite(), updateReqVO.getId()); - // 校验套餐被禁用 - TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId()); - - // 更新租户 - TenantDO updateObj = BeanUtils.toBean(updateReqVO, TenantDO.class); - tenantMapper.updateById(updateObj); - // 如果套餐发生变化,则修改其角色的权限 - if (ObjectUtil.notEqual(tenant.getPackageId(), updateReqVO.getPackageId())) { - updateTenantRoleMenu(tenant.getId(), tenantPackage.getMenuIds()); - } - } - - private void validTenantNameDuplicate(String name, Long id) { - TenantDO tenant = tenantMapper.selectByName(name); - if (tenant == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同名字的租户 - if (id == null) { - throw exception(TENANT_NAME_DUPLICATE, name); - } - if (!tenant.getId().equals(id)) { - throw exception(TENANT_NAME_DUPLICATE, name); - } - } - - private void validTenantWebsiteDuplicate(String website, Long id) { - if (StrUtil.isEmpty(website)) { - return; - } - TenantDO tenant = tenantMapper.selectByWebsite(website); - if (tenant == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同名字的租户 - if (id == null) { - throw exception(TENANT_WEBSITE_DUPLICATE, website); - } - if (!tenant.getId().equals(id)) { - throw exception(TENANT_WEBSITE_DUPLICATE, website); - } - } - - @Override - @DSTransactional - public void updateTenantRoleMenu(Long tenantId, Set menuIds) { - TenantUtils.execute(tenantId, () -> { - // 获得所有角色 - List roles = roleService.getRoleList(); - roles.forEach(role -> Assert.isTrue(tenantId.equals(role.getTenantId()), "角色({}/{}) 租户不匹配", - role.getId(), role.getTenantId(), tenantId)); // 兜底校验 - // 重新分配每个角色的权限 - roles.forEach(role -> { - // 如果是租户管理员,重新分配其权限为租户套餐的权限 - if (Objects.equals(role.getCode(), RoleCodeEnum.TENANT_ADMIN.getCode())) { - permissionService.assignRoleMenu(role.getId(), menuIds); - log.info("[updateTenantRoleMenu][租户管理员({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), menuIds); - return; - } - // 如果是其他角色,则去掉超过套餐的权限 - Set roleMenuIds = permissionService.getRoleMenuListByRoleId(role.getId()); - roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds); - permissionService.assignRoleMenu(role.getId(), roleMenuIds); - log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getId(), role.getTenantId(), roleMenuIds); - }); - }); - } - - @Override - public void deleteTenant(Long id) { - // 校验存在 - validateUpdateTenant(id); - // 删除 - tenantMapper.deleteById(id); - } - - private TenantDO validateUpdateTenant(Long id) { - TenantDO tenant = tenantMapper.selectById(id); - if (tenant == null) { - throw exception(TENANT_NOT_EXISTS); - } - // 内置租户,不允许删除 - if (isSystemTenant(tenant)) { - throw exception(TENANT_CAN_NOT_UPDATE_SYSTEM); - } - return tenant; - } - - @Override - public TenantDO getTenant(Long id) { - return tenantMapper.selectById(id); - } - - @Override - public PageResult getTenantPage(TenantPageReqVO pageReqVO) { - return tenantMapper.selectPage(pageReqVO); - } - - @Override - public TenantDO getTenantByName(String name) { - return tenantMapper.selectByName(name); - } - - @Override - public TenantDO getTenantByWebsite(String website) { - return tenantMapper.selectByWebsite(website); - } - - @Override - public Long getTenantCountByPackageId(Long packageId) { - return tenantMapper.selectCountByPackageId(packageId); - } - - @Override - public List getTenantListByPackageId(Long packageId) { - return tenantMapper.selectListByPackageId(packageId); - } - - @Override - public List getTenantListByStatus(Integer status) { - return tenantMapper.selectListByStatus(status); - } - - @Override - public void handleTenantInfo(TenantInfoHandler handler) { - // 如果禁用,则不执行逻辑 - if (isTenantDisable()) { - return; - } - // 获得租户 - TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId()); - // 执行处理器 - handler.handle(tenant); - } - - @Override - public void handleTenantMenu(TenantMenuHandler handler) { - // 如果禁用,则不执行逻辑 - if (isTenantDisable()) { - return; - } - // 获得租户,然后获得菜单 - TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId()); - Set menuIds; - if (isSystemTenant(tenant)) { // 系统租户,菜单是全量的 - menuIds = CollectionUtils.convertSet(menuService.getMenuList(), MenuDO::getId); - } else { - menuIds = tenantPackageService.getTenantPackage(tenant.getPackageId()).getMenuIds(); - } - // 执行处理器 - handler.handle(menuIds); - } - - private static boolean isSystemTenant(TenantDO tenant) { - return Objects.equals(tenant.getPackageId(), TenantDO.PACKAGE_ID_SYSTEM); - } - - private boolean isTenantDisable() { - return tenantProperties == null || Boolean.FALSE.equals(tenantProperties.getEnable()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantInfoHandler.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantInfoHandler.java deleted file mode 100644 index 5b5b9fe..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantInfoHandler.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.service.tenant.handler; - -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; - -/** - * 租户信息处理 - * 目的:尽量减少租户逻辑耦合到系统中 - * - * @author 芋道源码 - */ -public interface TenantInfoHandler { - - /** - * 基于传入的租户信息,进行相关逻辑的执行 - * 例如说,创建用户时,超过最大账户配额 - * - * @param tenant 租户信息 - */ - void handle(TenantDO tenant); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantMenuHandler.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantMenuHandler.java deleted file mode 100644 index 2e1be43..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/handler/TenantMenuHandler.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.system.service.tenant.handler; - -import java.util.Set; - -/** - * 租户菜单处理 - * 目的:尽量减少租户逻辑耦合到系统中 - * - * @author 芋道源码 - */ -public interface TenantMenuHandler { - - /** - * 基于传入的租户菜单【全】列表,进行相关逻辑的执行 - * 例如说,返回可分配菜单的时候,可以移除多余的 - * - * @param menuIds 菜单列表 - */ - void handle(Set menuIds); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java deleted file mode 100644 index d5c83bc..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java +++ /dev/null @@ -1,210 +0,0 @@ -package cn.iocoder.yudao.module.system.service.user; - -import cn.hutool.core.collection.CollUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportExcelVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import jakarta.validation.Valid; - -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 后台用户 Service 接口 - * - * @author 芋道源码 - */ -public interface AdminUserService { - - /** - * 创建用户 - * - * @param createReqVO 用户信息 - * @return 用户编号 - */ - Long createUser(@Valid UserSaveReqVO createReqVO); - - /** - * 注册用户 - * - * @param registerReqVO 用户信息 - * @return 用户编号 - */ - Long registerUser(@Valid AuthRegisterReqVO registerReqVO); - - /** - * 修改用户 - * - * @param updateReqVO 用户信息 - */ - void updateUser(@Valid UserSaveReqVO updateReqVO); - - /** - * 更新用户的最后登陆信息 - * - * @param id 用户编号 - * @param loginIp 登陆 IP - */ - void updateUserLogin(Long id, String loginIp); - - /** - * 修改用户个人信息 - * - * @param id 用户编号 - * @param reqVO 用户个人信息 - */ - void updateUserProfile(Long id, @Valid UserProfileUpdateReqVO reqVO); - - /** - * 修改用户个人密码 - * - * @param id 用户编号 - * @param reqVO 更新用户个人密码 - */ - void updateUserPassword(Long id, @Valid UserProfileUpdatePasswordReqVO reqVO); - - /** - * 修改密码 - * - * @param id 用户编号 - * @param password 密码 - */ - void updateUserPassword(Long id, String password); - - /** - * 修改状态 - * - * @param id 用户编号 - * @param status 状态 - */ - void updateUserStatus(Long id, Integer status); - - /** - * 删除用户 - * - * @param id 用户编号 - */ - void deleteUser(Long id); - - /** - * 通过用户名查询用户 - * - * @param username 用户名 - * @return 用户对象信息 - */ - AdminUserDO getUserByUsername(String username); - - /** - * 通过手机号获取用户 - * - * @param mobile 手机号 - * @return 用户对象信息 - */ - AdminUserDO getUserByMobile(String mobile); - - /** - * 获得用户分页列表 - * - * @param reqVO 分页条件 - * @return 分页列表 - */ - PageResult getUserPage(UserPageReqVO reqVO); - - /** - * 通过用户 ID 查询用户 - * - * @param id 用户ID - * @return 用户对象信息 - */ - AdminUserDO getUser(Long id); - - /** - * 获得指定部门的用户数组 - * - * @param deptIds 部门数组 - * @return 用户数组 - */ - List getUserListByDeptIds(Collection deptIds); - - /** - * 获得指定岗位的用户数组 - * - * @param postIds 岗位数组 - * @return 用户数组 - */ - List getUserListByPostIds(Collection postIds); - - /** - * 获得用户列表 - * - * @param ids 用户编号数组 - * @return 用户列表 - */ - List getUserList(Collection ids); - - /** - * 校验用户们是否有效。如下情况,视为无效: - * 1. 用户编号不存在 - * 2. 用户被禁用 - * - * @param ids 用户编号数组 - */ - void validateUserList(Collection ids); - - /** - * 获得用户 Map - * - * @param ids 用户编号数组 - * @return 用户 Map - */ - default Map getUserMap(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return new HashMap<>(); - } - return CollectionUtils.convertMap(getUserList(ids), AdminUserDO::getId); - } - - /** - * 获得用户列表,基于昵称模糊匹配 - * - * @param nickname 昵称 - * @return 用户列表 - */ - List getUserListByNickname(String nickname); - - /** - * 批量导入用户 - * - * @param importUsers 导入用户列表 - * @param isUpdateSupport 是否支持更新 - * @return 导入结果 - */ - UserImportRespVO importUserList(List importUsers, boolean isUpdateSupport); - - /** - * 获得指定状态的用户们 - * - * @param status 状态 - * @return 用户们 - */ - List getUserListByStatus(Integer status); - - /** - * 判断密码是否匹配 - * - * @param rawPassword 未加密的密码 - * @param encodedPassword 加密后的密码 - * @return 是否匹配 - */ - boolean isPasswordMatch(String rawPassword, String encodedPassword); - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java deleted file mode 100644 index f5ac58c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ /dev/null @@ -1,523 +0,0 @@ -package cn.iocoder.yudao.module.system.service.user; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.CollectionUtil; -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; -import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils; -import cn.iocoder.yudao.module.infra.api.config.ConfigApi; -import cn.iocoder.yudao.module.infra.api.file.FileApi; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.AuthRegisterReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportExcelVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper; -import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import cn.iocoder.yudao.module.system.service.dept.PostService; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import cn.iocoder.yudao.module.system.service.tenant.TenantService; -import com.google.common.annotations.VisibleForTesting; -import com.mzt.logapi.context.LogRecordContext; -import com.mzt.logapi.service.impl.DiffParseFunction; -import com.mzt.logapi.starter.annotation.LogRecord; -import jakarta.annotation.Resource; -import jakarta.validation.ConstraintViolationException; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Lazy; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.io.InputStream; -import java.time.LocalDateTime; -import java.util.*; - -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*; - -/** - * 后台用户 Service 实现类 - * - * @author 芋道源码 - */ -@Service("adminUserService") -@Slf4j -public class AdminUserServiceImpl implements AdminUserService { - - static final String USER_INIT_PASSWORD_KEY = "system.user.init-password"; - - static final String USER_REGISTER_ENABLED_KEY = "system.user.register-enabled"; - - @Resource - private AdminUserMapper userMapper; - - @Resource - private DeptService deptService; - @Resource - private PostService postService; - @Resource - private PermissionService permissionService; - @Resource - private PasswordEncoder passwordEncoder; - @Resource - @Lazy // 延迟,避免循环依赖报错 - private TenantService tenantService; - - @Resource - private UserPostMapper userPostMapper; - - @Resource - private ConfigApi configApi; - - @Override - @Transactional(rollbackFor = Exception.class) - @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_CREATE_SUB_TYPE, bizNo = "{{#user.id}}", - success = SYSTEM_USER_CREATE_SUCCESS) - public Long createUser(UserSaveReqVO createReqVO) { - // 1.1 校验账户配合 - tenantService.handleTenantInfo(tenant -> { - long count = userMapper.selectCount(); - if (count >= tenant.getAccountCount()) { - throw exception(USER_COUNT_MAX, tenant.getAccountCount()); - } - }); - // 1.2 校验正确性 - validateUserForCreateOrUpdate(null, createReqVO.getUsername(), - createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptId(), createReqVO.getPostIds()); - // 2.1 插入用户 - AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class); - user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 - user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码 - userMapper.insert(user); - // 2.2 插入关联岗位 - if (CollectionUtil.isNotEmpty(user.getPostIds())) { - userPostMapper.insertBatch(convertList(user.getPostIds(), - postId -> new UserPostDO().setUserId(user.getId()).setPostId(postId))); - } - - // 3. 记录操作日志上下文 - LogRecordContext.putVariable("user", user); - return user.getId(); - } - - @Override - public Long registerUser(AuthRegisterReqVO registerReqVO) { - // 1.1 校验是否开启注册 - if (ObjUtil.notEqual(configApi.getConfigValueByKey(USER_REGISTER_ENABLED_KEY), "true")) { - throw exception(USER_REGISTER_DISABLED); - } - // 1.2 校验账户配合 - tenantService.handleTenantInfo(tenant -> { - long count = userMapper.selectCount(); - if (count >= tenant.getAccountCount()) { - throw exception(USER_COUNT_MAX, tenant.getAccountCount()); - } - }); - // 1.3 校验正确性 - validateUserForCreateOrUpdate(null, registerReqVO.getUsername(), null, null, null, null); - - // 2. 插入用户 - AdminUserDO user = BeanUtils.toBean(registerReqVO, AdminUserDO.class); - user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 - user.setPassword(encodePassword(registerReqVO.getPassword())); // 加密密码 - userMapper.insert(user); - return user.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", - success = SYSTEM_USER_UPDATE_SUCCESS) - public void updateUser(UserSaveReqVO updateReqVO) { - updateReqVO.setPassword(null); // 特殊:此处不更新密码 - // 1. 校验正确性 - AdminUserDO oldUser = validateUserForCreateOrUpdate(updateReqVO.getId(), updateReqVO.getUsername(), - updateReqVO.getMobile(), updateReqVO.getEmail(), updateReqVO.getDeptId(), updateReqVO.getPostIds()); - - // 2.1 更新用户 - AdminUserDO updateObj = BeanUtils.toBean(updateReqVO, AdminUserDO.class); - userMapper.updateById(updateObj); - // 2.2 更新岗位 - updateUserPost(updateReqVO, updateObj); - - // 3. 记录操作日志上下文 - LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldUser, UserSaveReqVO.class)); - LogRecordContext.putVariable("user", oldUser); - } - - private void updateUserPost(UserSaveReqVO reqVO, AdminUserDO updateObj) { - Long userId = reqVO.getId(); - Set dbPostIds = convertSet(userPostMapper.selectListByUserId(userId), UserPostDO::getPostId); - // 计算新增和删除的岗位编号 - Set postIds = CollUtil.emptyIfNull(updateObj.getPostIds()); - Collection createPostIds = CollUtil.subtract(postIds, dbPostIds); - Collection deletePostIds = CollUtil.subtract(dbPostIds, postIds); - // 执行新增和删除。对于已经授权的岗位,不用做任何处理 - if (!CollectionUtil.isEmpty(createPostIds)) { - userPostMapper.insertBatch(convertList(createPostIds, - postId -> new UserPostDO().setUserId(userId).setPostId(postId))); - } - if (!CollectionUtil.isEmpty(deletePostIds)) { - userPostMapper.deleteByUserIdAndPostId(userId, deletePostIds); - } - } - - @Override - public void updateUserLogin(Long id, String loginIp) { - userMapper.updateById(new AdminUserDO().setId(id).setLoginIp(loginIp).setLoginDate(LocalDateTime.now())); - } - - @Override - public void updateUserProfile(Long id, UserProfileUpdateReqVO reqVO) { - // 校验正确性 - validateUserExists(id); - validateEmailUnique(id, reqVO.getEmail()); - validateMobileUnique(id, reqVO.getMobile()); - // 执行更新 - userMapper.updateById(BeanUtils.toBean(reqVO, AdminUserDO.class).setId(id)); - } - - @Override - public void updateUserPassword(Long id, UserProfileUpdatePasswordReqVO reqVO) { - // 校验旧密码密码 - validateOldPassword(id, reqVO.getOldPassword()); - // 执行更新 - AdminUserDO updateObj = new AdminUserDO().setId(id); - updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码 - userMapper.updateById(updateObj); - } - - @Override - @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_UPDATE_PASSWORD_SUB_TYPE, bizNo = "{{#id}}", - success = SYSTEM_USER_UPDATE_PASSWORD_SUCCESS) - public void updateUserPassword(Long id, String password) { - // 1. 校验用户存在 - AdminUserDO user = validateUserExists(id); - - // 2. 更新密码 - AdminUserDO updateObj = new AdminUserDO(); - updateObj.setId(id); - updateObj.setPassword(encodePassword(password)); // 加密密码 - userMapper.updateById(updateObj); - - // 3. 记录操作日志上下文 - LogRecordContext.putVariable("user", user); - LogRecordContext.putVariable("newPassword", updateObj.getPassword()); - } - - @Override - public void updateUserStatus(Long id, Integer status) { - // 校验用户存在 - validateUserExists(id); - // 更新状态 - AdminUserDO updateObj = new AdminUserDO(); - updateObj.setId(id); - updateObj.setStatus(status); - userMapper.updateById(updateObj); - } - - @Override - @Transactional(rollbackFor = Exception.class) - @LogRecord(type = SYSTEM_USER_TYPE, subType = SYSTEM_USER_DELETE_SUB_TYPE, bizNo = "{{#id}}", - success = SYSTEM_USER_DELETE_SUCCESS) - public void deleteUser(Long id) { - // 1. 校验用户存在 - AdminUserDO user = validateUserExists(id); - - // 2.1 删除用户 - userMapper.deleteById(id); - // 2.2 删除用户关联数据 - permissionService.processUserDeleted(id); - // 2.2 删除用户岗位 - userPostMapper.deleteByUserId(id); - - // 3. 记录操作日志上下文 - LogRecordContext.putVariable("user", user); - } - - @Override - public AdminUserDO getUserByUsername(String username) { - return userMapper.selectByUsername(username); - } - - @Override - public AdminUserDO getUserByMobile(String mobile) { - return userMapper.selectByMobile(mobile); - } - - @Override - public PageResult getUserPage(UserPageReqVO reqVO) { - // 如果有角色编号,查询角色对应的用户编号 - Set userIds = reqVO.getRoleId() != null ? - permissionService.getUserRoleIdListByRoleId(singleton(reqVO.getRoleId())) : null; - - // 分页查询 - return userMapper.selectPage(reqVO, getDeptCondition(reqVO.getDeptId()), userIds); - } - - @Override - public AdminUserDO getUser(Long id) { - return userMapper.selectById(id); - } - - @Override - public List getUserListByDeptIds(Collection deptIds) { - if (CollUtil.isEmpty(deptIds)) { - return Collections.emptyList(); - } - return userMapper.selectListByDeptIds(deptIds); - } - - @Override - public List getUserListByPostIds(Collection postIds) { - if (CollUtil.isEmpty(postIds)) { - return Collections.emptyList(); - } - Set userIds = convertSet(userPostMapper.selectListByPostIds(postIds), UserPostDO::getUserId); - if (CollUtil.isEmpty(userIds)) { - return Collections.emptyList(); - } - return userMapper.selectBatchIds(userIds); - } - - @Override - public List getUserList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return Collections.emptyList(); - } - return userMapper.selectBatchIds(ids); - } - - @Override - public void validateUserList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return; - } - // 获得岗位信息 - List users = userMapper.selectBatchIds(ids); - Map userMap = CollectionUtils.convertMap(users, AdminUserDO::getId); - // 校验 - ids.forEach(id -> { - AdminUserDO user = userMap.get(id); - if (user == null) { - throw exception(USER_NOT_EXISTS); - } - if (!CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus())) { - throw exception(USER_IS_DISABLE, user.getNickname()); - } - }); - } - - @Override - public List getUserListByNickname(String nickname) { - return userMapper.selectListByNickname(nickname); - } - - /** - * 获得部门条件:查询指定部门的子部门编号们,包括自身 - * - * @param deptId 部门编号 - * @return 部门编号集合 - */ - private Set getDeptCondition(Long deptId) { - if (deptId == null) { - return Collections.emptySet(); - } - Set deptIds = convertSet(deptService.getChildDeptList(deptId), DeptDO::getId); - deptIds.add(deptId); // 包括自身 - return deptIds; - } - - private AdminUserDO validateUserForCreateOrUpdate(Long id, String username, String mobile, String email, - Long deptId, Set postIds) { - // 关闭数据权限,避免因为没有数据权限,查询不到数据,进而导致唯一校验不正确 - return DataPermissionUtils.executeIgnore(() -> { - // 校验用户存在 - AdminUserDO user = validateUserExists(id); - // 校验用户名唯一 - validateUsernameUnique(id, username); - // 校验手机号唯一 - validateMobileUnique(id, mobile); - // 校验邮箱唯一 - validateEmailUnique(id, email); - // 校验部门处于开启状态 - deptService.validateDeptList(CollectionUtils.singleton(deptId)); - // 校验岗位处于开启状态 - postService.validatePostList(postIds); - return user; - }); - } - - @VisibleForTesting - AdminUserDO validateUserExists(Long id) { - if (id == null) { - return null; - } - AdminUserDO user = userMapper.selectById(id); - if (user == null) { - throw exception(USER_NOT_EXISTS); - } - return user; - } - - @VisibleForTesting - void validateUsernameUnique(Long id, String username) { - if (StrUtil.isBlank(username)) { - return; - } - AdminUserDO user = userMapper.selectByUsername(username); - if (user == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的用户 - if (id == null) { - throw exception(USER_USERNAME_EXISTS); - } - if (!user.getId().equals(id)) { - throw exception(USER_USERNAME_EXISTS); - } - } - - @VisibleForTesting - void validateEmailUnique(Long id, String email) { - if (StrUtil.isBlank(email)) { - return; - } - AdminUserDO user = userMapper.selectByEmail(email); - if (user == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的用户 - if (id == null) { - throw exception(USER_EMAIL_EXISTS); - } - if (!user.getId().equals(id)) { - throw exception(USER_EMAIL_EXISTS); - } - } - - @VisibleForTesting - void validateMobileUnique(Long id, String mobile) { - if (StrUtil.isBlank(mobile)) { - return; - } - AdminUserDO user = userMapper.selectByMobile(mobile); - if (user == null) { - return; - } - // 如果 id 为空,说明不用比较是否为相同 id 的用户 - if (id == null) { - throw exception(USER_MOBILE_EXISTS); - } - if (!user.getId().equals(id)) { - throw exception(USER_MOBILE_EXISTS); - } - } - - /** - * 校验旧密码 - * @param id 用户 id - * @param oldPassword 旧密码 - */ - @VisibleForTesting - void validateOldPassword(Long id, String oldPassword) { - AdminUserDO user = userMapper.selectById(id); - if (user == null) { - throw exception(USER_NOT_EXISTS); - } - if (!isPasswordMatch(oldPassword, user.getPassword())) { - throw exception(USER_PASSWORD_FAILED); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入 - public UserImportRespVO importUserList(List importUsers, boolean isUpdateSupport) { - // 1.1 参数校验 - if (CollUtil.isEmpty(importUsers)) { - throw exception(USER_IMPORT_LIST_IS_EMPTY); - } - // 1.2 初始化密码不能为空 - String initPassword = configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY); - if (StrUtil.isEmpty(initPassword)) { - throw exception(USER_IMPORT_INIT_PASSWORD); - } - - // 2. 遍历,逐个创建 or 更新 - UserImportRespVO respVO = UserImportRespVO.builder().createUsernames(new ArrayList<>()) - .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build(); - importUsers.forEach(importUser -> { - // 2.1.1 校验字段是否符合要求 - try { - ValidationUtils.validate(BeanUtils.toBean(importUser, UserSaveReqVO.class).setPassword(initPassword)); - } catch (ConstraintViolationException ex){ - respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage()); - return; - } - // 2.1.2 校验,判断是否有不符合的原因 - try { - validateUserForCreateOrUpdate(null, null, importUser.getMobile(), importUser.getEmail(), - importUser.getDeptId(), null); - } catch (ServiceException ex) { - respVO.getFailureUsernames().put(importUser.getUsername(), ex.getMessage()); - return; - } - - // 2.2.1 判断如果不存在,在进行插入 - AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername()); - if (existUser == null) { - userMapper.insert(BeanUtils.toBean(importUser, AdminUserDO.class) - .setPassword(encodePassword(initPassword)).setPostIds(new HashSet<>())); // 设置默认密码及空岗位编号数组 - respVO.getCreateUsernames().add(importUser.getUsername()); - return; - } - // 2.2.2 如果存在,判断是否允许更新 - if (!isUpdateSupport) { - respVO.getFailureUsernames().put(importUser.getUsername(), USER_USERNAME_EXISTS.getMsg()); - return; - } - AdminUserDO updateUser = BeanUtils.toBean(importUser, AdminUserDO.class); - updateUser.setId(existUser.getId()); - userMapper.updateById(updateUser); - respVO.getUpdateUsernames().add(importUser.getUsername()); - }); - return respVO; - } - - @Override - public List getUserListByStatus(Integer status) { - return userMapper.selectListByStatus(status); - } - - @Override - public boolean isPasswordMatch(String rawPassword, String encodedPassword) { - return passwordEncoder.matches(rawPassword, encodedPassword); - } - - /** - * 对密码进行加密 - * - * @param password 密码 - * @return 加密后的密码 - */ - private String encodePassword(String password) { - return passwordEncoder.encode(password); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java deleted file mode 100644 index 2a6a46f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/oauth2/OAuth2Utils.java +++ /dev/null @@ -1,103 +0,0 @@ -package cn.iocoder.yudao.module.system.util.oauth2; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; - -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.*; - -/** - * OAuth2 相关的工具类 - * - * @author 芋道源码 - */ -public class OAuth2Utils { - - /** - * 构建授权码模式下,重定向的 URI - * - * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 getSuccessfulRedirect 方法 - * - * @param redirectUri 重定向 URI - * @param authorizationCode 授权码 - * @param state 状态 - * @return 授权码模式下的重定向 URI - */ - public static String buildAuthorizationCodeRedirectUri(String redirectUri, String authorizationCode, String state) { - Map query = new LinkedHashMap<>(); - query.put("code", authorizationCode); - if (state != null) { - query.put("state", state); - } - return HttpUtils.append(redirectUri, query, null, false); - } - - /** - * 构建简化模式下,重定向的 URI - * - * copy from Spring Security OAuth2 的 AuthorizationEndpoint 类的 appendAccessToken 方法 - * - * @param redirectUri 重定向 URI - * @param accessToken 访问令牌 - * @param state 状态 - * @param expireTime 过期时间 - * @param scopes 授权范围 - * @param additionalInformation 附加信息 - * @return 简化授权模式下的重定向 URI - */ - public static String buildImplicitRedirectUri(String redirectUri, String accessToken, String state, LocalDateTime expireTime, - Collection scopes, Map additionalInformation) { - Map vars = new LinkedHashMap(); - Map keys = new HashMap(); - vars.put("access_token", accessToken); - vars.put("token_type", SecurityFrameworkUtils.AUTHORIZATION_BEARER.toLowerCase()); - if (state != null) { - vars.put("state", state); - } - if (expireTime != null) { - vars.put("expires_in", getExpiresIn(expireTime)); - } - if (CollUtil.isNotEmpty(scopes)) { - vars.put("scope", buildScopeStr(scopes)); - } - if (CollUtil.isNotEmpty(additionalInformation)) { - for (String key : additionalInformation.keySet()) { - Object value = additionalInformation.get(key); - if (value != null) { - keys.put("extra_" + key, key); - vars.put("extra_" + key, value); - } - } - } - // Do not include the refresh token (even if there is one) - return HttpUtils.append(redirectUri, vars, keys, true); - } - - public static String buildUnsuccessfulRedirect(String redirectUri, String responseType, String state, - String error, String description) { - Map query = new LinkedHashMap(); - query.put("error", error); - query.put("error_description", description); - if (state != null) { - query.put("state", state); - } - return HttpUtils.append(redirectUri, query, null, !responseType.contains("code")); - } - - public static long getExpiresIn(LocalDateTime expireTime) { - return LocalDateTimeUtil.between(LocalDateTime.now(), expireTime, ChronoUnit.SECONDS); - } - - public static String buildScopeStr(Collection scopes) { - return CollUtil.join(scopes, " "); - } - - public static List buildScopes(String scope) { - return StrUtil.split(scope, ' '); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/package-info.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/package-info.java deleted file mode 100644 index 97ca1f1..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 每个模块的 util 包,放专属当前模块的 Utils 工具类 - */ -package cn.iocoder.yudao.module.system.util; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg1.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg1.png deleted file mode 100644 index c481457..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg1.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg2.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg2.png deleted file mode 100644 index bf8fb38..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg2.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg3.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg3.png deleted file mode 100644 index f871d3d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg3.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg4.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg4.png deleted file mode 100644 index 2e3d871..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg4.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg5.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg5.png deleted file mode 100644 index fe383b7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg5.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg6.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg6.png deleted file mode 100644 index 5024ceb..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg6.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg7.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg7.png deleted file mode 100644 index efe76f8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg7.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg8.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg8.png deleted file mode 100644 index 2727aa3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg8.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg9.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg9.png deleted file mode 100644 index 4463aa2..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/original/bg9.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/1.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/1.png deleted file mode 100644 index ef11324..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/1.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/10.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/10.png deleted file mode 100644 index 297e44c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/10.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/11.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/11.png deleted file mode 100644 index d9b1da8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/11.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/12.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/12.png deleted file mode 100644 index 07e7313..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/12.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/13.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/13.png deleted file mode 100644 index 82c3dd9..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/13.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/14.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/14.png deleted file mode 100644 index 0b9a866..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/14.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/15.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/15.png deleted file mode 100644 index 86b0d1c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/15.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/16.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/16.png deleted file mode 100644 index e90a6e2..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/16.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/17.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/17.png deleted file mode 100644 index a82cbc7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/17.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/18.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/18.png deleted file mode 100644 index d3f3cfd..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/18.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/19.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/19.png deleted file mode 100644 index eb2855b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/19.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/8.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/8.png deleted file mode 100644 index 3cb5ce1..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/8.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/9.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/9.png deleted file mode 100644 index 384d354..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/11/9.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/2.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/2.png deleted file mode 100644 index baf3f06..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/2.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/3.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/3.png deleted file mode 100644 index ccaf617..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/3.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/4.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/4.png deleted file mode 100644 index 7dab162..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/jigsaw/slidingBlock/4.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg1.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg1.png deleted file mode 100644 index 14e7345..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg1.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg10.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg10.png deleted file mode 100644 index 1ea1d6d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg10.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg2.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg2.png deleted file mode 100644 index 0edb329..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg2.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg3.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg3.png deleted file mode 100644 index 9167996..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg3.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg4.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg4.png deleted file mode 100644 index e8e8e6c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg4.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg5.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg5.png deleted file mode 100644 index 66a3181..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg5.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg6.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg6.png deleted file mode 100644 index 9b0f5d8..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg6.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg7.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg7.png deleted file mode 100644 index db41c74..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg7.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg8.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg8.png deleted file mode 100644 index 3496813..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg8.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg9.png b/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg9.png deleted file mode 100644 index 4e7b477..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/main/resources/images/pic-click/bg9.png +++ /dev/null diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java deleted file mode 100644 index 7848272..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/controller/admin/oauth2/OAuth2OpenControllerTest.java +++ /dev/null @@ -1,337 +0,0 @@ -package cn.iocoder.yudao.module.system.controller.admin.oauth2; - -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.exception.ErrorCode; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.collection.SetUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAccessTokenRespVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenAuthorizeInfoRespVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.open.OAuth2OpenCheckTokenRespVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.enums.oauth2.OAuth2GrantTypeEnum; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ApproveService; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2ClientService; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2GrantService; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; -import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import jakarta.servlet.http.HttpServletRequest; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static java.util.Arrays.asList; -import static org.hamcrest.CoreMatchers.anyOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * {@link OAuth2OpenController} 的单元测试 - * - * @author 芋道源码 - */ -public class OAuth2OpenControllerTest extends BaseMockitoUnitTest { - - @InjectMocks - private OAuth2OpenController oauth2OpenController; - - @Mock - private OAuth2GrantService oauth2GrantService; - @Mock - private OAuth2ClientService oauth2ClientService; - @Mock - private OAuth2ApproveService oauth2ApproveService; - @Mock - private OAuth2TokenService oauth2TokenService; - - @Test - public void testPostAccessToken_authorizationCode() { - // 准备参数 - String granType = OAuth2GrantTypeEnum.AUTHORIZATION_CODE.getGrantType(); - String code = randomString(); - String redirectUri = randomString(); - String state = randomString(); - HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); - // mock 方法(client) - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); - when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), eq(granType), eq(new ArrayList<>()), eq(redirectUri))).thenReturn(client); - - // mock 方法(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) - .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS)); - when(oauth2GrantService.grantAuthorizationCodeForAccessToken(eq("test_client_id"), - eq(code), eq(redirectUri), eq(state))).thenReturn(accessTokenDO); - - // 调用 - CommonResult result = oauth2OpenController.postAccessToken(request, granType, - code, redirectUri, state, null, null, null, null); - // 断言 - assertEquals(0, result.getCode()); - assertPojoEquals(accessTokenDO, result.getData()); - assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L)); // 执行过程会过去几毫秒 - } - - @Test - public void testPostAccessToken_password() { - // 准备参数 - String granType = OAuth2GrantTypeEnum.PASSWORD.getGrantType(); - String username = randomString(); - String password = randomString(); - String scope = "write read"; - HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); - // mock 方法(client) - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); - when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), - eq(granType), eq(Lists.newArrayList("write", "read")), isNull())).thenReturn(client); - - // mock 方法(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) - .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS)); - when(oauth2GrantService.grantPassword(eq(username), eq(password), eq("test_client_id"), - eq(Lists.newArrayList("write", "read")))).thenReturn(accessTokenDO); - - // 调用 - CommonResult result = oauth2OpenController.postAccessToken(request, granType, - null, null, null, username, password, scope, null); - // 断言 - assertEquals(0, result.getCode()); - assertPojoEquals(accessTokenDO, result.getData()); - assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L)); // 执行过程会过去几毫秒 - } - - @Test - public void testPostAccessToken_refreshToken() { - // 准备参数 - String granType = OAuth2GrantTypeEnum.REFRESH_TOKEN.getGrantType(); - String refreshToken = randomString(); - String password = randomString(); - HttpServletRequest request = mockRequest("test_client_id", "test_client_secret"); - // mock 方法(client) - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("test_client_id"); - when(oauth2ClientService.validOAuthClientFromCache(eq("test_client_id"), eq("test_client_secret"), - eq(granType), eq(Lists.newArrayList()), isNull())).thenReturn(client); - - // mock 方法(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) - .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30000L, ChronoUnit.MILLIS)); - when(oauth2GrantService.grantRefreshToken(eq(refreshToken), eq("test_client_id"))).thenReturn(accessTokenDO); - - // 调用 - CommonResult result = oauth2OpenController.postAccessToken(request, granType, - null, null, null, null, password, null, refreshToken); - // 断言 - assertEquals(0, result.getCode()); - assertPojoEquals(accessTokenDO, result.getData()); - assertTrue(ObjectUtils.equalsAny(result.getData().getExpiresIn(), 29L, 30L)); // 执行过程会过去几毫秒 - } - - @Test - public void testPostAccessToken_implicit() { - // 调用,并断言 - assertServiceException(() -> oauth2OpenController.postAccessToken(null, - OAuth2GrantTypeEnum.IMPLICIT.getGrantType(), null, null, null, - null, null, null, null), - new ErrorCode(400, "Token 接口不支持 implicit 授权模式")); - } - - @Test - public void testRevokeToken() { - // 准备参数 - HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret"); - String token = randomString(); - // mock 方法(client) - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id"); - when(oauth2ClientService.validOAuthClientFromCache(eq("demo_client_id"), - eq("demo_client_secret"), isNull(), isNull(), isNull())).thenReturn(client); - // mock 方法(移除) - when(oauth2GrantService.revokeToken(eq("demo_client_id"), eq(token))).thenReturn(true); - - // 调用 - CommonResult result = oauth2OpenController.revokeToken(request, token); - // 断言 - assertEquals(0, result.getCode()); - assertTrue(result.getData()); - } - - @Test - public void testCheckToken() { - // 准备参数 - HttpServletRequest request = mockRequest("demo_client_id", "demo_client_secret"); - String token = randomString(); - // mock 方法 - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setUserType(UserTypeEnum.ADMIN.getValue()).setExpiresTime(LocalDateTimeUtil.of(1653485731195L)); - when(oauth2TokenService.checkAccessToken(eq(token))).thenReturn(accessTokenDO); - - // 调用 - CommonResult result = oauth2OpenController.checkToken(request, token); - // 断言 - assertEquals(0, result.getCode()); - assertPojoEquals(accessTokenDO, result.getData()); - assertEquals(1653485731L, result.getData().getExp()); // 执行过程会过去几毫秒 - } - - @Test - public void testAuthorize() { - // 准备参数 - String clientId = randomString(); - // mock 方法(client) - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("demo_client_id").setScopes(ListUtil.toList("read", "write", "all")); - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(client); - // mock 方法(approve) - List approves = asList( - randomPojo(OAuth2ApproveDO.class).setScope("read").setApproved(true), - randomPojo(OAuth2ApproveDO.class).setScope("write").setApproved(false)); - when(oauth2ApproveService.getApproveList(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId))).thenReturn(approves); - - // 调用 - CommonResult result = oauth2OpenController.authorize(clientId); - // 断言 - assertEquals(0, result.getCode()); - assertPojoEquals(client, result.getData().getClient()); - assertEquals(new KeyValue<>("read", true), result.getData().getScopes().get(0)); - assertEquals(new KeyValue<>("write", false), result.getData().getScopes().get(1)); - assertEquals(new KeyValue<>("all", false), result.getData().getScopes().get(2)); - } - - @Test - public void testApproveOrDeny_grantTypeError() { - // 调用,并断言 - assertServiceException(() -> oauth2OpenController.approveOrDeny(randomString(), null, - null, null, null, null), - new ErrorCode(400, "response_type 参数值只允许 code 和 token")); - } - - @Test // autoApprove = true,但是不通过 - public void testApproveOrDeny_autoApproveNo() { - // 准备参数 - String responseType = "code"; - String clientId = randomString(); - String scope = "{\"read\": true, \"write\": false}"; - String redirectUri = randomString(); - String state = randomString(); - // mock 方法 - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class); - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"), - eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); - - // 调用 - CommonResult result = oauth2OpenController.approveOrDeny(responseType, clientId, - scope, redirectUri, true, state); - // 断言 - assertEquals(0, result.getCode()); - assertNull(result.getData()); - } - - @Test // autoApprove = false,但是不通过 - public void testApproveOrDeny_ApproveNo() { - // 准备参数 - String responseType = "token"; - String clientId = randomString(); - String scope = "{\"read\": true, \"write\": false}"; - String redirectUri = "https://www.iocoder.cn"; - String state = "test"; - // mock 方法 - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class); - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"), - eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); - - // 调用 - CommonResult result = oauth2OpenController.approveOrDeny(responseType, clientId, - scope, redirectUri, false, state); - // 断言 - assertEquals(0, result.getCode()); - assertEquals("https://www.iocoder.cn#error=access_denied&error_description=User%20denied%20access&state=test", result.getData()); - } - - @Test // autoApprove = true,通过 + token - public void testApproveOrDeny_autoApproveWithToken() { - // 准备参数 - String responseType = "token"; - String clientId = randomString(); - String scope = "{\"read\": true, \"write\": false}"; - String redirectUri = "https://www.iocoder.cn"; - String state = "test"; - // mock 方法(client) - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null); - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("implicit"), - eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); - // mock 方法(场景一) - when(oauth2ApproveService.checkForPreApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), - eq(clientId), eq(SetUtils.asSet("read", "write")))).thenReturn(true); - // mock 方法(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) - .setAccessToken("test_access_token").setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 30010L, ChronoUnit.MILLIS)); - when(oauth2GrantService.grantImplicit(isNull(), eq(UserTypeEnum.ADMIN.getValue()), - eq(clientId), eq(ListUtil.toList("read")))).thenReturn(accessTokenDO); - - // 调用 - CommonResult result = oauth2OpenController.approveOrDeny(responseType, clientId, - scope, redirectUri, true, state); - // 断言 - assertEquals(0, result.getCode()); - assertThat(result.getData(), anyOf( // 29 和 30 都有一定概率,主要是时间计算 - is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=29&scope=read"), - is("https://www.iocoder.cn#access_token=test_access_token&token_type=bearer&state=test&expires_in=30&scope=read") - )); - } - - @Test // autoApprove = false,通过 + code - public void testApproveOrDeny_approveWithCode() { - // 准备参数 - String responseType = "code"; - String clientId = randomString(); - String scope = "{\"read\": true, \"write\": false}"; - String redirectUri = "https://www.iocoder.cn"; - String state = "test"; - // mock 方法(client) - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId(clientId).setAdditionalInformation(null); - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId), isNull(), eq("authorization_code"), - eq(asSet("read", "write")), eq(redirectUri))).thenReturn(client); - // mock 方法(场景二) - when(oauth2ApproveService.updateAfterApproval(isNull(), eq(UserTypeEnum.ADMIN.getValue()), eq(clientId), - eq(MapUtil.builder(new LinkedHashMap()).put("read", true).put("write", false).build()))) - .thenReturn(true); - // mock 方法(访问令牌) - String authorizationCode = "test_code"; - when(oauth2GrantService.grantAuthorizationCodeForCode(isNull(), eq(UserTypeEnum.ADMIN.getValue()), - eq(clientId), eq(ListUtil.toList("read")), eq(redirectUri), eq(state))).thenReturn(authorizationCode); - - // 调用 - CommonResult result = oauth2OpenController.approveOrDeny(responseType, clientId, - scope, redirectUri, false, state); - // 断言 - assertEquals(0, result.getCode()); - assertEquals("https://www.iocoder.cn?code=test_code&state=test", result.getData()); - } - - private HttpServletRequest mockRequest(String clientId, String secret) { - HttpServletRequest request = mock(HttpServletRequest.class); - when(request.getParameter(eq("client_id"))).thenReturn(clientId); - when(request.getParameter(eq("client_secret"))).thenReturn(secret); - return request; - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java deleted file mode 100644 index b04f426..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java +++ /dev/null @@ -1,160 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import com.google.common.collect.Lists; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.MockedStatic; -import org.mockito.stubbing.Answer; - -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.anyMap; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mockStatic; - -/** - * {@link cn.iocoder.yudao.module.system.framework.sms.core.client.impl.AliyunSmsClient} 的单元测试 - * - * @author 芋道源码 - */ -public class AliyunSmsClientTest extends BaseMockitoUnitTest { - - private final SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(randomString()) // 随机一个 apiKey,避免构建报错 - .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 - .setSignature("芋道源码"); - - @InjectMocks - private final AliyunSmsClient smsClient = new AliyunSmsClient(properties); - - @Test - public void tesSendSms_success() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("code", 1234), new KeyValue<>("op", "login")); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"Message\":\"OK\",\"RequestId\":\"30067CE9-3710-5984-8881-909B21D8DB28\",\"Code\":\"OK\",\"BizId\":\"800025323183427988\"}"); - httpUtilsMockedStatic.when(() -> HttpUtils.encodeUtf8(anyString())) - .then((Answer) invocationOnMock -> (String) invocationOnMock.getArguments()[0]); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertTrue(result.getSuccess()); - assertEquals("30067CE9-3710-5984-8881-909B21D8DB28", result.getApiRequestId()); - assertEquals("OK", result.getApiCode()); - assertEquals("OK", result.getApiMsg()); - assertEquals("800025323183427988", result.getSerialNo()); - } - } - - @Test - public void tesSendSms_fail() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("code", 1234), new KeyValue<>("op", "login")); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"Message\":\"手机号码格式错误\",\"RequestId\":\"B7700B8E-227E-5886-9564-26036172F01F\",\"Code\":\"isv.MOBILE_NUMBER_ILLEGAL\"}"); - httpUtilsMockedStatic.when(() -> HttpUtils.encodeUtf8(anyString())) - .then((Answer) invocationOnMock -> (String) invocationOnMock.getArguments()[0]); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams); - // 断言 - assertFalse(result.getSuccess()); - assertEquals("B7700B8E-227E-5886-9564-26036172F01F", result.getApiRequestId()); - assertEquals("isv.MOBILE_NUMBER_ILLEGAL", result.getApiCode()); - assertEquals("手机号码格式错误", result.getApiMsg()); - assertNull(result.getSerialNo()); - } - } - - @Test - public void testParseSmsReceiveStatus() { - // 准备参数 - String text = "[\n" + - " {\n" + - " \"phone_number\" : \"13900000001\",\n" + - " \"send_time\" : \"2017-01-01 11:12:13\",\n" + - " \"report_time\" : \"2017-02-02 22:23:24\",\n" + - " \"success\" : true,\n" + - " \"err_code\" : \"DELIVERED\",\n" + - " \"err_msg\" : \"用户接收成功\",\n" + - " \"sms_size\" : \"1\",\n" + - " \"biz_id\" : \"12345\",\n" + - " \"out_id\" : \"67890\"\n" + - " }\n" + - "]"; - // mock 方法 - - // 调用 - List statuses = smsClient.parseSmsReceiveStatus(text); - // 断言 - assertEquals(1, statuses.size()); - assertTrue(statuses.get(0).getSuccess()); - assertEquals("DELIVERED", statuses.get(0).getErrorCode()); - assertEquals("用户接收成功", statuses.get(0).getErrorMsg()); - assertEquals("13900000001", statuses.get(0).getMobile()); - assertEquals(LocalDateTime.of(2017, 2, 2, 22, 23, 24), - statuses.get(0).getReceiveTime()); - assertEquals("12345", statuses.get(0).getSerialNo()); - assertEquals(67890L, statuses.get(0).getLogId()); - } - - @Test - public void testGetSmsTemplate() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - String apiTemplateId = randomString(); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"TemplateCode\":\"SMS_207945135\",\"RequestId\":\"6F4CC077-29C8-5BA5-AB62-5FF95068A5AC\",\"Message\":\"OK\",\"TemplateContent\":\"您的验证码${code},该验证码5分钟内有效,请勿泄漏于他人!\",\"TemplateName\":\"公告通知\",\"TemplateType\":0,\"Code\":\"OK\",\"CreateDate\":\"2020-12-23 17:34:42\",\"Reason\":\"无审批备注\",\"TemplateStatus\":1}"); - httpUtilsMockedStatic.when(() -> HttpUtils.encodeUtf8(anyString())) - .then((Answer) invocationOnMock -> (String) invocationOnMock.getArguments()[0]); - - // 调用 - SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); - // 断言 - assertEquals("SMS_207945135", result.getId()); - assertEquals("您的验证码${code},该验证码5分钟内有效,请勿泄漏于他人!", result.getContent()); - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); - assertEquals("无审批备注", result.getAuditReason()); - } - } - - @Test - public void testConvertSmsTemplateAuditStatus() { - assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), - smsClient.convertSmsTemplateAuditStatus(0)); - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), - smsClient.convertSmsTemplateAuditStatus(1)); - assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), - smsClient.convertSmsTemplateAuditStatus(2)); - assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3), - "未知审核状态(3)"); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java deleted file mode 100644 index 3f97412..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java +++ /dev/null @@ -1,127 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import com.google.common.collect.Lists; - -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.MockedStatic; - -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mockStatic; - -/** - * {@link HuaweiSmsClient} 的单元测试 - * - * @author scholar - */ -public class HuaweiSmsClientTest extends BaseMockitoUnitTest { - - private final SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错 - .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 - .setSignature("芋道源码"); - - @InjectMocks - private HuaweiSmsClient smsClient = new HuaweiSmsClient(properties); - - @Test - public void testDoSendSms_success() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"result\":[{\"originTo\":\"+86155****5678\",\"createTime\":\"2018-05-25T16:34:34Z\",\"from\":\"1069********0012\",\"smsMsgId\":\"d6e3cdd0-522b-4692-8304-a07553cdf591_8539659\",\"status\":\"000000\",\"countryId\":\"CN\",\"total\":2}],\"code\":\"000000\",\"description\":\"Success\"}\n"); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertTrue(result.getSuccess()); - assertEquals("d6e3cdd0-522b-4692-8304-a07553cdf591_8539659", result.getSerialNo()); - assertEquals("000000", result.getApiCode()); - } - } - - @Test - public void testDoSendSms_fail_01() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"result\":[{\"total\":1,\"originTo\":\"17321315478\",\"createTime\":\"2024-08-18T11:32:20Z\",\"from\":\"x8824060312575\",\"smsMsgId\":\"06e4b966-ad87-479f-8b74-f57fb7aafb60_304613461\",\"countryId\":\"CN\",\"status\":\"E200033\"}],\"code\":\"E000510\",\"description\":\"The SMS fails to be sent. For details, see status.\"}"); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertFalse(result.getSuccess()); - assertEquals("06e4b966-ad87-479f-8b74-f57fb7aafb60_304613461", result.getSerialNo()); - assertEquals("E200033", result.getApiCode()); - } - } - - @Test - public void testDoSendSms_fail_02() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"code\":\"E000102\",\"description\":\"Invalid app_key.\"}"); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertFalse(result.getSuccess()); - assertEquals("E000102", result.getApiCode()); - assertEquals("Invalid app_key.", result.getApiMsg()); - } - } - - @Test - public void testParseSmsReceiveStatus() { - // 准备参数 - String text = "sequence=1&total=1&statusDesc=%E7%94%A8%E6%88%B7%E5%B7%B2%E6%88%90%E5%8A%9F%E6%94%B6%E5%88%B0%E7%9F%AD%E4%BF%A1&updateTime=2024-08-15T03%3A00%3A34Z&source=2&smsMsgId=70207ed7-1d02-41b0-8537-bb25fd1c2364_143684459&status=DELIVRD&extend=176"; - - // 调用 - List statuses = smsClient.parseSmsReceiveStatus(text); - // 断言 - assertEquals(1, statuses.size()); - SmsReceiveRespDTO status = statuses.get(0); - assertTrue(status.getSuccess()); - assertEquals("DELIVRD", status.getErrorCode()); - assertEquals(LocalDateTime.of(2024, 8, 15, 3, 0, 34), status.getReceiveTime()); - assertEquals("70207ed7-1d02-41b0-8537-bb25fd1c2364_143684459", status.getSerialNo()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java deleted file mode 100644 index 93b99bc..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java +++ /dev/null @@ -1,131 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import com.google.common.collect.Lists; - -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.MockedStatic; - -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mockStatic; - -/** - * {@link QiniuSmsClient} 的单元测试 - * - * @author scholar - */ -public class QiniuSmsClientTest extends BaseMockitoUnitTest { - - private final SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(randomString())// 随机一个 apiKey,避免构建报错 - .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 - .setSignature("芋道源码"); - - @InjectMocks - private QiniuSmsClient smsClient = new QiniuSmsClient(properties); - - @Test - public void testDoSendSms_success() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"message_id\":\"17245678901\"}"); - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertTrue(result.getSuccess()); - assertEquals("17245678901", result.getSerialNo()); - } - } - - @Test - public void testDoSendSms_fail() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString() + " " + randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"error\":\"BadToken\",\"message\":\"Your authorization token is invalid\",\"request_id\":\"etziWcJFo1C8Ne8X\"}"); - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertFalse(result.getSuccess()); - assertEquals("BadToken", result.getApiCode()); - assertEquals("Your authorization token is invalid", result.getApiMsg()); - assertEquals("etziWcJFo1C8Ne8X", result.getApiRequestId()); - } - } - - @Test - public void testGetSmsTemplate() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - String apiTemplateId = randomString(); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.get(anyString(), anyMap())) - .thenReturn("{\"audit_status\":\"passed\",\"created_at\":1724231187,\"description\":\"\",\"disable_broadcast\":false,\"disable_broadcast_reason\":\"\",\"disable_reason\":\"\",\"disabled\":false,\"id\":\"1826184073773596672\",\"is_oversea\":false,\"name\":\"dd\",\"parameters\":[\"code\"],\"reject_reason\":\"\",\"signature_id\":\"1826099896017498112\",\"signature_text\":\"yudao\",\"template\":\"您的验证码为:${code}\",\"type\":\"verification\",\"uid\":1383022432,\"updated_at\":1724288561,\"variable_count\":0}"); - // 调用 - SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); - // 断言 - assertEquals("1826184073773596672", result.getId()); - assertEquals("您的验证码为:${code}", result.getContent()); - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); - assertEquals("", result.getAuditReason()); - } - } - - @Test - public void testParseSmsReceiveStatus() { - // 准备参数 - String text = "{\"items\":[{\"mobile\":\"18881234567\",\"message_id\":\"10135515063508004167\",\"status\":\"DELIVRD\",\"delivrd_at\":1724591666,\"error\":\"DELIVRD\",\"seq\":\"123\"}]}"; - // 调用 - List statuses = smsClient.parseSmsReceiveStatus(text); - // 断言 - assertEquals(1, statuses.size()); - SmsReceiveRespDTO status = statuses.get(0); - assertTrue(status.getSuccess()); - assertEquals("DELIVRD", status.getErrorMsg()); - assertEquals(LocalDateTime.of(2024, 8, 25, 21, 14, 26), status.getReceiveTime()); - assertEquals("18881234567", status.getMobile()); - assertEquals("10135515063508004167", status.getSerialNo()); - assertEquals(123, status.getLogId()); - } - - @Test - public void testConvertSmsTemplateAuditStatus() { - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), - smsClient.convertSmsTemplateAuditStatus("passed")); - assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), - smsClient.convertSmsTemplateAuditStatus("reviewing")); - assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), - smsClient.convertSmsTemplateAuditStatus("rejected")); - assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus("unknown"), - "未知审核状态(3)"); - } -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java deleted file mode 100644 index faba754..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java +++ /dev/null @@ -1,151 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.hutool.core.collection.ListUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -import java.util.List; - -/** - * 各种 {@link SmsClient} 的集成测试 - * - * @author 芋道源码 - */ -public class SmsClientTests { - - // ========== 阿里云 ========== - - @Test - @Disabled - public void testAliyunSmsClient_getSmsTemplate() throws Throwable { - SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(System.getenv("SMS_ALIYUN_ACCESS_KEY")) - .setApiSecret(System.getenv("SMS_ALIYUN_SECRET_KEY")); - AliyunSmsClient client = new AliyunSmsClient(properties); - // 准备参数 - String apiTemplateId = "SMS_207945135"; - // 调用 - SmsTemplateRespDTO template = client.getSmsTemplate(apiTemplateId); - // 打印结果 - System.out.println(template); - } - - @Test - @Disabled - public void testAliyunSmsClient_sendSms() throws Throwable { - SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(System.getenv("SMS_ALIYUN_ACCESS_KEY")) - .setApiSecret(System.getenv("SMS_ALIYUN_SECRET_KEY")) - .setSignature("Ballcat"); - AliyunSmsClient client = new AliyunSmsClient(properties); - // 准备参数 - Long sendLogId = System.currentTimeMillis(); - String mobile = "15601691323"; - String apiTemplateId = "SMS_207945135"; - // 调用 - SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, ListUtil.of(new KeyValue<>("code", "1024"))); - // 打印结果 - System.out.println(sendRespDTO); - } - - // ========== 腾讯云 ========== - - @Test - @Disabled - public void testTencentSmsClient_sendSms() throws Throwable { - String sdkAppId = "1400500458"; - SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(System.getenv("SMS_TENCENT_ACCESS_KEY") + " " + sdkAppId) - .setApiSecret(System.getenv("SMS_TENCENT_SECRET_KEY")) - .setSignature("芋道源码"); - TencentSmsClient client = new TencentSmsClient(properties); - // 准备参数 - Long sendLogId = System.currentTimeMillis(); - String mobile = "15601691323"; - String apiTemplateId = "358212"; - // 调用 - SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, ListUtil.of(new KeyValue<>("code", "1024"))); - // 打印结果 - System.out.println(sendRespDTO); - } - - @Test - @Disabled - public void testTencentSmsClient_getSmsTemplate() throws Throwable { - String sdkAppId = "1400500458"; - SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(System.getenv("SMS_TENCENT_ACCESS_KEY") + " " + sdkAppId) - .setApiSecret(System.getenv("SMS_TENCENT_SECRET_KEY")) - .setSignature("芋道源码"); - TencentSmsClient client = new TencentSmsClient(properties); - // 准备参数 - String apiTemplateId = "358212"; - // 调用 - SmsTemplateRespDTO template = client.getSmsTemplate(apiTemplateId); - // 打印结果 - System.out.println(template); - } - - // ========== 华为云 ========== - - @Test - @Disabled - public void testHuaweiSmsClient_sendSms() throws Throwable { - String sender = "x8824060312575"; - SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(System.getenv("SMS_HUAWEI_ACCESS_KEY") + " " + sender) - .setApiSecret(System.getenv("SMS_HUAWEI_SECRET_KEY")) - .setSignature("runpu"); - HuaweiSmsClient client = new HuaweiSmsClient(properties); - // 准备参数 - Long sendLogId = System.currentTimeMillis(); - String mobile = "17321315478"; - String apiTemplateId = "3644cdab863546a3b718d488659a99ef"; - List> templateParams = ListUtil.of(new KeyValue<>("code", "1024")); - // 调用 - SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams); - // 打印结果 - System.out.println(smsSendRespDTO); - } - - // ========== 七牛云 ========== - - @Test - @Disabled - public void testQiniuSmsClient_sendSms() throws Throwable { - SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey("SMS_QINIU_ACCESS_KEY") - .setApiSecret("SMS_QINIU_SECRET_KEY"); - QiniuSmsClient client = new QiniuSmsClient(properties); - // 准备参数 - Long sendLogId = System.currentTimeMillis(); - String mobile = "17321315478"; - String apiTemplateId = "3644cdab863546a3b718d488659a99ef"; - List> templateParams = ListUtil.of(new KeyValue<>("code", "1122")); - // 调用 - SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams); - // 打印结果 - System.out.println(smsSendRespDTO); - } - - @Test - @Disabled - public void testQiniuSmsClient_getSmsTemplate() throws Throwable { - SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey("SMS_QINIU_ACCESS_KEY") - .setApiSecret("SMS_QINIU_SECRET_KEY"); - QiniuSmsClient client = new QiniuSmsClient(properties); - // 准备参数 - String apiTemplateId = "3644cdab863546a3b718d488659a99ef"; - // 调用 - SmsTemplateRespDTO template = client.getSmsTemplate(apiTemplateId); - // 打印结果 - System.out.println(template); - } -} - diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClientTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClientTest.java deleted file mode 100644 index 060a345..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClientTest.java +++ /dev/null @@ -1,218 +0,0 @@ -package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; - -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.util.http.HttpUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import com.google.common.collect.Lists; - -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.MockedStatic; - -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mockStatic; - -/** - * {@link TencentSmsClient} 的单元测试 - * - * @author shiwp - */ -public class TencentSmsClientTest extends BaseMockitoUnitTest { - - private final SmsChannelProperties properties = new SmsChannelProperties() - .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错 - .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 - .setSignature("芋道源码"); - - @InjectMocks - private TencentSmsClient smsClient = new TencentSmsClient(properties); - - @Test - public void testDoSendSms_success() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\n" + - " \"Response\": {\n" + - " \"SendStatusSet\": [\n" + - " {\n" + - " \"SerialNo\": \"5000:1045710669157053657849499619\",\n" + - " \"PhoneNumber\": \"+8618511122233\",\n" + - " \"Fee\": 1,\n" + - " \"SessionContext\": \"test\",\n" + - " \"Code\": \"Ok\",\n" + - " \"Message\": \"send success\",\n" + - " \"IsoCode\": \"CN\"\n" + - " },\n" + - " ],\n" + - " \"RequestId\": \"a0aabda6-cf91-4f3e-a81f-9198114a2279\"\n" + - " }\n" + - "}"); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertTrue(result.getSuccess()); - assertEquals("5000:1045710669157053657849499619", result.getSerialNo()); - assertEquals("a0aabda6-cf91-4f3e-a81f-9198114a2279", result.getApiRequestId()); - assertEquals("send success", result.getApiMsg()); - } - } - - @Test - public void testDoSendSms_fail_01() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\n" + - " \"Response\": {\n" + - " \"SendStatusSet\": [\n" + - " {\n" + - " \"SerialNo\": \"5000:1045710669157053657849499619\",\n" + - " \"PhoneNumber\": \"+8618511122233\",\n" + - " \"Fee\": 1,\n" + - " \"SessionContext\": \"test\",\n" + - " \"Code\": \"ERROR\",\n" + - " \"Message\": \"send success\",\n" + - " \"IsoCode\": \"CN\"\n" + - " },\n" + - " ],\n" + - " \"RequestId\": \"a0aabda6-cf91-4f3e-a81f-9198114a2279\"\n" + - " }\n" + - "}"); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertFalse(result.getSuccess()); - assertEquals("5000:1045710669157053657849499619", result.getSerialNo()); - assertEquals("a0aabda6-cf91-4f3e-a81f-9198114a2279", result.getApiRequestId()); - assertEquals("send success", result.getApiMsg()); - } - } - - @Test - public void testDoSendSms_fail_02() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - Long sendLogId = randomLongId(); - String mobile = randomString(); - String apiTemplateId = randomString(); - List> templateParams = Lists.newArrayList( - new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); - - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{\"Response\":{\"Error\":{\"Code\":\"AuthFailure.SecretIdNotFound\",\"Message\":\"The SecretId is not found, please ensure that your SecretId is correct.\"},\"RequestId\":\"2a88f82a-261c-4ac6-9fa9-c7d01aaa486a\"}}"); - - // 调用 - SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, - apiTemplateId, templateParams); - // 断言 - assertFalse(result.getSuccess()); - assertEquals("2a88f82a-261c-4ac6-9fa9-c7d01aaa486a", result.getApiRequestId()); - assertEquals("AuthFailure.SecretIdNotFound", result.getApiCode()); - assertEquals("The SecretId is not found, please ensure that your SecretId is correct.", result.getApiMsg()); - } - } - - @Test - public void testParseSmsReceiveStatus() { - // 准备参数 - String text = "[\n" + - " {\n" + - " \"user_receive_time\": \"2015-10-17 08:03:04\",\n" + - " \"nationcode\": \"86\",\n" + - " \"mobile\": \"13900000001\",\n" + - " \"report_status\": \"SUCCESS\",\n" + - " \"errmsg\": \"DELIVRD\",\n" + - " \"description\": \"用户短信送达成功\",\n" + - " \"sid\": \"12345\",\n" + - " \"ext\": {\"logId\":\"67890\"}\n" + - " }\n" + - "]"; - - // 调用 - List statuses = smsClient.parseSmsReceiveStatus(text); - // 断言 - assertEquals(1, statuses.size()); - assertTrue(statuses.get(0).getSuccess()); - assertEquals("DELIVRD", statuses.get(0).getErrorCode()); - assertEquals("13900000001", statuses.get(0).getMobile()); - assertEquals(LocalDateTime.of(2015, 10, 17, 8, 3, 4), statuses.get(0).getReceiveTime()); - assertEquals("12345", statuses.get(0).getSerialNo()); - } - - @Test - public void testGetSmsTemplate() throws Throwable { - try (MockedStatic httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { - // 准备参数 - String apiTemplateId = "1122"; - - // mock 方法 - httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) - .thenReturn("{ \"Response\": {\n" + - " \"DescribeTemplateStatusSet\": [\n" + - " {\n" + - " \"TemplateName\": \"验证码\",\n" + - " \"TemplateId\": 1122,\n" + - " \"International\": 0,\n" + - " \"ReviewReply\": \"审批备注\",\n" + - " \"CreateTime\": 1617379200,\n" + - " \"TemplateContent\": \"您的验证码是{1}\",\n" + - " \"StatusCode\": 0\n" + - " },\n" + - " \n" + - " ],\n" + - " \"RequestId\": \"f36e4f00-605e-49b1-ad0d-bfaba81c7325\"\n" + - " }}"); - - // 调用 - SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); - // 断言 - assertEquals("1122", result.getId()); - assertEquals("您的验证码是{1}", result.getContent()); - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); - assertEquals("审批备注", result.getAuditReason()); - } - } - - @Test - public void testConvertSmsTemplateAuditStatus() { - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), - smsClient.convertSmsTemplateAuditStatus(0)); - assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), - smsClient.convertSmsTemplateAuditStatus(1)); - assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), - smsClient.convertSmsTemplateAuditStatus(-1)); - assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3), - "未知审核状态(3)"); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java deleted file mode 100644 index 62baea3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImplTest.java +++ /dev/null @@ -1,358 +0,0 @@ -package cn.iocoder.yudao.module.system.service.auth; - -import cn.hutool.core.util.ReflectUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; -import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; -import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import cn.iocoder.yudao.module.system.service.logger.LoginLogService; -import cn.iocoder.yudao.module.system.service.member.MemberService; -import cn.iocoder.yudao.module.system.service.oauth2.OAuth2TokenService; -import cn.iocoder.yudao.module.system.service.social.SocialUserService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import com.anji.captcha.model.common.ResponseModel; -import com.anji.captcha.service.CaptchaService; -import jakarta.annotation.Resource; -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -@Import(AdminAuthServiceImpl.class) -public class AdminAuthServiceImplTest extends BaseDbUnitTest { - - @Resource - private AdminAuthServiceImpl authService; - - @MockBean - private AdminUserService userService; - @MockBean - private CaptchaService captchaService; - @MockBean - private LoginLogService loginLogService; - @MockBean - private SocialUserService socialUserService; - @MockBean - private SmsCodeApi smsCodeApi; - @MockBean - private OAuth2TokenService oauth2TokenService; - @MockBean - private MemberService memberService; - @MockBean - private Validator validator; - - @BeforeEach - public void setUp() { - authService.setCaptchaEnable(true); - // 注入一个 Validator 对象 - ReflectUtil.setFieldValue(authService, "validator", - Validation.buildDefaultValidatorFactory().getValidator()); - } - - @Test - public void testAuthenticate_success() { - // 准备参数 - String username = randomString(); - String password = randomString(); - // mock user 数据 - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) - .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(userService.getUserByUsername(eq(username))).thenReturn(user); - // mock password 匹配 - when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); - - // 调用 - AdminUserDO loginUser = authService.authenticate(username, password); - // 校验 - assertPojoEquals(user, loginUser); - } - - @Test - public void testAuthenticate_userNotFound() { - // 准备参数 - String username = randomString(); - String password = randomString(); - - // 调用, 并断言异常 - assertServiceException(() -> authService.authenticate(username, password), - AUTH_LOGIN_BAD_CREDENTIALS); - verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) - && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()) - && o.getUserId() == null) - ); - } - - @Test - public void testAuthenticate_badCredentials() { - // 准备参数 - String username = randomString(); - String password = randomString(); - // mock user 数据 - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) - .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(userService.getUserByUsername(eq(username))).thenReturn(user); - - // 调用, 并断言异常 - assertServiceException(() -> authService.authenticate(username, password), - AUTH_LOGIN_BAD_CREDENTIALS); - verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) - && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()) - && o.getUserId().equals(user.getId())) - ); - } - - @Test - public void testAuthenticate_userDisabled() { - // 准备参数 - String username = randomString(); - String password = randomString(); - // mock user 数据 - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) - .setPassword(password).setStatus(CommonStatusEnum.DISABLE.getStatus())); - when(userService.getUserByUsername(eq(username))).thenReturn(user); - // mock password 匹配 - when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); - - // 调用, 并断言异常 - assertServiceException(() -> authService.authenticate(username, password), - AUTH_LOGIN_USER_DISABLED); - verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) - && o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult()) - && o.getUserId().equals(user.getId())) - ); - } - - @Test - public void testLogin_success() { - // 准备参数 - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o -> - o.setUsername("test_username").setPassword("test_password") - .setSocialType(randomEle(SocialTypeEnum.values()).getType())); - - // mock 验证码正确 - authService.setCaptchaEnable(false); - // mock user 数据 - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username") - .setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(userService.getUserByUsername(eq("test_username"))).thenReturn(user); - // mock password 匹配 - when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true); - // mock 缓存登录用户到 Redis - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) - .thenReturn(accessTokenDO); - - // 调用,并校验 - AuthLoginRespVO loginRespVO = authService.login(reqVO); - assertPojoEquals(accessTokenDO, loginRespVO); - // 校验调用参数 - verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) - && o.getUserId().equals(user.getId())) - ); - verify(socialUserService).bindSocialUser(eq(new SocialUserBindReqDTO( - user.getId(), UserTypeEnum.ADMIN.getValue(), - reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState()))); - } - - @Test - public void testSendSmsCode() { - // 准备参数 - String mobile = randomString(); - Integer scene = SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(); - AuthSmsSendReqVO reqVO = new AuthSmsSendReqVO(mobile, scene); - // mock 方法(用户信息) - AdminUserDO user = randomPojo(AdminUserDO.class); - when(userService.getUserByMobile(eq(mobile))).thenReturn(user); - - // 调用 - authService.sendSmsCode(reqVO); - // 断言 - verify(smsCodeApi).sendSmsCode(argThat(sendReqDTO -> { - assertEquals(mobile, sendReqDTO.getMobile()); - assertEquals(scene, sendReqDTO.getScene()); - return true; - })); - } - - @Test - public void testSmsLogin_success() { - // 准备参数 - String mobile = randomString(); - String code = randomString(); - AuthSmsLoginReqVO reqVO = new AuthSmsLoginReqVO(mobile, code); - // mock 方法(验证码) - doNothing().when(smsCodeApi).useSmsCode((argThat(smsCodeUseReqDTO -> { - assertEquals(mobile, smsCodeUseReqDTO.getMobile()); - assertEquals(code, smsCodeUseReqDTO.getCode()); - assertEquals(SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), smsCodeUseReqDTO.getScene()); - return true; - }))); - // mock 方法(用户信息) - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L)); - when(userService.getUserByMobile(eq(mobile))).thenReturn(user); - // mock 缓存登录用户到 Redis - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) - .thenReturn(accessTokenDO); - - // 调用,并断言 - AuthLoginRespVO loginRespVO = authService.smsLogin(reqVO); - assertPojoEquals(accessTokenDO, loginRespVO); - // 断言调用 - verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_MOBILE.getType()) - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) - && o.getUserId().equals(user.getId())) - ); - } - - @Test - public void testSocialLogin_success() { - // 准备参数 - AuthSocialLoginReqVO reqVO = randomPojo(AuthSocialLoginReqVO.class); - // mock 方法(绑定的用户编号) - Long userId = 1L; - when(socialUserService.getSocialUserByCode(eq(UserTypeEnum.ADMIN.getValue()), eq(reqVO.getType()), - eq(reqVO.getCode()), eq(reqVO.getState()))).thenReturn(new SocialUserRespDTO(randomString(), randomString(), randomString(), userId)); - // mock(用户) - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId)); - when(userService.getUser(eq(userId))).thenReturn(user); - // mock 缓存登录用户到 Redis - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq("default"), isNull())) - .thenReturn(accessTokenDO); - - // 调用,并断言 - AuthLoginRespVO loginRespVO = authService.socialLogin(reqVO); - assertPojoEquals(accessTokenDO, loginRespVO); - // 断言调用 - verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_SOCIAL.getType()) - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) - && o.getUserId().equals(user.getId())) - ); - } - - @Test - public void testValidateCaptcha_successWithEnable() { - // 准备参数 - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); - - // mock 验证通过 - when(captchaService.verification(argThat(captchaVO -> { - assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification()); - return true; - }))).thenReturn(ResponseModel.success()); - - // 调用,无需断言 - authService.validateCaptcha(reqVO); - } - - @Test - public void testValidateCaptcha_successWithDisable() { - // 准备参数 - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); - - // mock 验证码关闭 - authService.setCaptchaEnable(false); - - // 调用,无需断言 - authService.validateCaptcha(reqVO); - } - - @Test - public void testCaptcha_fail() { - // 准备参数 - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); - - // mock 验证通过 - when(captchaService.verification(argThat(captchaVO -> { - assertEquals(reqVO.getCaptchaVerification(), captchaVO.getCaptchaVerification()); - return true; - }))).thenReturn(ResponseModel.errorMsg("就是不对")); - - // 调用, 并断言异常 - assertServiceException(() -> authService.validateCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR, "就是不对"); - // 校验调用参数 - verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) - && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())) - ); - } - - @Test - public void testRefreshToken() { - // 准备参数 - String refreshToken = randomString(); - // mock 方法 - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); - when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq("default"))) - .thenReturn(accessTokenDO); - - // 调用 - AuthLoginRespVO loginRespVO = authService.refreshToken(refreshToken); - // 断言 - assertPojoEquals(accessTokenDO, loginRespVO); - } - - @Test - public void testLogout_success() { - // 准备参数 - String token = randomString(); - // mock - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) - .setUserType(UserTypeEnum.ADMIN.getValue())); - when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO); - - // 调用 - authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); - // 校验调用参数 - verify(loginLogService).createLoginLog( - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType()) - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) - ); - // 调用,并校验 - - } - - @Test - public void testLogout_fail() { - // 准备参数 - String token = randomString(); - - // 调用 - authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); - // 校验调用参数 - verify(loginLogService, never()).createLoginLog(any()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java deleted file mode 100644 index bcf55bd..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImplTest.java +++ /dev/null @@ -1,296 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dept; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link DeptServiceImpl} 的单元测试类 - * - * @author niudehua - */ -@Import(DeptServiceImpl.class) -public class DeptServiceImplTest extends BaseDbUnitTest { - - @Resource - private DeptServiceImpl deptService; - @Resource - private DeptMapper deptMapper; - - @Test - public void testCreateDept() { - // 准备参数 - DeptSaveReqVO reqVO = randomPojo(DeptSaveReqVO.class, o -> { - o.setId(null); // 防止 id 被设置 - o.setParentId(DeptDO.PARENT_ID_ROOT); - o.setStatus(randomCommonStatus()); - }); - - // 调用 - Long deptId = deptService.createDept(reqVO); - // 断言 - assertNotNull(deptId); - // 校验记录的属性是否正确 - DeptDO deptDO = deptMapper.selectById(deptId); - assertPojoEquals(reqVO, deptDO, "id"); - } - - @Test - public void testUpdateDept() { - // mock 数据 - DeptDO dbDeptDO = randomPojo(DeptDO.class, o -> o.setStatus(randomCommonStatus())); - deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 - // 准备参数 - DeptSaveReqVO reqVO = randomPojo(DeptSaveReqVO.class, o -> { - // 设置更新的 ID - o.setParentId(DeptDO.PARENT_ID_ROOT); - o.setId(dbDeptDO.getId()); - o.setStatus(randomCommonStatus()); - }); - - // 调用 - deptService.updateDept(reqVO); - // 校验是否更新正确 - DeptDO deptDO = deptMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, deptDO); - } - - @Test - public void testDeleteDept_success() { - // mock 数据 - DeptDO dbDeptDO = randomPojo(DeptDO.class); - deptMapper.insert(dbDeptDO);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbDeptDO.getId(); - - // 调用 - deptService.deleteDept(id); - // 校验数据不存在了 - assertNull(deptMapper.selectById(id)); - } - - @Test - public void testDeleteDept_exitsChildren() { - // mock 数据 - DeptDO parentDept = randomPojo(DeptDO.class); - deptMapper.insert(parentDept);// @Sql: 先插入出一条存在的数据 - // 准备参数 - DeptDO childrenDeptDO = randomPojo(DeptDO.class, o -> { - o.setParentId(parentDept.getId()); - o.setStatus(randomCommonStatus()); - }); - // 插入子部门 - deptMapper.insert(childrenDeptDO); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.deleteDept(parentDept.getId()), DEPT_EXITS_CHILDREN); - } - - @Test - public void testValidateDeptExists_notFound() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.validateDeptExists(id), DEPT_NOT_FOUND); - } - - @Test - public void testValidateParentDept_parentError() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.validateParentDept(id, id), - DEPT_PARENT_ERROR); - } - - @Test - public void testValidateParentDept_parentIsChild() { - // mock 数据(父节点) - DeptDO parentDept = randomPojo(DeptDO.class); - deptMapper.insert(parentDept); - // mock 数据(子节点) - DeptDO childDept = randomPojo(DeptDO.class, o -> { - o.setParentId(parentDept.getId()); - }); - deptMapper.insert(childDept); - - // 准备参数 - Long id = parentDept.getId(); - Long parentId = childDept.getId(); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.validateParentDept(id, parentId), DEPT_PARENT_IS_CHILD); - } - - @Test - public void testValidateNameUnique_duplicate() { - // mock 数据 - DeptDO deptDO = randomPojo(DeptDO.class); - deptMapper.insert(deptDO); - - // 准备参数 - Long id = randomLongId(); - Long parentId = deptDO.getParentId(); - String name = deptDO.getName(); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.validateDeptNameUnique(id, parentId, name), - DEPT_NAME_DUPLICATE); - } - - @Test - public void testGetDept() { - // mock 数据 - DeptDO deptDO = randomPojo(DeptDO.class); - deptMapper.insert(deptDO); - // 准备参数 - Long id = deptDO.getId(); - - // 调用 - DeptDO dbDept = deptService.getDept(id); - // 断言 - assertEquals(deptDO, dbDept); - } - - @Test - public void testGetDeptList_ids() { - // mock 数据 - DeptDO deptDO01 = randomPojo(DeptDO.class); - deptMapper.insert(deptDO01); - DeptDO deptDO02 = randomPojo(DeptDO.class); - deptMapper.insert(deptDO02); - // 准备参数 - List ids = Arrays.asList(deptDO01.getId(), deptDO02.getId()); - - // 调用 - List deptDOList = deptService.getDeptList(ids); - // 断言 - assertEquals(2, deptDOList.size()); - assertEquals(deptDO01, deptDOList.get(0)); - assertEquals(deptDO02, deptDOList.get(1)); - } - - @Test - public void testGetDeptList_reqVO() { - // mock 数据 - DeptDO dept = randomPojo(DeptDO.class, o -> { // 等会查询到 - o.setName("开发部"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - deptMapper.insert(dept); - // 测试 name 不匹配 - deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setName("发"))); - // 测试 status 不匹配 - deptMapper.insert(ObjectUtils.cloneIgnoreId(dept, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 准备参数 - DeptListReqVO reqVO = new DeptListReqVO(); - reqVO.setName("开"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - List sysDeptDOS = deptService.getDeptList(reqVO); - // 断言 - assertEquals(1, sysDeptDOS.size()); - assertPojoEquals(dept, sysDeptDOS.get(0)); - } - - @Test - public void testGetChildDeptList() { - // mock 数据(1 级别子节点) - DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); - deptMapper.insert(dept1); - DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); - deptMapper.insert(dept2); - // mock 数据(2 级子节点) - DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); - deptMapper.insert(dept1a); - DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); - deptMapper.insert(dept2a); - // 准备参数 - Long id = dept1.getParentId(); - - // 调用 - List result = deptService.getChildDeptList(id); - // 断言 - assertEquals(result.size(), 2); - assertPojoEquals(dept1, result.get(0)); - assertPojoEquals(dept1a, result.get(1)); - } - - @Test - public void testGetChildDeptListFromCache() { - // mock 数据(1 级别子节点) - DeptDO dept1 = randomPojo(DeptDO.class, o -> o.setName("1")); - deptMapper.insert(dept1); - DeptDO dept2 = randomPojo(DeptDO.class, o -> o.setName("2")); - deptMapper.insert(dept2); - // mock 数据(2 级子节点) - DeptDO dept1a = randomPojo(DeptDO.class, o -> o.setName("1-a").setParentId(dept1.getId())); - deptMapper.insert(dept1a); - DeptDO dept2a = randomPojo(DeptDO.class, o -> o.setName("2-a").setParentId(dept2.getId())); - deptMapper.insert(dept2a); - // 准备参数 - Long id = dept1.getParentId(); - - // 调用 - Set result = deptService.getChildDeptIdListFromCache(id); - // 断言 - assertEquals(result.size(), 2); - assertTrue(result.contains(dept1.getId())); - assertTrue(result.contains(dept1a.getId())); - } - - @Test - public void testValidateDeptList_success() { - // mock 数据 - DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.ENABLE.getStatus()); - deptMapper.insert(deptDO); - // 准备参数 - List ids = singletonList(deptDO.getId()); - - // 调用,无需断言 - deptService.validateDeptList(ids); - } - - @Test - public void testValidateDeptList_notFound() { - // 准备参数 - List ids = singletonList(randomLongId()); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_FOUND); - } - - @Test - public void testValidateDeptList_notEnable() { - // mock 数据 - DeptDO deptDO = randomPojo(DeptDO.class).setStatus(CommonStatusEnum.DISABLE.getStatus()); - deptMapper.insert(deptDO); - // 准备参数 - List ids = singletonList(deptDO.getId()); - - // 调用, 并断言异常 - assertServiceException(() -> deptService.validateDeptList(ids), DEPT_NOT_ENABLE, deptDO.getName()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImplTest.java deleted file mode 100644 index 657c09d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/PostServiceImplTest.java +++ /dev/null @@ -1,248 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dept; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dept.vo.post.PostSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.Arrays; -import java.util.List; -import java.util.function.Consumer; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link PostServiceImpl} 的单元测试类 - * - * @author niudehua - */ -@Import(PostServiceImpl.class) -public class PostServiceImplTest extends BaseDbUnitTest { - - @Resource - private PostServiceImpl postService; - - @Resource - private PostMapper postMapper; - - @Test - public void testCreatePost_success() { - // 准备参数 - PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, - o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus())) - .setId(null); // 防止 id 被设置 - // 调用 - Long postId = postService.createPost(reqVO); - - // 断言 - assertNotNull(postId); - // 校验记录的属性是否正确 - PostDO post = postMapper.selectById(postId); - assertPojoEquals(reqVO, post, "id"); - } - - @Test - public void testUpdatePost_success() { - // mock 数据 - PostDO postDO = randomPostDO(); - postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据 - // 准备参数 - PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, o -> { - // 设置更新的 ID - o.setId(postDO.getId()); - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); - }); - - // 调用 - postService.updatePost(reqVO); - // 校验是否更新正确 - PostDO post = postMapper.selectById(reqVO.getId()); - assertPojoEquals(reqVO, post); - } - - @Test - public void testDeletePost_success() { - // mock 数据 - PostDO postDO = randomPostDO(); - postMapper.insert(postDO); - // 准备参数 - Long id = postDO.getId(); - - // 调用 - postService.deletePost(id); - assertNull(postMapper.selectById(id)); - } - - @Test - public void testValidatePost_notFoundForDelete() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> postService.deletePost(id), POST_NOT_FOUND); - } - - @Test - public void testValidatePost_nameDuplicateForCreate() { - // mock 数据 - PostDO postDO = randomPostDO(); - postMapper.insert(postDO);// @Sql: 先插入出一条存在的数据 - // 准备参数 - PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, - // 模拟 name 重复 - o -> o.setName(postDO.getName())); - assertServiceException(() -> postService.createPost(reqVO), POST_NAME_DUPLICATE); - } - - @Test - public void testValidatePost_codeDuplicateForUpdate() { - // mock 数据 - PostDO postDO = randomPostDO(); - postMapper.insert(postDO); - // mock 数据:稍后模拟重复它的 code - PostDO codePostDO = randomPostDO(); - postMapper.insert(codePostDO); - // 准备参数 - PostSaveReqVO reqVO = randomPojo(PostSaveReqVO.class, o -> { - // 设置更新的 ID - o.setId(postDO.getId()); - // 模拟 code 重复 - o.setCode(codePostDO.getCode()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> postService.updatePost(reqVO), POST_CODE_DUPLICATE); - } - - @Test - public void testGetPostPage() { - // mock 数据 - PostDO postDO = randomPojo(PostDO.class, o -> { - o.setName("码仔"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - postMapper.insert(postDO); - // 测试 name 不匹配 - postMapper.insert(cloneIgnoreId(postDO, o -> o.setName("程序员"))); - // 测试 status 不匹配 - postMapper.insert(cloneIgnoreId(postDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 准备参数 - PostPageReqVO reqVO = new PostPageReqVO(); - reqVO.setName("码"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - PageResult pageResult = postService.getPostPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(postDO, pageResult.getList().get(0)); - } - - @Test - public void testGetPostList() { - // mock 数据 - PostDO postDO01 = randomPojo(PostDO.class); - postMapper.insert(postDO01); - // 测试 id 不匹配 - PostDO postDO02 = randomPojo(PostDO.class); - postMapper.insert(postDO02); - // 准备参数 - List ids = singletonList(postDO01.getId()); - - // 调用 - List list = postService.getPostList(ids); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(postDO01, list.get(0)); - } - - @Test - public void testGetPostList_idsAndStatus() { - // mock 数据 - PostDO postDO01 = randomPojo(PostDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - postMapper.insert(postDO01); - // 测试 status 不匹配 - PostDO postDO02 = randomPojo(PostDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - postMapper.insert(postDO02); - // 准备参数 - List ids = Arrays.asList(postDO01.getId(), postDO02.getId()); - - // 调用 - List list = postService.getPostList(ids, singletonList(CommonStatusEnum.ENABLE.getStatus())); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(postDO01, list.get(0)); - } - - @Test - public void testGetPost() { - // mock 数据 - PostDO dbPostDO = randomPostDO(); - postMapper.insert(dbPostDO); - // 准备参数 - Long id = dbPostDO.getId(); - // 调用 - PostDO post = postService.getPost(id); - // 断言 - assertNotNull(post); - assertPojoEquals(dbPostDO, post); - } - - @Test - public void testValidatePostList_success() { - // mock 数据 - PostDO postDO = randomPostDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); - postMapper.insert(postDO); - // 准备参数 - List ids = singletonList(postDO.getId()); - - // 调用,无需断言 - postService.validatePostList(ids); - } - - @Test - public void testValidatePostList_notFound() { - // 准备参数 - List ids = singletonList(randomLongId()); - - // 调用, 并断言异常 - assertServiceException(() -> postService.validatePostList(ids), POST_NOT_FOUND); - } - - @Test - public void testValidatePostList_notEnable() { - // mock 数据 - PostDO postDO = randomPostDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); - postMapper.insert(postDO); - // 准备参数 - List ids = singletonList(postDO.getId()); - - // 调用, 并断言异常 - assertServiceException(() -> postService.validatePostList(ids), POST_NOT_ENABLE, - postDO.getName()); - } - - @SafeVarargs - private static PostDO randomPostDO(Consumer... consumers) { - Consumer consumer = (o) -> { - o.setStatus(randomCommonStatus()); // 保证 status 的范围 - }; - return randomPojo(PostDO.class, ArrayUtils.append(consumer, consumers)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java deleted file mode 100644 index 999570e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictDataServiceImplTest.java +++ /dev/null @@ -1,352 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dict; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.data.DictDataSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; -import cn.iocoder.yudao.module.system.dal.mysql.dict.DictDataMapper; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.List; -import java.util.function.Consumer; - -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -@Import(DictDataServiceImpl.class) -public class DictDataServiceImplTest extends BaseDbUnitTest { - - @Resource - private DictDataServiceImpl dictDataService; - - @Resource - private DictDataMapper dictDataMapper; - @MockBean - private DictTypeService dictTypeService; - - @Test - public void testGetDictDataList() { - // mock 数据 - DictDataDO dictDataDO01 = randomDictDataDO().setDictType("yunai").setSort(2) - .setStatus(CommonStatusEnum.ENABLE.getStatus()); - dictDataMapper.insert(dictDataDO01); - DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setSort(1) - .setStatus(CommonStatusEnum.ENABLE.getStatus()); - dictDataMapper.insert(dictDataDO02); - DictDataDO dictDataDO03 = randomDictDataDO().setDictType("yunai").setSort(3) - .setStatus(CommonStatusEnum.DISABLE.getStatus()); - dictDataMapper.insert(dictDataDO03); - DictDataDO dictDataDO04 = randomDictDataDO().setDictType("yunai2").setSort(3) - .setStatus(CommonStatusEnum.DISABLE.getStatus()); - dictDataMapper.insert(dictDataDO04); - // 准备参数 - Integer status = CommonStatusEnum.ENABLE.getStatus(); - String dictType = "yunai"; - - // 调用 - List dictDataDOList = dictDataService.getDictDataList(status, dictType); - // 断言 - assertEquals(2, dictDataDOList.size()); - assertPojoEquals(dictDataDO02, dictDataDOList.get(0)); - assertPojoEquals(dictDataDO01, dictDataDOList.get(1)); - } - - @Test - public void testGetDictDataPage() { - // mock 数据 - DictDataDO dbDictData = randomPojo(DictDataDO.class, o -> { // 等会查询到 - o.setLabel("芋艿"); - o.setDictType("yunai"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - dictDataMapper.insert(dbDictData); - // 测试 label 不匹配 - dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setLabel("艿"))); - // 测试 dictType 不匹配 - dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setDictType("nai"))); - // 测试 status 不匹配 - dictDataMapper.insert(cloneIgnoreId(dbDictData, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 准备参数 - DictDataPageReqVO reqVO = new DictDataPageReqVO(); - reqVO.setLabel("芋"); - reqVO.setDictType("yunai"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - PageResult pageResult = dictDataService.getDictDataPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbDictData, pageResult.getList().get(0)); - } - - @Test - public void testGetDictData() { - // mock 数据 - DictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData); - // 准备参数 - Long id = dbDictData.getId(); - - // 调用 - DictDataDO dictData = dictDataService.getDictData(id); - // 断言 - assertPojoEquals(dbDictData, dictData); - } - - @Test - public void testCreateDictData_success() { - // 准备参数 - DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, - o -> o.setStatus(randomCommonStatus())) - .setId(null); // 防止 id 被赋值 - // mock 方法 - when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); - - // 调用 - Long dictDataId = dictDataService.createDictData(reqVO); - // 断言 - assertNotNull(dictDataId); - // 校验记录的属性是否正确 - DictDataDO dictData = dictDataMapper.selectById(dictDataId); - assertPojoEquals(reqVO, dictData, "id"); - } - - @Test - public void testUpdateDictData_success() { - // mock 数据 - DictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 - // 准备参数 - DictDataSaveReqVO reqVO = randomPojo(DictDataSaveReqVO.class, o -> { - o.setId(dbDictData.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - }); - // mock 方法,字典类型 - when(dictTypeService.getDictType(eq(reqVO.getDictType()))).thenReturn(randomDictTypeDO(reqVO.getDictType())); - - // 调用 - dictDataService.updateDictData(reqVO); - // 校验是否更新正确 - DictDataDO dictData = dictDataMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, dictData); - } - - @Test - public void testDeleteDictData_success() { - // mock 数据 - DictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbDictData.getId(); - - // 调用 - dictDataService.deleteDictData(id); - // 校验数据不存在了 - assertNull(dictDataMapper.selectById(id)); - } - - @Test - public void testValidateDictDataExists_success() { - // mock 数据 - DictDataDO dbDictData = randomDictDataDO(); - dictDataMapper.insert(dbDictData);// @Sql: 先插入出一条存在的数据 - - // 调用成功 - dictDataService.validateDictDataExists(dbDictData.getId()); - } - - @Test - public void testValidateDictDataExists_notExists() { - assertServiceException(() -> dictDataService.validateDictDataExists(randomLongId()), DICT_DATA_NOT_EXISTS); - } - - @Test - public void testValidateDictTypeExists_success() { - // mock 方法,数据类型被禁用 - String type = randomString(); - when(dictTypeService.getDictType(eq(type))).thenReturn(randomDictTypeDO(type)); - - // 调用, 成功 - dictDataService.validateDictTypeExists(type); - } - - @Test - public void testValidateDictTypeExists_notExists() { - assertServiceException(() -> dictDataService.validateDictTypeExists(randomString()), DICT_TYPE_NOT_EXISTS); - } - - @Test - public void testValidateDictTypeExists_notEnable() { - // mock 方法,数据类型被禁用 - String dictType = randomString(); - when(dictTypeService.getDictType(eq(dictType))).thenReturn( - randomPojo(DictTypeDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.validateDictTypeExists(dictType), DICT_TYPE_NOT_ENABLE); - } - - @Test - public void testValidateDictDataValueUnique_success() { - // 调用,成功 - dictDataService.validateDictDataValueUnique(randomLongId(), randomString(), randomString()); - } - - @Test - public void testValidateDictDataValueUnique_valueDuplicateForCreate() { - // 准备参数 - String dictType = randomString(); - String value = randomString(); - // mock 数据 - dictDataMapper.insert(randomDictDataDO(o -> { - o.setDictType(dictType); - o.setValue(value); - })); - - // 调用,校验异常 - assertServiceException(() -> dictDataService.validateDictDataValueUnique(null, dictType, value), - DICT_DATA_VALUE_DUPLICATE); - } - - @Test - public void testValidateDictDataValueUnique_valueDuplicateForUpdate() { - // 准备参数 - Long id = randomLongId(); - String dictType = randomString(); - String value = randomString(); - // mock 数据 - dictDataMapper.insert(randomDictDataDO(o -> { - o.setDictType(dictType); - o.setValue(value); - })); - - // 调用,校验异常 - assertServiceException(() -> dictDataService.validateDictDataValueUnique(id, dictType, value), - DICT_DATA_VALUE_DUPLICATE); - } - - @Test - public void testGetDictDataCountByDictType() { - // mock 数据 - dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai"))); - dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("tudou"))); - dictDataMapper.insert(randomDictDataDO(o -> o.setDictType("yunai"))); - // 准备参数 - String dictType = "yunai"; - - // 调用 - long count = dictDataService.getDictDataCountByDictType(dictType); - // 校验 - assertEquals(2L, count); - } - - @Test - public void testValidateDictDataList_success() { - // mock 数据 - DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); - dictDataMapper.insert(dictDataDO); - // 准备参数 - String dictType = dictDataDO.getDictType(); - List values = singletonList(dictDataDO.getValue()); - - // 调用,无需断言 - dictDataService.validateDictDataList(dictType, values); - } - - @Test - public void testValidateDictDataList_notFound() { - // 准备参数 - String dictType = randomString(); - List values = singletonList(randomString()); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), DICT_DATA_NOT_EXISTS); - } - - @Test - public void testValidateDictDataList_notEnable() { - // mock 数据 - DictDataDO dictDataDO = randomDictDataDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); - dictDataMapper.insert(dictDataDO); - // 准备参数 - String dictType = dictDataDO.getDictType(); - List values = singletonList(dictDataDO.getValue()); - - // 调用, 并断言异常 - assertServiceException(() -> dictDataService.validateDictDataList(dictType, values), - DICT_DATA_NOT_ENABLE, dictDataDO.getLabel()); - } - - @Test - public void testGetDictData_dictType() { - // mock 数据 - DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setValue("1"); - dictDataMapper.insert(dictDataDO); - DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setValue("2"); - dictDataMapper.insert(dictDataDO02); - // 准备参数 - String dictType = "yunai"; - String value = "1"; - - // 调用 - DictDataDO dbDictData = dictDataService.getDictData(dictType, value); - // 断言 - assertEquals(dictDataDO, dbDictData); - } - - @Test - public void testParseDictData() { - // mock 数据 - DictDataDO dictDataDO = randomDictDataDO().setDictType("yunai").setLabel("1"); - dictDataMapper.insert(dictDataDO); - DictDataDO dictDataDO02 = randomDictDataDO().setDictType("yunai").setLabel("2"); - dictDataMapper.insert(dictDataDO02); - // 准备参数 - String dictType = "yunai"; - String label = "1"; - - // 调用 - DictDataDO dbDictData = dictDataService.parseDictData(dictType, label); - // 断言 - assertEquals(dictDataDO, dbDictData); - } - - // ========== 随机对象 ========== - - @SafeVarargs - private static DictDataDO randomDictDataDO(Consumer... consumers) { - Consumer consumer = (o) -> { - o.setStatus(randomCommonStatus()); // 保证 status 的范围 - }; - return randomPojo(DictDataDO.class, ArrayUtils.append(consumer, consumers)); - } - - /** - * 生成一个有效的字典类型 - * - * @param type 字典类型 - * @return DictTypeDO 对象 - */ - private static DictTypeDO randomDictTypeDO(String type) { - return randomPojo(DictTypeDO.class, o -> { - o.setType(type); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 是开启 - }); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImplTest.java deleted file mode 100644 index e04b96f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dict/DictTypeServiceImplTest.java +++ /dev/null @@ -1,271 +0,0 @@ -package cn.iocoder.yudao.module.system.service.dict; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.dict.vo.type.DictTypeSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictTypeDO; -import cn.iocoder.yudao.module.system.dal.mysql.dict.DictTypeMapper; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.List; -import java.util.function.Consumer; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -@Import(DictTypeServiceImpl.class) -public class DictTypeServiceImplTest extends BaseDbUnitTest { - - @Resource - private DictTypeServiceImpl dictTypeService; - - @Resource - private DictTypeMapper dictTypeMapper; - @MockBean - private DictDataService dictDataService; - - @Test - public void testGetDictTypePage() { - // mock 数据 - DictTypeDO dbDictType = randomPojo(DictTypeDO.class, o -> { // 等会查询到 - o.setName("yunai"); - o.setType("芋艿"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2021, 1, 15)); - }); - dictTypeMapper.insert(dbDictType); - // 测试 name 不匹配 - dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setName("tudou"))); - // 测试 type 不匹配 - dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setType("土豆"))); - // 测试 status 不匹配 - dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - dictTypeMapper.insert(cloneIgnoreId(dbDictType, o -> o.setCreateTime(buildTime(2021, 1, 1)))); - // 准备参数 - DictTypePageReqVO reqVO = new DictTypePageReqVO(); - reqVO.setName("nai"); - reqVO.setType("艿"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2021, 1, 10, 2021, 1, 20)); - - // 调用 - PageResult pageResult = dictTypeService.getDictTypePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbDictType, pageResult.getList().get(0)); - } - - @Test - public void testGetDictType_id() { - // mock 数据 - DictTypeDO dbDictType = randomDictTypeDO(); - dictTypeMapper.insert(dbDictType); - // 准备参数 - Long id = dbDictType.getId(); - - // 调用 - DictTypeDO dictType = dictTypeService.getDictType(id); - // 断言 - assertNotNull(dictType); - assertPojoEquals(dbDictType, dictType); - } - - @Test - public void testGetDictType_type() { - // mock 数据 - DictTypeDO dbDictType = randomDictTypeDO(); - dictTypeMapper.insert(dbDictType); - // 准备参数 - String type = dbDictType.getType(); - - // 调用 - DictTypeDO dictType = dictTypeService.getDictType(type); - // 断言 - assertNotNull(dictType); - assertPojoEquals(dbDictType, dictType); - } - - @Test - public void testCreateDictType_success() { - // 准备参数 - DictTypeSaveReqVO reqVO = randomPojo(DictTypeSaveReqVO.class, - o -> o.setStatus(randomEle(CommonStatusEnum.values()).getStatus())) - .setId(null); // 避免 id 被赋值 - - // 调用 - Long dictTypeId = dictTypeService.createDictType(reqVO); - // 断言 - assertNotNull(dictTypeId); - // 校验记录的属性是否正确 - DictTypeDO dictType = dictTypeMapper.selectById(dictTypeId); - assertPojoEquals(reqVO, dictType, "id"); - } - - @Test - public void testUpdateDictType_success() { - // mock 数据 - DictTypeDO dbDictType = randomDictTypeDO(); - dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 - // 准备参数 - DictTypeSaveReqVO reqVO = randomPojo(DictTypeSaveReqVO.class, o -> { - o.setId(dbDictType.getId()); // 设置更新的 ID - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); - }); - - // 调用 - dictTypeService.updateDictType(reqVO); - // 校验是否更新正确 - DictTypeDO dictType = dictTypeMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, dictType); - } - - @Test - public void testDeleteDictType_success() { - // mock 数据 - DictTypeDO dbDictType = randomDictTypeDO(); - dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbDictType.getId(); - - // 调用 - dictTypeService.deleteDictType(id); - // 校验数据不存在了 - assertNull(dictTypeMapper.selectById(id)); - } - - @Test - public void testDeleteDictType_hasChildren() { - // mock 数据 - DictTypeDO dbDictType = randomDictTypeDO(); - dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbDictType.getId(); - // mock 方法 - when(dictDataService.getDictDataCountByDictType(eq(dbDictType.getType()))).thenReturn(1L); - - // 调用, 并断言异常 - assertServiceException(() -> dictTypeService.deleteDictType(id), DICT_TYPE_HAS_CHILDREN); - } - - @Test - public void testGetDictTypeList() { - // 准备参数 - DictTypeDO dictTypeDO01 = randomDictTypeDO(); - dictTypeMapper.insert(dictTypeDO01); - DictTypeDO dictTypeDO02 = randomDictTypeDO(); - dictTypeMapper.insert(dictTypeDO02); - // mock 方法 - - // 调用 - List dictTypeDOList = dictTypeService.getDictTypeList(); - // 断言 - assertEquals(2, dictTypeDOList.size()); - assertPojoEquals(dictTypeDO01, dictTypeDOList.get(0)); - assertPojoEquals(dictTypeDO02, dictTypeDOList.get(1)); - } - - @Test - public void testValidateDictDataExists_success() { - // mock 数据 - DictTypeDO dbDictType = randomDictTypeDO(); - dictTypeMapper.insert(dbDictType);// @Sql: 先插入出一条存在的数据 - - // 调用成功 - dictTypeService.validateDictTypeExists(dbDictType.getId()); - } - - @Test - public void testValidateDictDataExists_notExists() { - assertServiceException(() -> dictTypeService.validateDictTypeExists(randomLongId()), DICT_TYPE_NOT_EXISTS); - } - - @Test - public void testValidateDictTypeUnique_success() { - // 调用,成功 - dictTypeService.validateDictTypeUnique(randomLongId(), randomString()); - } - - @Test - public void testValidateDictTypeUnique_valueDuplicateForCreate() { - // 准备参数 - String type = randomString(); - // mock 数据 - dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type))); - - // 调用,校验异常 - assertServiceException(() -> dictTypeService.validateDictTypeUnique(null, type), - DICT_TYPE_TYPE_DUPLICATE); - } - - @Test - public void testValidateDictTypeUnique_valueDuplicateForUpdate() { - // 准备参数 - Long id = randomLongId(); - String type = randomString(); - // mock 数据 - dictTypeMapper.insert(randomDictTypeDO(o -> o.setType(type))); - - // 调用,校验异常 - assertServiceException(() -> dictTypeService.validateDictTypeUnique(id, type), - DICT_TYPE_TYPE_DUPLICATE); - } - - @Test - public void testValidateDictTypNameUnique_success() { - // 调用,成功 - dictTypeService.validateDictTypeNameUnique(randomLongId(), randomString()); - } - - @Test - public void testValidateDictTypeNameUnique_nameDuplicateForCreate() { - // 准备参数 - String name = randomString(); - // mock 数据 - dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); - - // 调用,校验异常 - assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(null, name), - DICT_TYPE_NAME_DUPLICATE); - } - - @Test - public void testValidateDictTypeNameUnique_nameDuplicateForUpdate() { - // 准备参数 - Long id = randomLongId(); - String name = randomString(); - // mock 数据 - dictTypeMapper.insert(randomDictTypeDO(o -> o.setName(name))); - - // 调用,校验异常 - assertServiceException(() -> dictTypeService.validateDictTypeNameUnique(id, name), - DICT_TYPE_NAME_DUPLICATE); - } - - // ========== 随机对象 ========== - - @SafeVarargs - private static DictTypeDO randomDictTypeDO(Consumer... consumers) { - Consumer consumer = (o) -> { - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - }; - return randomPojo(DictTypeDO.class, ArrayUtils.append(consumer, consumers)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImplTest.java deleted file mode 100644 index f5135da..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/LoginLogServiceImplTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package cn.iocoder.yudao.module.system.service.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.loginlog.LoginLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO; -import cn.iocoder.yudao.module.system.dal.mysql.logger.LoginLogMapper; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; - -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.CAPTCHA_CODE_ERROR; -import static cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum.SUCCESS; -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Import(LoginLogServiceImpl.class) -public class LoginLogServiceImplTest extends BaseDbUnitTest { - - @Resource - private LoginLogServiceImpl loginLogService; - - @Resource - private LoginLogMapper loginLogMapper; - - @Test - public void testGetLoginLogPage() { - // mock 数据 - LoginLogDO loginLogDO = randomPojo(LoginLogDO.class, o -> { - o.setUserIp("192.168.199.16"); - o.setUsername("wang"); - o.setResult(SUCCESS.getResult()); - o.setCreateTime(buildTime(2021, 3, 6)); - }); - loginLogMapper.insert(loginLogDO); - // 测试 status 不匹配 - loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setResult(CAPTCHA_CODE_ERROR.getResult()))); - // 测试 ip 不匹配 - loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setUserIp("192.168.128.18"))); - // 测试 username 不匹配 - loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setUsername("yunai"))); - // 测试 createTime 不匹配 - loginLogMapper.insert(cloneIgnoreId(loginLogDO, o -> o.setCreateTime(buildTime(2021, 2, 6)))); - // 构造调用参数 - LoginLogPageReqVO reqVO = new LoginLogPageReqVO(); - reqVO.setUsername("wang"); - reqVO.setUserIp("192.168.199"); - reqVO.setStatus(true); - reqVO.setCreateTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7)); - - // 调用 - PageResult pageResult = loginLogService.getLoginLogPage(reqVO); - // 断言,只查到了一条符合条件的 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(loginLogDO, pageResult.getList().get(0)); - } - - @Test - public void testCreateLoginLog() { - LoginLogCreateReqDTO reqDTO = randomPojo(LoginLogCreateReqDTO.class); - - // 调用 - loginLogService.createLoginLog(reqDTO); - // 断言 - LoginLogDO loginLogDO = loginLogMapper.selectOne(null); - assertPojoEquals(reqDTO, loginLogDO); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java deleted file mode 100644 index 6951b69..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/logger/OperateLogServiceImplTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package cn.iocoder.yudao.module.system.service.logger; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.framework.test.core.util.RandomUtils; -import cn.iocoder.yudao.framework.common.biz.system.logger.dto.OperateLogCreateReqDTO; -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogPageReqDTO; -import cn.iocoder.yudao.module.system.controller.admin.logger.vo.operatelog.OperateLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.logger.OperateLogDO; -import cn.iocoder.yudao.module.system.dal.mysql.logger.OperateLogMapper; -import jakarta.annotation.Resource; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Import({OperateLogServiceImpl.class}) -public class OperateLogServiceImplTest extends BaseDbUnitTest { - - @Resource - private OperateLogService operateLogServiceImpl; - - @Resource - private OperateLogMapper operateLogMapper; - - @Test - public void testCreateOperateLog() { - OperateLogCreateReqDTO reqVO = RandomUtils.randomPojo(OperateLogCreateReqDTO.class); - - // 调研 - operateLogServiceImpl.createOperateLog(reqVO); - // 断言 - OperateLogDO operateLogDO = operateLogMapper.selectOne(null); - assertPojoEquals(reqVO, operateLogDO); - } - - @Test - public void testGetOperateLogPage_vo() { - // 构造操作日志 - OperateLogDO operateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> { - o.setUserId(2048L); - o.setBizId(999L); - o.setType("订单"); - o.setSubType("创建订单"); - o.setAction("修改编号为 1 的用户信息"); - o.setCreateTime(buildTime(2021, 3, 6)); - }); - operateLogMapper.insert(operateLogDO); - // 测试 userId 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(1024L))); - // 测试 bizId 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setBizId(888L))); - // 测试 type 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType("退款"))); - // 测试 subType 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setSubType("创建退款"))); - // 测试 action 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setAction("修改编号为 1 退款信息"))); - // 测试 createTime 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setCreateTime(buildTime(2021, 2, 6)))); - - // 构造调用参数 - OperateLogPageReqVO reqVO = new OperateLogPageReqVO(); - reqVO.setUserId(2048L); - reqVO.setBizId(999L); - reqVO.setType("订"); - reqVO.setSubType("订单"); - reqVO.setAction("用户信息"); - reqVO.setCreateTime(buildBetweenTime(2021, 3, 5, 2021, 3, 7)); - - // 调用 - PageResult pageResult = operateLogServiceImpl.getOperateLogPage(reqVO); - // 断言,只查到了一条符合条件的 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(operateLogDO, pageResult.getList().get(0)); - } - - @Test - public void testGetOperateLogPage_dto() { - // 构造操作日志 - OperateLogDO operateLogDO = RandomUtils.randomPojo(OperateLogDO.class, o -> { - o.setUserId(2048L); - o.setBizId(999L); - o.setType("订单"); - }); - operateLogMapper.insert(operateLogDO); - // 测试 userId 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setUserId(1024L))); - // 测试 bizId 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setBizId(888L))); - // 测试 type 不匹配 - operateLogMapper.insert(cloneIgnoreId(operateLogDO, o -> o.setType("退款"))); - - // 构造调用参数 - OperateLogPageReqDTO reqDTO = new OperateLogPageReqDTO(); - reqDTO.setUserId(2048L); - reqDTO.setBizId(999L); - reqDTO.setType("订单"); - - // 调用 - PageResult pageResult = operateLogServiceImpl.getOperateLogPage(reqDTO); - // 断言,只查到了一条符合条件的 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(operateLogDO, pageResult.getList().get(0)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java deleted file mode 100755 index a3f237e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java +++ /dev/null @@ -1,179 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; -import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -/** - * {@link MailAccountServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(MailAccountServiceImpl.class) -public class MailAccountServiceImplTest extends BaseDbUnitTest { - - @Resource - private MailAccountServiceImpl mailAccountService; - - @Resource - private MailAccountMapper mailAccountMapper; - - @MockBean - private MailTemplateService mailTemplateService; - - @Test - public void testCreateMailAccount_success() { - // 准备参数 - MailAccountSaveReqVO reqVO = randomPojo(MailAccountSaveReqVO.class, o -> o.setMail(randomEmail())) - .setId(null); // 防止 id 被赋值 - - // 调用 - Long mailAccountId = mailAccountService.createMailAccount(reqVO); - // 断言 - assertNotNull(mailAccountId); - // 校验记录的属性是否正确 - MailAccountDO mailAccount = mailAccountMapper.selectById(mailAccountId); - assertPojoEquals(reqVO, mailAccount, "id"); - } - - @Test - public void testUpdateMailAccount_success() { - // mock 数据 - MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 - // 准备参数 - MailAccountSaveReqVO reqVO = randomPojo(MailAccountSaveReqVO.class, o -> { - o.setId(dbMailAccount.getId()); // 设置更新的 ID - o.setMail(randomEmail()); - }); - - // 调用 - mailAccountService.updateMailAccount(reqVO); - // 校验是否更新正确 - MailAccountDO mailAccount = mailAccountMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, mailAccount); - } - - @Test - public void testUpdateMailAccount_notExists() { - // 准备参数 - MailAccountSaveReqVO reqVO = randomPojo(MailAccountSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> mailAccountService.updateMailAccount(reqVO), MAIL_ACCOUNT_NOT_EXISTS); - } - - @Test - public void testDeleteMailAccount_success() { - // mock 数据 - MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbMailAccount.getId(); - // mock 方法(无关联模版) - when(mailTemplateService.getMailTemplateCountByAccountId(eq(id))).thenReturn(0L); - - // 调用 - mailAccountService.deleteMailAccount(id); - // 校验数据不存在了 - assertNull(mailAccountMapper.selectById(id)); - } - - @Test - public void testGetMailAccountFromCache() { - // mock 数据 - MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbMailAccount.getId(); - - // 调用 - MailAccountDO mailAccount = mailAccountService.getMailAccountFromCache(id); - // 断言 - assertPojoEquals(dbMailAccount, mailAccount); - } - - @Test - public void testDeleteMailAccount_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> mailAccountService.deleteMailAccount(id), MAIL_ACCOUNT_NOT_EXISTS); - } - - @Test - public void testGetMailAccountPage() { - // mock 数据 - MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到 - o.setMail("768@qq.com"); - o.setUsername("yunai"); - }); - mailAccountMapper.insert(dbMailAccount); - // 测试 mail 不匹配 - mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com"))); - // 测试 username 不匹配 - mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou"))); - // 准备参数 - MailAccountPageReqVO reqVO = new MailAccountPageReqVO(); - reqVO.setMail("768"); - reqVO.setUsername("yu"); - - // 调用 - PageResult pageResult = mailAccountService.getMailAccountPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbMailAccount, pageResult.getList().get(0)); - } - - @Test - public void testGetMailAccount() { - // mock 数据 - MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbMailAccount.getId(); - - // 调用 - MailAccountDO mailAccount = mailAccountService.getMailAccount(id); - // 断言 - assertPojoEquals(dbMailAccount, mailAccount); - } - - @Test - public void testGetMailAccountList() { - // mock 数据 - MailAccountDO dbMailAccount01 = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(dbMailAccount01); - MailAccountDO dbMailAccount02 = randomPojo(MailAccountDO.class); - mailAccountMapper.insert(dbMailAccount02); - // 准备参数 - - // 调用 - List list = mailAccountService.getMailAccountList(); - // 断言 - assertEquals(2, list.size()); - assertPojoEquals(dbMailAccount01, list.get(0)); - assertPojoEquals(dbMailAccount02, list.get(1)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java deleted file mode 100755 index 482aa5a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java +++ /dev/null @@ -1,183 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper; -import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.Map; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link MailLogServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(MailLogServiceImpl.class) -public class MailLogServiceImplTest extends BaseDbUnitTest { - - @Resource - private MailLogServiceImpl mailLogService; - - @Resource - private MailLogMapper mailLogMapper; - - @Test - public void testCreateMailLog() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String toMail = randomEmail(); - MailAccountDO account = randomPojo(MailAccountDO.class); - MailTemplateDO template = randomPojo(MailTemplateDO.class); - String templateContent = randomString(); - Map templateParams = randomTemplateParams(); - Boolean isSend = true; - // mock 方法 - - // 调用 - Long logId = mailLogService.createMailLog(userId, userType, toMail, account, template, templateContent, templateParams, isSend); - // 断言 - MailLogDO log = mailLogMapper.selectById(logId); - assertNotNull(log); - assertEquals(MailSendStatusEnum.INIT.getStatus(), log.getSendStatus()); - assertEquals(userId, log.getUserId()); - assertEquals(userType, log.getUserType()); - assertEquals(toMail, log.getToMail()); - assertEquals(account.getId(), log.getAccountId()); - assertEquals(account.getMail(), log.getFromMail()); - assertEquals(template.getId(), log.getTemplateId()); - assertEquals(template.getCode(), log.getTemplateCode()); - assertEquals(template.getNickname(), log.getTemplateNickname()); - assertEquals(template.getTitle(), log.getTemplateTitle()); - assertEquals(templateContent, log.getTemplateContent()); - assertEquals(templateParams, log.getTemplateParams()); - } - - @Test - public void testUpdateMailSendResult_success() { - // mock 数据 - MailLogDO log = randomPojo(MailLogDO.class, o -> { - o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); - o.setSendTime(null).setSendMessageId(null).setSendException(null) - .setTemplateParams(randomTemplateParams()); - }); - mailLogMapper.insert(log); - // 准备参数 - Long logId = log.getId(); - String messageId = randomString(); - - // 调用 - mailLogService.updateMailSendResult(logId, messageId, null); - // 断言 - MailLogDO dbLog = mailLogMapper.selectById(logId); - assertEquals(MailSendStatusEnum.SUCCESS.getStatus(), dbLog.getSendStatus()); - assertNotNull(dbLog.getSendTime()); - assertEquals(messageId, dbLog.getSendMessageId()); - assertNull(dbLog.getSendException()); - } - - @Test - public void testUpdateMailSendResult_exception() { - // mock 数据 - MailLogDO log = randomPojo(MailLogDO.class, o -> { - o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); - o.setSendTime(null).setSendMessageId(null).setSendException(null) - .setTemplateParams(randomTemplateParams()); - }); - mailLogMapper.insert(log); - // 准备参数 - Long logId = log.getId(); - Exception exception = new NullPointerException("测试异常"); - - // 调用 - mailLogService.updateMailSendResult(logId, null, exception); - // 断言 - MailLogDO dbLog = mailLogMapper.selectById(logId); - assertEquals(MailSendStatusEnum.FAILURE.getStatus(), dbLog.getSendStatus()); - assertNotNull(dbLog.getSendTime()); - assertNull(dbLog.getSendMessageId()); - assertEquals("NullPointerException: 测试异常", dbLog.getSendException()); - } - - @Test - public void testGetMailLog() { - // mock 数据 - MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> o.setTemplateParams(randomTemplateParams())); - mailLogMapper.insert(dbMailLog); - // 准备参数 - Long id = dbMailLog.getId(); - - // 调用 - MailLogDO mailLog = mailLogService.getMailLog(id); - // 断言 - assertPojoEquals(dbMailLog, mailLog); - } - - @Test - public void testGetMailLogPage() { - // mock 数据 - MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> { // 等会查询到 - o.setUserId(1L); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setToMail("768@qq.com"); - o.setAccountId(10L); - o.setTemplateId(100L); - o.setSendStatus(MailSendStatusEnum.INIT.getStatus()); - o.setSendTime(buildTime(2023, 2, 10)); - o.setTemplateParams(randomTemplateParams()); - }); - mailLogMapper.insert(dbMailLog); - // 测试 userId 不匹配 - mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserId(2L))); - // 测试 userType 不匹配 - mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 toMail 不匹配 - mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setToMail("788@.qq.com"))); - // 测试 accountId 不匹配 - mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setAccountId(11L))); - // 测试 templateId 不匹配 - mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setTemplateId(101L))); - // 测试 sendStatus 不匹配 - mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setSendStatus(MailSendStatusEnum.SUCCESS.getStatus()))); - // 测试 sendTime 不匹配 - mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setSendTime(buildTime(2023, 3, 10)))); - // 准备参数 - MailLogPageReqVO reqVO = new MailLogPageReqVO(); - reqVO.setUserId(1L); - reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); - reqVO.setToMail("768"); - reqVO.setAccountId(10L); - reqVO.setTemplateId(100L); - reqVO.setSendStatus(MailSendStatusEnum.INIT.getStatus()); - reqVO.setSendTime((buildBetweenTime(2023, 2, 1, 2023, 2, 15))); - - // 调用 - PageResult pageResult = mailLogService.getMailLogPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbMailLog, pageResult.getList().get(0)); - } - - private static Map randomTemplateParams() { - return MapUtil.builder().put(randomString(), randomString()) - .put(randomString(), randomString()).build(); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java deleted file mode 100644 index 1ef1e12..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java +++ /dev/null @@ -1,329 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.framework.test.core.util.RandomUtils; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage; -import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer; -import cn.iocoder.yudao.module.system.service.member.MemberService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import org.assertj.core.util.Lists; -import org.dromara.hutool.extra.mail.*; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockedStatic; - -import java.util.HashMap; -import java.util.Map; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -public class MailSendServiceImplTest extends BaseMockitoUnitTest { - - @InjectMocks - private MailSendServiceImpl mailSendService; - - @Mock - private AdminUserService adminUserService; - @Mock - private MemberService memberService; - @Mock - private MailAccountService mailAccountService; - @Mock - private MailTemplateService mailTemplateService; - @Mock - private MailLogService mailLogService; - @Mock - private MailProducer mailProducer; - - /** - * 用于快速测试你的邮箱账号是否正常 - */ - @Test - @Disabled - public void testDemo() { - MailAccount mailAccount = new MailAccount() -// .setFrom("奥特曼 ") - .setFrom("ydym_test@163.com") // 邮箱地址 - .setHost("smtp.163.com").setPort(465).setSslEnable(true) // SMTP 服务器 - .setAuth(true).setUser("ydym_test@163.com").setPass("WBZTEINMIFVRYSOE".toCharArray()); // 登录账号密码 - String messageId = MailUtil.send(mailAccount, "7685413@qq.com", "主题", "内容", false); - System.out.println("发送结果:" + messageId); - } - - @Test - public void testSendSingleMailToAdmin() { - // 准备参数 - Long userId = randomLongId(); - String templateCode = RandomUtils.randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock adminUserService 的方法 - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setMobile("15601691300")); - when(adminUserService.getUser(eq(userId))).thenReturn(user); - - // mock MailTemplateService 的方法 - MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String title = RandomUtils.randomString(); - when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) - .thenReturn(title); - String content = RandomUtils.randomString(); - when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock MailAccountService 的方法 - MailAccountDO account = randomPojo(MailAccountDO.class); - when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); - // mock MailLogService 的方法 - Long mailLogId = randomLongId(); - when(mailLogService.createMailLog(eq(userId), eq(UserTypeEnum.ADMIN.getValue()), eq(user.getEmail()), - eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); - - // 调用 - Long resultMailLogId = mailSendService.sendSingleMailToAdmin(null, userId, templateCode, templateParams); - // 断言 - assertEquals(mailLogId, resultMailLogId); - // 断言调用 - verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(user.getEmail()), - eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); - } - - @Test - public void testSendSingleMailToMember() { - // 准备参数 - Long userId = randomLongId(); - String templateCode = RandomUtils.randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock memberService 的方法 - String mail = randomEmail(); - when(memberService.getMemberUserEmail(eq(userId))).thenReturn(mail); - - // mock MailTemplateService 的方法 - MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String title = RandomUtils.randomString(); - when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) - .thenReturn(title); - String content = RandomUtils.randomString(); - when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock MailAccountService 的方法 - MailAccountDO account = randomPojo(MailAccountDO.class); - when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); - // mock MailLogService 的方法 - Long mailLogId = randomLongId(); - when(mailLogService.createMailLog(eq(userId), eq(UserTypeEnum.MEMBER.getValue()), eq(mail), - eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); - - // 调用 - Long resultMailLogId = mailSendService.sendSingleMailToMember(null, userId, templateCode, templateParams); - // 断言 - assertEquals(mailLogId, resultMailLogId); - // 断言调用 - verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(mail), - eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); - } - - /** - * 发送成功,当短信模板开启时 - */ - @Test - public void testSendSingleMail_successWhenMailTemplateEnable() { - // 准备参数 - String mail = randomEmail(); - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String templateCode = RandomUtils.randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock MailTemplateService 的方法 - MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String title = RandomUtils.randomString(); - when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) - .thenReturn(title); - String content = RandomUtils.randomString(); - when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock MailAccountService 的方法 - MailAccountDO account = randomPojo(MailAccountDO.class); - when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); - // mock MailLogService 的方法 - Long mailLogId = randomLongId(); - when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail), - eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId); - - // 调用 - Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams); - // 断言 - assertEquals(mailLogId, resultMailLogId); - // 断言调用 - verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(mail), - eq(account.getId()), eq(template.getNickname()), eq(title), eq(content)); - } - - /** - * 发送成功,当短信模板关闭时 - */ - @Test - public void testSendSingleMail_successWhenSmsTemplateDisable() { - // 准备参数 - String mail = randomEmail(); - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String templateCode = RandomUtils.randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock MailTemplateService 的方法 - MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.DISABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String title = RandomUtils.randomString(); - when(mailTemplateService.formatMailTemplateContent(eq(template.getTitle()), eq(templateParams))) - .thenReturn(title); - String content = RandomUtils.randomString(); - when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock MailAccountService 的方法 - MailAccountDO account = randomPojo(MailAccountDO.class); - when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account); - // mock MailLogService 的方法 - Long mailLogId = randomLongId(); - when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail), - eq(account), eq(template), eq(content), eq(templateParams), eq(false))).thenReturn(mailLogId); - - // 调用 - Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams); - // 断言 - assertEquals(mailLogId, resultMailLogId); - // 断言调用 - verify(mailProducer, times(0)).sendMailSendMessage(anyLong(), anyString(), - anyLong(), anyString(), anyString(), anyString()); - } - - @Test - public void testValidateMailTemplateValid_notExists() { - // 准备参数 - String templateCode = RandomUtils.randomString(); - // mock 方法 - - // 调用,并断言异常 - assertServiceException(() -> mailSendService.validateMailTemplate(templateCode), - MAIL_TEMPLATE_NOT_EXISTS); - } - - @Test - public void testValidateTemplateParams_paramMiss() { - // 准备参数 - MailTemplateDO template = randomPojo(MailTemplateDO.class, - o -> o.setParams(Lists.newArrayList("code"))); - Map templateParams = new HashMap<>(); - // mock 方法 - - // 调用,并断言异常 - assertServiceException(() -> mailSendService.validateTemplateParams(template, templateParams), - MAIL_SEND_TEMPLATE_PARAM_MISS, "code"); - } - - @Test - public void testValidateMail_notExists() { - // 准备参数 - // mock 方法 - - // 调用,并断言异常 - assertServiceException(() -> mailSendService.validateMail(null), - MAIL_SEND_MAIL_NOT_EXISTS); - } - - @Test - public void testDoSendMail_success() { - try (final MockedStatic mailUtilMock = mockStatic(MailUtil.class)) { - // 准备参数 - MailSendMessage message = randomPojo(MailSendMessage.class, o -> o.setNickname("芋艿")); - // mock 方法(获得邮箱账号) - MailAccountDO account = randomPojo(MailAccountDO.class, o -> o.setMail("7685@qq.com")); - when(mailAccountService.getMailAccountFromCache(eq(message.getAccountId()))) - .thenReturn(account); - - // mock 方法(发送邮件) - String messageId = randomString(); - mailUtilMock.when(() -> MailUtil.send( - argThat(mailAccount -> { - assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom()); - assertTrue(mailAccount.isAuth()); - assertEquals(account.getUsername(), mailAccount.getUser()); - assertArrayEquals(account.getPassword().toCharArray(), mailAccount.getPass()); - assertEquals(account.getHost(), mailAccount.getHost()); - assertEquals(account.getPort(), mailAccount.getPort()); - assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); - return true; - }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))) - .thenReturn(messageId); - - // 调用 - mailSendService.doSendMail(message); - // 断言 - verify(mailLogService).updateMailSendResult(eq(message.getLogId()), eq(messageId), isNull()); - } - } - - @Test - public void testDoSendMail_exception() { - try (MockedStatic mailUtilMock = mockStatic(MailUtil.class)) { - // 准备参数 - MailSendMessage message = randomPojo(MailSendMessage.class, o -> o.setNickname("芋艿")); - // mock 方法(获得邮箱账号) - MailAccountDO account = randomPojo(MailAccountDO.class, o -> o.setMail("7685@qq.com")); - when(mailAccountService.getMailAccountFromCache(eq(message.getAccountId()))) - .thenReturn(account); - - // mock 方法(发送邮件) - Exception e = new NullPointerException("啦啦啦"); - mailUtilMock.when(() -> MailUtil.send(argThat(mailAccount -> { - assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom()); - assertTrue(mailAccount.isAuth()); - assertEquals(account.getUsername(), mailAccount.getUser()); - assertArrayEquals(account.getPassword().toCharArray(), mailAccount.getPass()); - assertEquals(account.getHost(), mailAccount.getHost()); - assertEquals(account.getPort(), mailAccount.getPort()); - assertEquals(account.getSslEnable(), mailAccount.isSslEnable()); - return true; - }), eq(message.getMail()), eq(message.getTitle()), eq(message.getContent()), eq(true))).thenThrow(e); - - // 调用 - mailSendService.doSendMail(message); - // 断言 - verify(mailLogService).updateMailSendResult(eq(message.getLogId()), isNull(), same(e)); - } - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java deleted file mode 100755 index 70059b2..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java +++ /dev/null @@ -1,215 +0,0 @@ -package cn.iocoder.yudao.module.system.service.mail; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateSaveReqVO; -import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link MailTemplateServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(MailTemplateServiceImpl.class) -public class MailTemplateServiceImplTest extends BaseDbUnitTest { - - @Resource - private MailTemplateServiceImpl mailTemplateService; - - @Resource - private MailTemplateMapper mailTemplateMapper; - - @Test - public void testCreateMailTemplate_success() { - // 准备参数 - MailTemplateSaveReqVO reqVO = randomPojo(MailTemplateSaveReqVO.class) - .setId(null); // 防止 id 被赋值 - - // 调用 - Long mailTemplateId = mailTemplateService.createMailTemplate(reqVO); - // 断言 - assertNotNull(mailTemplateId); - // 校验记录的属性是否正确 - MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId); - assertPojoEquals(reqVO, mailTemplate, "id"); - } - - @Test - public void testUpdateMailTemplate_success() { - // mock 数据 - MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据 - // 准备参数 - MailTemplateSaveReqVO reqVO = randomPojo(MailTemplateSaveReqVO.class, o -> { - o.setId(dbMailTemplate.getId()); // 设置更新的 ID - }); - - // 调用 - mailTemplateService.updateMailTemplate(reqVO); - // 校验是否更新正确 - MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, mailTemplate); - } - - @Test - public void testUpdateMailTemplate_notExists() { - // 准备参数 - MailTemplateSaveReqVO reqVO = randomPojo(MailTemplateSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> mailTemplateService.updateMailTemplate(reqVO), MAIL_TEMPLATE_NOT_EXISTS); - } - - @Test - public void testDeleteMailTemplate_success() { - // mock 数据 - MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbMailTemplate.getId(); - - // 调用 - mailTemplateService.deleteMailTemplate(id); - // 校验数据不存在了 - assertNull(mailTemplateMapper.selectById(id)); - } - - @Test - public void testDeleteMailTemplate_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> mailTemplateService.deleteMailTemplate(id), MAIL_TEMPLATE_NOT_EXISTS); - } - - @Test - public void testGetMailTemplatePage() { - // mock 数据 - MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到 - o.setName("源码"); - o.setCode("test_01"); - o.setAccountId(1L); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2023, 2, 3)); - }); - mailTemplateMapper.insert(dbMailTemplate); - // 测试 name 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道"))); - // 测试 code 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02"))); - // 测试 accountId 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); - // 测试 status 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5)))); - // 准备参数 - MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO(); - reqVO.setName("源"); - reqVO.setCode("est_01"); - reqVO.setAccountId(1L); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5)); - - // 调用 - PageResult pageResult = mailTemplateService.getMailTemplatePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbMailTemplate, pageResult.getList().get(0)); - } - - @Test - public void testGetMailTemplateList() { - // mock 数据 - MailTemplateDO dbMailTemplate01 = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(dbMailTemplate01); - MailTemplateDO dbMailTemplate02 = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(dbMailTemplate02); - - // 调用 - List list = mailTemplateService.getMailTemplateList(); - // 断言 - assertEquals(2, list.size()); - assertEquals(dbMailTemplate01, list.get(0)); - assertEquals(dbMailTemplate02, list.get(1)); - } - - @Test - public void testGetMailTemplate() { - // mock 数据 - MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(dbMailTemplate); - // 准备参数 - Long id = dbMailTemplate.getId(); - - // 调用 - MailTemplateDO mailTemplate = mailTemplateService.getMailTemplate(id); - // 断言 - assertPojoEquals(dbMailTemplate, mailTemplate); - } - - @Test - public void testGetMailTemplateByCodeFromCache() { - // mock 数据 - MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(dbMailTemplate); - // 准备参数 - String code = dbMailTemplate.getCode(); - - // 调用 - MailTemplateDO mailTemplate = mailTemplateService.getMailTemplateByCodeFromCache(code); - // 断言 - assertPojoEquals(dbMailTemplate, mailTemplate); - } - - @Test - public void testFormatMailTemplateContent() { - // 准备参数 - Map params = new HashMap<>(); - params.put("name", "小红"); - params.put("what", "饭"); - - // 调用,并断言 - assertEquals("小红,你好,饭吃了吗?", - mailTemplateService.formatMailTemplateContent("{name},你好,{what}吃了吗?", params)); - } - - @Test - public void testCountByAccountId() { - // mock 数据 - MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class); - mailTemplateMapper.insert(dbMailTemplate); - // 测试 accountId 不匹配 - mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L))); - // 准备参数 - Long accountId = dbMailTemplate.getAccountId(); - - // 调用 - long count = mailTemplateService.getMailTemplateCountByAccountId(accountId); - // 断言 - assertEquals(1, count); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImplTest.java deleted file mode 100644 index 7b40af3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notice/NoticeServiceImplTest.java +++ /dev/null @@ -1,130 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notice; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notice.vo.NoticeSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notice.NoticeDO; -import cn.iocoder.yudao.module.system.dal.mysql.notice.NoticeMapper; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; - -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND; -import static org.junit.jupiter.api.Assertions.*; - -@Import(NoticeServiceImpl.class) -class NoticeServiceImplTest extends BaseDbUnitTest { - - @Resource - private NoticeServiceImpl noticeService; - - @Resource - private NoticeMapper noticeMapper; - - @Test - public void testGetNoticePage_success() { - // 插入前置数据 - NoticeDO dbNotice = randomPojo(NoticeDO.class, o -> { - o.setTitle("尼古拉斯赵四来啦!"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - noticeMapper.insert(dbNotice); - // 测试 title 不匹配 - noticeMapper.insert(cloneIgnoreId(dbNotice, o -> o.setTitle("尼古拉斯凯奇也来啦!"))); - // 测试 status 不匹配 - noticeMapper.insert(cloneIgnoreId(dbNotice, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 准备参数 - NoticePageReqVO reqVO = new NoticePageReqVO(); - reqVO.setTitle("尼古拉斯赵四来啦!"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - PageResult pageResult = noticeService.getNoticePage(reqVO); - // 验证查询结果经过筛选 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbNotice, pageResult.getList().get(0)); - } - - @Test - public void testGetNotice_success() { - // 插入前置数据 - NoticeDO dbNotice = randomPojo(NoticeDO.class); - noticeMapper.insert(dbNotice); - - // 查询 - NoticeDO notice = noticeService.getNotice(dbNotice.getId()); - - // 验证插入与读取对象是否一致 - assertNotNull(notice); - assertPojoEquals(dbNotice, notice); - } - - @Test - public void testCreateNotice_success() { - // 准备参数 - NoticeSaveReqVO reqVO = randomPojo(NoticeSaveReqVO.class) - .setId(null); // 避免 id 被赋值 - - // 调用 - Long noticeId = noticeService.createNotice(reqVO); - // 校验插入属性是否正确 - assertNotNull(noticeId); - NoticeDO notice = noticeMapper.selectById(noticeId); - assertPojoEquals(reqVO, notice, "id"); - } - - @Test - public void testUpdateNotice_success() { - // 插入前置数据 - NoticeDO dbNoticeDO = randomPojo(NoticeDO.class); - noticeMapper.insert(dbNoticeDO); - - // 准备更新参数 - NoticeSaveReqVO reqVO = randomPojo(NoticeSaveReqVO.class, o -> o.setId(dbNoticeDO.getId())); - - // 更新 - noticeService.updateNotice(reqVO); - // 检验是否更新成功 - NoticeDO notice = noticeMapper.selectById(reqVO.getId()); - assertPojoEquals(reqVO, notice); - } - - @Test - public void testDeleteNotice_success() { - // 插入前置数据 - NoticeDO dbNotice = randomPojo(NoticeDO.class); - noticeMapper.insert(dbNotice); - - // 删除 - noticeService.deleteNotice(dbNotice.getId()); - - // 检查是否删除成功 - assertNull(noticeMapper.selectById(dbNotice.getId())); - } - - @Test - public void testValidateNoticeExists_success() { - // 插入前置数据 - NoticeDO dbNotice = randomPojo(NoticeDO.class); - noticeMapper.insert(dbNotice); - - // 成功调用 - noticeService.validateNoticeExists(dbNotice.getId()); - } - - @Test - public void testValidateNoticeExists_noExists() { - assertServiceException(() -> - noticeService.validateNoticeExists(randomLongId()), NOTICE_NOT_FOUND); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java deleted file mode 100644 index cfd193b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java +++ /dev/null @@ -1,276 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notify; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper; -import jakarta.annotation.Resource; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static org.junit.jupiter.api.Assertions.*; - -/** -* {@link NotifyMessageServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ -@Import(NotifyMessageServiceImpl.class) -public class NotifyMessageServiceImplTest extends BaseDbUnitTest { - - @Resource - private NotifyMessageServiceImpl notifyMessageService; - - @Resource - private NotifyMessageMapper notifyMessageMapper; - - @Test - public void testCreateNotifyMessage_success() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class); - String templateContent = randomString(); - Map templateParams = randomTemplateParams(); - // mock 方法 - - // 调用 - Long messageId = notifyMessageService.createNotifyMessage(userId, userType, - template, templateContent, templateParams); - // 断言 - NotifyMessageDO message = notifyMessageMapper.selectById(messageId); - assertNotNull(message); - assertEquals(userId, message.getUserId()); - assertEquals(userType, message.getUserType()); - assertEquals(template.getId(), message.getTemplateId()); - assertEquals(template.getCode(), message.getTemplateCode()); - assertEquals(template.getType(), message.getTemplateType()); - assertEquals(template.getNickname(), message.getTemplateNickname()); - assertEquals(templateContent, message.getTemplateContent()); - assertEquals(templateParams, message.getTemplateParams()); - assertEquals(false, message.getReadStatus()); - assertNull(message.getReadTime()); - } - - @Test - public void testGetNotifyMessagePage() { - // mock 数据 - NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 - o.setUserId(1L); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setTemplateCode("test_01"); - o.setTemplateType(10); - o.setCreateTime(buildTime(2022, 1, 2)); - o.setTemplateParams(randomTemplateParams()); - }); - notifyMessageMapper.insert(dbNotifyMessage); - // 测试 userId 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); - // 测试 userType 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 templateCode 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateCode("test_11"))); - // 测试 templateType 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateType(20))); - // 测试 createTime 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1)))); - // 准备参数 - NotifyMessagePageReqVO reqVO = new NotifyMessagePageReqVO(); - reqVO.setUserId(1L); - reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); - reqVO.setTemplateCode("est_01"); - reqVO.setTemplateType(10); - reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10)); - - // 调用 - PageResult pageResult = notifyMessageService.getNotifyMessagePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0)); - } - - @Test - public void testGetNotifyMessage() { - // mock 数据 - NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, - o -> o.setTemplateParams(randomTemplateParams())); - notifyMessageMapper.insert(dbNotifyMessage); - // 准备参数 - Long id = dbNotifyMessage.getId(); - - // 调用 - NotifyMessageDO notifyMessage = notifyMessageService.getNotifyMessage(id); - assertPojoEquals(dbNotifyMessage, notifyMessage); - } - - @Test - public void testGetMyNotifyMessagePage() { - // mock 数据 - NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 - o.setUserId(1L); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setReadStatus(true); - o.setCreateTime(buildTime(2022, 1, 2)); - o.setTemplateParams(randomTemplateParams()); - }); - notifyMessageMapper.insert(dbNotifyMessage); - // 测试 userId 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); - // 测试 userType 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 readStatus 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(false))); - // 测试 createTime 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1)))); - // 准备参数 - Long userId = 1L; - Integer userType = UserTypeEnum.ADMIN.getValue(); - NotifyMessageMyPageReqVO reqVO = new NotifyMessageMyPageReqVO(); - reqVO.setReadStatus(true); - reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10)); - - // 调用 - PageResult pageResult = notifyMessageService.getMyMyNotifyMessagePage(reqVO, userId, userType); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0)); - } - - @Test - public void testGetUnreadNotifyMessageList() { - // mock 数据 - NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 - o.setUserId(1L); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setReadStatus(false); - o.setTemplateParams(randomTemplateParams()); - }); - notifyMessageMapper.insert(dbNotifyMessage); - // 测试 userId 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); - // 测试 userType 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 readStatus 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); - // 准备参数 - Long userId = 1L; - Integer userType = UserTypeEnum.ADMIN.getValue(); - Integer size = 10; - - // 调用 - List list = notifyMessageService.getUnreadNotifyMessageList(userId, userType, size); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbNotifyMessage, list.get(0)); - } - - @Test - public void testGetUnreadNotifyMessageCount() { - // mock 数据 - NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 - o.setUserId(1L); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setReadStatus(false); - o.setTemplateParams(randomTemplateParams()); - }); - notifyMessageMapper.insert(dbNotifyMessage); - // 测试 userId 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); - // 测试 userType 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 readStatus 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); - // 准备参数 - Long userId = 1L; - Integer userType = UserTypeEnum.ADMIN.getValue(); - - // 调用,并断言 - assertEquals(1, notifyMessageService.getUnreadNotifyMessageCount(userId, userType)); - } - - @Test - public void testUpdateNotifyMessageRead() { - // mock 数据 - NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 - o.setUserId(1L); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setReadStatus(false); - o.setReadTime(null); - o.setTemplateParams(randomTemplateParams()); - }); - notifyMessageMapper.insert(dbNotifyMessage); - // 测试 userId 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); - // 测试 userType 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 readStatus 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); - // 准备参数 - Collection ids = Arrays.asList(dbNotifyMessage.getId(), dbNotifyMessage.getId() + 1, - dbNotifyMessage.getId() + 2, dbNotifyMessage.getId() + 3); - Long userId = 1L; - Integer userType = UserTypeEnum.ADMIN.getValue(); - - // 调用 - int updateCount = notifyMessageService.updateNotifyMessageRead(ids, userId, userType); - // 断言 - assertEquals(1, updateCount); - NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId()); - assertTrue(notifyMessage.getReadStatus()); - assertNotNull(notifyMessage.getReadTime()); - } - - @Test - public void testUpdateAllNotifyMessageRead() { - // mock 数据 - NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到 - o.setUserId(1L); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setReadStatus(false); - o.setReadTime(null); - o.setTemplateParams(randomTemplateParams()); - }); - notifyMessageMapper.insert(dbNotifyMessage); - // 测试 userId 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L))); - // 测试 userType 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 readStatus 不匹配 - notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true))); - // 准备参数 - Long userId = 1L; - Integer userType = UserTypeEnum.ADMIN.getValue(); - - // 调用 - int updateCount = notifyMessageService.updateAllNotifyMessageRead(userId, userType); - // 断言 - assertEquals(1, updateCount); - NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId()); - assertTrue(notifyMessage.getReadStatus()); - assertNotNull(notifyMessage.getReadTime()); - } - - private static Map randomTemplateParams() { - return MapUtil.builder().put(randomString(), randomString()) - .put(randomString(), randomString()).build(); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java deleted file mode 100644 index 9dbdac4..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java +++ /dev/null @@ -1,190 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notify; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; -import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.HashMap; -import java.util.Map; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTICE_NOT_FOUND; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_SEND_TEMPLATE_PARAM_MISS; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -class NotifySendServiceImplTest extends BaseMockitoUnitTest { - - @InjectMocks - private NotifySendServiceImpl notifySendService; - - @Mock - private NotifyTemplateService notifyTemplateService; - @Mock - private NotifyMessageService notifyMessageService; - - @Test - public void testSendSingleNotifyToAdmin() { - // 准备参数 - Long userId = randomLongId(); - String templateCode = randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock NotifyTemplateService 的方法 - NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String content = randomString(); - when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock NotifyMessageService 的方法 - Long messageId = randomLongId(); - when(notifyMessageService.createNotifyMessage(eq(userId), eq(UserTypeEnum.ADMIN.getValue()), - eq(template), eq(content), eq(templateParams))).thenReturn(messageId); - - // 调用 - Long resultMessageId = notifySendService.sendSingleNotifyToAdmin(userId, templateCode, templateParams); - // 断言 - assertEquals(messageId, resultMessageId); - } - - @Test - public void testSendSingleNotifyToMember() { - // 准备参数 - Long userId = randomLongId(); - String templateCode = randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock NotifyTemplateService 的方法 - NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String content = randomString(); - when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock NotifyMessageService 的方法 - Long messageId = randomLongId(); - when(notifyMessageService.createNotifyMessage(eq(userId), eq(UserTypeEnum.MEMBER.getValue()), - eq(template), eq(content), eq(templateParams))).thenReturn(messageId); - - // 调用 - Long resultMessageId = notifySendService.sendSingleNotifyToMember(userId, templateCode, templateParams); - // 断言 - assertEquals(messageId, resultMessageId); - } - - /** - * 发送成功,当短信模板开启时 - */ - @Test - public void testSendSingleNotify_successWhenMailTemplateEnable() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String templateCode = randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock NotifyTemplateService 的方法 - NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String content = randomString(); - when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock NotifyMessageService 的方法 - Long messageId = randomLongId(); - when(notifyMessageService.createNotifyMessage(eq(userId), eq(userType), - eq(template), eq(content), eq(templateParams))).thenReturn(messageId); - - // 调用 - Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams); - // 断言 - assertEquals(messageId, resultMessageId); - } - - /** - * 发送成功,当短信模板关闭时 - */ - @Test - public void testSendSingleMail_successWhenSmsTemplateDisable() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String templateCode = randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock NotifyTemplateService 的方法 - NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.DISABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - - // 调用 - Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams); - // 断言 - assertNull(resultMessageId); - verify(notifyTemplateService, never()).formatNotifyTemplateContent(anyString(), anyMap()); - verify(notifyMessageService, never()).createNotifyMessage(anyLong(), anyInt(), any(), anyString(), anyMap()); - } - - @Test - public void testCheckMailTemplateValid_notExists() { - // 准备参数 - String templateCode = randomString(); - // mock 方法 - - // 调用,并断言异常 - assertServiceException(() -> notifySendService.validateNotifyTemplate(templateCode), - NOTICE_NOT_FOUND); - } - - @Test - public void testCheckTemplateParams_paramMiss() { - // 准备参数 - NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, - o -> o.setParams(Lists.newArrayList("code"))); - Map templateParams = new HashMap<>(); - // mock 方法 - - // 调用,并断言异常 - assertServiceException(() -> notifySendService.validateTemplateParams(template, templateParams), - NOTIFY_SEND_TEMPLATE_PARAM_MISS, "code"); - } - - @Test - public void testSendBatchNotify() { - // 准备参数 - // mock 方法 - - // 调用 - UnsupportedOperationException exception = Assertions.assertThrows( - UnsupportedOperationException.class, - () -> notifySendService.sendBatchNotify(null, null, null, null, null) - ); - // 断言 - assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java deleted file mode 100644 index ba1f98e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java +++ /dev/null @@ -1,178 +0,0 @@ -package cn.iocoder.yudao.module.system.service.notify; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.HashMap; -import java.util.Map; - -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link NotifyTemplateServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(NotifyTemplateServiceImpl.class) -public class NotifyTemplateServiceImplTest extends BaseDbUnitTest { - - @Resource - private NotifyTemplateServiceImpl notifyTemplateService; - - @Resource - private NotifyTemplateMapper notifyTemplateMapper; - - @Test - public void testCreateNotifyTemplate_success() { - // 准备参数 - NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class, - o -> o.setStatus(randomCommonStatus())) - .setId(null); // 防止 id 被赋值 - - // 调用 - Long notifyTemplateId = notifyTemplateService.createNotifyTemplate(reqVO); - // 断言 - assertNotNull(notifyTemplateId); - // 校验记录的属性是否正确 - NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId); - assertPojoEquals(reqVO, notifyTemplate, "id"); - } - - @Test - public void testUpdateNotifyTemplate_success() { - // mock 数据 - NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); - notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据 - // 准备参数 - NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class, o -> { - o.setId(dbNotifyTemplate.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - }); - - // 调用 - notifyTemplateService.updateNotifyTemplate(reqVO); - // 校验是否更新正确 - NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, notifyTemplate); - } - - @Test - public void testUpdateNotifyTemplate_notExists() { - // 准备参数 - NotifyTemplateSaveReqVO reqVO = randomPojo(NotifyTemplateSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> notifyTemplateService.updateNotifyTemplate(reqVO), NOTIFY_TEMPLATE_NOT_EXISTS); - } - - @Test - public void testDeleteNotifyTemplate_success() { - // mock 数据 - NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); - notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbNotifyTemplate.getId(); - - // 调用 - notifyTemplateService.deleteNotifyTemplate(id); - // 校验数据不存在了 - assertNull(notifyTemplateMapper.selectById(id)); - } - - @Test - public void testDeleteNotifyTemplate_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> notifyTemplateService.deleteNotifyTemplate(id), NOTIFY_TEMPLATE_NOT_EXISTS); - } - - @Test - public void testGetNotifyTemplatePage() { - // mock 数据 - NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到 - o.setName("芋头"); - o.setCode("test_01"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2022, 2, 3)); - }); - notifyTemplateMapper.insert(dbNotifyTemplate); - // 测试 name 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投"))); - // 测试 code 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02"))); - // 测试 status 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5)))); - // 准备参数 - NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO(); - reqVO.setName("芋"); - reqVO.setCode("est_01"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5)); - - // 调用 - PageResult pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0)); - } - - @Test - public void testGetNotifyTemplate() { - // mock 数据 - NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); - notifyTemplateMapper.insert(dbNotifyTemplate); - // 准备参数 - Long id = dbNotifyTemplate.getId(); - - // 调用 - NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplate(id); - // 断言 - assertPojoEquals(dbNotifyTemplate, notifyTemplate); - } - - @Test - public void testGetNotifyTemplateByCodeFromCache() { - // mock 数据 - NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class); - notifyTemplateMapper.insert(dbNotifyTemplate); - // 准备参数 - String code = dbNotifyTemplate.getCode(); - - // 调用 - NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplateByCodeFromCache(code); - // 断言 - assertPojoEquals(dbNotifyTemplate, notifyTemplate); - } - - @Test - public void testFormatNotifyTemplateContent() { - // 准备参数 - Map params = new HashMap<>(); - params.put("name", "小红"); - params.put("what", "饭"); - - // 调用,并断言 - assertEquals("小红,你好,饭吃了吗?", - notifyTemplateService.formatNotifyTemplateContent("{name},你好,{what}吃了吗?", params)); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImplTest.java deleted file mode 100644 index 142201c..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ApproveServiceImplTest.java +++ /dev/null @@ -1,270 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.util.date.DateUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ApproveDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ApproveMapper; -import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; -import java.util.*; - -import static cn.hutool.core.util.RandomUtil.*; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -/** - * {@link OAuth2ApproveServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(OAuth2ApproveServiceImpl.class) -public class OAuth2ApproveServiceImplTest extends BaseDbUnitTest { - - @Resource - private OAuth2ApproveServiceImpl oauth2ApproveService; - - @Resource - private OAuth2ApproveMapper oauth2ApproveMapper; - - @MockBean - private OAuth2ClientService oauth2ClientService; - - @Test - public void checkForPreApproval_clientAutoApprove() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - List requestedScopes = Lists.newArrayList("read"); - // mock 方法 - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))) - .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(requestedScopes)); - - // 调用 - boolean success = oauth2ApproveService.checkForPreApproval(userId, userType, - clientId, requestedScopes); - // 断言 - assertTrue(success); - List result = oauth2ApproveMapper.selectList(); - assertEquals(1, result.size()); - assertEquals(userId, result.get(0).getUserId()); - assertEquals(userType, result.get(0).getUserType()); - assertEquals(clientId, result.get(0).getClientId()); - assertEquals("read", result.get(0).getScope()); - assertTrue(result.get(0).getApproved()); - assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime())); - } - - @Test - public void checkForPreApproval_approve() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - List requestedScopes = Lists.newArrayList("read"); - // mock 方法 - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))) - .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null)); - // mock 数据 - OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId) - .setUserType(userType).setClientId(clientId).setScope("read") - .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)).setApproved(true); // 同意 - oauth2ApproveMapper.insert(approve); - - // 调用 - boolean success = oauth2ApproveService.checkForPreApproval(userId, userType, - clientId, requestedScopes); - // 断言 - assertTrue(success); - } - - @Test - public void checkForPreApproval_reject() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - List requestedScopes = Lists.newArrayList("read"); - // mock 方法 - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))) - .thenReturn(randomPojo(OAuth2ClientDO.class).setAutoApproveScopes(null)); - // mock 数据 - OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId) - .setUserType(userType).setClientId(clientId).setScope("read") - .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)).setApproved(false); // 拒绝 - oauth2ApproveMapper.insert(approve); - - // 调用 - boolean success = oauth2ApproveService.checkForPreApproval(userId, userType, - clientId, requestedScopes); - // 断言 - assertFalse(success); - } - - @Test - public void testUpdateAfterApproval_none() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - - // 调用 - boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId, - null); - // 断言 - assertTrue(success); - List result = oauth2ApproveMapper.selectList(); - assertEquals(0, result.size()); - } - - @Test - public void testUpdateAfterApproval_approved() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - Map requestedScopes = new LinkedHashMap<>(); // 有序,方便判断 - requestedScopes.put("read", true); - requestedScopes.put("write", false); - // mock 方法 - - // 调用 - boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId, - requestedScopes); - // 断言 - assertTrue(success); - List result = oauth2ApproveMapper.selectList(); - assertEquals(2, result.size()); - // read - assertEquals(userId, result.get(0).getUserId()); - assertEquals(userType, result.get(0).getUserType()); - assertEquals(clientId, result.get(0).getClientId()); - assertEquals("read", result.get(0).getScope()); - assertTrue(result.get(0).getApproved()); - assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime())); - // write - assertEquals(userId, result.get(1).getUserId()); - assertEquals(userType, result.get(1).getUserType()); - assertEquals(clientId, result.get(1).getClientId()); - assertEquals("write", result.get(1).getScope()); - assertFalse(result.get(1).getApproved()); - assertFalse(DateUtils.isExpired(result.get(1).getExpiresTime())); - } - - @Test - public void testUpdateAfterApproval_reject() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - Map requestedScopes = new LinkedHashMap<>(); - requestedScopes.put("write", false); - // mock 方法 - - // 调用 - boolean success = oauth2ApproveService.updateAfterApproval(userId, userType, clientId, - requestedScopes); - // 断言 - assertFalse(success); - List result = oauth2ApproveMapper.selectList(); - assertEquals(1, result.size()); - // write - assertEquals(userId, result.get(0).getUserId()); - assertEquals(userType, result.get(0).getUserType()); - assertEquals(clientId, result.get(0).getClientId()); - assertEquals("write", result.get(0).getScope()); - assertFalse(result.get(0).getApproved()); - assertFalse(DateUtils.isExpired(result.get(0).getExpiresTime())); - } - - @Test - public void testGetApproveList() { - // 准备参数 - Long userId = 10L; - Integer userType = UserTypeEnum.ADMIN.getValue(); - String clientId = randomString(); - // mock 数据 - OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class).setUserId(userId) - .setUserType(userType).setClientId(clientId).setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), 1L, ChronoUnit.DAYS)); - oauth2ApproveMapper.insert(approve); // 未过期 - oauth2ApproveMapper.insert(ObjectUtil.clone(approve).setId(null) - .setExpiresTime(LocalDateTimeUtil.offset(LocalDateTime.now(), -1L, ChronoUnit.DAYS))); // 已过期 - - // 调用 - List result = oauth2ApproveService.getApproveList(userId, userType, clientId); - // 断言 - assertEquals(1, result.size()); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(approve, result.get(0), "expiresTime"); - } - - @Test - public void testSaveApprove_insert() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - String scope = randomString(); - Boolean approved = randomBoolean(); - LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()); - // mock 方法 - - // 调用 - oauth2ApproveService.saveApprove(userId, userType, clientId, - scope, approved, expireTime); - // 断言 - List result = oauth2ApproveMapper.selectList(); - assertEquals(1, result.size()); - assertEquals(userId, result.get(0).getUserId()); - assertEquals(userType, result.get(0).getUserType()); - assertEquals(clientId, result.get(0).getClientId()); - assertEquals(scope, result.get(0).getScope()); - assertEquals(approved, result.get(0).getApproved()); - assertEquals(expireTime, result.get(0).getExpiresTime()); - } - - @Test - public void testSaveApprove_update() { - // mock 数据 - OAuth2ApproveDO approve = randomPojo(OAuth2ApproveDO.class); - oauth2ApproveMapper.insert(approve); - // 准备参数 - Long userId = approve.getUserId(); - Integer userType = approve.getUserType(); - String clientId = approve.getClientId(); - String scope = approve.getScope(); - Boolean approved = randomBoolean(); - LocalDateTime expireTime = LocalDateTime.ofInstant(randomDay(1, 30).toInstant(), ZoneId.systemDefault()); - // mock 方法 - - // 调用 - oauth2ApproveService.saveApprove(userId, userType, clientId, - scope, approved, expireTime); - // 断言 - List result = oauth2ApproveMapper.selectList(); - assertEquals(1, result.size()); - assertEquals(approve.getId(), result.get(0).getId()); - assertEquals(userId, result.get(0).getUserId()); - assertEquals(userType, result.get(0).getUserType()); - assertEquals(clientId, result.get(0).getClientId()); - assertEquals(scope, result.get(0).getScope()); - assertEquals(approved, result.get(0).getApproved()); - assertEquals(expireTime, result.get(0).getExpiresTime()); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java deleted file mode 100755 index 05e85cb..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java +++ /dev/null @@ -1,220 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2ClientMapper; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.Collections; - -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mockStatic; - -/** - * {@link OAuth2ClientServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(OAuth2ClientServiceImpl.class) -public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { - - @Resource - private OAuth2ClientServiceImpl oauth2ClientService; - - @Resource - private OAuth2ClientMapper oauth2ClientMapper; - - @Test - public void testCreateOAuth2Client_success() { - // 准备参数 - OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class, - o -> o.setLogo(randomString())) - .setId(null); // 防止 id 被赋值 - - // 调用 - Long oauth2ClientId = oauth2ClientService.createOAuth2Client(reqVO); - // 断言 - assertNotNull(oauth2ClientId); - // 校验记录的属性是否正确 - OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(oauth2ClientId); - assertPojoEquals(reqVO, oAuth2Client, "id"); - } - - @Test - public void testUpdateOAuth2Client_success() { - // mock 数据 - OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class); - oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据 - // 准备参数 - OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class, o -> { - o.setId(dbOAuth2Client.getId()); // 设置更新的 ID - o.setLogo(randomString()); - }); - - // 调用 - oauth2ClientService.updateOAuth2Client(reqVO); - // 校验是否更新正确 - OAuth2ClientDO oAuth2Client = oauth2ClientMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, oAuth2Client); - } - - @Test - public void testUpdateOAuth2Client_notExists() { - // 准备参数 - OAuth2ClientSaveReqVO reqVO = randomPojo(OAuth2ClientSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> oauth2ClientService.updateOAuth2Client(reqVO), OAUTH2_CLIENT_NOT_EXISTS); - } - - @Test - public void testDeleteOAuth2Client_success() { - // mock 数据 - OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class); - oauth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbOAuth2Client.getId(); - - // 调用 - oauth2ClientService.deleteOAuth2Client(id); - // 校验数据不存在了 - assertNull(oauth2ClientMapper.selectById(id)); - } - - @Test - public void testDeleteOAuth2Client_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> oauth2ClientService.deleteOAuth2Client(id), OAUTH2_CLIENT_NOT_EXISTS); - } - - @Test - public void testValidateClientIdExists_withId() { - // mock 数据 - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("tudou"); - oauth2ClientMapper.insert(client); - // 准备参数 - Long id = randomLongId(); - String clientId = "tudou"; - - // 调用,不会报错 - assertServiceException(() -> oauth2ClientService.validateClientIdExists(id, clientId), OAUTH2_CLIENT_EXISTS); - } - - @Test - public void testValidateClientIdExists_noId() { - // mock 数据 - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("tudou"); - oauth2ClientMapper.insert(client); - // 准备参数 - String clientId = "tudou"; - - // 调用,不会报错 - assertServiceException(() -> oauth2ClientService.validateClientIdExists(null, clientId), OAUTH2_CLIENT_EXISTS); - } - - @Test - public void testGetOAuth2Client() { - // mock 数据 - OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class); - oauth2ClientMapper.insert(clientDO); - // 准备参数 - Long id = clientDO.getId(); - - // 调用,并断言 - OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2Client(id); - assertPojoEquals(clientDO, dbClientDO); - } - - @Test - public void testGetOAuth2ClientFromCache() { - // mock 数据 - OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class); - oauth2ClientMapper.insert(clientDO); - // 准备参数 - String clientId = clientDO.getClientId(); - - // 调用,并断言 - OAuth2ClientDO dbClientDO = oauth2ClientService.getOAuth2ClientFromCache(clientId); - assertPojoEquals(clientDO, dbClientDO); - } - - @Test - public void testGetOAuth2ClientPage() { - // mock 数据 - OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到 - o.setName("潜龙"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - oauth2ClientMapper.insert(dbOAuth2Client); - // 测试 name 不匹配 - oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰"))); - // 测试 status 不匹配 - oauth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 准备参数 - OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO(); - reqVO.setName("龙"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - PageResult pageResult = oauth2ClientService.getOAuth2ClientPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0)); - } - - @Test - public void testValidOAuthClientFromCache() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(OAuth2ClientServiceImpl.class))) - .thenReturn(oauth2ClientService); - - // mock 方法 - OAuth2ClientDO client = randomPojo(OAuth2ClientDO.class).setClientId("default") - .setStatus(CommonStatusEnum.ENABLE.getStatus()); - oauth2ClientMapper.insert(client); - OAuth2ClientDO client02 = randomPojo(OAuth2ClientDO.class).setClientId("disable") - .setStatus(CommonStatusEnum.DISABLE.getStatus()); - oauth2ClientMapper.insert(client02); - - // 调用,并断言 - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache(randomString(), - null, null, null, null), OAUTH2_CLIENT_NOT_EXISTS); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("disable", - null, null, null, null), OAUTH2_CLIENT_DISABLE); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - randomString(), null, null, null), OAUTH2_CLIENT_CLIENT_SECRET_ERROR); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - null, randomString(), null, null), OAUTH2_CLIENT_AUTHORIZED_GRANT_TYPE_NOT_EXISTS); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - null, null, Collections.singleton(randomString()), null), OAUTH2_CLIENT_SCOPE_OVER); - assertServiceException(() -> oauth2ClientService.validOAuthClientFromCache("default", - null, null, null, "test"), OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH, "test"); - // 成功调用(1:参数完整) - OAuth2ClientDO result = oauth2ClientService.validOAuthClientFromCache(client.getClientId(), client.getSecret(), - client.getAuthorizedGrantTypes().get(0), client.getScopes(), client.getRedirectUris().get(0)); - assertPojoEquals(client, result); - // 成功调用(2:只有 clientId 参数) - result = oauth2ClientService.validOAuthClientFromCache(client.getClientId()); - assertPojoEquals(client, result); - } - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImplTest.java deleted file mode 100644 index cba9d3e..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2CodeServiceImplTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.hutool.core.util.RandomUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.util.date.DateUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2CodeMapper; -import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_EXPIRE; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CODE_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link OAuth2CodeServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(OAuth2CodeServiceImpl.class) -class OAuth2CodeServiceImplTest extends BaseDbUnitTest { - - @Resource - private OAuth2CodeServiceImpl oauth2CodeService; - - @Resource - private OAuth2CodeMapper oauth2CodeMapper; - - @Test - public void testCreateAuthorizationCode() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = RandomUtil.randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - List scopes = Lists.newArrayList("read", "write"); - String redirectUri = randomString(); - String state = randomString(); - - // 调用 - OAuth2CodeDO codeDO = oauth2CodeService.createAuthorizationCode(userId, userType, clientId, - scopes, redirectUri, state); - // 断言 - OAuth2CodeDO dbCodeDO = oauth2CodeMapper.selectByCode(codeDO.getCode()); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(codeDO, dbCodeDO, "expiresTime", "createTime", "updateTime", "deleted"); - assertEquals(userId, codeDO.getUserId()); - assertEquals(userType, codeDO.getUserType()); - assertEquals(clientId, codeDO.getClientId()); - assertEquals(scopes, codeDO.getScopes()); - assertEquals(redirectUri, codeDO.getRedirectUri()); - assertEquals(state, codeDO.getState()); - assertFalse(DateUtils.isExpired(codeDO.getExpiresTime())); - } - - @Test - public void testConsumeAuthorizationCode_null() { - // 调用,并断言 - assertServiceException(() -> oauth2CodeService.consumeAuthorizationCode(randomString()), - OAUTH2_CODE_NOT_EXISTS); - } - - @Test - public void testConsumeAuthorizationCode_expired() { - // 准备参数 - String code = "test_code"; - // mock 数据 - OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class).setCode(code) - .setExpiresTime(LocalDateTime.now().minusDays(1)); - oauth2CodeMapper.insert(codeDO); - - // 调用,并断言 - assertServiceException(() -> oauth2CodeService.consumeAuthorizationCode(code), - OAUTH2_CODE_EXPIRE); - } - - @Test - public void testConsumeAuthorizationCode_success() { - // 准备参数 - String code = "test_code"; - // mock 数据 - OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class).setCode(code) - .setExpiresTime(LocalDateTime.now().plusDays(1)); - oauth2CodeMapper.insert(codeDO); - - // 调用 - OAuth2CodeDO result = oauth2CodeService.consumeAuthorizationCode(code); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(codeDO, result, "expiresTime"); - assertNull(oauth2CodeMapper.selectByCode(code)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java deleted file mode 100644 index 52c7228..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2GrantServiceImplTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2CodeDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; -import com.google.common.collect.Lists; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.List; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static java.util.Collections.emptyList; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -/** - * {@link OAuth2GrantServiceImpl} 的单元测试 - * - * @author 芋道源码 - */ -public class OAuth2GrantServiceImplTest extends BaseMockitoUnitTest { - - @InjectMocks - private OAuth2GrantServiceImpl oauth2GrantService; - - @Mock - private OAuth2TokenService oauth2TokenService; - @Mock - private OAuth2CodeService oauth2CodeService; - @Mock - private AdminAuthService adminAuthService; - - @Test - public void testGrantImplicit() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - List scopes = Lists.newArrayList("read", "write"); - // mock 方法 - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); - when(oauth2TokenService.createAccessToken(eq(userId), eq(userType), - eq(clientId), eq(scopes))).thenReturn(accessTokenDO); - - // 调用,并断言 - assertPojoEquals(accessTokenDO, oauth2GrantService.grantImplicit( - userId, userType, clientId, scopes)); - } - - @Test - public void testGrantAuthorizationCodeForCode() { - // 准备参数 - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String clientId = randomString(); - List scopes = Lists.newArrayList("read", "write"); - String redirectUri = randomString(); - String state = randomString(); - // mock 方法 - OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class); - when(oauth2CodeService.createAuthorizationCode(eq(userId), eq(userType), - eq(clientId), eq(scopes), eq(redirectUri), eq(state))).thenReturn(codeDO); - - // 调用,并断言 - assertEquals(codeDO.getCode(), oauth2GrantService.grantAuthorizationCodeForCode(userId, userType, - clientId, scopes, redirectUri, state)); - } - - @Test - public void testGrantAuthorizationCodeForAccessToken() { - // 准备参数 - String clientId = randomString(); - String code = randomString(); - List scopes = Lists.newArrayList("read", "write"); - String redirectUri = randomString(); - String state = randomString(); - // mock 方法(code) - OAuth2CodeDO codeDO = randomPojo(OAuth2CodeDO.class, o -> { - o.setClientId(clientId); - o.setRedirectUri(redirectUri); - o.setState(state); - o.setScopes(scopes); - }); - when(oauth2CodeService.consumeAuthorizationCode(eq(code))).thenReturn(codeDO); - // mock 方法(创建令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); - when(oauth2TokenService.createAccessToken(eq(codeDO.getUserId()), eq(codeDO.getUserType()), - eq(codeDO.getClientId()), eq(codeDO.getScopes()))).thenReturn(accessTokenDO); - - // 调用,并断言 - assertPojoEquals(accessTokenDO, oauth2GrantService.grantAuthorizationCodeForAccessToken( - clientId, code, redirectUri, state)); - } - - @Test - public void testGrantPassword() { - // 准备参数 - String username = randomString(); - String password = randomString(); - String clientId = randomString(); - List scopes = Lists.newArrayList("read", "write"); - // mock 方法(认证) - AdminUserDO user = randomPojo(AdminUserDO.class); - when(adminAuthService.authenticate(eq(username), eq(password))).thenReturn(user); - // mock 方法(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); - when(oauth2TokenService.createAccessToken(eq(user.getId()), eq(UserTypeEnum.ADMIN.getValue()), - eq(clientId), eq(scopes))).thenReturn(accessTokenDO); - - // 调用,并断言 - assertPojoEquals(accessTokenDO, oauth2GrantService.grantPassword( - username, password, clientId, scopes)); - } - - @Test - public void testGrantRefreshToken() { - // 准备参数 - String refreshToken = randomString(); - String clientId = randomString(); - // mock 方法 - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); - when(oauth2TokenService.refreshAccessToken(eq(refreshToken), eq(clientId))) - .thenReturn(accessTokenDO); - - // 调用,并断言 - assertPojoEquals(accessTokenDO, oauth2GrantService.grantRefreshToken( - refreshToken, clientId)); - } - - @Test - public void testGrantClientCredentials() { - assertThrows(UnsupportedOperationException.class, - () -> oauth2GrantService.grantClientCredentials(randomString(), emptyList()), - "暂时不支持 client_credentials 授权模式"); - } - - @Test - public void testRevokeToken_clientIdError() { - // 准备参数 - String clientId = randomString(); - String accessToken = randomString(); - // mock 方法 - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class); - when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO); - - // 调用,并断言 - assertFalse(oauth2GrantService.revokeToken(clientId, accessToken)); - } - - @Test - public void testRevokeToken_success() { - // 准备参数 - String clientId = randomString(); - String accessToken = randomString(); - // mock 方法(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setClientId(clientId); - when(oauth2TokenService.getAccessToken(eq(accessToken))).thenReturn(accessTokenDO); - // mock 方法(移除) - when(oauth2TokenService.removeAccessToken(eq(accessToken))).thenReturn(accessTokenDO); - - // 调用,并断言 - assertTrue(oauth2GrantService.revokeToken(clientId, accessToken)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java deleted file mode 100644 index 03f78b4..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2TokenServiceImplTest.java +++ /dev/null @@ -1,329 +0,0 @@ -package cn.iocoder.yudao.module.system.service.oauth2; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.exception.ErrorCode; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.date.DateUtils; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.token.OAuth2AccessTokenPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO; -import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2RefreshTokenDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper; -import cn.iocoder.yudao.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper; -import cn.iocoder.yudao.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import jakarta.annotation.Resource; -import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import java.time.LocalDateTime; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -/** - * {@link OAuth2TokenServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import({OAuth2TokenServiceImpl.class, OAuth2AccessTokenRedisDAO.class}) -public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest { - - @Resource - private OAuth2TokenServiceImpl oauth2TokenService; - - @Resource - private OAuth2AccessTokenMapper oauth2AccessTokenMapper; - @Resource - private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper; - - @Resource - private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO; - - @MockBean - private OAuth2ClientService oauth2ClientService; - @MockBean - private AdminUserService adminUserService; - - @Test - public void testCreateAccessToken() { - TenantContextHolder.setTenantId(0L); - // 准备参数 - Long userId = randomLongId(); - Integer userType = UserTypeEnum.ADMIN.getValue(); - String clientId = randomString(); - List scopes = Lists.newArrayList("read", "write"); - // mock 方法 - OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId) - .setAccessTokenValiditySeconds(30).setRefreshTokenValiditySeconds(60); - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); - // mock 数据(用户) - AdminUserDO user = randomPojo(AdminUserDO.class); - when(adminUserService.getUser(userId)).thenReturn(user); - - // 调用 - OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, userType, clientId, scopes); - // 断言访问令牌 - OAuth2AccessTokenDO dbAccessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken()); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(accessTokenDO, dbAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted"); - assertEquals(userId, accessTokenDO.getUserId()); - assertEquals(userType, accessTokenDO.getUserType()); - assertEquals(2, accessTokenDO.getUserInfo().size()); - assertEquals(user.getNickname(), accessTokenDO.getUserInfo().get("nickname")); - assertEquals(user.getDeptId().toString(), accessTokenDO.getUserInfo().get("deptId")); - assertEquals(clientId, accessTokenDO.getClientId()); - assertEquals(scopes, accessTokenDO.getScopes()); - assertFalse(DateUtils.isExpired(accessTokenDO.getExpiresTime())); - // 断言访问令牌的缓存 - OAuth2AccessTokenDO redisAccessTokenDO = oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken()); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(accessTokenDO, redisAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted"); - // 断言刷新令牌 - OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectList().get(0); - assertPojoEquals(accessTokenDO, refreshTokenDO, "id", "expiresTime", "createTime", "updateTime", "deleted"); - assertFalse(DateUtils.isExpired(refreshTokenDO.getExpiresTime())); - } - - @Test - public void testRefreshAccessToken_null() { - // 准备参数 - String refreshToken = randomString(); - String clientId = randomString(); - // mock 方法 - - // 调用,并断言 - assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), - new ErrorCode(400, "无效的刷新令牌")); - } - - @Test - public void testRefreshAccessToken_clientIdError() { - // 准备参数 - String refreshToken = randomString(); - String clientId = randomString(); - // mock 方法 - OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId); - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); - // mock 数据(访问令牌) - OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) - .setRefreshToken(refreshToken).setClientId("error"); - oauth2RefreshTokenMapper.insert(refreshTokenDO); - - // 调用,并断言 - assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), - new ErrorCode(400, "刷新令牌的客户端编号不正确")); - } - - @Test - public void testRefreshAccessToken_expired() { - // 准备参数 - String refreshToken = randomString(); - String clientId = randomString(); - // mock 方法 - OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId); - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); - // mock 数据(访问令牌) - OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) - .setRefreshToken(refreshToken).setClientId(clientId) - .setExpiresTime(LocalDateTime.now().minusDays(1)); - oauth2RefreshTokenMapper.insert(refreshTokenDO); - - // 调用,并断言 - assertServiceException(() -> oauth2TokenService.refreshAccessToken(refreshToken, clientId), - new ErrorCode(401, "刷新令牌已过期")); - assertEquals(0, oauth2AccessTokenMapper.selectCount()); - } - - @Test - public void testRefreshAccessToken_success() { - TenantContextHolder.setTenantId(0L); - // 准备参数 - String refreshToken = randomString(); - String clientId = randomString(); - // mock 方法 - OAuth2ClientDO clientDO = randomPojo(OAuth2ClientDO.class).setClientId(clientId) - .setAccessTokenValiditySeconds(30); - when(oauth2ClientService.validOAuthClientFromCache(eq(clientId))).thenReturn(clientDO); - // mock 数据(访问令牌) - OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class, o -> - o.setRefreshToken(refreshToken).setClientId(clientId) - .setExpiresTime(LocalDateTime.now().plusDays(1)) - .setUserType(UserTypeEnum.ADMIN.getValue()) - .setTenantId(TenantContextHolder.getTenantId())); - oauth2RefreshTokenMapper.insert(refreshTokenDO); - // mock 数据(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class).setRefreshToken(refreshToken) - .setUserType(refreshTokenDO.getUserType()); - oauth2AccessTokenMapper.insert(accessTokenDO); - oauth2AccessTokenRedisDAO.set(accessTokenDO); - // mock 数据(用户) - AdminUserDO user = randomPojo(AdminUserDO.class); - when(adminUserService.getUser(refreshTokenDO.getUserId())).thenReturn(user); - - // 调用 - OAuth2AccessTokenDO newAccessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId); - // 断言,老的访问令牌被删除 - assertNull(oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken())); - assertNull(oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken())); - // 断言,新的访问令牌 - OAuth2AccessTokenDO dbAccessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(newAccessTokenDO.getAccessToken()); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(newAccessTokenDO, dbAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted"); - assertPojoEquals(newAccessTokenDO, refreshTokenDO, "id", "expiresTime", "createTime", "updateTime", "deleted", - "creator", "updater"); - assertFalse(DateUtils.isExpired(newAccessTokenDO.getExpiresTime())); - // 断言,新的访问令牌的缓存 - OAuth2AccessTokenDO redisAccessTokenDO = oauth2AccessTokenRedisDAO.get(newAccessTokenDO.getAccessToken()); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(newAccessTokenDO, redisAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted"); - } - - @Test - public void testGetAccessToken() { - // mock 数据(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) - .setExpiresTime(LocalDateTime.now().plusDays(1)); - oauth2AccessTokenMapper.insert(accessTokenDO); - // 准备参数 - String accessToken = accessTokenDO.getAccessToken(); - - // 调用 - OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken); - // 断言 - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(accessTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted", - "creator", "updater"); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(accessTokenDO, oauth2AccessTokenRedisDAO.get(accessToken), "expiresTime", "createTime", "updateTime", "deleted", - "creator", "updater"); - } - - @Test - public void testCheckAccessToken_null() { - // 调研,并断言 - assertServiceException(() -> oauth2TokenService.checkAccessToken(randomString()), - new ErrorCode(401, "访问令牌不存在")); - } - - @Test - public void testCheckAccessToken_expired() { - // mock 数据(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) - .setExpiresTime(LocalDateTime.now().minusDays(1)); - oauth2AccessTokenMapper.insert(accessTokenDO); - // 准备参数 - String accessToken = accessTokenDO.getAccessToken(); - - // 调研,并断言 - assertServiceException(() -> oauth2TokenService.checkAccessToken(accessToken), - new ErrorCode(401, "访问令牌已过期")); - } - - @Test - public void testCheckAccessToken_refreshToken() { - // mock 数据(访问令牌) - OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) - .setExpiresTime(LocalDateTime.now().plusDays(1)); - oauth2RefreshTokenMapper.insert(refreshTokenDO); - // 准备参数 - String accessToken = refreshTokenDO.getRefreshToken(); - - // 调研,并断言 - OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken); - // 断言 - assertPojoEquals(refreshTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted", - "creator", "updater"); - } - - @Test - public void testCheckAccessToken_success() { - // mock 数据(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) - .setExpiresTime(LocalDateTime.now().plusDays(1)); - oauth2AccessTokenMapper.insert(accessTokenDO); - // 准备参数 - String accessToken = accessTokenDO.getAccessToken(); - - // 调研,并断言 - OAuth2AccessTokenDO result = oauth2TokenService.getAccessToken(accessToken); - // 断言 - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(accessTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted", - "creator", "updater"); - } - - @Test - public void testRemoveAccessToken_null() { - // 调用,并断言 - assertNull(oauth2TokenService.removeAccessToken(randomString())); - } - - @Test - public void testRemoveAccessToken_success() { - // mock 数据(访问令牌) - OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class) - .setExpiresTime(LocalDateTime.now().plusDays(1)); - oauth2AccessTokenMapper.insert(accessTokenDO); - // mock 数据(刷新令牌) - OAuth2RefreshTokenDO refreshTokenDO = randomPojo(OAuth2RefreshTokenDO.class) - .setRefreshToken(accessTokenDO.getRefreshToken()); - oauth2RefreshTokenMapper.insert(refreshTokenDO); - // 调用 - OAuth2AccessTokenDO result = oauth2TokenService.removeAccessToken(accessTokenDO.getAccessToken()); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(accessTokenDO, result, "expiresTime", "createTime", "updateTime", "deleted", - "creator", "updater"); - // 断言数据 - assertNull(oauth2AccessTokenMapper.selectByAccessToken(accessTokenDO.getAccessToken())); - assertNull(oauth2RefreshTokenMapper.selectByRefreshToken(accessTokenDO.getRefreshToken())); - assertNull(oauth2AccessTokenRedisDAO.get(accessTokenDO.getAccessToken())); - } - - - @Test - public void testGetAccessTokenPage() { - // mock 数据 - OAuth2AccessTokenDO dbAccessToken = randomPojo(OAuth2AccessTokenDO.class, o -> { // 等会查询到 - o.setUserId(10L); - o.setUserType(1); - o.setClientId("test_client"); - o.setExpiresTime(LocalDateTime.now().plusDays(1)); - }); - oauth2AccessTokenMapper.insert(dbAccessToken); - // 测试 userId 不匹配 - oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setUserId(20L))); - // 测试 userType 不匹配 - oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setUserType(2))); - // 测试 userType 不匹配 - oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setClientId("it_client"))); - // 测试 expireTime 不匹配 - oauth2AccessTokenMapper.insert(cloneIgnoreId(dbAccessToken, o -> o.setExpiresTime(LocalDateTimeUtil.now()))); - // 准备参数 - OAuth2AccessTokenPageReqVO reqVO = new OAuth2AccessTokenPageReqVO(); - reqVO.setUserId(10L); - reqVO.setUserType(1); - reqVO.setClientId("test"); - - // 调用 - PageResult pageResult = oauth2TokenService.getAccessTokenPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - // TODO @芋艿:expiresTime 被屏蔽,仅 win11 会复现,建议后续修复。 - assertPojoEquals(dbAccessToken, pageResult.getList().get(0), "expiresTime"); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java deleted file mode 100644 index cc39379..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImplTest.java +++ /dev/null @@ -1,331 +0,0 @@ -package cn.iocoder.yudao.module.system.service.permission; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuListReqVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuSaveVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import cn.iocoder.yudao.module.system.dal.mysql.permission.MenuMapper; -import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; -import cn.iocoder.yudao.module.system.service.tenant.TenantService; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO.ID_ROOT; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.verify; - -@Import(MenuServiceImpl.class) -public class MenuServiceImplTest extends BaseDbUnitTest { - - @Resource - private MenuServiceImpl menuService; - - @Resource - private MenuMapper menuMapper; - - @MockBean - private PermissionService permissionService; - @MockBean - private TenantService tenantService; - - @Test - public void testCreateMenu_success() { - // mock 数据(构造父菜单) - MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, - "parent", 0L); - menuMapper.insert(menuDO); - Long parentId = menuDO.getId(); - // 准备参数 - MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> { - o.setParentId(parentId); - o.setName("testSonName"); - o.setType(MenuTypeEnum.MENU.getType()); - }).setId(null); // 防止 id 被赋值 - Long menuId = menuService.createMenu(reqVO); - - // 校验记录的属性是否正确 - MenuDO dbMenu = menuMapper.selectById(menuId); - assertPojoEquals(reqVO, dbMenu, "id"); - } - - @Test - public void testUpdateMenu_success() { - // mock 数据(构造父子菜单) - MenuDO sonMenuDO = createParentAndSonMenu(); - Long sonId = sonMenuDO.getId(); - // 准备参数 - MenuSaveVO reqVO = randomPojo(MenuSaveVO.class, o -> { - o.setId(sonId); - o.setName("testSonName"); // 修改名字 - o.setParentId(sonMenuDO.getParentId()); - o.setType(MenuTypeEnum.MENU.getType()); - }); - - // 调用 - menuService.updateMenu(reqVO); - // 校验记录的属性是否正确 - MenuDO dbMenu = menuMapper.selectById(sonId); - assertPojoEquals(reqVO, dbMenu); - } - - @Test - public void testUpdateMenu_sonIdNotExist() { - // 准备参数 - MenuSaveVO reqVO = randomPojo(MenuSaveVO.class); - // 调用,并断言异常 - assertServiceException(() -> menuService.updateMenu(reqVO), MENU_NOT_EXISTS); - } - - @Test - public void testDeleteMenu_success() { - // mock 数据 - MenuDO menuDO = randomPojo(MenuDO.class); - menuMapper.insert(menuDO); - // 准备参数 - Long id = menuDO.getId(); - - // 调用 - menuService.deleteMenu(id); - // 断言 - MenuDO dbMenuDO = menuMapper.selectById(id); - assertNull(dbMenuDO); - verify(permissionService).processMenuDeleted(id); - } - - @Test - public void testDeleteMenu_menuNotExist() { - assertServiceException(() -> menuService.deleteMenu(randomLongId()), - MENU_NOT_EXISTS); - } - - @Test - public void testDeleteMenu_existChildren() { - // mock 数据(构造父子菜单) - MenuDO sonMenu = createParentAndSonMenu(); - // 准备参数 - Long parentId = sonMenu.getParentId(); - - // 调用并断言异常 - assertServiceException(() -> menuService.deleteMenu(parentId), MENU_EXISTS_CHILDREN); - } - - @Test - public void testGetMenuList_all() { - // mock 数据 - MenuDO menu100 = randomPojo(MenuDO.class); - menuMapper.insert(menu100); - MenuDO menu101 = randomPojo(MenuDO.class); - menuMapper.insert(menu101); - // 准备参数 - - // 调用 - List list = menuService.getMenuList(); - // 断言 - assertEquals(2, list.size()); - assertPojoEquals(menu100, list.get(0)); - assertPojoEquals(menu101, list.get(1)); - } - - @Test - public void testGetMenuList() { - // mock 数据 - MenuDO menuDO = randomPojo(MenuDO.class, o -> o.setName("芋艿").setStatus(CommonStatusEnum.ENABLE.getStatus())); - menuMapper.insert(menuDO); - // 测试 status 不匹配 - menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 name 不匹配 - menuMapper.insert(cloneIgnoreId(menuDO, o -> o.setName("艿"))); - // 准备参数 - MenuListReqVO reqVO = new MenuListReqVO().setName("芋").setStatus(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - List result = menuService.getMenuList(reqVO); - // 断言 - assertEquals(1, result.size()); - assertPojoEquals(menuDO, result.get(0)); - } - - @Test - public void testGetMenuListByTenant() { - // mock 数据 - MenuDO menu100 = randomPojo(MenuDO.class, o -> o.setId(100L).setStatus(CommonStatusEnum.ENABLE.getStatus())); - menuMapper.insert(menu100); - MenuDO menu101 = randomPojo(MenuDO.class, o -> o.setId(101L).setStatus(CommonStatusEnum.DISABLE.getStatus())); - menuMapper.insert(menu101); - MenuDO menu102 = randomPojo(MenuDO.class, o -> o.setId(102L).setStatus(CommonStatusEnum.ENABLE.getStatus())); - menuMapper.insert(menu102); - // mock 过滤菜单 - Set menuIds = asSet(100L, 101L); - doNothing().when(tenantService).handleTenantMenu(argThat(handler -> { - handler.handle(menuIds); - return true; - })); - // 准备参数 - MenuListReqVO reqVO = new MenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - List result = menuService.getMenuListByTenant(reqVO); - // 断言 - assertEquals(1, result.size()); - assertPojoEquals(menu100, result.get(0)); - } - - @Test - public void testGetMenuIdListByPermissionFromCache() { - // mock 数据 - MenuDO menu100 = randomPojo(MenuDO.class); - menuMapper.insert(menu100); - MenuDO menu101 = randomPojo(MenuDO.class); - menuMapper.insert(menu101); - // 准备参数 - String permission = menu100.getPermission(); - - // 调用 - List ids = menuService.getMenuIdListByPermissionFromCache(permission); - // 断言 - assertEquals(1, ids.size()); - assertEquals(menu100.getId(), ids.get(0)); - } - - @Test - public void testGetMenuList_ids() { - // mock 数据 - MenuDO menu100 = randomPojo(MenuDO.class); - menuMapper.insert(menu100); - MenuDO menu101 = randomPojo(MenuDO.class); - menuMapper.insert(menu101); - // 准备参数 - Collection ids = Collections.singleton(menu100.getId()); - - // 调用 - List list = menuService.getMenuList(ids); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(menu100, list.get(0)); - } - - @Test - public void testGetMenu() { - // mock 数据 - MenuDO menu = randomPojo(MenuDO.class); - menuMapper.insert(menu); - // 准备参数 - Long id = menu.getId(); - - // 调用 - MenuDO dbMenu = menuService.getMenu(id); - // 断言 - assertPojoEquals(menu, dbMenu); - } - - @Test - public void testValidateParentMenu_success() { - // mock 数据 - MenuDO menuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", 0L); - menuMapper.insert(menuDO); - // 准备参数 - Long parentId = menuDO.getId(); - - // 调用,无需断言 - menuService.validateParentMenu(parentId, null); - } - - @Test - public void testValidateParentMenu_canNotSetSelfToBeParent() { - // 调用,并断言异常 - assertServiceException(() -> menuService.validateParentMenu(1L, 1L), - MENU_PARENT_ERROR); - } - - @Test - public void testValidateParentMenu_parentNotExist() { - // 调用,并断言异常 - assertServiceException(() -> menuService.validateParentMenu(randomLongId(), null), - MENU_PARENT_NOT_EXISTS); - } - - @Test - public void testValidateParentMenu_parentTypeError() { - // mock 数据 - MenuDO menuDO = buildMenuDO(MenuTypeEnum.BUTTON, "parent", 0L); - menuMapper.insert(menuDO); - // 准备参数 - Long parentId = menuDO.getId(); - - // 调用,并断言异常 - assertServiceException(() -> menuService.validateParentMenu(parentId, null), - MENU_PARENT_NOT_DIR_OR_MENU); - } - - @Test - public void testValidateMenu_Name_success() { - // mock 父子菜单 - MenuDO sonMenu = createParentAndSonMenu(); - // 准备参数 - Long parentId = sonMenu.getParentId(); - Long otherSonMenuId = randomLongId(); - String otherSonMenuName = randomString(); - - // 调用,无需断言 - menuService.validateMenuName(parentId, otherSonMenuName, otherSonMenuId); - } - - @Test - public void testValidateMenu_sonMenuNameNameDuplicate() { - // mock 父子菜单 - MenuDO sonMenu = createParentAndSonMenu(); - // 准备参数 - Long parentId = sonMenu.getParentId(); - Long otherSonMenuId = randomLongId(); - String otherSonMenuName = sonMenu.getName(); //相同名称 - - // 调用,并断言异常 - assertServiceException(() -> menuService.validateMenuName(parentId, otherSonMenuName, otherSonMenuId), - MENU_NAME_DUPLICATE); - } - - // ====================== 初始化方法 ====================== - - /** - * 插入父子菜单,返回子菜单 - * - * @return 子菜单 - */ - private MenuDO createParentAndSonMenu() { - // 构造父子菜单 - MenuDO parentMenuDO = buildMenuDO(MenuTypeEnum.MENU, "parent", ID_ROOT); - menuMapper.insert(parentMenuDO); - // 构建子菜单 - MenuDO sonMenuDO = buildMenuDO(MenuTypeEnum.MENU, "testSonName", - parentMenuDO.getParentId()); - menuMapper.insert(sonMenuDO); - return sonMenuDO; - } - - private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId) { - return buildMenuDO(type, name, parentId, randomCommonStatus()); - } - - private MenuDO buildMenuDO(MenuTypeEnum type, String name, Long parentId, Integer status) { - return randomPojo(MenuDO.class, o -> o.setId(null).setName(name).setParentId(parentId) - .setType(type.getType()).setStatus(status)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java deleted file mode 100644 index 9180444..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java +++ /dev/null @@ -1,527 +0,0 @@ -package cn.iocoder.yudao.module.system.service.permission; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; -import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import static cn.hutool.core.collection.ListUtil.toList; -import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -@Import({PermissionServiceImpl.class}) -public class PermissionServiceTest extends BaseDbUnitTest { - - @Resource - private PermissionServiceImpl permissionService; - - @Resource - private RoleMenuMapper roleMenuMapper; - @Resource - private UserRoleMapper userRoleMapper; - - @MockBean - private RoleService roleService; - @MockBean - private MenuService menuService; - @MockBean - private DeptService deptService; - @MockBean - private AdminUserService userService; - - @Test - public void testHasAnyPermissions_superAdmin() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - String[] roles = new String[]{"system:user:query", "system:user:create"}; - // mock 用户登录的角色 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); - // mock 其它方法 - when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); - - // 调用,并断言 - assertTrue(permissionService.hasAnyPermissions(userId, roles)); - } - } - - @Test - public void testHasAnyPermissions_normal() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - String[] roles = new String[]{"system:user:query", "system:user:create"}; - // mock 用户登录的角色 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); - // mock 菜单 - Long menuId = 1000L; - when(menuService.getMenuIdListByPermissionFromCache( - eq("system:user:create"))).thenReturn(singletonList(menuId)); - roleMenuMapper.insert(randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1000L)); - - // 调用,并断言 - assertTrue(permissionService.hasAnyPermissions(userId, roles)); - } - } - - @Test - public void testHasAnyRoles() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - String[] roles = new String[]{"yunai", "tudou"}; - // mock 用户与角色的缓存 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("tudou") - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); - - // 调用,并断言 - assertTrue(permissionService.hasAnyRoles(userId, roles)); - } - } - - // ========== 角色-菜单的相关方法 ========== - - @Test - public void testAssignRoleMenu() { - // 准备参数 - Long roleId = 1L; - Set menuIds = asSet(200L, 300L); - // mock 数据 - RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(100L); - roleMenuMapper.insert(roleMenu01); - RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(200L); - roleMenuMapper.insert(roleMenu02); - - // 调用 - permissionService.assignRoleMenu(roleId, menuIds); - // 断言 - List roleMenuList = roleMenuMapper.selectList(); - assertEquals(2, roleMenuList.size()); - assertEquals(1L, roleMenuList.get(0).getRoleId()); - assertEquals(200L, roleMenuList.get(0).getMenuId()); - assertEquals(1L, roleMenuList.get(1).getRoleId()); - assertEquals(300L, roleMenuList.get(1).getMenuId()); - } - - @Test - public void testProcessRoleDeleted() { - // 准备参数 - Long roleId = randomLongId(); - // mock 数据 UserRole - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setRoleId(roleId)); // 被删除 - userRoleMapper.insert(userRoleDO01); - UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除 - userRoleMapper.insert(userRoleDO02); - // mock 数据 RoleMenu - RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(roleId)); // 被删除 - roleMenuMapper.insert(roleMenuDO01); - RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除 - roleMenuMapper.insert(roleMenuDO02); - - // 调用 - permissionService.processRoleDeleted(roleId); - // 断言数据 RoleMenuDO - List dbRoleMenus = roleMenuMapper.selectList(); - assertEquals(1, dbRoleMenus.size()); - assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02); - // 断言数据 UserRoleDO - List dbUserRoles = userRoleMapper.selectList(); - assertEquals(1, dbUserRoles.size()); - assertPojoEquals(dbUserRoles.get(0), userRoleDO02); - } - - @Test - public void testProcessMenuDeleted() { - // 准备参数 - Long menuId = randomLongId(); - // mock 数据 - RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setMenuId(menuId)); // 被删除 - roleMenuMapper.insert(roleMenuDO01); - RoleMenuDO roleMenuDO02 = randomPojo(RoleMenuDO.class); // 不被删除 - roleMenuMapper.insert(roleMenuDO02); - - // 调用 - permissionService.processMenuDeleted(menuId); - // 断言数据 - List dbRoleMenus = roleMenuMapper.selectList(); - assertEquals(1, dbRoleMenus.size()); - assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02); - } - - @Test - public void testGetRoleMenuIds_superAdmin() { - // 准备参数 - Long roleId = 100L; - // mock 方法 - when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true); - List menuList = singletonList(randomPojo(MenuDO.class).setId(1L)); - when(menuService.getMenuList()).thenReturn(menuList); - - // 调用 - Set menuIds = permissionService.getRoleMenuListByRoleId(roleId); - // 断言 - assertEquals(singleton(1L), menuIds); - } - - @Test - public void testGetRoleMenuIds_normal() { - // 准备参数 - Long roleId = 100L; - // mock 数据 - RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); - roleMenuMapper.insert(roleMenu01); - RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(2L); - roleMenuMapper.insert(roleMenu02); - - // 调用 - Set menuIds = permissionService.getRoleMenuListByRoleId(roleId); - // 断言 - assertEquals(asSet(1L, 2L), menuIds); - } - - @Test - public void testGetMenuRoleIdListByMenuIdFromCache() { - // 准备参数 - Long menuId = 1L; - // mock 数据 - RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1L); - roleMenuMapper.insert(roleMenu01); - RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(200L).setMenuId(1L); - roleMenuMapper.insert(roleMenu02); - - // 调用 - Set roleIds = permissionService.getMenuRoleIdListByMenuIdFromCache(menuId); - // 断言 - assertEquals(asSet(100L, 200L), roleIds); - } - - // ========== 用户-角色的相关方法 ========== - - @Test - public void testAssignUserRole() { - // 准备参数 - Long userId = 1L; - Set roleIds = asSet(200L, 300L); - // mock 数据 - UserRoleDO userRole01 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(100L); - userRoleMapper.insert(userRole01); - UserRoleDO userRole02 = randomPojo(UserRoleDO.class).setUserId(1L).setRoleId(200L); - userRoleMapper.insert(userRole02); - - // 调用 - permissionService.assignUserRole(userId, roleIds); - // 断言 - List userRoleDOList = userRoleMapper.selectList(); - assertEquals(2, userRoleDOList.size()); - assertEquals(1L, userRoleDOList.get(0).getUserId()); - assertEquals(200L, userRoleDOList.get(0).getRoleId()); - assertEquals(1L, userRoleDOList.get(1).getUserId()); - assertEquals(300L, userRoleDOList.get(1).getRoleId()); - } - - @Test - public void testProcessUserDeleted() { - // 准备参数 - Long userId = randomLongId(); - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(userId)); // 被删除 - userRoleMapper.insert(userRoleDO01); - UserRoleDO userRoleDO02 = randomPojo(UserRoleDO.class); // 不被删除 - userRoleMapper.insert(userRoleDO02); - - // 调用 - permissionService.processUserDeleted(userId); - // 断言数据 - List dbUserRoles = userRoleMapper.selectList(); - assertEquals(1, dbUserRoles.size()); - assertPojoEquals(dbUserRoles.get(0), userRoleDO02); - } - - @Test - public void testGetUserRoleIdListByUserId() { - // 准备参数 - Long userId = 1L; - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); - - // 调用 - Set result = permissionService.getUserRoleIdListByUserId(userId); - // 断言 - assertEquals(asSet(10L, 20L), result); - } - - @Test - public void testGetUserRoleIdListByUserIdFromCache() { - // 准备参数 - Long userId = 1L; - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); - - // 调用 - Set result = permissionService.getUserRoleIdListByUserIdFromCache(userId); - // 断言 - assertEquals(asSet(10L, 20L), result); - } - - @Test - public void testGetUserRoleIdsFromCache() { - // 准备参数 - Long userId = 1L; - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); - - // 调用 - Set result = permissionService.getUserRoleIdListByUserIdFromCache(userId); - // 断言 - assertEquals(asSet(10L, 20L), result); - } - - @Test - public void testGetUserRoleIdListByRoleId() { - // 准备参数 - Collection roleIds = asSet(10L, 20L); - // mock 数据 - UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L)); - userRoleMapper.insert(userRoleDO01); - UserRoleDO roleMenuDO02 = randomPojo(UserRoleDO.class, o -> o.setUserId(2L).setRoleId(20L)); - userRoleMapper.insert(roleMenuDO02); - - // 调用 - Set result = permissionService.getUserRoleIdListByRoleId(roleIds); - // 断言 - assertEquals(asSet(1L, 2L), result); - } - - @Test - public void testGetEnableUserRoleListByUserIdFromCache() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户登录的角色 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(200L)); - RoleDO role01 = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - RoleDO role02 = randomPojo(RoleDO.class, o -> o.setId(200L) - .setStatus(CommonStatusEnum.DISABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(asSet(100L, 200L)))) - .thenReturn(toList(role01, role02)); - - // 调用 - List result = permissionService.getEnableUserRoleListByUserIdFromCache(userId); - // 断言 - assertEquals(1, result.size()); - assertPojoEquals(role01, result.get(0)); - } - } - - // ========== 用户-部门的相关方法 ========== - - @Test - public void testAssignRoleDataScope() { - // 准备参数 - Long roleId = 1L; - Integer dataScope = 2; - Set dataScopeDeptIds = asSet(10L, 20L); - - // 调用 - permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds); - // 断言 - verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds)); - } - - @Test - public void testGetDeptDataPermission_All() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertTrue(result.getAll()); - assertFalse(result.getSelf()); - assertTrue(CollUtil.isEmpty(result.getDeptIds())); - } - } - - @Test - public void testGetDeptDataPermission_DeptCustom() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), - null, null); // 最后返回 null 的目的,看看会不会重复调用 - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); - assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); - } - } - - @Test - public void testGetDeptDataPermission_DeptOnly() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), - null, null); // 最后返回 null 的目的,看看会不会重复调用 - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(1, result.getDeptIds().size()); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); - } - } - - @Test - public void testGetDeptDataPermission_DeptAndChild() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), - null, null); // 最后返回 null 的目的,看看会不会重复调用 - // mock 方法(部门) - DeptDO deptDO = randomPojo(DeptDO.class); - when(deptService.getChildDeptIdListFromCache(eq(3L))).thenReturn(singleton(deptDO.getId())); - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(2, result.getDeptIds().size()); - assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); - } - } - - @Test - public void testGetDeptDataPermission_Self() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertTrue(result.getSelf()); - assertTrue(CollUtil.isEmpty(result.getDeptIds())); - } - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java deleted file mode 100644 index fc87193..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java +++ /dev/null @@ -1,372 +0,0 @@ -package cn.iocoder.yudao.module.system.service.permission; - -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; -import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.verify; - -@Import(RoleServiceImpl.class) -public class RoleServiceImplTest extends BaseDbUnitTest { - - @Resource - private RoleServiceImpl roleService; - - @Resource - private RoleMapper roleMapper; - - @MockBean - private PermissionService permissionService; - - @Test - public void testCreateRole() { - // 准备参数 - RoleSaveReqVO reqVO = randomPojo(RoleSaveReqVO.class) - .setId(null) // 防止 id 被赋值 - .setStatus(randomCommonStatus()); - - // 调用 - Long roleId = roleService.createRole(reqVO, null); - // 断言 - RoleDO roleDO = roleMapper.selectById(roleId); - assertPojoEquals(reqVO, roleDO, "id"); - assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType()); - assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope()); - } - - @Test - public void testUpdateRole() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); - roleMapper.insert(roleDO); - // 准备参数 - Long id = roleDO.getId(); - RoleSaveReqVO reqVO = randomPojo(RoleSaveReqVO.class, o -> o.setId(id) - .setStatus(randomCommonStatus())); - - // 调用 - roleService.updateRole(reqVO); - // 断言 - RoleDO newRoleDO = roleMapper.selectById(id); - assertPojoEquals(reqVO, newRoleDO); - } - - @Test - public void testUpdateRoleDataScope() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); - roleMapper.insert(roleDO); - // 准备参数 - Long id = roleDO.getId(); - Integer dataScope = randomEle(DataScopeEnum.values()).getScope(); - Set dataScopeRoleIds = randomSet(Long.class); - - // 调用 - roleService.updateRoleDataScope(id, dataScope, dataScopeRoleIds); - // 断言 - RoleDO dbRoleDO = roleMapper.selectById(id); - assertEquals(dataScope, dbRoleDO.getDataScope()); - assertEquals(dataScopeRoleIds, dbRoleDO.getDataScopeDeptIds()); - } - - @Test - public void testDeleteRole() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.CUSTOM.getType())); - roleMapper.insert(roleDO); - // 参数准备 - Long id = roleDO.getId(); - - // 调用 - roleService.deleteRole(id); - // 断言 - assertNull(roleMapper.selectById(id)); - // verify 删除相关数据 - verify(permissionService).processRoleDeleted(id); - } - - @Test - public void testValidateRoleDuplicate_success() { - // 调用,不会抛异常 - roleService.validateRoleDuplicate(randomString(), randomString(), null); - } - - @Test - public void testValidateRoleDuplicate_nameDuplicate() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setName("role_name")); - roleMapper.insert(roleDO); - // 准备参数 - String name = "role_name"; - - // 调用,并断言异常 - assertServiceException(() -> roleService.validateRoleDuplicate(name, randomString(), null), - ROLE_NAME_DUPLICATE, name); - } - - @Test - public void testValidateRoleDuplicate_codeDuplicate() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setCode("code")); - roleMapper.insert(roleDO); - // 准备参数 - String code = "code"; - - // 调用,并断言异常 - assertServiceException(() -> roleService.validateRoleDuplicate(randomString(), code, null), - ROLE_CODE_DUPLICATE, code); - } - - @Test - public void testValidateUpdateRole_success() { - RoleDO roleDO = randomPojo(RoleDO.class); - roleMapper.insert(roleDO); - // 准备参数 - Long id = roleDO.getId(); - - // 调用,无异常 - roleService.validateRoleForUpdate(id); - } - - @Test - public void testValidateUpdateRole_roleIdNotExist() { - assertServiceException(() -> roleService.validateRoleForUpdate(randomLongId()), ROLE_NOT_EXISTS); - } - - @Test - public void testValidateUpdateRole_systemRoleCanNotBeUpdate() { - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setType(RoleTypeEnum.SYSTEM.getType())); - roleMapper.insert(roleDO); - // 准备参数 - Long id = roleDO.getId(); - - assertServiceException(() -> roleService.validateRoleForUpdate(id), - ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE); - } - - @Test - public void testGetRole() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class); - roleMapper.insert(roleDO); - // 参数准备 - Long id = roleDO.getId(); - - // 调用 - RoleDO dbRoleDO = roleService.getRole(id); - // 断言 - assertPojoEquals(roleDO, dbRoleDO); - } - - @Test - public void testGetRoleFromCache() { - // mock 数据(缓存) - RoleDO roleDO = randomPojo(RoleDO.class); - roleMapper.insert(roleDO); - // 参数准备 - Long id = roleDO.getId(); - - // 调用 - RoleDO dbRoleDO = roleService.getRoleFromCache(id); - // 断言 - assertPojoEquals(roleDO, dbRoleDO); - } - - @Test - public void testGetRoleListByStatus() { - // mock 数据 - RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - roleMapper.insert(dbRole01); - RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - roleMapper.insert(dbRole02); - - // 调用 - List list = roleService.getRoleListByStatus( - singleton(CommonStatusEnum.ENABLE.getStatus())); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbRole01, list.get(0)); - } - - @Test - public void testGetRoleList() { - // mock 数据 - RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - roleMapper.insert(dbRole01); - RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - roleMapper.insert(dbRole02); - - // 调用 - List list = roleService.getRoleList(); - // 断言 - assertEquals(2, list.size()); - assertPojoEquals(dbRole01, list.get(0)); - assertPojoEquals(dbRole02, list.get(1)); - } - - @Test - public void testGetRoleList_ids() { - // mock 数据 - RoleDO dbRole01 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - roleMapper.insert(dbRole01); - RoleDO dbRole02 = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - roleMapper.insert(dbRole02); - // 准备参数 - Collection ids = singleton(dbRole01.getId()); - - // 调用 - List list = roleService.getRoleList(ids); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbRole01, list.get(0)); - } - - @Test - public void testGetRoleListFromCache() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) - .thenReturn(roleService); - - // mock 数据 - RoleDO dbRole = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - roleMapper.insert(dbRole); - // 测试 id 不匹配 - roleMapper.insert(cloneIgnoreId(dbRole, o -> {})); - // 准备参数 - Collection ids = singleton(dbRole.getId()); - - // 调用 - List list = roleService.getRoleListFromCache(ids); - // 断言 - assertEquals(1, list.size()); - assertPojoEquals(dbRole, list.get(0)); - } - } - - @Test - public void testGetRolePage() { - // mock 数据 - RoleDO dbRole = randomPojo(RoleDO.class, o -> { // 等会查询到 - o.setName("土豆"); - o.setCode("tudou"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2022, 2, 8)); - }); - roleMapper.insert(dbRole); - // 测试 name 不匹配 - roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setName("红薯"))); - // 测试 code 不匹配 - roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCode("hong"))); - // 测试 createTime 不匹配 - roleMapper.insert(cloneIgnoreId(dbRole, o -> o.setCreateTime(buildTime(2022, 2, 16)))); - // 准备参数 - RolePageReqVO reqVO = new RolePageReqVO(); - reqVO.setName("土豆"); - reqVO.setCode("tu"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 12)); - - // 调用 - PageResult pageResult = roleService.getRolePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbRole, pageResult.getList().get(0)); - } - - @Test - public void testHasAnySuperAdmin_true() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) - .thenReturn(roleService); - - // mock 数据 - RoleDO dbRole = randomPojo(RoleDO.class).setCode("super_admin"); - roleMapper.insert(dbRole); - // 准备参数 - Long id = dbRole.getId(); - - // 调用,并调用 - assertTrue(roleService.hasAnySuperAdmin(singletonList(id))); - } - } - - @Test - public void testHasAnySuperAdmin_false() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(RoleServiceImpl.class))) - .thenReturn(roleService); - - // mock 数据 - RoleDO dbRole = randomPojo(RoleDO.class).setCode("tenant_admin"); - roleMapper.insert(dbRole); - // 准备参数 - Long id = dbRole.getId(); - - // 调用,并调用 - assertFalse(roleService.hasAnySuperAdmin(singletonList(id))); - } - } - - @Test - public void testValidateRoleList_success() { - // mock 数据 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - roleMapper.insert(roleDO); - // 准备参数 - List ids = singletonList(roleDO.getId()); - - // 调用,无需断言 - roleService.validateRoleList(ids); - } - - @Test - public void testValidateRoleList_notFound() { - // 准备参数 - List ids = singletonList(randomLongId()); - - // 调用, 并断言异常 - assertServiceException(() -> roleService.validateRoleList(ids), ROLE_NOT_EXISTS); - } - - @Test - public void testValidateRoleList_notEnable() { - // mock 数据 - RoleDO RoleDO = randomPojo(RoleDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - roleMapper.insert(RoleDO); - // 准备参数 - List ids = singletonList(RoleDO.getId()); - - // 调用, 并断言异常 - assertServiceException(() -> roleService.validateRoleList(ids), ROLE_IS_DISABLE, RoleDO.getName()); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java deleted file mode 100644 index 295911a..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java +++ /dev/null @@ -1,217 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClientFactory; -import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; -import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -@Import(SmsChannelServiceImpl.class) -public class SmsChannelServiceTest extends BaseDbUnitTest { - - @Resource - private SmsChannelServiceImpl smsChannelService; - - @Resource - private SmsChannelMapper smsChannelMapper; - - @MockBean - private SmsClientFactory smsClientFactory; - @MockBean - private SmsTemplateService smsTemplateService; - - @Test - public void testCreateSmsChannel_success() { - // 准备参数 - SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class, o -> o.setStatus(randomCommonStatus())) - .setId(null); // 防止 id 被赋值 - - // 调用 - Long smsChannelId = smsChannelService.createSmsChannel(reqVO); - // 断言 - assertNotNull(smsChannelId); - // 校验记录的属性是否正确 - SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId); - assertPojoEquals(reqVO, smsChannel, "id"); - } - - @Test - public void testUpdateSmsChannel_success() { - // mock 数据 - SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); - smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 - // 准备参数 - SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class, o -> { - o.setId(dbSmsChannel.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - o.setCallbackUrl(randomString()); - }); - - // 调用 - smsChannelService.updateSmsChannel(reqVO); - // 校验是否更新正确 - SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, smsChannel); - } - - @Test - public void testUpdateSmsChannel_notExists() { - // 准备参数 - SmsChannelSaveReqVO reqVO = randomPojo(SmsChannelSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> smsChannelService.updateSmsChannel(reqVO), SMS_CHANNEL_NOT_EXISTS); - } - - @Test - public void testDeleteSmsChannel_success() { - // mock 数据 - SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); - smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbSmsChannel.getId(); - - // 调用 - smsChannelService.deleteSmsChannel(id); - // 校验数据不存在了 - assertNull(smsChannelMapper.selectById(id)); - } - - @Test - public void testDeleteSmsChannel_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_NOT_EXISTS); - } - - @Test - public void testDeleteSmsChannel_hasChildren() { - // mock 数据 - SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); - smsChannelMapper.insert(dbSmsChannel);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbSmsChannel.getId(); - // mock 方法 - when(smsTemplateService.getSmsTemplateCountByChannelId(eq(id))).thenReturn(10L); - - // 调用, 并断言异常 - assertServiceException(() -> smsChannelService.deleteSmsChannel(id), SMS_CHANNEL_HAS_CHILDREN); - } - - @Test - public void testGetSmsChannel() { - // mock 数据 - SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class); - smsChannelMapper.insert(dbSmsChannel); // @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbSmsChannel.getId(); - - // 调用,并断言 - assertPojoEquals(dbSmsChannel, smsChannelService.getSmsChannel(id)); - } - - @Test - public void testGetSmsChannelList() { - // mock 数据 - SmsChannelDO dbSmsChannel01 = randomPojo(SmsChannelDO.class); - smsChannelMapper.insert(dbSmsChannel01); - SmsChannelDO dbSmsChannel02 = randomPojo(SmsChannelDO.class); - smsChannelMapper.insert(dbSmsChannel02); - // 准备参数 - - // 调用 - List list = smsChannelService.getSmsChannelList(); - // 断言 - assertEquals(2, list.size()); - assertPojoEquals(dbSmsChannel01, list.get(0)); - assertPojoEquals(dbSmsChannel02, list.get(1)); - } - - @Test - public void testGetSmsChannelPage() { - // mock 数据 - SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class, o -> { // 等会查询到 - o.setSignature("芋道源码"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2020, 12, 12)); - }); - smsChannelMapper.insert(dbSmsChannel); - // 测试 signature 不匹配 - smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setSignature("源码"))); - // 测试 status 不匹配 - smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11)))); - // 准备参数 - SmsChannelPageReqVO reqVO = new SmsChannelPageReqVO(); - reqVO.setSignature("芋道"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); - - // 调用 - PageResult pageResult = smsChannelService.getSmsChannelPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSmsChannel, pageResult.getList().get(0)); - } - - @Test - public void testGetSmsClient_id() { - // mock 数据 - SmsChannelDO channel = randomPojo(SmsChannelDO.class); - smsChannelMapper.insert(channel); - // 准备参数 - Long id = channel.getId(); - // mock 方法 - SmsClient mockClient = mock(SmsClient.class); - SmsChannelProperties properties = BeanUtils.toBean(channel, SmsChannelProperties.class); - when(smsClientFactory.createOrUpdateSmsClient(eq(properties))).thenReturn(mockClient); - - // 调用 - SmsClient client = smsChannelService.getSmsClient(id); - // 断言 - assertSame(client, mockClient); - } - - @Test - public void testGetSmsClient_code() { - // 准备参数 - String code = randomString(); - // mock 方法 - SmsClient mockClient = mock(SmsClient.class); - when(smsClientFactory.getSmsClient(eq(code))).thenReturn(mockClient); - - // 调用 - SmsClient client = smsChannelService.getSmsClient(code); - // 断言 - assertSame(client, mockClient); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImplTest.java deleted file mode 100644 index 093a5ae..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsCodeServiceImplTest.java +++ /dev/null @@ -1,191 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeValidateReqDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsCodeDO; -import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsCodeMapper; -import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; -import cn.iocoder.yudao.module.system.framework.sms.config.SmsCodeProperties; -import jakarta.annotation.Resource; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import java.time.Duration; -import java.time.LocalDateTime; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@Import(SmsCodeServiceImpl.class) -public class SmsCodeServiceImplTest extends BaseDbUnitTest { - - @Resource - private SmsCodeServiceImpl smsCodeService; - - @Resource - private SmsCodeMapper smsCodeMapper; - - @MockBean - private SmsCodeProperties smsCodeProperties; - @MockBean - private SmsSendService smsSendService; - - @BeforeEach - public void setUp() { - when(smsCodeProperties.getExpireTimes()).thenReturn(Duration.ofMinutes(5)); - when(smsCodeProperties.getSendFrequency()).thenReturn(Duration.ofMinutes(1)); - when(smsCodeProperties.getSendMaximumQuantityPerDay()).thenReturn(10); - when(smsCodeProperties.getBeginCode()).thenReturn(9999); - when(smsCodeProperties.getEndCode()).thenReturn(9999); - } - - @Test - public void sendSmsCode_success() { - // 准备参数 - SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> { - o.setMobile("15601691300"); - o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene()); - }); - - // 调用 - smsCodeService.sendSmsCode(reqDTO); - // 断言 code 验证码 - SmsCodeDO smsCodeDO = smsCodeMapper.selectOne(null); - assertPojoEquals(reqDTO, smsCodeDO); - assertEquals("9999", smsCodeDO.getCode()); - assertEquals(1, smsCodeDO.getTodayIndex()); - assertFalse(smsCodeDO.getUsed()); - // 断言调用 - verify(smsSendService).sendSingleSms(eq(reqDTO.getMobile()), isNull(), isNull(), - eq("user-sms-login"), eq(MapUtil.of("code", "9999"))); - } - - @Test - public void sendSmsCode_tooFast() { - // mock 数据 - SmsCodeDO smsCodeDO = randomPojo(SmsCodeDO.class, - o -> o.setMobile("15601691300").setTodayIndex(1)); - smsCodeMapper.insert(smsCodeDO); - // 准备参数 - SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> { - o.setMobile("15601691300"); - o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene()); - }); - - // 调用,并断言异常 - assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), - SMS_CODE_SEND_TOO_FAST); - } - - @Test - public void sendSmsCode_exceedDay() { - // mock 数据 - SmsCodeDO smsCodeDO = randomPojo(SmsCodeDO.class, - o -> o.setMobile("15601691300").setTodayIndex(10).setCreateTime(LocalDateTime.now())); - smsCodeMapper.insert(smsCodeDO); - // 准备参数 - SmsCodeSendReqDTO reqDTO = randomPojo(SmsCodeSendReqDTO.class, o -> { - o.setMobile("15601691300"); - o.setScene(SmsSceneEnum.MEMBER_LOGIN.getScene()); - }); - when(smsCodeProperties.getSendFrequency()).thenReturn(Duration.ofMillis(0)); - - // 调用,并断言异常 - assertServiceException(() -> smsCodeService.sendSmsCode(reqDTO), - SMS_CODE_EXCEED_SEND_MAXIMUM_QUANTITY_PER_DAY); - } - - @Test - public void testUseSmsCode_success() { - // 准备参数 - SmsCodeUseReqDTO reqDTO = randomPojo(SmsCodeUseReqDTO.class, o -> { - o.setMobile("15601691300"); - o.setScene(randomEle(SmsSceneEnum.values()).getScene()); - }); - smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> { - o.setMobile(reqDTO.getMobile()).setScene(reqDTO.getScene()) - .setCode(reqDTO.getCode()).setUsed(false); - })); - - // 调用 - smsCodeService.useSmsCode(reqDTO); - // 断言 - SmsCodeDO smsCodeDO = smsCodeMapper.selectOne(null); - assertTrue(smsCodeDO.getUsed()); - assertNotNull(smsCodeDO.getUsedTime()); - assertEquals(reqDTO.getUsedIp(), smsCodeDO.getUsedIp()); - } - - @Test - public void validateSmsCode_success() { - // 准备参数 - SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { - o.setMobile("15601691300"); - o.setScene(randomEle(SmsSceneEnum.values()).getScene()); - }); - smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile()) - .setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(false))); - - // 调用 - smsCodeService.validateSmsCode(reqDTO); - } - - @Test - public void validateSmsCode_notFound() { - // 准备参数 - SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { - o.setMobile("15601691300"); - o.setScene(randomEle(SmsSceneEnum.values()).getScene()); - }); - - // 调用,并断言异常 - assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO), - SMS_CODE_NOT_FOUND); - } - - @Test - public void validateSmsCode_expired() { - // 准备参数 - SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { - o.setMobile("15601691300"); - o.setScene(randomEle(SmsSceneEnum.values()).getScene()); - }); - smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile()) - .setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(false) - .setCreateTime(LocalDateTime.now().minusMinutes(6)))); - - // 调用,并断言异常 - assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO), - SMS_CODE_EXPIRED); - } - - @Test - public void validateSmsCode_used() { - // 准备参数 - SmsCodeValidateReqDTO reqDTO = randomPojo(SmsCodeValidateReqDTO.class, o -> { - o.setMobile("15601691300"); - o.setScene(randomEle(SmsSceneEnum.values()).getScene()); - }); - smsCodeMapper.insert(randomPojo(SmsCodeDO.class, o -> o.setMobile(reqDTO.getMobile()) - .setScene(reqDTO.getScene()).setCode(reqDTO.getCode()).setUsed(true) - .setCreateTime(LocalDateTime.now()))); - - // 调用,并断言异常 - assertServiceException(() -> smsCodeService.validateSmsCode(reqDTO), - SMS_CODE_USED); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java deleted file mode 100644 index 570e8ce..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java +++ /dev/null @@ -1,190 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper; -import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum; -import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum; -import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Map; -import java.util.function.Consumer; - -import static cn.hutool.core.util.RandomUtil.randomBoolean; -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@Import(SmsLogServiceImpl.class) -public class SmsLogServiceImplTest extends BaseDbUnitTest { - - @Resource - private SmsLogServiceImpl smsLogService; - - @Resource - private SmsLogMapper smsLogMapper; - - @Test - public void testGetSmsLogPage() { - // mock 数据 - SmsLogDO dbSmsLog = randomSmsLogDO(o -> { // 等会查询到 - o.setChannelId(1L); - o.setTemplateId(10L); - o.setMobile("15601691300"); - o.setSendStatus(SmsSendStatusEnum.INIT.getStatus()); - o.setSendTime(buildTime(2020, 11, 11)); - o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus()); - o.setReceiveTime(buildTime(2021, 11, 11)); - }); - smsLogMapper.insert(dbSmsLog); - // 测试 channelId 不匹配 - smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setChannelId(2L))); - // 测试 templateId 不匹配 - smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setTemplateId(20L))); - // 测试 mobile 不匹配 - smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setMobile("18818260999"))); - // 测试 sendStatus 不匹配 - smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus()))); - // 测试 sendTime 不匹配 - smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setSendTime(buildTime(2020, 12, 12)))); - // 测试 receiveStatus 不匹配 - smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setReceiveStatus(SmsReceiveStatusEnum.SUCCESS.getStatus()))); - // 测试 receiveTime 不匹配 - smsLogMapper.insert(cloneIgnoreId(dbSmsLog, o -> o.setReceiveTime(buildTime(2021, 12, 12)))); - // 准备参数 - SmsLogPageReqVO reqVO = new SmsLogPageReqVO(); - reqVO.setChannelId(1L); - reqVO.setTemplateId(10L); - reqVO.setMobile("156"); - reqVO.setSendStatus(SmsSendStatusEnum.INIT.getStatus()); - reqVO.setSendTime(buildBetweenTime(2020, 11, 1, 2020, 11, 30)); - reqVO.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus()); - reqVO.setReceiveTime(buildBetweenTime(2021, 11, 1, 2021, 11, 30)); - - // 调用 - PageResult pageResult = smsLogService.getSmsLogPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSmsLog, pageResult.getList().get(0)); - } - - @Test - public void testCreateSmsLog() { - // 准备参数 - String mobile = randomString(); - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - Boolean isSend = randomBoolean(); - SmsTemplateDO templateDO = randomPojo(SmsTemplateDO.class, - o -> o.setType(randomEle(SmsTemplateTypeEnum.values()).getType())); - String templateContent = randomString(); - Map templateParams = randomTemplateParams(); - // mock 方法 - - // 调用 - Long logId = smsLogService.createSmsLog(mobile, userId, userType, isSend, - templateDO, templateContent, templateParams); - // 断言 - SmsLogDO logDO = smsLogMapper.selectById(logId); - assertEquals(isSend ? SmsSendStatusEnum.INIT.getStatus() : SmsSendStatusEnum.IGNORE.getStatus(), - logDO.getSendStatus()); - assertEquals(mobile, logDO.getMobile()); - assertEquals(userType, logDO.getUserType()); - assertEquals(userId, logDO.getUserId()); - assertEquals(templateDO.getId(), logDO.getTemplateId()); - assertEquals(templateDO.getCode(), logDO.getTemplateCode()); - assertEquals(templateDO.getType(), logDO.getTemplateType()); - assertEquals(templateDO.getChannelId(), logDO.getChannelId()); - assertEquals(templateDO.getChannelCode(), logDO.getChannelCode()); - assertEquals(templateContent, logDO.getTemplateContent()); - assertEquals(templateParams, logDO.getTemplateParams()); - assertEquals(SmsReceiveStatusEnum.INIT.getStatus(), logDO.getReceiveStatus()); - } - - @Test - public void testUpdateSmsSendResult() { - // mock 数据 - SmsLogDO dbSmsLog = randomSmsLogDO( - o -> o.setSendStatus(SmsSendStatusEnum.IGNORE.getStatus())); - smsLogMapper.insert(dbSmsLog); - // 准备参数 - Long id = dbSmsLog.getId(); - Boolean success = randomBoolean(); - String apiSendCode = randomString(); - String apiSendMsg = randomString(); - String apiRequestId = randomString(); - String apiSerialNo = randomString(); - - // 调用 - smsLogService.updateSmsSendResult(id, success, - apiSendCode, apiSendMsg, apiRequestId, apiSerialNo); - // 断言 - dbSmsLog = smsLogMapper.selectById(id); - assertEquals(success ? SmsSendStatusEnum.SUCCESS.getStatus() : SmsSendStatusEnum.FAILURE.getStatus(), - dbSmsLog.getSendStatus()); - assertNotNull(dbSmsLog.getSendTime()); - assertEquals(apiSendCode, dbSmsLog.getApiSendCode()); - assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg()); - assertEquals(apiRequestId, dbSmsLog.getApiRequestId()); - assertEquals(apiSerialNo, dbSmsLog.getApiSerialNo()); - } - - @Test - public void testUpdateSmsReceiveResult() { - // mock 数据 - SmsLogDO dbSmsLog = randomSmsLogDO( - o -> o.setReceiveStatus(SmsReceiveStatusEnum.INIT.getStatus())); - smsLogMapper.insert(dbSmsLog); - // 准备参数 - Long id = dbSmsLog.getId(); - Boolean success = randomBoolean(); - LocalDateTime receiveTime = randomLocalDateTime(); - String apiReceiveCode = randomString(); - String apiReceiveMsg = randomString(); - - // 调用 - smsLogService.updateSmsReceiveResult(id, success, receiveTime, apiReceiveCode, apiReceiveMsg); - // 断言 - dbSmsLog = smsLogMapper.selectById(id); - assertEquals(success ? SmsReceiveStatusEnum.SUCCESS.getStatus() - : SmsReceiveStatusEnum.FAILURE.getStatus(), dbSmsLog.getReceiveStatus()); - assertEquals(receiveTime, dbSmsLog.getReceiveTime()); - assertEquals(apiReceiveCode, dbSmsLog.getApiReceiveCode()); - assertEquals(apiReceiveMsg, dbSmsLog.getApiReceiveMsg()); - } - - // ========== 随机对象 ========== - - @SafeVarargs - private static SmsLogDO randomSmsLogDO(Consumer... consumers) { - Consumer consumer = (o) -> { - o.setTemplateParams(randomTemplateParams()); - o.setTemplateType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 templateType 的范围 - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); // 保证 userType 的范围 - o.setSendStatus(randomEle(SmsSendStatusEnum.values()).getStatus()); // 保证 sendStatus 的范围 - o.setReceiveStatus(randomEle(SmsReceiveStatusEnum.values()).getStatus()); // 保证 receiveStatus 的范围 - }; - return randomPojo(SmsLogDO.class, ArrayUtils.append(consumer, consumers)); - } - - private static Map randomTemplateParams() { - return MapUtil.builder().put(randomString(), randomString()) - .put(randomString(), randomString()).build(); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java deleted file mode 100644 index 487c6f7..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java +++ /dev/null @@ -1,298 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage; -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer; -import cn.iocoder.yudao.module.system.service.member.MemberService; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import org.assertj.core.util.Lists; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -public class SmsSendServiceImplTest extends BaseMockitoUnitTest { - - @InjectMocks - private SmsSendServiceImpl smsSendService; - - @Mock - private AdminUserService adminUserService; - @Mock - private MemberService memberService; - @Mock - private SmsChannelService smsChannelService; - @Mock - private SmsTemplateService smsTemplateService; - @Mock - private SmsLogService smsLogService; - @Mock - private SmsProducer smsProducer; - - @Test - public void testSendSingleSmsToAdmin() { - // 准备参数 - Long userId = randomLongId(); - String templateCode = randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock adminUserService 的方法 - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setMobile("15601691300")); - when(adminUserService.getUser(eq(userId))).thenReturn(user); - - // mock SmsTemplateService 的方法 - SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String content = randomString(); - when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock SmsChannelService 的方法 - SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); - // mock SmsLogService 的方法 - Long smsLogId = randomLongId(); - when(smsLogService.createSmsLog(eq(user.getMobile()), eq(userId), eq(UserTypeEnum.ADMIN.getValue()), eq(Boolean.TRUE), eq(template), - eq(content), eq(templateParams))).thenReturn(smsLogId); - - // 调用 - Long resultSmsLogId = smsSendService.sendSingleSmsToAdmin(null, userId, templateCode, templateParams); - // 断言 - assertEquals(smsLogId, resultSmsLogId); - // 断言调用 - verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(user.getMobile()), - eq(template.getChannelId()), eq(template.getApiTemplateId()), - eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); - } - - @Test - public void testSendSingleSmsToUser() { - // 准备参数 - Long userId = randomLongId(); - String templateCode = randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock memberService 的方法 - String mobile = "15601691300"; - when(memberService.getMemberUserMobile(eq(userId))).thenReturn(mobile); - - // mock SmsTemplateService 的方法 - SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String content = randomString(); - when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock SmsChannelService 的方法 - SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); - // mock SmsLogService 的方法 - Long smsLogId = randomLongId(); - when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(UserTypeEnum.MEMBER.getValue()), eq(Boolean.TRUE), eq(template), - eq(content), eq(templateParams))).thenReturn(smsLogId); - - // 调用 - Long resultSmsLogId = smsSendService.sendSingleSmsToMember(null, userId, templateCode, templateParams); - // 断言 - assertEquals(smsLogId, resultSmsLogId); - // 断言调用 - verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(mobile), - eq(template.getChannelId()), eq(template.getApiTemplateId()), - eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); - } - - /** - * 发送成功,当短信模板开启时 - */ - @Test - public void testSendSingleSms_successWhenSmsTemplateEnable() { - // 准备参数 - String mobile = randomString(); - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String templateCode = randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock SmsTemplateService 的方法 - SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String content = randomString(); - when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock SmsChannelService 的方法 - SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); - // mock SmsLogService 的方法 - Long smsLogId = randomLongId(); - when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.TRUE), eq(template), - eq(content), eq(templateParams))).thenReturn(smsLogId); - - // 调用 - Long resultSmsLogId = smsSendService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); - // 断言 - assertEquals(smsLogId, resultSmsLogId); - // 断言调用 - verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(mobile), - eq(template.getChannelId()), eq(template.getApiTemplateId()), - eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); - } - - /** - * 发送成功,当短信模板关闭时 - */ - @Test - public void testSendSingleSms_successWhenSmsTemplateDisable() { - // 准备参数 - String mobile = randomString(); - Long userId = randomLongId(); - Integer userType = randomEle(UserTypeEnum.values()).getValue(); - String templateCode = randomString(); - Map templateParams = MapUtil.builder().put("code", "1234") - .put("op", "login").build(); - // mock SmsTemplateService 的方法 - SmsTemplateDO template = randomPojo(SmsTemplateDO.class, o -> { - o.setStatus(CommonStatusEnum.DISABLE.getStatus()); - o.setContent("验证码为{code}, 操作为{op}"); - o.setParams(Lists.newArrayList("code", "op")); - }); - when(smsTemplateService.getSmsTemplateByCodeFromCache(eq(templateCode))).thenReturn(template); - String content = randomString(); - when(smsTemplateService.formatSmsTemplateContent(eq(template.getContent()), eq(templateParams))) - .thenReturn(content); - // mock SmsChannelService 的方法 - SmsChannelDO smsChannel = randomPojo(SmsChannelDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(smsChannelService.getSmsChannel(eq(template.getChannelId()))).thenReturn(smsChannel); - // mock SmsLogService 的方法 - Long smsLogId = randomLongId(); - when(smsLogService.createSmsLog(eq(mobile), eq(userId), eq(userType), eq(Boolean.FALSE), eq(template), - eq(content), eq(templateParams))).thenReturn(smsLogId); - - // 调用 - Long resultSmsLogId = smsSendService.sendSingleSms(mobile, userId, userType, templateCode, templateParams); - // 断言 - assertEquals(smsLogId, resultSmsLogId); - // 断言调用 - verify(smsProducer, times(0)).sendSmsSendMessage(anyLong(), anyString(), - anyLong(), any(), anyList()); - } - - @Test - public void testCheckSmsTemplateValid_notExists() { - // 准备参数 - String templateCode = randomString(); - // mock 方法 - - // 调用,并断言异常 - assertServiceException(() -> smsSendService.validateSmsTemplate(templateCode), - SMS_SEND_TEMPLATE_NOT_EXISTS); - } - - @Test - public void testBuildTemplateParams_paramMiss() { - // 准备参数 - SmsTemplateDO template = randomPojo(SmsTemplateDO.class, - o -> o.setParams(Lists.newArrayList("code"))); - Map templateParams = new HashMap<>(); - // mock 方法 - - // 调用,并断言异常 - assertServiceException(() -> smsSendService.buildTemplateParams(template, templateParams), - SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, "code"); - } - - @Test - public void testCheckMobile_notExists() { - // 准备参数 - // mock 方法 - - // 调用,并断言异常 - assertServiceException(() -> smsSendService.validateMobile(null), - SMS_SEND_MOBILE_NOT_EXISTS); - } - - @Test - public void testSendBatchNotify() { - // 准备参数 - // mock 方法 - - // 调用 - UnsupportedOperationException exception = Assertions.assertThrows( - UnsupportedOperationException.class, - () -> smsSendService.sendBatchSms(null, null, null, null, null) - ); - // 断言 - assertEquals("暂时不支持该操作,感兴趣可以实现该功能哟!", exception.getMessage()); - } - - @Test - @SuppressWarnings("unchecked") - public void testDoSendSms() throws Throwable { - // 准备参数 - SmsSendMessage message = randomPojo(SmsSendMessage.class); - // mock SmsClientFactory 的方法 - SmsClient smsClient = spy(SmsClient.class); - when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient); - // mock SmsClient 的方法 - SmsSendRespDTO sendResult = randomPojo(SmsSendRespDTO.class); - when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()), - eq(message.getTemplateParams()))).thenReturn(sendResult); - - // 调用 - smsSendService.doSendSms(message); - // 断言 - verify(smsLogService).updateSmsSendResult(eq(message.getLogId()), - eq(sendResult.getSuccess()), eq(sendResult.getApiCode()), - eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getSerialNo())); - } - - @Test - public void testReceiveSmsStatus() throws Throwable { - // 准备参数 - String channelCode = randomString(); - String text = randomString(); - // mock SmsClientFactory 的方法 - SmsClient smsClient = spy(SmsClient.class); - when(smsChannelService.getSmsClient(eq(channelCode))).thenReturn(smsClient); - // mock SmsClient 的方法 - List receiveResults = randomPojoList(SmsReceiveRespDTO.class); - - // 调用 - smsSendService.receiveSmsStatus(channelCode, text); - // 断言 - receiveResults.forEach(result -> smsLogService.updateSmsReceiveResult(eq(result.getLogId()), eq(result.getSuccess()), - eq(result.getReceiveTime()), eq(result.getErrorCode()), eq(result.getErrorCode()))); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java deleted file mode 100644 index 22f737d..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java +++ /dev/null @@ -1,348 +0,0 @@ -package cn.iocoder.yudao.module.system.service.sms; - -import cn.hutool.core.map.MapUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; -import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient; -import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; -import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper; -import cn.iocoder.yudao.module.system.enums.sms.SmsTemplateTypeEnum; -import com.google.common.collect.Lists; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -@Import(SmsTemplateServiceImpl.class) -public class SmsTemplateServiceImplTest extends BaseDbUnitTest { - - @Resource - private SmsTemplateServiceImpl smsTemplateService; - - @Resource - private SmsTemplateMapper smsTemplateMapper; - - @MockBean - private SmsChannelService smsChannelService; - @MockBean - private SmsClient smsClient; - - @Test - public void testFormatSmsTemplateContent() { - // 准备参数 - String content = "正在进行登录操作{operation},您的验证码是{code}"; - Map params = MapUtil.builder("operation", "登录") - .put("code", "1234").build(); - - // 调用 - String result = smsTemplateService.formatSmsTemplateContent(content, params); - // 断言 - assertEquals("正在进行登录操作登录,您的验证码是1234", result); - } - - @Test - public void testParseTemplateContentParams() { - // 准备参数 - String content = "正在进行登录操作{operation},您的验证码是{code}"; - // mock 方法 - - // 调用 - List params = smsTemplateService.parseTemplateContentParams(content); - // 断言 - assertEquals(Lists.newArrayList("operation", "code"), params); - } - - @Test - @SuppressWarnings("unchecked") - public void testCreateSmsTemplate_success() throws Throwable { - // 准备参数 - SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class, o -> { - o.setContent("正在进行登录操作{operation},您的验证码是{code}"); - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 - }).setId(null); // 防止 id 被赋值 - // mock Channel 的方法 - SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { - o.setId(reqVO.getChannelId()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 - }); - when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); - // mock 获得 API 短信模板成功 - when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); - when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn( - randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()))); - - // 调用 - Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO); - // 断言 - assertNotNull(smsTemplateId); - // 校验记录的属性是否正确 - SmsTemplateDO smsTemplate = smsTemplateMapper.selectById(smsTemplateId); - assertPojoEquals(reqVO, smsTemplate, "id"); - assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); - assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); - } - - @Test - @SuppressWarnings("unchecked") - public void testUpdateSmsTemplate_success() throws Throwable { - // mock 数据 - SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); - smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 - // 准备参数 - SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class, o -> { - o.setId(dbSmsTemplate.getId()); // 设置更新的 ID - o.setContent("正在进行登录操作{operation},您的验证码是{code}"); - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 - }); - // mock 方法 - SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { - o.setId(reqVO.getChannelId()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 - }); - when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); - // mock 获得 API 短信模板成功 - when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); - when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn( - randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()))); - - // 调用 - smsTemplateService.updateSmsTemplate(reqVO); - // 校验是否更新正确 - SmsTemplateDO smsTemplate = smsTemplateMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, smsTemplate); - assertEquals(Lists.newArrayList("operation", "code"), smsTemplate.getParams()); - assertEquals(channelDO.getCode(), smsTemplate.getChannelCode()); - } - - @Test - public void testUpdateSmsTemplate_notExists() { - // 准备参数 - SmsTemplateSaveReqVO reqVO = randomPojo(SmsTemplateSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> smsTemplateService.updateSmsTemplate(reqVO), SMS_TEMPLATE_NOT_EXISTS); - } - - @Test - public void testDeleteSmsTemplate_success() { - // mock 数据 - SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); - smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbSmsTemplate.getId(); - - // 调用 - smsTemplateService.deleteSmsTemplate(id); - // 校验数据不存在了 - assertNull(smsTemplateMapper.selectById(id)); - } - - @Test - public void testDeleteSmsTemplate_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> smsTemplateService.deleteSmsTemplate(id), SMS_TEMPLATE_NOT_EXISTS); - } - - @Test - public void testGetSmsTemplate() { - // mock 数据 - SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); - smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbSmsTemplate.getId(); - - // 调用 - SmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplate(id); - // 校验 - assertPojoEquals(dbSmsTemplate, smsTemplate); - } - - @Test - public void testGetSmsTemplateByCodeFromCache() { - // mock 数据 - SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); - smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 - // 准备参数 - String code = dbSmsTemplate.getCode(); - - // 调用 - SmsTemplateDO smsTemplate = smsTemplateService.getSmsTemplateByCodeFromCache(code); - // 校验 - assertPojoEquals(dbSmsTemplate, smsTemplate); - } - - @Test - public void testGetSmsTemplatePage() { - // mock 数据 - SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> { // 等会查询到 - o.setType(SmsTemplateTypeEnum.PROMOTION.getType()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCode("tudou"); - o.setContent("芋道源码"); - o.setApiTemplateId("yunai"); - o.setChannelId(1L); - o.setCreateTime(buildTime(2021, 11, 11)); - }); - smsTemplateMapper.insert(dbSmsTemplate); - // 测试 type 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setType(SmsTemplateTypeEnum.VERIFICATION_CODE.getType()))); - // 测试 status 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 code 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCode("yuanma"))); - // 测试 content 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setContent("源码"))); - // 测试 apiTemplateId 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setApiTemplateId("nai"))); - // 测试 channelId 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); - // 测试 createTime 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setCreateTime(buildTime(2021, 12, 12)))); - // 准备参数 - SmsTemplatePageReqVO reqVO = new SmsTemplatePageReqVO(); - reqVO.setType(SmsTemplateTypeEnum.PROMOTION.getType()); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCode("tu"); - reqVO.setContent("芋道"); - reqVO.setApiTemplateId("yu"); - reqVO.setChannelId(1L); - reqVO.setCreateTime(buildBetweenTime(2021, 11, 1, 2021, 12, 1)); - - // 调用 - PageResult pageResult = smsTemplateService.getSmsTemplatePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSmsTemplate, pageResult.getList().get(0)); - } - - @Test - public void testGetSmsTemplateCountByChannelId() { - // mock 数据 - SmsTemplateDO dbSmsTemplate = randomPojo(SmsTemplateDO.class, o -> o.setChannelId(1L)); - smsTemplateMapper.insert(dbSmsTemplate); - // 测试 channelId 不匹配 - smsTemplateMapper.insert(ObjectUtils.cloneIgnoreId(dbSmsTemplate, o -> o.setChannelId(2L))); - // 准备参数 - Long channelId = 1L; - - // 调用 - Long count = smsTemplateService.getSmsTemplateCountByChannelId(channelId); - // 断言 - assertEquals(1, count); - } - - @Test - public void testValidateSmsChannel_success() { - // 准备参数 - Long channelId = randomLongId(); - // mock 方法 - SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { - o.setId(channelId); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 保证 status 开启,创建必须处于这个状态 - }); - when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO); - - // 调用 - SmsChannelDO returnChannelDO = smsTemplateService.validateSmsChannel(channelId); - // 断言 - assertPojoEquals(returnChannelDO, channelDO); - } - - @Test - public void testValidateSmsChannel_notExists() { - // 准备参数 - Long channelId = randomLongId(); - - // 调用,校验异常 - assertServiceException(() -> smsTemplateService.validateSmsChannel(channelId), - SMS_CHANNEL_NOT_EXISTS); - } - - @Test - public void testValidateSmsChannel_disable() { - // 准备参数 - Long channelId = randomLongId(); - // mock 方法 - SmsChannelDO channelDO = randomPojo(SmsChannelDO.class, o -> { - o.setId(channelId); - o.setStatus(CommonStatusEnum.DISABLE.getStatus()); // 保证 status 禁用,触发失败 - }); - when(smsChannelService.getSmsChannel(eq(channelId))).thenReturn(channelDO); - - // 调用,校验异常 - assertServiceException(() -> smsTemplateService.validateSmsChannel(channelId), - SMS_CHANNEL_DISABLE); - } - - @Test - public void testValidateDictDataValueUnique_success() { - // 调用,成功 - smsTemplateService.validateSmsTemplateCodeDuplicate(randomLongId(), randomString()); - } - - @Test - public void testValidateSmsTemplateCodeDuplicate_valueDuplicateForCreate() { - // 准备参数 - String code = randomString(); - // mock 数据 - smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code))); - - // 调用,校验异常 - assertServiceException(() -> smsTemplateService.validateSmsTemplateCodeDuplicate(null, code), - SMS_TEMPLATE_CODE_DUPLICATE, code); - } - - @Test - public void testValidateDictDataValueUnique_valueDuplicateForUpdate() { - // 准备参数 - Long id = randomLongId(); - String code = randomString(); - // mock 数据 - smsTemplateMapper.insert(randomSmsTemplateDO(o -> o.setCode(code))); - - // 调用,校验异常 - assertServiceException(() -> smsTemplateService.validateSmsTemplateCodeDuplicate(id, code), - SMS_TEMPLATE_CODE_DUPLICATE, code); - } - - // ========== 随机对象 ========== - - @SafeVarargs - private static SmsTemplateDO randomSmsTemplateDO(Consumer... consumers) { - Consumer consumer = (o) -> { - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - o.setType(randomEle(SmsTemplateTypeEnum.values()).getType()); // 保证 type 的 范围 - }; - return randomPojo(SmsTemplateDO.class, ArrayUtils.append(consumer, consumers)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java deleted file mode 100644 index 02f7a61..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialClientServiceImplTest.java +++ /dev/null @@ -1,471 +0,0 @@ -package cn.iocoder.yudao.module.system.service.social; - -import cn.binarywang.wx.miniapp.api.WxMaService; -import cn.binarywang.wx.miniapp.api.WxMaUserService; -import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo; -import cn.hutool.core.util.ReflectUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.client.SocialClientSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO; -import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import cn.iocoder.yudao.module.system.framework.justauth.core.AuthRequestFactory; -import com.binarywang.spring.starter.wxjava.miniapp.properties.WxMaProperties; -import com.binarywang.spring.starter.wxjava.mp.properties.WxMpProperties; -import jakarta.annotation.Resource; -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.mp.api.WxMpService; -import me.zhyd.oauth.config.AuthConfig; -import me.zhyd.oauth.model.AuthResponse; -import me.zhyd.oauth.model.AuthUser; -import me.zhyd.oauth.request.AuthDefaultRequest; -import me.zhyd.oauth.request.AuthRequest; -import me.zhyd.oauth.utils.AuthStateUtils; -import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.data.redis.core.StringRedisTemplate; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * {@link SocialClientServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(SocialClientServiceImpl.class) -public class SocialClientServiceImplTest extends BaseDbUnitTest { - - @Resource - private SocialClientServiceImpl socialClientService; - - @Resource - private SocialClientMapper socialClientMapper; - - @MockBean - private AuthRequestFactory authRequestFactory; - - @MockBean - private WxMpService wxMpService; - @MockBean - private WxMpProperties wxMpProperties; - @MockBean - private StringRedisTemplate stringRedisTemplate; - @MockBean - private WxMaService wxMaService; - @MockBean - private WxMaProperties wxMaProperties; - - @Test - public void testGetAuthorizeUrl() { - try (MockedStatic authStateUtilsMock = mockStatic(AuthStateUtils.class)) { - // 准备参数 - Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - String redirectUri = "sss"; - // mock 获得对应的 AuthRequest 实现 - AuthRequest authRequest = mock(AuthRequest.class); - when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); - // mock 方法 - authStateUtilsMock.when(AuthStateUtils::createState).thenReturn("aoteman"); - when(authRequest.authorize(eq("aoteman"))).thenReturn("https://www.iocoder.cn?redirect_uri=yyy"); - - // 调用 - String url = socialClientService.getAuthorizeUrl(socialType, userType, redirectUri); - // 断言 - assertEquals("https://www.iocoder.cn?redirect_uri=sss", url); - } - } - - @Test - public void testAuthSocialUser_success() { - // 准备参数 - Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - String code = randomString(); - String state = randomString(); - // mock 方法(AuthRequest) - AuthRequest authRequest = mock(AuthRequest.class); - when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); - // mock 方法(AuthResponse) - AuthUser authUser = randomPojo(AuthUser.class); - AuthResponse authResponse = new AuthResponse<>(2000, null, authUser); - when(authRequest.login(argThat(authCallback -> { - assertEquals(code, authCallback.getCode()); - assertEquals(state, authCallback.getState()); - return true; - }))).thenReturn(authResponse); - - // 调用 - AuthUser result = socialClientService.getAuthUser(socialType, userType, code, state); - // 断言 - assertSame(authUser, result); - } - - @Test - public void testAuthSocialUser_fail() { - // 准备参数 - Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - String code = randomString(); - String state = randomString(); - // mock 方法(AuthRequest) - AuthRequest authRequest = mock(AuthRequest.class); - when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); - // mock 方法(AuthResponse) - AuthResponse authResponse = new AuthResponse<>(0, "模拟失败", null); - when(authRequest.login(argThat(authCallback -> { - assertEquals(code, authCallback.getCode()); - assertEquals(state, authCallback.getState()); - return true; - }))).thenReturn(authResponse); - - // 调用并断言 - assertServiceException( - () -> socialClientService.getAuthUser(socialType, userType, code, state), - SOCIAL_USER_AUTH_FAILURE, "模拟失败"); - } - - @Test - public void testBuildAuthRequest_clientNull() { - // 准备参数 - Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); - Integer userType = randomPojo(SocialTypeEnum.class).getType(); - // mock 获得对应的 AuthRequest 实现 - AuthRequest authRequest = mock(AuthDefaultRequest.class); - AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(authRequest, "config"); - when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); - - // 调用 - AuthRequest result = socialClientService.buildAuthRequest(socialType, userType); - // 断言 - assertSame(authRequest, result); - assertSame(authConfig, ReflectUtil.getFieldValue(authConfig, "config")); - } - - @Test - public void testBuildAuthRequest_clientDisable() { - // 准备参数 - Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); - Integer userType = randomPojo(SocialTypeEnum.class).getType(); - // mock 获得对应的 AuthRequest 实现 - AuthRequest authRequest = mock(AuthDefaultRequest.class); - AuthConfig authConfig = (AuthConfig) ReflectUtil.getFieldValue(authRequest, "config"); - when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); - // mock 数据 - SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()) - .setUserType(userType).setSocialType(socialType)); - socialClientMapper.insert(client); - - // 调用 - AuthRequest result = socialClientService.buildAuthRequest(socialType, userType); - // 断言 - assertSame(authRequest, result); - assertSame(authConfig, ReflectUtil.getFieldValue(authConfig, "config")); - } - - @Test - public void testBuildAuthRequest_clientEnable() { - // 准备参数 - Integer socialType = SocialTypeEnum.WECHAT_MP.getType(); - Integer userType = randomPojo(SocialTypeEnum.class).getType(); - // mock 获得对应的 AuthRequest 实现 - AuthConfig authConfig = mock(AuthConfig.class); - AuthRequest authRequest = mock(AuthDefaultRequest.class); - ReflectUtil.setFieldValue(authRequest, "config", authConfig); - when(authRequestFactory.get(eq("WECHAT_MP"))).thenReturn(authRequest); - // mock 数据 - SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) - .setUserType(userType).setSocialType(socialType)); - socialClientMapper.insert(client); - - // 调用 - AuthRequest result = socialClientService.buildAuthRequest(socialType, userType); - // 断言 - assertSame(authRequest, result); - assertNotSame(authConfig, ReflectUtil.getFieldValue(authRequest, "config")); - } - - // =================== 微信公众号独有 =================== - - @Test - public void testCreateWxMpJsapiSignature() throws WxErrorException { - // 准备参数 - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - String url = randomString(); - // mock 方法 - WxJsapiSignature signature = randomPojo(WxJsapiSignature.class); - when(wxMpService.createJsapiSignature(eq(url))).thenReturn(signature); - - // 调用 - WxJsapiSignature result = socialClientService.createWxMpJsapiSignature(userType, url); - // 断言 - assertSame(signature, result); - } - - @Test - public void testGetWxMpService_clientNull() { - // 准备参数 - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - // mock 方法 - - // 调用 - WxMpService result = socialClientService.getWxMpService(userType); - // 断言 - assertSame(wxMpService, result); - } - - @Test - public void testGetWxMpService_clientDisable() { - // 准备参数 - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - // mock 数据 - SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()) - .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MP.getType())); - socialClientMapper.insert(client); - - // 调用 - WxMpService result = socialClientService.getWxMpService(userType); - // 断言 - assertSame(wxMpService, result); - } - - @Test - public void testGetWxMpService_clientEnable() { - // 准备参数 - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - // mock 数据 - SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) - .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MP.getType())); - socialClientMapper.insert(client); - // mock 方法 - WxMpProperties.ConfigStorage configStorage = mock(WxMpProperties.ConfigStorage.class); - when(wxMpProperties.getConfigStorage()).thenReturn(configStorage); - - // 调用 - WxMpService result = socialClientService.getWxMpService(userType); - // 断言 - assertNotSame(wxMpService, result); - assertEquals(client.getClientId(), result.getWxMpConfigStorage().getAppId()); - assertEquals(client.getClientSecret(), result.getWxMpConfigStorage().getSecret()); - } - - // =================== 微信小程序独有 =================== - - @Test - public void testGetWxMaPhoneNumberInfo_success() throws WxErrorException { - // 准备参数 - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - String phoneCode = randomString(); - // mock 方法 - WxMaUserService userService = mock(WxMaUserService.class); - when(wxMaService.getUserService()).thenReturn(userService); - WxMaPhoneNumberInfo phoneNumber = randomPojo(WxMaPhoneNumberInfo.class); - when(userService.getPhoneNoInfo(eq(phoneCode))).thenReturn(phoneNumber); - - // 调用 - WxMaPhoneNumberInfo result = socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode); - // 断言 - assertSame(phoneNumber, result); - } - - @Test - public void testGetWxMaPhoneNumberInfo_exception() throws WxErrorException { - // 准备参数 - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - String phoneCode = randomString(); - // mock 方法 - WxMaUserService userService = mock(WxMaUserService.class); - when(wxMaService.getUserService()).thenReturn(userService); - WxErrorException wxErrorException = new WxErrorException(new NullPointerException()); - when(userService.getPhoneNoInfo(eq(phoneCode))).thenThrow(wxErrorException); - - // 调用并断言异常 - assertServiceException(() -> socialClientService.getWxMaPhoneNumberInfo(userType, phoneCode), - SOCIAL_CLIENT_WEIXIN_MINI_APP_PHONE_CODE_ERROR); - } - - @Test - public void testGetWxMaService_clientNull() { - // 准备参数 - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - // mock 方法 - - // 调用 - WxMaService result = socialClientService.getWxMaService(userType); - // 断言 - assertSame(wxMaService, result); - } - - @Test - public void testGetWxMaService_clientDisable() { - // 准备参数 - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - // mock 数据 - SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()) - .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MINI_PROGRAM.getType())); - socialClientMapper.insert(client); - - // 调用 - WxMaService result = socialClientService.getWxMaService(userType); - // 断言 - assertSame(wxMaService, result); - } - - @Test - public void testGetWxMaService_clientEnable() { - // 准备参数 - Integer userType = randomPojo(UserTypeEnum.class).getValue(); - // mock 数据 - SocialClientDO client = randomPojo(SocialClientDO.class, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()) - .setUserType(userType).setSocialType(SocialTypeEnum.WECHAT_MINI_PROGRAM.getType())); - socialClientMapper.insert(client); - // mock 方法 - WxMaProperties.ConfigStorage configStorage = mock(WxMaProperties.ConfigStorage.class); - when(wxMaProperties.getConfigStorage()).thenReturn(configStorage); - - // 调用 - WxMaService result = socialClientService.getWxMaService(userType); - // 断言 - assertNotSame(wxMaService, result); - assertEquals(client.getClientId(), result.getWxMaConfig().getAppid()); - assertEquals(client.getClientSecret(), result.getWxMaConfig().getSecret()); - } - - // =================== 客户端管理 =================== - - @Test - public void testCreateSocialClient_success() { - // 准备参数 - SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, - o -> o.setSocialType(randomEle(SocialTypeEnum.values()).getType()) - .setUserType(randomEle(UserTypeEnum.values()).getValue()) - .setStatus(randomCommonStatus())) - .setId(null); // 防止 id 被赋值 - - // 调用 - Long socialClientId = socialClientService.createSocialClient(reqVO); - // 断言 - assertNotNull(socialClientId); - // 校验记录的属性是否正确 - SocialClientDO socialClient = socialClientMapper.selectById(socialClientId); - assertPojoEquals(reqVO, socialClient, "id"); - } - - @Test - public void testUpdateSocialClient_success() { - // mock 数据 - SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class); - socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据 - // 准备参数 - SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class, o -> { - o.setId(dbSocialClient.getId()); // 设置更新的 ID - o.setSocialType(randomEle(SocialTypeEnum.values()).getType()) - .setUserType(randomEle(UserTypeEnum.values()).getValue()) - .setStatus(randomCommonStatus()); - }); - - // 调用 - socialClientService.updateSocialClient(reqVO); - // 校验是否更新正确 - SocialClientDO socialClient = socialClientMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, socialClient); - } - - @Test - public void testUpdateSocialClient_notExists() { - // 准备参数 - SocialClientSaveReqVO reqVO = randomPojo(SocialClientSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> socialClientService.updateSocialClient(reqVO), SOCIAL_CLIENT_NOT_EXISTS); - } - - @Test - public void testDeleteSocialClient_success() { - // mock 数据 - SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class); - socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbSocialClient.getId(); - - // 调用 - socialClientService.deleteSocialClient(id); - // 校验数据不存在了 - assertNull(socialClientMapper.selectById(id)); - } - - @Test - public void testDeleteSocialClient_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> socialClientService.deleteSocialClient(id), SOCIAL_CLIENT_NOT_EXISTS); - } - - @Test - public void testGetSocialClient() { - // mock 数据 - SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class); - socialClientMapper.insert(dbSocialClient);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbSocialClient.getId(); - - // 调用 - SocialClientDO socialClient = socialClientService.getSocialClient(id); - // 校验数据正确 - assertPojoEquals(dbSocialClient, socialClient); - } - - @Test - public void testGetSocialClientPage() { - // mock 数据 - SocialClientDO dbSocialClient = randomPojo(SocialClientDO.class, o -> { // 等会查询到 - o.setName("芋头"); - o.setSocialType(SocialTypeEnum.GITEE.getType()); - o.setUserType(UserTypeEnum.ADMIN.getValue()); - o.setClientId("yudao"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - socialClientMapper.insert(dbSocialClient); - // 测试 name 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setName(randomString()))); - // 测试 socialType 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setSocialType(SocialTypeEnum.DINGTALK.getType()))); - // 测试 userType 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setUserType(UserTypeEnum.MEMBER.getValue()))); - // 测试 clientId 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setClientId("dao"))); - // 测试 status 不匹配 - socialClientMapper.insert(cloneIgnoreId(dbSocialClient, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 准备参数 - SocialClientPageReqVO reqVO = new SocialClientPageReqVO(); - reqVO.setName("芋"); - reqVO.setSocialType(SocialTypeEnum.GITEE.getType()); - reqVO.setUserType(UserTypeEnum.ADMIN.getValue()); - reqVO.setClientId("yu"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - - // 调用 - PageResult pageResult = socialClientService.getSocialClientPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSocialClient, pageResult.getList().get(0)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java deleted file mode 100644 index 8416415..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/social/SocialUserServiceImplTest.java +++ /dev/null @@ -1,288 +0,0 @@ -package cn.iocoder.yudao.module.system.service.social; - -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserRespDTO; -import cn.iocoder.yudao.module.system.controller.admin.socail.vo.user.SocialUserPageReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserBindDO; -import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialUserDO; -import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserBindMapper; -import cn.iocoder.yudao.module.system.dal.mysql.social.SocialUserMapper; -import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum; -import jakarta.annotation.Resource; -import me.zhyd.oauth.model.AuthUser; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import java.util.List; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.hutool.core.util.RandomUtil.randomLong; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SOCIAL_USER_NOT_FOUND; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.when; - -/** - * {@link SocialUserServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(SocialUserServiceImpl.class) -public class SocialUserServiceImplTest extends BaseDbUnitTest { - - @Resource - private SocialUserServiceImpl socialUserService; - - @Resource - private SocialUserMapper socialUserMapper; - @Resource - private SocialUserBindMapper socialUserBindMapper; - - @MockBean - private SocialClientService socialClientService; - - @Test - public void testGetSocialUserList() { - Long userId = 1L; - Integer userType = UserTypeEnum.ADMIN.getValue(); - // mock 获得社交用户 - SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(SocialTypeEnum.GITEE.getType()); - socialUserMapper.insert(socialUser); // 可被查到 - socialUserMapper.insert(randomPojo(SocialUserDO.class)); // 不可被查到 - // mock 获得绑定 - socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 可被查询到 - .setUserId(userId).setUserType(userType).setSocialType(SocialTypeEnum.GITEE.getType()) - .setSocialUserId(socialUser.getId())); - socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class) // 不可被查询到 - .setUserId(2L).setUserType(userType).setSocialType(SocialTypeEnum.DINGTALK.getType())); - - // 调用 - List result = socialUserService.getSocialUserList(userId, userType); - // 断言 - assertEquals(1, result.size()); - assertPojoEquals(socialUser, result.get(0)); - } - - @Test - public void testBindSocialUser() { - // 准备参数 - SocialUserBindReqDTO reqDTO = new SocialUserBindReqDTO() - .setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue()) - .setSocialType(SocialTypeEnum.GITEE.getType()).setCode("test_code").setState("test_state"); - // mock 数据:获得社交用户 - SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(reqDTO.getSocialType()) - .setCode(reqDTO.getCode()).setState(reqDTO.getState()); - socialUserMapper.insert(socialUser); - // mock 数据:用户可能之前已经绑定过该社交类型 - socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserId(1L).setUserType(UserTypeEnum.ADMIN.getValue()) - .setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(-1L)); - // mock 数据:社交用户可能之前绑定过别的用户 - socialUserBindMapper.insert(randomPojo(SocialUserBindDO.class).setUserType(UserTypeEnum.ADMIN.getValue()) - .setSocialType(SocialTypeEnum.GITEE.getType()).setSocialUserId(socialUser.getId())); - - // 调用 - String openid = socialUserService.bindSocialUser(reqDTO); - // 断言 - List socialUserBinds = socialUserBindMapper.selectList(); - assertEquals(1, socialUserBinds.size()); - assertEquals(socialUser.getOpenid(), openid); - } - - @Test - public void testUnbindSocialUser_success() { - // 准备参数 - Long userId = 1L; - Integer userType = UserTypeEnum.ADMIN.getValue(); - Integer type = SocialTypeEnum.GITEE.getType(); - String openid = "test_openid"; - // mock 数据:社交用户 - SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(type).setOpenid(openid); - socialUserMapper.insert(socialUser); - // mock 数据:社交绑定关系 - SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType) - .setUserId(userId).setSocialType(type); - socialUserBindMapper.insert(socialUserBind); - - // 调用 - socialUserService.unbindSocialUser(userId, userType, type, openid); - // 断言 - assertEquals(0, socialUserBindMapper.selectCount(null).intValue()); - } - - @Test - public void testUnbindSocialUser_notFound() { - // 调用,并断言 - assertServiceException( - () -> socialUserService.unbindSocialUser(randomLong(), UserTypeEnum.ADMIN.getValue(), - SocialTypeEnum.GITEE.getType(), "test_openid"), - SOCIAL_USER_NOT_FOUND); - } - - @Test - public void testGetSocialUser() { - // 准备参数 - Integer userType = UserTypeEnum.ADMIN.getValue(); - Integer type = SocialTypeEnum.GITEE.getType(); - String code = "tudou"; - String state = "yuanma"; - // mock 社交用户 - SocialUserDO socialUserDO = randomPojo(SocialUserDO.class).setType(type).setCode(code).setState(state); - socialUserMapper.insert(socialUserDO); - // mock 社交用户的绑定 - Long userId = randomLong(); - SocialUserBindDO socialUserBind = randomPojo(SocialUserBindDO.class).setUserType(userType).setUserId(userId) - .setSocialType(type).setSocialUserId(socialUserDO.getId()); - socialUserBindMapper.insert(socialUserBind); - - // 调用 - SocialUserRespDTO socialUser = socialUserService.getSocialUserByCode(userType, type, code, state); - // 断言 - assertEquals(userId, socialUser.getUserId()); - assertEquals(socialUserDO.getOpenid(), socialUser.getOpenid()); - } - - @Test - public void testAuthSocialUser_exists() { - // 准备参数 - Integer socialType = SocialTypeEnum.GITEE.getType(); - Integer userType = randomEle(SocialTypeEnum.values()).getType(); - String code = "tudou"; - String state = "yuanma"; - // mock 方法 - SocialUserDO socialUser = randomPojo(SocialUserDO.class).setType(socialType).setCode(code).setState(state); - socialUserMapper.insert(socialUser); - - // 调用 - SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); - // 断言 - assertPojoEquals(socialUser, result); - } - - @Test - public void testAuthSocialUser_notNull() { - // mock 数据 - SocialUserDO socialUser = randomPojo(SocialUserDO.class, - o -> o.setType(SocialTypeEnum.GITEE.getType()).setCode("tudou").setState("yuanma")); - socialUserMapper.insert(socialUser); - // 准备参数 - Integer socialType = SocialTypeEnum.GITEE.getType(); - Integer userType = randomEle(SocialTypeEnum.values()).getType(); - String code = "tudou"; - String state = "yuanma"; - - // 调用 - SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); - // 断言 - assertPojoEquals(socialUser, result); - } - - @Test - public void testAuthSocialUser_insert() { - // 准备参数 - Integer socialType = SocialTypeEnum.GITEE.getType(); - Integer userType = randomEle(SocialTypeEnum.values()).getType(); - String code = "tudou"; - String state = "yuanma"; - // mock 方法 - AuthUser authUser = randomPojo(AuthUser.class); - when(socialClientService.getAuthUser(eq(socialType), eq(userType), eq(code), eq(state))).thenReturn(authUser); - - // 调用 - SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); - // 断言 - assertBindSocialUser(socialType, result, authUser); - assertEquals(code, result.getCode()); - assertEquals(state, result.getState()); - } - - @Test - public void testAuthSocialUser_update() { - // 准备参数 - Integer socialType = SocialTypeEnum.GITEE.getType(); - Integer userType = randomEle(SocialTypeEnum.values()).getType(); - String code = "tudou"; - String state = "yuanma"; - // mock 数据 - socialUserMapper.insert(randomPojo(SocialUserDO.class).setType(socialType).setOpenid("test_openid")); - // mock 方法 - AuthUser authUser = randomPojo(AuthUser.class); - when(socialClientService.getAuthUser(eq(socialType), eq(userType), eq(code), eq(state))).thenReturn(authUser); - - // 调用 - SocialUserDO result = socialUserService.authSocialUser(socialType, userType, code, state); - // 断言 - assertBindSocialUser(socialType, result, authUser); - assertEquals(code, result.getCode()); - assertEquals(state, result.getState()); - } - - private void assertBindSocialUser(Integer type, SocialUserDO socialUser, AuthUser authUser) { - assertEquals(authUser.getToken().getAccessToken(), socialUser.getToken()); - assertEquals(toJsonString(authUser.getToken()), socialUser.getRawTokenInfo()); - assertEquals(authUser.getNickname(), socialUser.getNickname()); - assertEquals(authUser.getAvatar(), socialUser.getAvatar()); - assertEquals(toJsonString(authUser.getRawUserInfo()), socialUser.getRawUserInfo()); - assertEquals(type, socialUser.getType()); - assertEquals(authUser.getUuid(), socialUser.getOpenid()); - } - - @Test - public void testGetSocialUser_id() { - // mock 数据 - SocialUserDO socialUserDO = randomPojo(SocialUserDO.class); - socialUserMapper.insert(socialUserDO); - // 参数准备 - Long id = socialUserDO.getId(); - - // 调用 - SocialUserDO dbSocialUserDO = socialUserService.getSocialUser(id); - // 断言 - assertPojoEquals(socialUserDO, dbSocialUserDO); - } - - @Test - public void testGetSocialUserPage() { - // mock 数据 - SocialUserDO dbSocialUser = randomPojo(SocialUserDO.class, o -> { // 等会查询到 - o.setType(SocialTypeEnum.GITEE.getType()); - o.setNickname("芋艿"); - o.setOpenid("yudaoyuanma"); - o.setCreateTime(buildTime(2020, 1, 15)); - }); - socialUserMapper.insert(dbSocialUser); - // 测试 type 不匹配 - socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setType(SocialTypeEnum.DINGTALK.getType()))); - // 测试 nickname 不匹配 - socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setNickname(randomString()))); - // 测试 openid 不匹配 - socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setOpenid("java"))); - // 测试 createTime 不匹配 - socialUserMapper.insert(cloneIgnoreId(dbSocialUser, o -> o.setCreateTime(buildTime(2020, 1, 21)))); - // 准备参数 - SocialUserPageReqVO reqVO = new SocialUserPageReqVO(); - reqVO.setType(SocialTypeEnum.GITEE.getType()); - reqVO.setNickname("芋"); - reqVO.setOpenid("yudao"); - reqVO.setCreateTime(buildBetweenTime(2020, 1, 10, 2020, 1, 20)); - - // 调用 - PageResult pageResult = socialUserService.getSocialUserPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSocialUser, pageResult.getList().get(0)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImplTest.java deleted file mode 100755 index 0e030f3..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImplTest.java +++ /dev/null @@ -1,236 +0,0 @@ -package cn.iocoder.yudao.module.system.service.tenant; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; -import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Arrays.asList; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -/** -* {@link TenantPackageServiceImpl} 的单元测试类 -* -* @author 芋道源码 -*/ -@Import(TenantPackageServiceImpl.class) -public class TenantPackageServiceImplTest extends BaseDbUnitTest { - - @Resource - private TenantPackageServiceImpl tenantPackageService; - - @Resource - private TenantPackageMapper tenantPackageMapper; - - @MockBean - private TenantService tenantService; - - @Test - public void testCreateTenantPackage_success() { - // 准备参数 - TenantPackageSaveReqVO reqVO = randomPojo(TenantPackageSaveReqVO.class, - o -> o.setStatus(randomCommonStatus())) - .setId(null); // 防止 id 被赋值 - - // 调用 - Long tenantPackageId = tenantPackageService.createTenantPackage(reqVO); - // 断言 - assertNotNull(tenantPackageId); - // 校验记录的属性是否正确 - TenantPackageDO tenantPackage = tenantPackageMapper.selectById(tenantPackageId); - assertPojoEquals(reqVO, tenantPackage, "id"); - } - - @Test - public void testUpdateTenantPackage_success() { - // mock 数据 - TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, - o -> o.setStatus(randomCommonStatus())); - tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 - // 准备参数 - TenantPackageSaveReqVO reqVO = randomPojo(TenantPackageSaveReqVO.class, o -> { - o.setId(dbTenantPackage.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - }); - // mock 方法 - Long tenantId01 = randomLongId(); - Long tenantId02 = randomLongId(); - when(tenantService.getTenantListByPackageId(eq(reqVO.getId()))).thenReturn( - asList(randomPojo(TenantDO.class, o -> o.setId(tenantId01)), - randomPojo(TenantDO.class, o -> o.setId(tenantId02)))); - - // 调用 - tenantPackageService.updateTenantPackage(reqVO); - // 校验是否更新正确 - TenantPackageDO tenantPackage = tenantPackageMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, tenantPackage); - // 校验调用租户的菜单 - verify(tenantService).updateTenantRoleMenu(eq(tenantId01), eq(reqVO.getMenuIds())); - verify(tenantService).updateTenantRoleMenu(eq(tenantId02), eq(reqVO.getMenuIds())); - } - - @Test - public void testUpdateTenantPackage_notExists() { - // 准备参数 - TenantPackageSaveReqVO reqVO = randomPojo(TenantPackageSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> tenantPackageService.updateTenantPackage(reqVO), TENANT_PACKAGE_NOT_EXISTS); - } - - @Test - public void testDeleteTenantPackage_success() { - // mock 数据 - TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class); - tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbTenantPackage.getId(); - // mock 租户未使用该套餐 - when(tenantService.getTenantCountByPackageId(eq(id))).thenReturn(0L); - - // 调用 - tenantPackageService.deleteTenantPackage(id); - // 校验数据不存在了 - assertNull(tenantPackageMapper.selectById(id)); - } - - @Test - public void testDeleteTenantPackage_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> tenantPackageService.deleteTenantPackage(id), TENANT_PACKAGE_NOT_EXISTS); - } - - @Test - public void testDeleteTenantPackage_used() { - // mock 数据 - TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class); - tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbTenantPackage.getId(); - // mock 租户在使用该套餐 - when(tenantService.getTenantCountByPackageId(eq(id))).thenReturn(1L); - - // 调用, 并断言异常 - assertServiceException(() -> tenantPackageService.deleteTenantPackage(id), TENANT_PACKAGE_USED); - } - - @Test - public void testGetTenantPackagePage() { - // mock 数据 - TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, o -> { // 等会查询到 - o.setName("芋道源码"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setRemark("源码解析"); - o.setCreateTime(buildTime(2022, 10, 10)); - }); - tenantPackageMapper.insert(dbTenantPackage); - // 测试 name 不匹配 - tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setName("源码"))); - // 测试 status 不匹配 - tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 remark 不匹配 - tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setRemark("解析"))); - // 测试 createTime 不匹配 - tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, o -> o.setCreateTime(buildTime(2022, 11, 11)))); - // 准备参数 - TenantPackagePageReqVO reqVO = new TenantPackagePageReqVO(); - reqVO.setName("芋道"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setRemark("源码"); - reqVO.setCreateTime(buildBetweenTime(2022, 10, 9, 2022, 10, 11)); - - // 调用 - PageResult pageResult = tenantPackageService.getTenantPackagePage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbTenantPackage, pageResult.getList().get(0)); - } - - @Test - public void testValidTenantPackage_success() { - // mock 数据 - TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, - o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 - - // 调用 - TenantPackageDO result = tenantPackageService.validTenantPackage(dbTenantPackage.getId()); - // 断言 - assertPojoEquals(dbTenantPackage, result); - } - - @Test - public void testValidTenantPackage_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> tenantPackageService.validTenantPackage(id), TENANT_PACKAGE_NOT_EXISTS); - } - - @Test - public void testValidTenantPackage_disable() { - // mock 数据 - TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, - o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 - - // 调用, 并断言异常 - assertServiceException(() -> tenantPackageService.validTenantPackage(dbTenantPackage.getId()), - TENANT_PACKAGE_DISABLE, dbTenantPackage.getName()); - } - - @Test - public void testGetTenantPackage() { - // mock 数据 - TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class); - tenantPackageMapper.insert(dbTenantPackage);// @Sql: 先插入出一条存在的数据 - - // 调用 - TenantPackageDO result = tenantPackageService.getTenantPackage(dbTenantPackage.getId()); - // 断言 - assertPojoEquals(result, dbTenantPackage); - } - - @Test - public void testGetTenantPackageListByStatus() { - // mock 数据 - TenantPackageDO dbTenantPackage = randomPojo(TenantPackageDO.class, - o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus())); - tenantPackageMapper.insert(dbTenantPackage); - // 测试 status 不匹配 - tenantPackageMapper.insert(cloneIgnoreId(dbTenantPackage, - o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - - // 调用 - List list = tenantPackageService.getTenantPackageListByStatus( - CommonStatusEnum.ENABLE.getStatus()); - assertEquals(1, list.size()); - assertPojoEquals(dbTenantPackage, list.get(0)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java deleted file mode 100644 index f4d4f86..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java +++ /dev/null @@ -1,458 +0,0 @@ -package cn.iocoder.yudao.module.system.service.tenant; - -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.tenant.config.TenantProperties; -import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; -import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper; -import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; -import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; -import cn.iocoder.yudao.module.system.service.permission.MenuService; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import cn.iocoder.yudao.module.system.service.permission.RoleService; -import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; -import cn.iocoder.yudao.module.system.service.tenant.handler.TenantMenuHandler; -import cn.iocoder.yudao.module.system.service.user.AdminUserService; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; - -import jakarta.annotation.Resource; -import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO.PACKAGE_ID_SYSTEM; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static java.util.Arrays.asList; -import static java.util.Collections.singleton; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -/** - * {@link TenantServiceImpl} 的单元测试类 - * - * @author 芋道源码 - */ -@Import(TenantServiceImpl.class) -public class TenantServiceImplTest extends BaseDbUnitTest { - - @Resource - private TenantServiceImpl tenantService; - - @Resource - private TenantMapper tenantMapper; - - @MockBean - private TenantProperties tenantProperties; - @MockBean - private TenantPackageService tenantPackageService; - @MockBean - private AdminUserService userService; - @MockBean - private RoleService roleService; - @MockBean - private MenuService menuService; - @MockBean - private PermissionService permissionService; - - @BeforeEach - public void setUp() { - // 清理租户上下文 - TenantContextHolder.clear(); - } - - @Test - public void testGetTenantIdList() { - // mock 数据 - TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L)); - tenantMapper.insert(tenant); - - // 调用,并断言业务异常 - List result = tenantService.getTenantIdList(); - assertEquals(Collections.singletonList(1L), result); - } - - @Test - public void testValidTenant_notExists() { - assertServiceException(() -> tenantService.validTenant(randomLongId()), TENANT_NOT_EXISTS); - } - - @Test - public void testValidTenant_disable() { - // mock 数据 - TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.DISABLE.getStatus())); - tenantMapper.insert(tenant); - - // 调用,并断言业务异常 - assertServiceException(() -> tenantService.validTenant(1L), TENANT_DISABLE, tenant.getName()); - } - - @Test - public void testValidTenant_expired() { - // mock 数据 - TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus()) - .setExpireTime(buildTime(2020, 2, 2))); - tenantMapper.insert(tenant); - - // 调用,并断言业务异常 - assertServiceException(() -> tenantService.validTenant(1L), TENANT_EXPIRE, tenant.getName()); - } - - @Test - public void testValidTenant_success() { - // mock 数据 - TenantDO tenant = randomPojo(TenantDO.class, o -> o.setId(1L).setStatus(CommonStatusEnum.ENABLE.getStatus()) - .setExpireTime(LocalDateTime.now().plusDays(1))); - tenantMapper.insert(tenant); - - // 调用,并断言业务异常 - tenantService.validTenant(1L); - } - - @Test - public void testCreateTenant() { - // mock 套餐 100L - TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class, o -> o.setId(100L)); - when(tenantPackageService.validTenantPackage(eq(100L))).thenReturn(tenantPackage); - // mock 角色 200L - when(roleService.createRole(argThat(role -> { - assertEquals(RoleCodeEnum.TENANT_ADMIN.getName(), role.getName()); - assertEquals(RoleCodeEnum.TENANT_ADMIN.getCode(), role.getCode()); - assertEquals(0, role.getSort()); - assertEquals("系统自动生成", role.getRemark()); - return true; - }), eq(RoleTypeEnum.SYSTEM.getType()))).thenReturn(200L); - // mock 用户 300L - when(userService.createUser(argThat(user -> { - assertEquals("yunai", user.getUsername()); - assertEquals("yuanma", user.getPassword()); - assertEquals("芋道", user.getNickname()); - assertEquals("15601691300", user.getMobile()); - return true; - }))).thenReturn(300L); - - // 准备参数 - TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> { - o.setContactName("芋道"); - o.setContactMobile("15601691300"); - o.setPackageId(100L); - o.setStatus(randomCommonStatus()); - o.setWebsite("https://www.iocoder.cn"); - o.setUsername("yunai"); - o.setPassword("yuanma"); - }).setId(null); // 设置为 null,方便后面校验 - - // 调用 - Long tenantId = tenantService.createTenant(reqVO); - // 断言 - assertNotNull(tenantId); - // 校验记录的属性是否正确 - TenantDO tenant = tenantMapper.selectById(tenantId); - assertPojoEquals(reqVO, tenant, "id"); - assertEquals(300L, tenant.getContactUserId()); - // verify 分配权限 - verify(permissionService).assignRoleMenu(eq(200L), same(tenantPackage.getMenuIds())); - // verify 分配角色 - verify(permissionService).assignUserRole(eq(300L), eq(singleton(200L))); - } - - @Test - public void testUpdateTenant_success() { - // mock 数据 - TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setStatus(randomCommonStatus())); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - // 准备参数 - TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> { - o.setId(dbTenant.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - o.setWebsite(randomString()); - }); - - // mock 套餐 - TenantPackageDO tenantPackage = randomPojo(TenantPackageDO.class, - o -> o.setMenuIds(asSet(200L, 201L))); - when(tenantPackageService.validTenantPackage(eq(reqVO.getPackageId()))).thenReturn(tenantPackage); - // mock 所有角色 - RoleDO role100 = randomPojo(RoleDO.class, o -> o.setId(100L).setCode(RoleCodeEnum.TENANT_ADMIN.getCode())); - role100.setTenantId(dbTenant.getId()); - RoleDO role101 = randomPojo(RoleDO.class, o -> o.setId(101L)); - role101.setTenantId(dbTenant.getId()); - when(roleService.getRoleList()).thenReturn(asList(role100, role101)); - // mock 每个角色的权限 - when(permissionService.getRoleMenuListByRoleId(eq(101L))).thenReturn(asSet(201L, 202L)); - - // 调用 - tenantService.updateTenant(reqVO); - // 校验是否更新正确 - TenantDO tenant = tenantMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, tenant); - // verify 设置角色权限 - verify(permissionService).assignRoleMenu(eq(100L), eq(asSet(200L, 201L))); - verify(permissionService).assignRoleMenu(eq(101L), eq(asSet(201L))); - } - - @Test - public void testUpdateTenant_notExists() { - // 准备参数 - TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> tenantService.updateTenant(reqVO), TENANT_NOT_EXISTS); - } - - @Test - public void testUpdateTenant_system() { - // mock 数据 - TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM)); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - // 准备参数 - TenantSaveReqVO reqVO = randomPojo(TenantSaveReqVO.class, o -> { - o.setId(dbTenant.getId()); // 设置更新的 ID - }); - - // 调用,校验业务异常 - assertServiceException(() -> tenantService.updateTenant(reqVO), TENANT_CAN_NOT_UPDATE_SYSTEM); - } - - @Test - public void testDeleteTenant_success() { - // mock 数据 - TenantDO dbTenant = randomPojo(TenantDO.class, - o -> o.setStatus(randomCommonStatus())); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbTenant.getId(); - - // 调用 - tenantService.deleteTenant(id); - // 校验数据不存在了 - assertNull(tenantMapper.selectById(id)); - } - - @Test - public void testDeleteTenant_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> tenantService.deleteTenant(id), TENANT_NOT_EXISTS); - } - - @Test - public void testDeleteTenant_system() { - // mock 数据 - TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM)); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbTenant.getId(); - - // 调用, 并断言异常 - assertServiceException(() -> tenantService.deleteTenant(id), TENANT_CAN_NOT_UPDATE_SYSTEM); - } - - @Test - public void testGetTenant() { - // mock 数据 - TenantDO dbTenant = randomPojo(TenantDO.class); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbTenant.getId(); - - // 调用 - TenantDO result = tenantService.getTenant(id); - // 校验存在 - assertPojoEquals(result, dbTenant); - } - - @Test - public void testGetTenantPage() { - // mock 数据 - TenantDO dbTenant = randomPojo(TenantDO.class, o -> { // 等会查询到 - o.setName("芋道源码"); - o.setContactName("芋艿"); - o.setContactMobile("15601691300"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2020, 12, 12)); - }); - tenantMapper.insert(dbTenant); - // 测试 name 不匹配 - tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setName(randomString()))); - // 测试 contactName 不匹配 - tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactName(randomString()))); - // 测试 contactMobile 不匹配 - tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setContactMobile(randomString()))); - // 测试 status 不匹配 - tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - tenantMapper.insert(cloneIgnoreId(dbTenant, o -> o.setCreateTime(buildTime(2021, 12, 12)))); - // 准备参数 - TenantPageReqVO reqVO = new TenantPageReqVO(); - reqVO.setName("芋道"); - reqVO.setContactName("艿"); - reqVO.setContactMobile("1560"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); - - // 调用 - PageResult pageResult = tenantService.getTenantPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbTenant, pageResult.getList().get(0)); - } - - @Test - public void testGetTenantByName() { - // mock 数据 - TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setName("芋道")); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - - // 调用 - TenantDO result = tenantService.getTenantByName("芋道"); - // 校验存在 - assertPojoEquals(result, dbTenant); - } - - @Test - public void testGetTenantByWebsite() { - // mock 数据 - TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setWebsite("https://www.iocoder.cn")); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - - // 调用 - TenantDO result = tenantService.getTenantByWebsite("https://www.iocoder.cn"); - // 校验存在 - assertPojoEquals(result, dbTenant); - } - - @Test - public void testGetTenantListByPackageId() { - // mock 数据 - TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L)); - tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据 - TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L)); - tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据 - - // 调用 - List result = tenantService.getTenantListByPackageId(1L); - assertEquals(1, result.size()); - assertPojoEquals(dbTenant1, result.get(0)); - } - - @Test - public void testGetTenantCountByPackageId() { - // mock 数据 - TenantDO dbTenant1 = randomPojo(TenantDO.class, o -> o.setPackageId(1L)); - tenantMapper.insert(dbTenant1);// @Sql: 先插入出一条存在的数据 - TenantDO dbTenant2 = randomPojo(TenantDO.class, o -> o.setPackageId(2L)); - tenantMapper.insert(dbTenant2);// @Sql: 先插入出一条存在的数据 - - // 调用 - Long count = tenantService.getTenantCountByPackageId(1L); - assertEquals(1, count); - } - - @Test - public void testHandleTenantInfo_disable() { - // 准备参数 - TenantInfoHandler handler = mock(TenantInfoHandler.class); - // mock 禁用 - when(tenantProperties.getEnable()).thenReturn(false); - - // 调用 - tenantService.handleTenantInfo(handler); - // 断言 - verify(handler, never()).handle(any()); - } - - @Test - public void testHandleTenantInfo_success() { - // 准备参数 - TenantInfoHandler handler = mock(TenantInfoHandler.class); - // mock 未禁用 - when(tenantProperties.getEnable()).thenReturn(true); - // mock 租户 - TenantDO dbTenant = randomPojo(TenantDO.class); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - TenantContextHolder.setTenantId(dbTenant.getId()); - - // 调用 - tenantService.handleTenantInfo(handler); - // 断言 - verify(handler).handle(argThat(argument -> { - assertPojoEquals(dbTenant, argument); - return true; - })); - } - - @Test - public void testHandleTenantMenu_disable() { - // 准备参数 - TenantMenuHandler handler = mock(TenantMenuHandler.class); - // mock 禁用 - when(tenantProperties.getEnable()).thenReturn(false); - - // 调用 - tenantService.handleTenantMenu(handler); - // 断言 - verify(handler, never()).handle(any()); - } - - @Test // 系统租户的情况 - public void testHandleTenantMenu_system() { - // 准备参数 - TenantMenuHandler handler = mock(TenantMenuHandler.class); - // mock 未禁用 - when(tenantProperties.getEnable()).thenReturn(true); - // mock 租户 - TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(PACKAGE_ID_SYSTEM)); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - TenantContextHolder.setTenantId(dbTenant.getId()); - // mock 菜单 - when(menuService.getMenuList()).thenReturn(Arrays.asList(randomPojo(MenuDO.class, o -> o.setId(100L)), - randomPojo(MenuDO.class, o -> o.setId(101L)))); - - // 调用 - tenantService.handleTenantMenu(handler); - // 断言 - verify(handler).handle(asSet(100L, 101L)); - } - - @Test // 普通租户的情况 - public void testHandleTenantMenu_normal() { - // 准备参数 - TenantMenuHandler handler = mock(TenantMenuHandler.class); - // mock 未禁用 - when(tenantProperties.getEnable()).thenReturn(true); - // mock 租户 - TenantDO dbTenant = randomPojo(TenantDO.class, o -> o.setPackageId(200L)); - tenantMapper.insert(dbTenant);// @Sql: 先插入出一条存在的数据 - TenantContextHolder.setTenantId(dbTenant.getId()); - // mock 菜单 - when(tenantPackageService.getTenantPackage(eq(200L))).thenReturn(randomPojo(TenantPackageDO.class, - o -> o.setMenuIds(asSet(100L, 101L)))); - - // 调用 - tenantService.handleTenantMenu(handler); - // 断言 - verify(handler).handle(asSet(100L, 101L)); - } -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java deleted file mode 100644 index 6256c42..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java +++ /dev/null @@ -1,763 +0,0 @@ -package cn.iocoder.yudao.module.system.service.user; - -import cn.hutool.core.util.RandomUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.exception.ServiceException; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.module.infra.api.config.ConfigApi; -import cn.iocoder.yudao.module.infra.api.file.FileApi; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportExcelVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserImportRespVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO; -import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.UserPostDO; -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; -import cn.iocoder.yudao.module.system.dal.mysql.dept.UserPostMapper; -import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper; -import cn.iocoder.yudao.module.system.enums.common.SexEnum; -import cn.iocoder.yudao.module.system.service.dept.DeptService; -import cn.iocoder.yudao.module.system.service.dept.PostService; -import cn.iocoder.yudao.module.system.service.permission.PermissionService; -import cn.iocoder.yudao.module.system.service.tenant.TenantService; -import jakarta.annotation.Resource; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.stubbing.Answer; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Import; -import org.springframework.security.crypto.password.PasswordEncoder; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import static cn.hutool.core.util.RandomUtil.randomEle; -import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; -import static cn.iocoder.yudao.module.system.service.user.AdminUserServiceImpl.USER_INIT_PASSWORD_KEY; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; -import static org.assertj.core.util.Lists.newArrayList; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; - -@Import(AdminUserServiceImpl.class) -public class AdminUserServiceImplTest extends BaseDbUnitTest { - - @Resource - private AdminUserServiceImpl userService; - - @Resource - private AdminUserMapper userMapper; - @Resource - private UserPostMapper userPostMapper; - - @MockBean - private DeptService deptService; - @MockBean - private PostService postService; - @MockBean - private PermissionService permissionService; - @MockBean - private PasswordEncoder passwordEncoder; - @MockBean - private TenantService tenantService; - @MockBean - private FileApi fileApi; - @MockBean - private ConfigApi configApi; - - @BeforeEach - public void before() { - // mock 初始化密码 - when(configApi.getConfigValueByKey(USER_INIT_PASSWORD_KEY)).thenReturn("yudaoyuanma"); - } - - @Test - public void testCreatUser_success() { - // 准备参数 - UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> { - o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); - o.setMobile(randomString()); - o.setPostIds(asSet(1L, 2L)); - }).setId(null); // 避免 id 被赋值 - // mock 账户额度充足 - TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(1)); - doNothing().when(tenantService).handleTenantInfo(argThat(handler -> { - handler.handle(tenant); - return true; - })); - // mock deptService 的方法 - DeptDO dept = randomPojo(DeptDO.class, o -> { - o.setId(reqVO.getDeptId()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); - // mock postService 的方法 - List posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> - randomPojo(PostDO.class, o -> { - o.setId(postId); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - })); - when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); - // mock passwordEncoder 的方法 - when(passwordEncoder.encode(eq(reqVO.getPassword()))).thenReturn("yudaoyuanma"); - - // 调用 - Long userId = userService.createUser(reqVO); - // 断言 - AdminUserDO user = userMapper.selectById(userId); - assertPojoEquals(reqVO, user, "password", "id"); - assertEquals("yudaoyuanma", user.getPassword()); - assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus()); - // 断言关联岗位 - List userPosts = userPostMapper.selectListByUserId(user.getId()); - assertEquals(1L, userPosts.get(0).getPostId()); - assertEquals(2L, userPosts.get(1).getPostId()); - } - - @Test - public void testCreatUser_max() { - // 准备参数 - UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class); - // mock 账户额度不足 - TenantDO tenant = randomPojo(TenantDO.class, o -> o.setAccountCount(-1)); - doNothing().when(tenantService).handleTenantInfo(argThat(handler -> { - handler.handle(tenant); - return true; - })); - - // 调用,并断言异常 - assertServiceException(() -> userService.createUser(reqVO), USER_COUNT_MAX, -1); - } - - @Test - public void testUpdateUser_success() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(o -> o.setPostIds(asSet(1L, 2L))); - userMapper.insert(dbUser); - userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(1L)); - userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(2L)); - // 准备参数 - UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> { - o.setId(dbUser.getId()); - o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); - o.setMobile(randomString()); - o.setPostIds(asSet(2L, 3L)); - }); - // mock deptService 的方法 - DeptDO dept = randomPojo(DeptDO.class, o -> { - o.setId(reqVO.getDeptId()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); - // mock postService 的方法 - List posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> - randomPojo(PostDO.class, o -> { - o.setId(postId); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - })); - when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); - - // 调用 - userService.updateUser(reqVO); - // 断言 - AdminUserDO user = userMapper.selectById(reqVO.getId()); - assertPojoEquals(reqVO, user, "password"); - // 断言关联岗位 - List userPosts = userPostMapper.selectListByUserId(user.getId()); - assertEquals(2L, userPosts.get(0).getPostId()); - assertEquals(3L, userPosts.get(1).getPostId()); - } - - @Test - public void testUpdateUserLogin() { - // mock 数据 - AdminUserDO user = randomAdminUserDO(o -> o.setLoginDate(null)); - userMapper.insert(user); - // 准备参数 - Long id = user.getId(); - String loginIp = randomString(); - - // 调用 - userService.updateUserLogin(id, loginIp); - // 断言 - AdminUserDO dbUser = userMapper.selectById(id); - assertEquals(loginIp, dbUser.getLoginIp()); - assertNotNull(dbUser.getLoginDate()); - } - - @Test - public void testUpdateUserProfile_success() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(); - userMapper.insert(dbUser); - // 准备参数 - Long userId = dbUser.getId(); - UserProfileUpdateReqVO reqVO = randomPojo(UserProfileUpdateReqVO.class, o -> { - o.setMobile(randomString()); - o.setSex(RandomUtil.randomEle(SexEnum.values()).getSex()); - o.setAvatar(randomURL()); - }); - - // 调用 - userService.updateUserProfile(userId, reqVO); - // 断言 - AdminUserDO user = userMapper.selectById(userId); - assertPojoEquals(reqVO, user); - } - - @Test - public void testUpdateUserPassword_success() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(o -> o.setPassword("encode:tudou")); - userMapper.insert(dbUser); - // 准备参数 - Long userId = dbUser.getId(); - UserProfileUpdatePasswordReqVO reqVO = randomPojo(UserProfileUpdatePasswordReqVO.class, o -> { - o.setOldPassword("tudou"); - o.setNewPassword("yuanma"); - }); - // mock 方法 - when(passwordEncoder.encode(anyString())).then( - (Answer) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0)); - when(passwordEncoder.matches(eq(reqVO.getOldPassword()), eq(dbUser.getPassword()))).thenReturn(true); - - // 调用 - userService.updateUserPassword(userId, reqVO); - // 断言 - AdminUserDO user = userMapper.selectById(userId); - assertEquals("encode:yuanma", user.getPassword()); - } - - @Test - public void testUpdateUserPassword02_success() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(); - userMapper.insert(dbUser); - // 准备参数 - Long userId = dbUser.getId(); - String password = "yudao"; - // mock 方法 - when(passwordEncoder.encode(anyString())).then( - (Answer) invocationOnMock -> "encode:" + invocationOnMock.getArgument(0)); - - // 调用 - userService.updateUserPassword(userId, password); - // 断言 - AdminUserDO user = userMapper.selectById(userId); - assertEquals("encode:" + password, user.getPassword()); - } - - @Test - public void testUpdateUserStatus() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(); - userMapper.insert(dbUser); - // 准备参数 - Long userId = dbUser.getId(); - Integer status = randomCommonStatus(); - - // 调用 - userService.updateUserStatus(userId, status); - // 断言 - AdminUserDO user = userMapper.selectById(userId); - assertEquals(status, user.getStatus()); - } - - @Test - public void testDeleteUser_success(){ - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(); - userMapper.insert(dbUser); - // 准备参数 - Long userId = dbUser.getId(); - - // 调用数据 - userService.deleteUser(userId); - // 校验结果 - assertNull(userMapper.selectById(userId)); - // 校验调用次数 - verify(permissionService, times(1)).processUserDeleted(eq(userId)); - } - - @Test - public void testGetUserByUsername() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(); - userMapper.insert(dbUser); - // 准备参数 - String username = dbUser.getUsername(); - - // 调用 - AdminUserDO user = userService.getUserByUsername(username); - // 断言 - assertPojoEquals(dbUser, user); - } - - @Test - public void testGetUserByMobile() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(); - userMapper.insert(dbUser); - // 准备参数 - String mobile = dbUser.getMobile(); - - // 调用 - AdminUserDO user = userService.getUserByMobile(mobile); - // 断言 - assertPojoEquals(dbUser, user); - } - - @Test - public void testGetUserPage() { - // mock 数据 - AdminUserDO dbUser = initGetUserPageData(); - // 准备参数 - UserPageReqVO reqVO = new UserPageReqVO(); - reqVO.setUsername("tu"); - reqVO.setMobile("1560"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); - reqVO.setDeptId(1L); // 其中,1L 是 2L 的父部门 - // mock 方法 - List deptList = newArrayList(randomPojo(DeptDO.class, o -> o.setId(2L))); - when(deptService.getChildDeptList(eq(reqVO.getDeptId()))).thenReturn(deptList); - - // 调用 - PageResult pageResult = userService.getUserPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbUser, pageResult.getList().get(0)); - } - - /** - * 初始化 getUserPage 方法的测试数据 - */ - private AdminUserDO initGetUserPageData() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(o -> { // 等会查询到 - o.setUsername("tudou"); - o.setMobile("15601691300"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2020, 12, 12)); - o.setDeptId(2L); - }); - userMapper.insert(dbUser); - // 测试 username 不匹配 - userMapper.insert(cloneIgnoreId(dbUser, o -> o.setUsername("dou"))); - // 测试 mobile 不匹配 - userMapper.insert(cloneIgnoreId(dbUser, o -> o.setMobile("18818260888"))); - // 测试 status 不匹配 - userMapper.insert(cloneIgnoreId(dbUser, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - userMapper.insert(cloneIgnoreId(dbUser, o -> o.setCreateTime(buildTime(2020, 11, 11)))); - // 测试 dept 不匹配 - userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(0L))); - return dbUser; - } - - @Test - public void testGetUser() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(); - userMapper.insert(dbUser); - // 准备参数 - Long userId = dbUser.getId(); - - // 调用 - AdminUserDO user = userService.getUser(userId); - // 断言 - assertPojoEquals(dbUser, user); - } - - @Test - public void testGetUserListByDeptIds() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(o -> o.setDeptId(1L)); - userMapper.insert(dbUser); - // 测试 deptId 不匹配 - userMapper.insert(cloneIgnoreId(dbUser, o -> o.setDeptId(2L))); - // 准备参数 - Collection deptIds = singleton(1L); - - // 调用 - List list = userService.getUserListByDeptIds(deptIds); - // 断言 - assertEquals(1, list.size()); - assertEquals(dbUser, list.get(0)); - } - - /** - * 情况一,校验不通过,导致插入失败 - */ - @Test - public void testImportUserList_01() { - // 准备参数 - UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { - o.setEmail(randomEmail()); - o.setMobile(randomMobile()); - }); - // mock 方法,模拟失败 - doThrow(new ServiceException(DEPT_NOT_FOUND)).when(deptService).validateDeptList(any()); - - // 调用 - UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true); - // 断言 - assertEquals(0, respVO.getCreateUsernames().size()); - assertEquals(0, respVO.getUpdateUsernames().size()); - assertEquals(1, respVO.getFailureUsernames().size()); - assertEquals(DEPT_NOT_FOUND.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername())); - } - - /** - * 情况二,不存在,进行插入 - */ - @Test - public void testImportUserList_02() { - // 准备参数 - UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 - o.setEmail(randomEmail()); - o.setMobile(randomMobile()); - }); - // mock deptService 的方法 - DeptDO dept = randomPojo(DeptDO.class, o -> { - o.setId(importUser.getDeptId()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); - // mock passwordEncoder 的方法 - when(passwordEncoder.encode(eq("yudaoyuanma"))).thenReturn("java"); - - // 调用 - UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true); - // 断言 - assertEquals(1, respVO.getCreateUsernames().size()); - AdminUserDO user = userMapper.selectByUsername(respVO.getCreateUsernames().get(0)); - assertPojoEquals(importUser, user); - assertEquals("java", user.getPassword()); - assertEquals(0, respVO.getUpdateUsernames().size()); - assertEquals(0, respVO.getFailureUsernames().size()); - } - - /** - * 情况三,存在,但是不强制更新 - */ - @Test - public void testImportUserList_03() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(); - userMapper.insert(dbUser); - // 准备参数 - UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 - o.setUsername(dbUser.getUsername()); - o.setEmail(randomEmail()); - o.setMobile(randomMobile()); - }); - // mock deptService 的方法 - DeptDO dept = randomPojo(DeptDO.class, o -> { - o.setId(importUser.getDeptId()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); - - // 调用 - UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), false); - // 断言 - assertEquals(0, respVO.getCreateUsernames().size()); - assertEquals(0, respVO.getUpdateUsernames().size()); - assertEquals(1, respVO.getFailureUsernames().size()); - assertEquals(USER_USERNAME_EXISTS.getMsg(), respVO.getFailureUsernames().get(importUser.getUsername())); - } - - /** - * 情况四,存在,强制更新 - */ - @Test - public void testImportUserList_04() { - // mock 数据 - AdminUserDO dbUser = randomAdminUserDO(); - userMapper.insert(dbUser); - // 准备参数 - UserImportExcelVO importUser = randomPojo(UserImportExcelVO.class, o -> { - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 - o.setUsername(dbUser.getUsername()); - o.setEmail(randomEmail()); - o.setMobile(randomMobile()); - }); - // mock deptService 的方法 - DeptDO dept = randomPojo(DeptDO.class, o -> { - o.setId(importUser.getDeptId()); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - }); - when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); - - // 调用 - UserImportRespVO respVO = userService.importUserList(newArrayList(importUser), true); - // 断言 - assertEquals(0, respVO.getCreateUsernames().size()); - assertEquals(1, respVO.getUpdateUsernames().size()); - AdminUserDO user = userMapper.selectByUsername(respVO.getUpdateUsernames().get(0)); - assertPojoEquals(importUser, user); - assertEquals(0, respVO.getFailureUsernames().size()); - } - - @Test - public void testValidateUserExists_notExists() { - assertServiceException(() -> userService.validateUserExists(randomLongId()), USER_NOT_EXISTS); - } - - @Test - public void testValidateUsernameUnique_usernameExistsForCreate() { - // 准备参数 - String username = randomString(); - // mock 数据 - userMapper.insert(randomAdminUserDO(o -> o.setUsername(username))); - - // 调用,校验异常 - assertServiceException(() -> userService.validateUsernameUnique(null, username), - USER_USERNAME_EXISTS); - } - - @Test - public void testValidateUsernameUnique_usernameExistsForUpdate() { - // 准备参数 - Long id = randomLongId(); - String username = randomString(); - // mock 数据 - userMapper.insert(randomAdminUserDO(o -> o.setUsername(username))); - - // 调用,校验异常 - assertServiceException(() -> userService.validateUsernameUnique(id, username), - USER_USERNAME_EXISTS); - } - - @Test - public void testValidateEmailUnique_emailExistsForCreate() { - // 准备参数 - String email = randomString(); - // mock 数据 - userMapper.insert(randomAdminUserDO(o -> o.setEmail(email))); - - // 调用,校验异常 - assertServiceException(() -> userService.validateEmailUnique(null, email), - USER_EMAIL_EXISTS); - } - - @Test - public void testValidateEmailUnique_emailExistsForUpdate() { - // 准备参数 - Long id = randomLongId(); - String email = randomString(); - // mock 数据 - userMapper.insert(randomAdminUserDO(o -> o.setEmail(email))); - - // 调用,校验异常 - assertServiceException(() -> userService.validateEmailUnique(id, email), - USER_EMAIL_EXISTS); - } - - @Test - public void testValidateMobileUnique_mobileExistsForCreate() { - // 准备参数 - String mobile = randomString(); - // mock 数据 - userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile))); - - // 调用,校验异常 - assertServiceException(() -> userService.validateMobileUnique(null, mobile), - USER_MOBILE_EXISTS); - } - - @Test - public void testValidateMobileUnique_mobileExistsForUpdate() { - // 准备参数 - Long id = randomLongId(); - String mobile = randomString(); - // mock 数据 - userMapper.insert(randomAdminUserDO(o -> o.setMobile(mobile))); - - // 调用,校验异常 - assertServiceException(() -> userService.validateMobileUnique(id, mobile), - USER_MOBILE_EXISTS); - } - - @Test - public void testValidateOldPassword_notExists() { - assertServiceException(() -> userService.validateOldPassword(randomLongId(), randomString()), - USER_NOT_EXISTS); - } - - @Test - public void testValidateOldPassword_passwordFailed() { - // mock 数据 - AdminUserDO user = randomAdminUserDO(); - userMapper.insert(user); - // 准备参数 - Long id = user.getId(); - String oldPassword = user.getPassword(); - - // 调用,校验异常 - assertServiceException(() -> userService.validateOldPassword(id, oldPassword), - USER_PASSWORD_FAILED); - // 校验调用 - verify(passwordEncoder, times(1)).matches(eq(oldPassword), eq(user.getPassword())); - } - - @Test - public void testUserListByPostIds() { - // 准备参数 - Collection postIds = asSet(10L, 20L); - // mock user1 数据 - AdminUserDO user1 = randomAdminUserDO(o -> o.setPostIds(asSet(10L, 30L))); - userMapper.insert(user1); - userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(10L)); - userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(30L)); - // mock user2 数据 - AdminUserDO user2 = randomAdminUserDO(o -> o.setPostIds(singleton(100L))); - userMapper.insert(user2); - userPostMapper.insert(new UserPostDO().setUserId(user2.getId()).setPostId(100L)); - - // 调用 - List result = userService.getUserListByPostIds(postIds); - // 断言 - assertEquals(1, result.size()); - assertEquals(user1, result.get(0)); - } - - @Test - public void testGetUserList() { - // mock 数据 - AdminUserDO user = randomAdminUserDO(); - userMapper.insert(user); - // 测试 id 不匹配 - userMapper.insert(randomAdminUserDO()); - // 准备参数 - Collection ids = singleton(user.getId()); - - // 调用 - List result = userService.getUserList(ids); - // 断言 - assertEquals(1, result.size()); - assertEquals(user, result.get(0)); - } - - @Test - public void testGetUserMap() { - // mock 数据 - AdminUserDO user = randomAdminUserDO(); - userMapper.insert(user); - // 测试 id 不匹配 - userMapper.insert(randomAdminUserDO()); - // 准备参数 - Collection ids = singleton(user.getId()); - - // 调用 - Map result = userService.getUserMap(ids); - // 断言 - assertEquals(1, result.size()); - assertEquals(user, result.get(user.getId())); - } - - @Test - public void testGetUserListByNickname() { - // mock 数据 - AdminUserDO user = randomAdminUserDO(o -> o.setNickname("芋头")); - userMapper.insert(user); - // 测试 nickname 不匹配 - userMapper.insert(randomAdminUserDO(o -> o.setNickname("源码"))); - // 准备参数 - String nickname = "芋"; - - // 调用 - List result = userService.getUserListByNickname(nickname); - // 断言 - assertEquals(1, result.size()); - assertEquals(user, result.get(0)); - } - - @Test - public void testGetUserListByStatus() { - // mock 数据 - AdminUserDO user = randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())); - userMapper.insert(user); - // 测试 status 不匹配 - userMapper.insert(randomAdminUserDO(o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()))); - // 准备参数 - Integer status = CommonStatusEnum.DISABLE.getStatus(); - - // 调用 - List result = userService.getUserListByStatus(status); - // 断言 - assertEquals(1, result.size()); - assertEquals(user, result.get(0)); - } - - @Test - public void testValidateUserList_success() { - // mock 数据 - AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.ENABLE.getStatus()); - userMapper.insert(userDO); - // 准备参数 - List ids = singletonList(userDO.getId()); - - // 调用,无需断言 - userService.validateUserList(ids); - } - - @Test - public void testValidateUserList_notFound() { - // 准备参数 - List ids = singletonList(randomLongId()); - - // 调用, 并断言异常 - assertServiceException(() -> userService.validateUserList(ids), USER_NOT_EXISTS); - } - - @Test - public void testValidateUserList_notEnable() { - // mock 数据 - AdminUserDO userDO = randomAdminUserDO().setStatus(CommonStatusEnum.DISABLE.getStatus()); - userMapper.insert(userDO); - // 准备参数 - List ids = singletonList(userDO.getId()); - - // 调用, 并断言异常 - assertServiceException(() -> userService.validateUserList(ids), USER_IS_DISABLE, - userDO.getNickname()); - } - - // ========== 随机对象 ========== - - @SafeVarargs - private static AdminUserDO randomAdminUserDO(Consumer... consumers) { - Consumer consumer = (o) -> { - o.setStatus(randomEle(CommonStatusEnum.values()).getStatus()); // 保证 status 的范围 - o.setSex(randomEle(SexEnum.values()).getSex()); // 保证 sex 的范围 - }; - return randomPojo(AdminUserDO.class, ArrayUtils.append(consumer, consumers)); - } - -} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/application-unit-test.yaml b/yudao-module-system/yudao-module-system-biz/src/test/resources/application-unit-test.yaml deleted file mode 100644 index c1f6573..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/resources/application-unit-test.yaml +++ /dev/null @@ -1,48 +0,0 @@ -spring: - main: - lazy-initialization: true # 开启懒加载,加快速度 - banner-mode: off # 单元测试,禁用 Banner - ---- #################### 数据库相关配置 #################### - -spring: - # 数据源配置项 - datasource: - name: ruoyi-vue-pro - url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 - driver-class-name: org.h2.Driver - username: sa - password: - druid: - async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 - initial-size: 1 # 单元测试,配置为 1,提升启动速度 - sql: - init: - schema-locations: classpath:/sql/create_tables.sql - - # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 - data: - redis: - host: 127.0.0.1 # 地址 - port: 16379 # 端口(单元测试,使用 16379 端口) - database: 0 # 数据库索引 - -mybatis: - lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 - ---- #################### 定时任务相关配置 #################### - ---- #################### 配置中心相关配置 #################### - ---- #################### 服务保障相关配置 #################### - -# Lock4j 配置项(单元测试,禁用 Lock4j) - ---- #################### 监控相关配置 #################### - ---- #################### 芋道相关配置 #################### - -# 芋道配置项,设置当前项目所有自定义的配置 -yudao: - info: - base-package: cn.iocoder.yudao.module diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/logback.xml b/yudao-module-system/yudao-module-system-biz/src/test/resources/logback.xml deleted file mode 100644 index daf756b..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/resources/logback.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql deleted file mode 100644 index e7946a1..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql +++ /dev/null @@ -1,33 +0,0 @@ -DELETE FROM "system_dept"; -DELETE FROM "system_dict_data"; -DELETE FROM "system_role"; -DELETE FROM "system_role_menu"; -DELETE FROM "system_menu"; -DELETE FROM "system_user_role"; -DELETE FROM "system_dict_type"; -DELETE FROM "system_user_session"; -DELETE FROM "system_post"; -DELETE FROM "system_user_post"; -DELETE FROM "system_notice"; -DELETE FROM "system_login_log"; -DELETE FROM "system_operate_log"; -DELETE FROM "system_users"; -DELETE FROM "system_sms_channel"; -DELETE FROM "system_sms_template"; -DELETE FROM "system_sms_log"; -DELETE FROM "system_sms_code"; -DELETE FROM "system_social_client"; -DELETE FROM "system_social_user"; -DELETE FROM "system_social_user_bind"; -DELETE FROM "system_tenant"; -DELETE FROM "system_tenant_package"; -DELETE FROM "system_oauth2_client"; -DELETE FROM "system_oauth2_approve"; -DELETE FROM "system_oauth2_access_token"; -DELETE FROM "system_oauth2_refresh_token"; -DELETE FROM "system_oauth2_code"; -DELETE FROM "system_mail_account"; -DELETE FROM "system_mail_template"; -DELETE FROM "system_mail_log"; -DELETE FROM "system_notify_template"; -DELETE FROM "system_notify_message"; diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql deleted file mode 100644 index 58f029f..0000000 --- a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql +++ /dev/null @@ -1,615 +0,0 @@ -CREATE TABLE IF NOT EXISTS "system_dept" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(30) NOT NULL DEFAULT '', - "parent_id" bigint NOT NULL DEFAULT '0', - "sort" int NOT NULL DEFAULT '0', - "leader_user_id" bigint DEFAULT NULL, - "phone" varchar(11) DEFAULT NULL, - "email" varchar(50) DEFAULT NULL, - "status" tinyint NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY ("id") -) COMMENT '部门表'; - -CREATE TABLE IF NOT EXISTS "system_dict_data" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "sort" int NOT NULL DEFAULT '0', - "label" varchar(100) NOT NULL DEFAULT '', - "value" varchar(100) NOT NULL DEFAULT '', - "dict_type" varchar(100) NOT NULL DEFAULT '', - "status" tinyint NOT NULL DEFAULT '0', - "color_type" varchar(100) NOT NULL DEFAULT '', - "css_class" varchar(100) NOT NULL DEFAULT '', - "remark" varchar(500) DEFAULT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '字典数据表'; - -CREATE TABLE IF NOT EXISTS "system_role" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(30) NOT NULL, - "code" varchar(100) NOT NULL, - "sort" int NOT NULL, - "data_scope" tinyint NOT NULL DEFAULT '1', - "data_scope_dept_ids" varchar(500) NOT NULL DEFAULT '', - "status" tinyint NOT NULL, - "type" tinyint NOT NULL, - "remark" varchar(500) DEFAULT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY ("id") -) COMMENT '角色信息表'; - -CREATE TABLE IF NOT EXISTS "system_role_menu" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "role_id" bigint NOT NULL, - "menu_id" bigint NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY ("id") -) COMMENT '角色和菜单关联表'; - -CREATE TABLE IF NOT EXISTS "system_menu" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(50) NOT NULL, - "permission" varchar(100) NOT NULL DEFAULT '', - "type" tinyint NOT NULL, - "sort" int NOT NULL DEFAULT '0', - "parent_id" bigint NOT NULL DEFAULT '0', - "path" varchar(200) DEFAULT '', - "icon" varchar(100) DEFAULT '#', - "component" varchar(255) DEFAULT NULL, - "component_name" varchar(255) DEFAULT NULL, - "status" tinyint NOT NULL DEFAULT '0', - "visible" bit NOT NULL DEFAULT TRUE, - "keep_alive" bit NOT NULL DEFAULT TRUE, - "always_show" bit NOT NULL DEFAULT TRUE, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '菜单权限表'; - -CREATE TABLE IF NOT EXISTS "system_user_role" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "user_id" bigint NOT NULL, - "role_id" bigint NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp DEFAULT NULL, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp DEFAULT NULL, - "deleted" bit DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY ("id") -) COMMENT '用户和角色关联表'; - -CREATE TABLE IF NOT EXISTS "system_dict_type" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(100) NOT NULL DEFAULT '', - "type" varchar(100) NOT NULL DEFAULT '', - "status" tinyint NOT NULL DEFAULT '0', - "remark" varchar(500) DEFAULT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "deleted_time" timestamp NOT NULL, - PRIMARY KEY ("id") -) COMMENT '字典类型表'; - -CREATE TABLE IF NOT EXISTS `system_user_session` ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - `token` varchar(32) NOT NULL, - `user_id` bigint DEFAULT NULL, - "user_type" tinyint NOT NULL, - `username` varchar(50) NOT NULL DEFAULT '', - `user_ip` varchar(50) DEFAULT NULL, - `user_agent` varchar(512) DEFAULT NULL, - `session_timeout` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updater` varchar(64) DEFAULT '' , - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY (`id`) -) COMMENT '用户在线 Session'; - -CREATE TABLE IF NOT EXISTS "system_post" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "code" varchar(64) NOT NULL, - "name" varchar(50) NOT NULL, - "sort" integer NOT NULL, - "status" tinyint NOT NULL, - "remark" varchar(500) DEFAULT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY ("id") -) COMMENT '岗位信息表'; - -CREATE TABLE IF NOT EXISTS `system_user_post`( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "user_id" bigint DEFAULT NULL, - "post_id" bigint DEFAULT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY (`id`) -) COMMENT ='用户岗位表'; - -CREATE TABLE IF NOT EXISTS "system_notice" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "title" varchar(50) NOT NULL COMMENT '公告标题', - "content" text NOT NULL COMMENT '公告内容', - "type" tinyint NOT NULL COMMENT '公告类型(1通知 2公告)', - "status" tinyint NOT NULL DEFAULT '0' COMMENT '公告状态(0正常 1关闭)', - "creator" varchar(64) DEFAULT '' COMMENT '创建者', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - "updater" varchar(64) DEFAULT '' COMMENT '更新者', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - "deleted" bit NOT NULL DEFAULT 0 COMMENT '是否删除', - "tenant_id" bigint not null default '0', - PRIMARY KEY("id") -) COMMENT '通知公告表'; - -CREATE TABLE IF NOT EXISTS `system_login_log` ( - `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, - `log_type` bigint(4) NOT NULL, - "user_id" bigint not null default '0', - "user_type" tinyint NOT NULL, - `trace_id` varchar(64) NOT NULL DEFAULT '', - `username` varchar(50) NOT NULL DEFAULT '', - `result` tinyint(4) NOT NULL, - `user_ip` varchar(50) NOT NULL, - `user_agent` varchar(512) NOT NULL, - `creator` varchar(64) DEFAULT '', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updater` varchar(64) DEFAULT '', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` bit(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) COMMENT ='系统访问记录'; - -CREATE TABLE IF NOT EXISTS `system_operate_log` ( - `id` bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY, - `trace_id` varchar(64) NOT NULL DEFAULT '', - `user_id` bigint(20) NOT NULL, - "user_type" tinyint not null default '0', - `type` varchar(50) NOT NULL, - `sub_type` varchar(50) NOT NULL, - `biz_id` bigint(20) NOT NULL, - `action` varchar(2000) NOT NULL DEFAULT '', - `extra` varchar(512) NOT NULL DEFAULT '', - `request_method` varchar(16) DEFAULT '', - `request_url` varchar(255) DEFAULT '', - `user_ip` varchar(50) DEFAULT NULL, - `user_agent` varchar(200) DEFAULT NULL, - `creator` varchar(64) DEFAULT '', - `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - `updater` varchar(64) DEFAULT '', - `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `deleted` bit(1) NOT NULL DEFAULT '0', - "tenant_id" bigint not null default '0', - PRIMARY KEY (`id`) -) COMMENT ='操作日志记录'; - -CREATE TABLE IF NOT EXISTS "system_users" ( - "id" bigint not null GENERATED BY DEFAULT AS IDENTITY, - "username" varchar(30) not null, - "password" varchar(100) not null default '', - "nickname" varchar(30) not null, - "remark" varchar(500) default null, - "dept_id" bigint default null, - "post_ids" varchar(255) default null, - "email" varchar(50) default '', - "mobile" varchar(11) default '', - "sex" tinyint default '0', - "avatar" varchar(100) default '', - "status" tinyint not null default '0', - "login_ip" varchar(50) default '', - "login_date" timestamp default null, - "creator" varchar(64) default '', - "create_time" timestamp not null default current_timestamp, - "updater" varchar(64) default '', - "update_time" timestamp not null default current_timestamp, - "deleted" bit not null default false, - "tenant_id" bigint not null default '0', - primary key ("id") -) comment '用户信息表'; - -CREATE TABLE IF NOT EXISTS "system_sms_channel" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "signature" varchar(10) NOT NULL, - "code" varchar(63) NOT NULL, - "status" tinyint NOT NULL, - "remark" varchar(255) DEFAULT NULL, - "api_key" varchar(63) NOT NULL, - "api_secret" varchar(63) DEFAULT NULL, - "callback_url" varchar(255) DEFAULT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '短信渠道'; - -CREATE TABLE IF NOT EXISTS "system_sms_template" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "type" tinyint NOT NULL, - "status" tinyint NOT NULL, - "code" varchar(63) NOT NULL, - "name" varchar(63) NOT NULL, - "content" varchar(255) NOT NULL, - "params" varchar(255) NOT NULL, - "remark" varchar(255) DEFAULT NULL, - "api_template_id" varchar(63) NOT NULL, - "channel_id" bigint NOT NULL, - "channel_code" varchar(63) NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '短信模板'; - -CREATE TABLE IF NOT EXISTS "system_sms_log" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "channel_id" bigint NOT NULL, - "channel_code" varchar(63) NOT NULL, - "template_id" bigint NOT NULL, - "template_code" varchar(63) NOT NULL, - "template_type" tinyint NOT NULL, - "template_content" varchar(255) NOT NULL, - "template_params" varchar(255) NOT NULL, - "api_template_id" varchar(63) NOT NULL, - "mobile" varchar(11) NOT NULL, - "user_id" bigint DEFAULT '0', - "user_type" tinyint DEFAULT '0', - "send_status" tinyint NOT NULL DEFAULT '0', - "send_time" timestamp DEFAULT NULL, - "send_code" int DEFAULT NULL, - "send_msg" varchar(255) DEFAULT NULL, - "api_send_code" varchar(63) DEFAULT NULL, - "api_send_msg" varchar(255) DEFAULT NULL, - "api_request_id" varchar(255) DEFAULT NULL, - "api_serial_no" varchar(255) DEFAULT NULL, - "receive_status" tinyint NOT NULL DEFAULT '0', - "receive_time" timestamp DEFAULT NULL, - "api_receive_code" varchar(63) DEFAULT NULL, - "api_receive_msg" varchar(255) DEFAULT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '短信日志'; - -CREATE TABLE IF NOT EXISTS "system_sms_code" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "mobile" varchar(11) NOT NULL, - "code" varchar(11) NOT NULL, - "scene" bigint NOT NULL, - "create_ip" varchar NOT NULL, - "today_index" int NOT NULL, - "used" bit NOT NULL DEFAULT FALSE, - "used_time" timestamp DEFAULT NULL, - "used_ip" varchar NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '短信日志'; - -CREATE TABLE IF NOT EXISTS "system_social_client" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(255) NOT NULL, - "social_type" int NOT NULL, - "user_type" int NOT NULL, - "client_id" varchar(255) NOT NULL, - "client_secret" varchar(255) NOT NULL, - "agent_id" varchar(255) NOT NULL, - "status" int NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY ("id") -) COMMENT '社交客户端表'; - -CREATE TABLE IF NOT EXISTS "system_social_user" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "type" tinyint NOT NULL, - "openid" varchar(64) NOT NULL, - "token" varchar(256) DEFAULT NULL, - "raw_token_info" varchar(1024) NOT NULL, - "nickname" varchar(32) NOT NULL, - "avatar" varchar(255) DEFAULT NULL, - "raw_user_info" varchar(1024) NOT NULL, - "code" varchar(64) NOT NULL, - "state" varchar(64), - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '社交用户'; - -CREATE TABLE IF NOT EXISTS "system_social_user_bind" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "user_id" bigint NOT NULL, - "user_type" tinyint NOT NULL, - "social_type" tinyint NOT NULL, - "social_user_id" number NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '社交用户的绑定'; - -CREATE TABLE IF NOT EXISTS "system_tenant" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(63) NOT NULL, - "contact_user_id" bigint NOT NULL DEFAULT '0', - "contact_name" varchar(255) NOT NULL, - "contact_mobile" varchar(255), - "status" tinyint NOT NULL, - "website" varchar(63) DEFAULT '', - "package_id" bigint NOT NULL, - "expire_time" timestamp NOT NULL, - "account_count" int NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '租户'; - -CREATE TABLE IF NOT EXISTS "system_tenant_package" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(30) NOT NULL, - "status" tinyint NOT NULL, - "remark" varchar(256), - "menu_ids" varchar(2048) NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '租户套餐表'; - -CREATE TABLE IF NOT EXISTS "system_oauth2_client" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "client_id" varchar NOT NULL, - "secret" varchar NOT NULL, - "name" varchar NOT NULL, - "logo" varchar NOT NULL, - "description" varchar, - "status" int NOT NULL, - "access_token_validity_seconds" int NOT NULL, - "refresh_token_validity_seconds" int NOT NULL, - "redirect_uris" varchar NOT NULL, - "authorized_grant_types" varchar NOT NULL, - "scopes" varchar NOT NULL DEFAULT '', - "auto_approve_scopes" varchar NOT NULL DEFAULT '', - "authorities" varchar NOT NULL DEFAULT '', - "resource_ids" varchar NOT NULL DEFAULT '', - "additional_information" varchar NOT NULL DEFAULT '', - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT 'OAuth2 客户端表'; - -CREATE TABLE IF NOT EXISTS "system_oauth2_approve" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "user_id" bigint NOT NULL, - "user_type" tinyint NOT NULL, - "client_id" varchar NOT NULL, - "scope" varchar NOT NULL, - "approved" bit NOT NULL DEFAULT FALSE, - "expires_time" datetime NOT NULL, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT 'OAuth2 批准表'; - -CREATE TABLE IF NOT EXISTS "system_oauth2_access_token" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "user_id" bigint NOT NULL, - "user_type" tinyint NOT NULL, - "user_info" varchar NOT NULL, - "access_token" varchar NOT NULL, - "refresh_token" varchar NOT NULL, - "client_id" varchar NOT NULL, - "scopes" varchar NOT NULL, - "approved" bit NOT NULL DEFAULT FALSE, - "expires_time" datetime NOT NULL, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null, - PRIMARY KEY ("id") -) COMMENT 'OAuth2 访问令牌'; - -CREATE TABLE IF NOT EXISTS "system_oauth2_refresh_token" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "user_id" bigint NOT NULL, - "user_type" tinyint NOT NULL, - "refresh_token" varchar NOT NULL, - "client_id" varchar NOT NULL, - "scopes" varchar NOT NULL, - "approved" bit NOT NULL DEFAULT FALSE, - "expires_time" datetime NOT NULL, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY ("id") -) COMMENT 'OAuth2 刷新令牌'; - -CREATE TABLE IF NOT EXISTS "system_oauth2_code" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "user_id" bigint NOT NULL, - "user_type" tinyint NOT NULL, - "code" varchar NOT NULL, - "client_id" varchar NOT NULL, - "scopes" varchar NOT NULL, - "expires_time" datetime NOT NULL, - "redirect_uri" varchar NOT NULL, - "state" varchar NOT NULL, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT 'OAuth2 刷新令牌'; - -CREATE TABLE IF NOT EXISTS "system_mail_account" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "mail" varchar NOT NULL, - "username" varchar NOT NULL, - "password" varchar NOT NULL, - "host" varchar NOT NULL, - "port" int NOT NULL, - "ssl_enable" bit NOT NULL, - "starttls_enable" bit NOT NULL, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '邮箱账号表'; - -CREATE TABLE IF NOT EXISTS "system_mail_template" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "code" varchar NOT NULL, - "account_id" bigint NOT NULL, - "nickname" varchar, - "title" varchar NOT NULL, - "content" varchar NOT NULL, - "params" varchar NOT NULL, - "status" varchar NOT NULL, - "remark" varchar, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '邮件模版表'; - -CREATE TABLE IF NOT EXISTS "system_mail_log" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "user_id" bigint, - "user_type" varchar, - "to_mail" varchar NOT NULL, - "account_id" bigint NOT NULL, - "from_mail" varchar NOT NULL, - "template_id" bigint NOT NULL, - "template_code" varchar NOT NULL, - "template_nickname" varchar, - "template_title" varchar NOT NULL, - "template_content" varchar NOT NULL, - "template_params" varchar NOT NULL, - "send_status" varchar NOT NULL, - "send_time" datetime, - "send_message_id" varchar, - "send_exception" varchar, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '邮件日志表'; - --- 将该建表 SQL 语句,添加到 yudao-module-system-biz 模块的 test/resources/sql/create_tables.sql 文件里 -CREATE TABLE IF NOT EXISTS "system_notify_template" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar NOT NULL, - "code" varchar NOT NULL, - "nickname" varchar NOT NULL, - "content" varchar NOT NULL, - "type" varchar NOT NULL, - "params" varchar, - "status" varchar NOT NULL, - "remark" varchar, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '站内信模板表'; - -CREATE TABLE IF NOT EXISTS "system_notify_message" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "user_id" bigint NOT NULL, - "user_type" varchar NOT NULL, - "template_id" bigint NOT NULL, - "template_code" varchar NOT NULL, - "template_nickname" varchar NOT NULL, - "template_content" varchar NOT NULL, - "template_type" int NOT NULL, - "template_params" varchar NOT NULL, - "read_status" bit NOT NULL, - "read_time" varchar, - "creator" varchar DEFAULT '', - "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar DEFAULT '', - "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - "tenant_id" bigint not null default '0', - PRIMARY KEY ("id") -) COMMENT '站内信消息表'; diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index efd53c8..c6132f1 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -23,12 +23,12 @@ cn.iocoder.boot - yudao-module-system-biz + yudao-module-system ${revision} cn.iocoder.boot - yudao-module-infra-biz + yudao-module-infra ${revision}